Add new trolls, fix inface targetting, fix a lot

This commit is contained in:
Jackz 2023-09-29 18:00:26 -05:00
parent 6e323b26d2
commit 16e676d56d
No known key found for this signature in database
GPG key ID: E0BBD94CF657F603
11 changed files with 415 additions and 199 deletions

View file

@ -4,7 +4,7 @@
//Allow MAX_TROLLS to be defined elsewhere
#if defined MAX_TROLLS
#else
#define MAX_TROLLS 51
#define MAX_TROLLS 54
#endif
enum trollModifier {
@ -26,6 +26,13 @@ enum trollFlag {
Flag_8 = 1 << 7,
}
enum valueType {
Value_None,
Value_Float,
Value_String,
Value_Integer
}
StringMap trollKV;
char trollIds[MAX_TROLLS+1][MAX_TROLL_NAME_LENGTH];
char DEFAULT_FLAG_PROMPT_MULTIPLE[] = "Enable options (Multiple)";
@ -57,6 +64,9 @@ enum struct TrollFlagPrompt {
}
}
// Very hacky but map a power (say 16, 32) to the index in a list
// Used to get the name of a flag (flag #16 for example) in ArrayList<string> of flag names
int GetIndexFromPower(int powerOfTwo) {
for(int i = 0; i < 16; i++) {
if(1 << i == powerOfTwo) {
@ -67,28 +77,30 @@ int GetIndexFromPower(int powerOfTwo) {
}
enum struct Troll {
int id;
int categoryID;
int id; // The id or the index into the global Trolls[] array
int categoryID; // The category this troll belongs in
char name[MAX_TROLL_NAME_LENGTH];
char description[128];
bool hidden;
int mods;
int mods; // Combination of valid modifiers. Only two are ever supported
// Flags
int activeFlagClients[MAXPLAYERS+1];
char flagPrompt[MAX_TROLL_FLAG_LENGTH];
ArrayList flagNames;
ArrayList flagPrompts;
// Custom timer
Timer timerFunction;
Handle timerHandles[MAXPLAYERS+1];
float timerInterval;
int timerRequiredFlags;
void SetTimer(float interval, Timer timer) {
void SetTimer(float interval, Timer timer, int requiredFlags = 0) {
this.timerInterval = interval;
this.timerFunction = timer;
this.timerRequiredFlags = requiredFlags;
for(int i = 0; i <= MAXPLAYERS; i++) {
this.timerHandles[i] = null;
}
@ -198,14 +210,13 @@ enum struct Troll {
return false;
}
int GetFlagCount() {
return this.flagNames != null ? this.flagNames.Length : 0;
}
int GetClientFlags(int client) {
return this.activeFlagClients[client];
}
void SetFlagPrompt(const char[] prompt) {
strcopy(this.flagPrompt, MAX_TROLL_FLAG_LENGTH, prompt);
}
void GetFlagPrompt(int index, TrollFlagPrompt prompt) {
this.flagPrompts.GetArray(index, prompt);
}
@ -357,15 +368,21 @@ void ApplyTroll(int victim, const char[] name, int activator, trollModifier modi
return;
}
bool isActive = Trolls[trollIndex].activeFlagClients[victim] > -1;
// Clear troll specific timer:
if(Trolls[trollIndex].timerInterval > 0.0) {
if(flags > -1)
Trolls[trollIndex].timerHandles[victim] = CreateTimer(Trolls[trollIndex].timerInterval, Trolls[trollIndex].timerFunction, victim, TIMER_REPEAT);
else if(Trolls[trollIndex].timerHandles[victim] != null) {
PrintToServer("epi: there is timer for \"%s\", flags=%d", name, flags);
if(!isActive) {
if(Trolls[trollIndex].timerRequiredFlags == 0 || Trolls[trollIndex].timerRequiredFlags & flags) {
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];
}
}
if(!silent && SilentMenuSelected[activator]) silent = true;
static int MetaInverseTrollID;
@ -393,7 +410,6 @@ void ApplyTroll(int victim, const char[] name, int activator, trollModifier modi
}
}
bool isActive = Trolls[trollIndex].activeFlagClients[victim] > -1;
// Toggle on flags for client, if it's not a single run.
if(modifier & TrollMod_Constant) {

View file

@ -86,5 +86,10 @@ void SetupsTrollCombos() {
combo.AddTroll("Dull Melee", .flags=2);
combo.AddTroll("Temp Health Quick Drain");
SetupCombo(combo, "Blindness");
combo.AddTroll("Vomit Player", 0, TrollMod_Constant);
combo.AddTroll("Shakey Camera", .flags=16);
combo.AddTroll("Randomize Angles", .flags=16);
PrintToServer("[FTT] Loaded %d troll combos", combos.Length);
}

View file

@ -1,4 +1,4 @@
public Action Command_InstaSpecial(int client, int args) {
Action Command_InstaSpecial(int client, int args) {
if(args < 1) {
Menu menu = new Menu(Insta_PlayerHandler);
menu.SetTitle("InstaSpecial: Choose a player");
@ -46,7 +46,7 @@ public Action Command_InstaSpecial(int client, int args) {
for (int i = 0; i < target_count; i++) {
int target = target_list[i];
if(GetClientTeam(target) == 2) {
if(SpawnSpecialForTarget(specialType, target)) {
if(SpawnSpecialForTarget(specialType, target, view_as<int>(Special_AlwaysTarget))) {
LogAction(client, target, "\"%L\" spawned Insta-%s™ nearby \"%L\"", client, arg2, target);
successes++;
} else {
@ -64,7 +64,7 @@ public Action Command_InstaSpecial(int client, int args) {
return Plugin_Handled;
}
public Action Command_InstaSpecialFace(int client, int args) {
Action Command_InstaSpecialFace(int client, int args) {
if(args < 1) {
Menu menu = new Menu(Insta_PlayerHandler);
menu.SetTitle("Inface: Choose a player");
@ -112,7 +112,7 @@ public Action Command_InstaSpecialFace(int client, int args) {
for (int i = 0; i < target_count; i++) {
int target = target_list[i];
if(GetClientTeam(target) == 2) {
if(SpawnSpecialForTarget(specialType, target, view_as<int>(Special_OnTarget))) {
if(SpawnSpecialForTarget(specialType, target, view_as<int>(Special_OnTarget) | view_as<int>(Special_AlwaysTarget))) {
LogAction(client, target, "\"%L\" spawned Insta-%s™ at player \"%L\"", client, arg2, target);
successes++;
} else {
@ -129,7 +129,7 @@ public Action Command_InstaSpecialFace(int client, int args) {
}
public Action Command_WitchAttack(int client, int args) {
Action Command_WitchAttack(int client, int args) {
if(args < 1) {
ReplyToCommand(client, "Usage: sm_witch_attack <user> [# of witches or 0 for all]");
}else{
@ -245,7 +245,7 @@ public Action Command_ApplyUser(int client, int args) {
target_name,
sizeof(target_name),
tn_is_ml)) <= 0
&& target_list[0] > 0) {
&& target_list[0] == 0) {
/* This function replies to the admin with a failure message */
ReplyToTargetError(client, target_count);
return Plugin_Handled;
@ -624,7 +624,7 @@ public Action Command_SmartCharge(int client, int args) {
return Plugin_Handled;
}
public Action Command_HealTarget(int client, int args) {
Action Command_HealTarget(int client, int args) {
if(args > 0) {
char arg1[32], arg2[4];
GetCmdArg(1, arg1, sizeof(arg1));
@ -688,4 +688,61 @@ public Action Command_HealTarget(int client, int args) {
ReplyToCommand(client, "Usage: /healbots <player> [# of bots or 0 for all]");
}
return Plugin_Handled;
}
Action Command_SetReverseFF(int client, int args) {
if(args < 2) {
ReplyToCommand(client, "Usage: sm_rff <target> <amount> [fire/explosion]*");
return Plugin_Handled;
}
char arg[32];
GetCmdArg(1, arg, sizeof(arg));
int target = GetSinglePlayer(client, arg, COMMAND_FILTER_NO_BOTS);
if(target <= 0) {
return Plugin_Handled;
}
GetCmdArg(1, arg, sizeof(arg));
GetCmdArg(2, arg, sizeof(arg));
int flag = -1;
if(StrEqual(arg, "0") || StrEqual(arg, "0.0")) {
flag = 8;
} else if(StrEqual(arg, "2")) {
flag = 2;
} else if(StrEqual(arg, "0.5") || StrEqual(arg, ".5")) {
flag = 4;
} else if(StrEqual(arg, "3")) {
flag = 8;
} else if(StrEqual(arg, "1")) {
flag = 1;
} else {
ReplyToCommand(client, "Unsupported amount. Possible values: 0, 2, 0.5, 3, 1");
return Plugin_Handled;
}
// args are 1-indexed so <=
for(int i = 3; i <= args; i++) {
GetCmdArg(i, arg, sizeof(arg));
if(arg[0] == 'f') {
flag |= 32;
} else if(arg[0] == 'e') {
flag |= 64;
} else {
ReplyToCommand(client, "Unknown arg: %s", arg);
}
}
ApplyTroll(target, "Reverse FF", client, TrollMod_Constant, flag);
return Plugin_Handled;
}
Action Command_SetMagnetShortcut(int client, int args) {
if(args < 1) {
ReplyToCommand(client, "Usage: sm_magnet <target>");
return Plugin_Handled;
}
char arg[32];
GetCmdArg(1, arg, sizeof(arg));
int target = GetSinglePlayer(client, arg, COMMAND_FILTER_NO_BOTS);
if(target <= 0) {
return Plugin_Handled;
}
ShowTrollsForCategory(client, GetClientUserId(target), 0);
return Plugin_Handled;
}

View file

@ -35,9 +35,10 @@ public void OnClientPutInServer(int client) {
}
public void Event_PlayerSpawn(Event event, const char[] name, bool dontBroadcast) {
int userid = event.GetInt("userid");
if(spIsActive)
if(spIsActive || g_iPendingSurvivorAdd) {
int userid = event.GetInt("userid");
CreateTimer(0.1, Timer_CheckSpecial, userid);
}
}
@ -134,7 +135,7 @@ public void Event_DoorToggle(Event event, const char[] name, bool dontBroadcast)
}
}
public void Event_SecondaryHealthUsed(Event event, const char[] name, bool dontBroadcast) {
void Event_SecondaryHealthUsed(Event event, const char[] name, bool dontBroadcast) {
int client = GetClientOfUserId(event.GetInt("userid"));
if(client && Trolls[slipperyShoesIndex].IsActive(client) && Trolls[slipperyShoesIndex].activeFlagClients[client] & 8) {
L4D_StaggerPlayer(client, client, NULL_VECTOR);
@ -142,29 +143,32 @@ public void Event_SecondaryHealthUsed(Event event, const char[] name, bool dontB
}
static float SPIT_VEL[3] = { 0.0, 0.0, -1.0 };
public Action Timer_CheckSpecial(Handle h, int specialID) {
Action Timer_CheckSpecial(Handle h, int specialID) {
int special = GetClientOfUserId(specialID);
if(special == 0) return Plugin_Continue;
// Check if new player is the spawned special:
if(spIsActive && special > 0 && IsFakeClient(special)) {
if(g_iPendingSurvivorAdd && GetClientTeam(special) == 2 && GetEntProp(special, Prop_Send, "m_humanSpectatorUserID") == 0) {
g_iPendingSurvivorAdd = false;
isCustomSurvivor[special] = true;
PrintToServer("EPI Debug: Custom Survivor %N", special);
} else if(spIsActive && IsFakeClient(special)) {
//g_iPendingSurvivorAdd
if(GetClientTeam(special) == 2 && g_iPendingSurvivorAdd && GetEntProp(special, Prop_Send, "m_humanSpectatorUserID") == 0) {
g_iPendingSurvivorAdd = false;
isCustomSurvivor[special] = true;
} else if(GetClientTeam(special) == 3) {
if(GetClientTeam(special) == 3) {
SpecialType type = view_as<SpecialType>(GetEntProp(special, Prop_Send, "m_zombieClass"));
if(type == spActiveRequest.type) {
// Ignore any fake clients with 'Bot' in name
static char buf[32];
GetClientName(special, buf, sizeof(buf));
if(StrContains(buf, "bot", false) == -1) {
if(spActiveRequest.targetUserId) {
pdata[special].attackerTargetUid = spActiveRequest.targetUserId;
pdata[special].specialAttackFlags = spActiveRequest.flags;
}
// Set the special's target and flags
pdata[special].attackerTargetUid = spActiveRequest.targetUserId;
pdata[special].specialAttackFlags = spActiveRequest.flags;
// Warp to the spawn location
TeleportEntity(special, spActiveRequest.position, spActiveRequest.angle, NULL_VECTOR);
// Kill on spawn if enabled
if(spActiveRequest.flags & view_as<int>(SPI_KillOnSpawn)) {
if(type == Special_Spitter) {
// Bug fix, spitter drops small puddle, so we spawn our own
float pos[3];
GetClientEyePosition(special, pos);
L4D2_SpitterPrj(special, pos, SPIT_VEL);
@ -172,8 +176,8 @@ public Action Timer_CheckSpecial(Handle h, int specialID) {
RequestFrame(Frame_Boom, special);
}
// Special spawned, run the next in the queue:
g_iSpId++;
ProcessSpecialQueue();
}
}
@ -194,15 +198,16 @@ public void Event_PlayerDeath(Event event, const char[] name, bool dontBroadcast
int userid = event.GetInt("userid");
int client = GetClientOfUserId(userid);
if(client > 0) {
pdata[client].specialAttackFlags = 0;
if(pdata[client].attackerTargetUid > 0) {
// If special died, clear & subtract one from counter
pdata[client].attackerTargetUid = 0;
pdata[client].specialAttackFlags = 0;
} else {
// If player died, stop the targetting
for(int i = 1; i <= MaxClients; i++) {
if(pdata[i].attackerTargetUid == userid) {
pdata[i].attackerTargetUid = 0;
pdata[i].specialAttackFlags = 0;
break;
}
}
@ -310,7 +315,6 @@ public Action L4D2_OnChooseVictim(int attacker, int &curTarget) {
if(spMagnetID == 0) spMagnetID = GetTrollID("Special Magnet");
if(tankMagnetID == 0) tankMagnetID = GetTrollID("Tank Magnet");
if(hMagnetChance.FloatValue < GetRandomFloat()) return Plugin_Continue;
L4D2Infected class = view_as<L4D2Infected>(GetEntProp(attacker, Prop_Send, "m_zombieClass"));
// Check for any existing victims
int existingTarget = GetClientOfUserId(pdata[attacker].attackerTargetUid);
@ -337,6 +341,10 @@ public Action L4D2_OnChooseVictim(int attacker, int &curTarget) {
return Plugin_Changed;
}
} else {
if(pdata[attacker].specialAttackFlags & view_as<int>(SPI_KillOnTargetIncap)) {
ForcePlayerSuicide(attacker);
}
PrintToServer("target (%N) not alive, resetting target for %d", existingTarget, attacker)
pdata[attacker].attackerTargetUid = 0;
}
}
@ -772,6 +780,9 @@ public Action Event_TakeDamage(int victim, int& attacker, int& inflictor, float&
if(damage > 0.0 && victim != attacker && Trolls[slipperyShoesIndex].IsActive(victim) && Trolls[slipperyShoesIndex].activeFlagClients[victim] & 16) {
L4D_StaggerPlayer(victim, victim, NULL_VECTOR);
}
if(Trolls[t_slotRouletteIndex].IsActive(victim)) {
SetSlot(victim, -1);
}
if(IsTrollActive(victim, "Damage Boost")) {
damage * 2;
@ -832,6 +843,9 @@ public Action OnVocalizeCommand(int client, const char[] vocalize, int initiator
SetEntPropFloat(client, Prop_Send, "m_flLaggedMovementValue", noRushingUsSpeed[client]);
PrintToConsoleAdmins("[FTT] NoRushingUs: Dropping speed for %N (now %.1f%)", client, noRushingUsSpeed[client] * 100.0);
}
if(Trolls[t_slotRouletteIndex].IsActive(client)) {
SetSlot(client, -1);
}
if(Trolls[vocalGagID].IsActive(client)) {
return Plugin_Handled;
}
@ -851,17 +865,17 @@ public void OnSceneStageChanged(int scene, SceneStages stage) {
static char sceneFile[32];
GetSceneFile(scene, sceneFile, sizeof(sceneFile));
if(StrContains(sceneFile, "warnboomer") > -1) {
SpawnSpecialForTarget(Special_Boomer, activator, view_as<int>(Special_OnTarget));
SpawnSpecialForTarget(Special_Boomer, activator, view_as<int>(Special_OnTarget) | view_as<int>(Special_AlwaysTarget));
} else if(StrContains(sceneFile, "warnhunter") > -1) {
SpawnSpecialForTarget(Special_Hunter, activator, view_as<int>(Special_OnTarget));
SpawnSpecialForTarget(Special_Hunter, activator, view_as<int>(Special_OnTarget) | view_as<int>(Special_AlwaysTarget));
} else if(StrContains(sceneFile, "warnsmoker") > -1) {
SpawnSpecialForTarget(Special_Smoker, activator, view_as<int>(Special_OnTarget));
SpawnSpecialForTarget(Special_Smoker, activator, view_as<int>(Special_OnTarget) | view_as<int>(Special_AlwaysTarget));
} else if(StrContains(sceneFile, "warnspitter") > -1) {
SpawnSpecialForTarget(Special_Spitter, activator, view_as<int>(Special_OnTarget));
SpawnSpecialForTarget(Special_Spitter, activator, view_as<int>(Special_OnTarget) | view_as<int>(Special_AlwaysTarget));
} else if(StrContains(sceneFile, "warnjockey") > -1) {
SpawnSpecialForTarget(Special_Jockey, activator, view_as<int>(Special_OnTarget));
SpawnSpecialForTarget(Special_Jockey, activator, view_as<int>(Special_OnTarget) | view_as<int>(Special_AlwaysTarget));
} else if(StrContains(sceneFile, "warncharger") > -1) {
SpawnSpecialForTarget(Special_Charger, activator, view_as<int>(Special_OnTarget));
SpawnSpecialForTarget(Special_Charger, activator, view_as<int>(Special_OnTarget) | view_as<int>(Special_AlwaysTarget));
}
if(Trolls[vocalizeSpecials].activeFlagClients[activator] & 1) {
@ -889,7 +903,7 @@ public Action SoundHook(int clients[MAXPLAYERS], int& numClients, char sample[PL
lastCrescendoUser = lastButtonUser;
if(IsPlayerFarDistance(lastButtonUser, AUTOPUNISH_FLOW_MIN_DISTANCE)) {
NotifyAllAdmins("Autopunishing player %N for activation of event far from team", lastButtonUser);
PrintChatToAdmins("Autopunishing player %N for activation of event far from team", lastButtonUser);
ShowActivityEx(0, "[FTT] ", "activated autopunish for crescendo activator %N (auto)", lastButtonUser);
LogAction(0, lastButtonUser, "\"%L\" automatic autopunish for crescendo activator \"%L\"", 0, lastButtonUser);
ActivateAutoPunish(lastButtonUser);
@ -993,7 +1007,7 @@ public Action OnAntiRush(int client, int &type, float distance) {
}
SpecialType special = view_as<SpecialType>(GetRandomInt(1,6));
fLastAntiRushEvent[client] = GetGameTime();
SpawnSpecialForTarget(special, client, view_as<int>(Special_SpawnDirectOnFailure | Special_OnTarget));
SpawnSpecialForTarget(special, client, view_as<int>(Special_SpawnDirectOnFailure | Special_OnTarget | Special_AlwaysTarget));
PrintToConsoleAll("[FTT] Spawning anti-rush special on %N (dist=%f) (special=%s)", client, distance, SPECIAL_NAMES[view_as<int>(special)-1]);
}
}
@ -1018,7 +1032,7 @@ public void L4D2_CInsectSwarm_CanHarm_Post(int acid, int spitter, int entity) {
public void Event_EnteredSpit(Event event, const char[] name, bool dontBroadcast) {
int client = GetClientOfUserId(event.GetInt("userid"));
if(Trolls[stickyGooIndex].IsActive(stickyGooIndex)) {
if(Trolls[stickyGooIndex].IsActive(client)) {
int flags = Trolls[stickyGooIndex].activeFlagClients[client];
float movement = 0.0;
if(flags & 1) movement = 0.9;
@ -1059,3 +1073,18 @@ void Event_HealSuccess(Event event, const char[] name, bool dontBroadcast) {
StopHealingBots();
}
}
public void L4D_OnVomitedUpon_Post(int victim, int attacker, bool boomerExplosion) {
if(Trolls[t_slotRouletteIndex].IsActive(victim)) {
SetSlot(victim, -1);
}
}
void Event_Incapped(Event event, const char[] name, bool dontBroadcast) {
int client = GetClientOfUserId(event.GetInt("subject"));
int attacker = GetClientOfUserId(event.GetInt("attacker"));
if(client > 0 && attacker > 0 && attacker <= MaxClients && IsFakeClient(attacker) && GetClientTeam(attacker) == 3) {
if(pdata[attacker].specialAttackFlags & view_as<int>(SPI_KillOnTargetIncap)) {
ForcePlayerSuicide(attacker);
}
}
}

View file

@ -44,14 +44,14 @@ public int Insta_SpecialHandler(Menu menu, MenuAction action, int client, int pa
}
SpecialType special = view_as<SpecialType>(specialInt);
if(inFace) {
if(SpawnSpecialForTarget(special, target, view_as<int>(Special_OnTarget | Special_SpawnDirectOnFailure))) {
if(SpawnSpecialForTarget(special, target, view_as<int>(Special_OnTarget) | view_as<int>(Special_SpawnDirectOnFailure) | view_as<int>(Special_AlwaysTarget))) {
LogAction(client, target, "\"%L\" spawned Insta-%s™ on \"%L\"", client, SPECIAL_NAMES[specialInt-1], target);
CShowActivityEx(client, "[FTT] ", "spawned {olive}Insta-%s™{default} on {olive}%N", SPECIAL_NAMES[specialInt-1], target);
} else {
ReplyToCommand(client, "Could not spawn special.");
}
} else {
if(SpawnSpecialForTarget(special, target)) {
if(SpawnSpecialForTarget(special, target, view_as<int>(Special_AlwaysTarget))) {
CShowActivityEx(client, "[FTT] ", "spawned {green}Insta-%s™{default} near {green}%N", SPECIAL_NAMES[specialInt-1], target);
LogAction(client, target, "\"%L\" spawned Insta-%s™ near \"%L\"", client, SPECIAL_NAMES[specialInt-1], target);
} else {
@ -223,7 +223,7 @@ public int ChooseModeMenuHandler(Menu menu, MenuAction action, int param1, int p
public int ChooseClumsySlotHandler(Menu menu, MenuAction action, int param1, int param2) {
if (action == MenuAction_Select) {
static char info[8];
char info[32];
menu.GetItem(param2, info, sizeof(info));
static char str[2][8];
ExplodeString(info, "|", str, 2, 8, false);
@ -240,13 +240,14 @@ public int ChooseClumsySlotHandler(Menu menu, MenuAction action, int param1, int
} else if(slot == -2) {
ShowThrowItAllMenu(param1, userid);
} else {
if(!GetClientWeaponNameSmart(client, slot, info, sizeof(info))) {
strcopy(info, sizeof(info), "-unk-");
}
CShowActivityEx(param1, "[FTT] ", "activated troll {yellow}Throw It All{default} ({olive}%s|%d{default}) for \"%N\"", info, slot, client);
LogAction(param1, client, "\"%L\" activated troll \"Throw It All\" (%s) for \"%L\". ", param1, info, client);
ThrowItemToPlayer(client, param1, slot);
}
if(slot != -2) {
LogAction(param1, client, "\"%L\" activated troll \"Throw It all\" slot=%d for \"%L\"", param1, slot, client);
ShowActivityEx(param1, "[FTT] ", "activated troll \"Throw It All\" for %N. ", client);
}
ShowThrowItAllMenu(param1, userid);
} else if (action == MenuAction_End)
delete menu;
@ -404,18 +405,7 @@ void ShowTrollMenu(int client, bool isComboList) {
for(int i = 1; i <= MaxClients; i++) {
if(IsClientConnected(i) && IsClientInGame(i) && (hAllowEnemyTeam.BoolValue || GetClientTeam(i) == clientTeam)) {
IntToString(GetClientUserId(i), userid, sizeof(userid));
int realPlayer = L4D_GetIdlePlayerOfBot(i);
// Incase player is idle, grab their bot instead of them
if(realPlayer > 0 && IsClientConnected(realPlayer)) {
if(IsPlayerAlive(i))
Format(display, sizeof(display), "%N (AFK)", realPlayer);
else
Format(display, sizeof(display), "%N (AFK/Dead)", realPlayer);
} else if(!IsPlayerAlive(i))
Format(display, sizeof(display), "%N (Dead)", i);
else {
GetClientName(i, display, sizeof(display));
}
GetMenuDisplayName(i, display, sizeof(display));
menu.AddItem(userid, display);
}
}
@ -527,11 +517,10 @@ void ShowThrowItAllMenu(int client, int userid) {
// Grab all the items the player has, add to menu
for(int slot = 0; slot <= 5; slot++) {
int item = GetPlayerWeaponSlot(victim, slot);
int item = GetClientWeaponNameSmart2(victim, slot, itmName, sizeof(itmName));
if(item > -1) {
GetEdictClassname(item, itmName, sizeof(itmName));
Format(info, sizeof(info), "%d|%d", userid, slot);
itmMenu.AddItem(info, itmName[7]);
itmMenu.AddItem(info, itmName);
}
}

View file

@ -174,20 +174,20 @@ ArrayList GetPhrasesArray(const char[] key) {
}
stock int FindClosestClientAdminPriority(int source, bool ignoreBots, float pos[3]) {
int c = FindClosestAdmin(source, ignoreBots, pos);
stock int FindClosestClientAdminPriority(int source, float pos[3]) {
int c = FindClosestAdmin(source, pos);
if(c == -1) return FindClosestClient(source, ignoreBots, pos);
else return c;
}
int FindClosestClient(int source, bool ignoreBots, float pos[3]) {
stock int FindClosestClient(int source, bool ignoreBots, float pos[3]) {
int closest = -1;
float minDist = -1.0;
static float pos1[3];
static float pos2[3];
GetClientAbsOrigin(source, pos1);
for(int i = 1; i <= MaxClients; i++) {
if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2 && IsPlayerAlive(i) && (!ignoreBots || !IsFakeClient(i)) && i != source) {
if(i != source && IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2 && IsPlayerAlive(i) && (!ignoreBots || !IsFakeClient(i)) ) {
GetClientAbsOrigin(i, pos2);
float dist = GetVectorDistance(pos1, pos2);
if(minDist == -1.0 || dist <= minDist) {
@ -200,19 +200,16 @@ int FindClosestClient(int source, bool ignoreBots, float pos[3]) {
return closest;
}
int FindClosestAdmin(int source, bool ignoreBots, float pos[3]) {
stock int FindClosestAdmin(int source, float pos[3]) {
int closest = -1;
float minDist = -1.0;
static float pos1[3];
static float pos2[3];
GetClientAbsOrigin(source, pos);
for(int i = 1; i <= MaxClients; i++) {
if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2 && IsPlayerAlive(i) &&
(ignoreBots || !IsFakeClient(i))
&& GetUserAdmin(i) != INVALID_ADMIN_ID && i != source
) {
if(i != source && IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2 && IsPlayerAlive(i) && !IsFakeClient(i) && GetUserAdmin(i) != INVALID_ADMIN_ID) {
GetClientAbsOrigin(i, pos2);
float dist = GetVectorDistance(pos1, pos2);
float dist = GetVectorDistance(pos1, pos2, true);
if(minDist == -1.0 || dist <= minDist) {
closest = i;
minDist = dist;
@ -223,9 +220,9 @@ int FindClosestAdmin(int source, bool ignoreBots, float pos[3]) {
return closest;
}
int SpawnItem(const char[] entityName, float pos[3], float ang[3] = NULL_VECTOR) {
int SpawnItem(const char[] itemName, float pos[3], float ang[3] = NULL_VECTOR) {
static char classname[32];
Format(classname, sizeof(classname), "weapon_%s", entityName);
Format(classname, sizeof(classname), "weapon_%s", itemName);
int spawner = CreateEntityByName(classname);
if(spawner == -1) return -1;
DispatchKeyValue(spawner, "solid", "6");
@ -262,16 +259,14 @@ void ThrowItemToPlayer(int victim, int target, int slot) {
}
stock void AddInFrontOf(float fVecOrigin[3], float fVecAngle[3], float fUnits, float fOutPut[3])
{
stock void AddInFrontOf(float fVecOrigin[3], float fVecAngle[3], float fUnits, float fOutPut[3]) {
float fVecView[3]; GetViewVector(fVecAngle, fVecView);
fOutPut[0] = fVecView[0] * fUnits + fVecOrigin[0];
fOutPut[1] = fVecView[1] * fUnits + fVecOrigin[1];
fOutPut[2] = fVecView[2] * fUnits + fVecOrigin[2];
}
stock void GetViewVector(float fVecAngle[3], float fOutPut[3])
{
stock void GetViewVector(float fVecAngle[3], float fOutPut[3]) {
fOutPut[0] = Cosine(fVecAngle[1] / (180 / FLOAT_PI));
fOutPut[1] = Sine(fVecAngle[1] / (180 / FLOAT_PI));
fOutPut[2] = -Sine(fVecAngle[0] / (180 / FLOAT_PI));
@ -297,60 +292,23 @@ stock void LookAtClient(int iClient, int iTarget) {
stock int GetClientRealHealth(int client) {
//First filter -> Must be a valid client, successfully in-game and not an spectator (The dont have health).
if(!client
|| !IsValidEntity(client)
|| !IsClientInGame(client)
|| !IsPlayerAlive(client)
|| IsClientObserver(client))
{
if(!client || !IsValidEntity(client) || !IsClientInGame(client) || !IsPlayerAlive(client) || IsClientObserver(client)) {
return -1;
}
//If the client is not on the survivors team, then just return the normal client health.
if(GetClientTeam(client) != 2)
{
} else if(GetClientTeam(client) != 2) {
return GetClientHealth(client);
}
//First, we get the amount of temporal health the client has
float buffer = GetEntPropFloat(client, Prop_Send, "m_healthBuffer");
//We declare the permanent and temporal health variables
float TempHealth;
int PermHealth = GetClientHealth(client);
//In case the buffer is 0 or less, we set the temporal health as 0, because the client has not used any pills or adrenaline yet
if(buffer <= 0.0)
{
TempHealth = 0.0;
}
//In case it is higher than 0, we proceed to calculate the temporl health
else
{
//This is the difference between the time we used the temporal item, and the current time
float tempHealth = 0.0;
if(buffer > 0.0) {
float difference = GetGameTime() - GetEntPropFloat(client, Prop_Send, "m_healthBufferTime");
//We get the decay rate from this convar (Note: Adrenaline uses this value)
float decay = GetConVarFloat(FindConVar("pain_pills_decay_rate"));
//This is a constant we create to determine the amount of health. This is the amount of time it has to pass
//before 1 Temporal HP is consumed.
float constant = 1.0/decay;
//Then we do the calcs
TempHealth = buffer - (difference / constant);
float decay = FindConVar("pain_pills_decay_rate").FloatValue;
float constant = 1.0 / decay;
tempHealth = buffer - (difference / constant);
if(tempHealth < 0.0) {
tempHealth = 0.0;
}
}
//If the temporal health resulted less than 0, then it is just 0.
if(TempHealth < 0.0)
{
TempHealth = 0.0;
}
//Return the value
return RoundToFloor(PermHealth + TempHealth);
return RoundToFloor(GetClientHealth(client) + tempHealth);
}
@ -497,10 +455,7 @@ void StopHealingBots(bool dontKill = false) {
KickClient(i);
}
}
if(!dontKill && IsValidHandle(stopHealingTimer)) {
delete stopHealingTimer;
}
stopHealingTimer = null;
delete stopHealingTimer;
if(hAbmAutoHard != null) hAbmAutoHard.IntValue = wasAbmAutoHard;
if(hSbFixEnabled != null) hSbFixEnabled.BoolValue = wasSbFixEnabled;
}
@ -580,3 +535,46 @@ stock void PrintToConsoleAdmins(const char[] format, any ...) {
}
PrintToServer("%s", buffer);
}
/**
* Shakes a client's screen with the specified amptitude,
* frequency & duration.
*
* @param client Client Index.
* @param amplitude Shake magnitude/amplitude.
* @param frequency Shake noise frequency.
* @param duration Shake lasts this long.
* @return True on success, false otherwise.
*/
stock bool ShakePlayer(int client, float amplitude=50.0, float frequency=150.0, float duration=3.0) {
if (amplitude <= 0.0) {
return false;
}
Handle userMessage = StartMessageOne("Shake", client);
if (userMessage == INVALID_HANDLE) {
return false;
}
if (GetFeatureStatus(FeatureType_Native, "GetUserMessageType") == FeatureStatus_Available
&& GetUserMessageType() == UM_Protobuf) {
PbSetInt(userMessage, "command", 0);
PbSetFloat(userMessage, "local_amplitude", amplitude);
PbSetFloat(userMessage, "frequency", frequency);
PbSetFloat(userMessage, "duration", duration);
} else {
BfWriteByte(userMessage, 0); // Shake Command
BfWriteFloat(userMessage, amplitude); // shake magnitude/amplitude
BfWriteFloat(userMessage, frequency); // shake noise frequency
BfWriteFloat(userMessage, duration); // shake lasts this long
}
EndMessage();
return true;
}
void SetSlot(int client, int slot) {
if(slot == -1)
slot = GetRandomInt(0, 4);
static char slotStr[8];
Format(slotStr, sizeof(slotStr), "slot%d", slot);
ClientCommand(client, slotStr);
}

View file

@ -26,7 +26,7 @@ ArrayList g_spSpawnQueue;
// target client index
stock bool SpawnSpecialForTarget(SpecialType specialType, int target, int spawnFlags = view_as<int>(Special_Anywhere)) {
static float pos[3];
int internalFlags = view_as<int>(SPI_AlwaysTarget);
if(spawnFlags & view_as<int>(Special_OnTarget)) {
static float ang[3];
static float testPos[3];
@ -35,9 +35,6 @@ stock bool SpawnSpecialForTarget(SpecialType specialType, int target, int spawnF
GetClientAbsOrigin(target, pos);
GetClientEyeAngles(target, ang);
if(specialType == Special_Boomer || specialType == Special_Spitter)
internalFlags |= view_as<int>(SPI_KillOnSpawn);
if(specialType == Special_Jockey || specialType == Special_Boomer || specialType == Special_Spitter) { // Else spawn a little bit off, and above (above for jockeys)
pos[2] += 25.0;
pos[0] += 5.0;
@ -45,31 +42,49 @@ stock bool SpawnSpecialForTarget(SpecialType specialType, int target, int spawnF
float minDistance = GetIdealMinDistance(specialType);
GetHorizontalPositionFromOrigin(pos, ang, minDistance, testPos);
if(!FindSuitablePosition(pos, testPos, minDistance, 100)) {
PrintToServer("[FTT] Could not find suitable position, falling back");
if(spawnFlags & view_as<int>(Special_SpawnDirectOnFailure))
GetClientAbsOrigin(target, pos);
else
L4D_GetRandomPZSpawnPosition(target, view_as<int>(specialType), 10, testPos);
PrintToServer("[FTT] Could not find suitable position, trying in front");
GetHorizontalPositionFromOrigin(pos, ang, -minDistance, testPos);
if(!FindSuitablePosition(pos, testPos, minDistance, 50)) {
PrintToServer("[FTT] Could not find suitable position, falling back");
if(spawnFlags & view_as<int>(Special_SpawnDirectOnFailure))
GetClientAbsOrigin(target, testPos);
else
L4D_GetRandomPZSpawnPosition(target, view_as<int>(specialType), 10, testPos);
}
}
pos = testPos;
}
pos[2] += 1.0;
return SpawnSpecialAtPosition(specialType, pos, ang, target, internalFlags);
} else {
if(L4D_GetRandomPZSpawnPosition(target, view_as<int>(specialType), 10, pos)) {
return SpawnSpecialAtPosition(specialType, pos, NULL_VECTOR, target, internalFlags);
}
return SpawnSpecialAtPosition(specialType, pos, ang, target, spawnFlags);
}
return false;
// Spawn via director, or fail
if(!L4D_GetRandomPZSpawnPosition(target, view_as<int>(specialType), 10, pos)) {
if(spawnFlags & view_as<int>(Special_SpawnDirectOnFailure))
GetClientAbsOrigin(target, pos);
else
return false;
}
return SpawnSpecialAtPosition(specialType, pos, NULL_VECTOR, target, spawnFlags);
}
// Target is optional
stock bool SpawnSpecialAtPosition(SpecialType special, const float destination[3], const float angle[3], int target = 0, int flags = 0) {
stock bool SpawnSpecialAtPosition(SpecialType specialType, const float destination[3], const float angle[3], int target = 0, int spawnFlags = 0) {
int internalFlags = 0;
if(spawnFlags & view_as<int>(Special_AlwaysTarget)) {
internalFlags |= view_as<int>(SPI_AlwaysTarget);
}
if(spawnFlags & view_as<int>(Special_KillOnIncap)) {
internalFlags |= view_as<int>(SPI_KillOnTargetIncap);
}
if(spawnFlags & view_as<int>(Special_OnTarget)) {
if(specialType == Special_Boomer || specialType == Special_Spitter)
internalFlags |= view_as<int>(SPI_KillOnSpawn);
}
SpecialSpawnRequest request;
request.type = special;
request.type = specialType;
if(target)
request.targetUserId = GetClientUserId(target);
request.flags = flags;
request.flags = internalFlags;
request.position = destination;
request.angle = angle;
g_spSpawnQueue.PushArray(request);
@ -100,27 +115,29 @@ bool ProcessSpecialQueue() {
CreateTimer(0.1, Timer_KickBot, bot);
}
CheatCommand(target, "z_spawn_old", SPECIAL_NAMES[view_as<int>(spActiveRequest.type) - 1], "auto");
return true;
} else if(spActiveRequest.type == Special_Witch) {
int witch = L4D2_SpawnWitch(spActiveRequest.position, spActiveRequest.angle);
DataPack pack;
CreateDataTimer(0.2, Timer_SetWitchTarget, pack);
pack.WriteCell(witch);
pack.WriteCell(GetClientUserId(target));
pack.WriteCell(spActiveRequest.targetUserId);
if(witch != -1)
SetWitchTarget(witch, target);
spIsActive = false;
return ProcessSpecialQueue();
} else if(spActiveRequest.type == Special_Tank) {
// BypassLimit();
int tank = L4D2_SpawnTank(spActiveRequest.position, spActiveRequest.angle);
if(tank > 0 && IsClientConnected(tank)) {
TeleportEntity(tank, spActiveRequest.position, spActiveRequest.angle, NULL_VECTOR);
PrintToConsoleAll("[ftt/debug] requested tank spawned %d -> %N", tank, target)
pdata[tank].attackerTargetUid = spActiveRequest.targetUserId;
pdata[tank].specialAttackFlags = view_as<int>(SPI_AlwaysTarget);
}
spIsActive = false;
return ProcessSpecialQueue();
}
return true;
return false;
}
spIsActive = false;
return false;
@ -146,8 +163,8 @@ stock SpecialType GetSpecialType(const char[] input) {
stock bool FindSuitablePosition(const float pos[3], float outputPos[3], float minDistance = 19000.0, int tries = 100) {
outputPos = pos;
for(int i = tries; i > 0; i--) {
// int nav = L4D_GetNearestNavArea(pos);
outputPos[1] += GetRandomFloat(-30.0, 30.0);
// Shift position aroudn randomly
outputPos[1] += GetRandomFloat(-35.0, 35.0);
float dist = GetVectorDistance(outputPos, pos, true);
if(dist >= minDistance && L4D2Direct_GetTerrorNavArea(outputPos) != Address_Null) { //5m^2
return true;
@ -191,7 +208,7 @@ stock bool GetGroundBehind(int client, float vPos[3], float vAng[3]) {
}
stock bool RayFilter_NonClient(int entity, int contentsMask) {
if (entity < 1 || entity > MaxClients) {
if (entity <= 0 || entity > MaxClients) {
if (IsValidEntity(entity)) {
static char eClass[128];
if (GetEntityClassname(entity, eClass, sizeof(eClass))) {

View file

@ -1,5 +1,5 @@
public Action Timer_ThrowTimer(Handle timer) {
Action Timer_ThrowTimer(Handle timer) {
int count = 0;
for(int i = 1; i < MaxClients; i++) {
if(IsClientConnected(i) && IsClientInGame(i) && IsPlayerAlive(i) && IsTrollActive(i, "Throw It All")) {
@ -17,20 +17,20 @@ Action Timer_RandomVelocity(Handle h, int client) {
return Plugin_Stop;
}
float bounds = 50.0;
if(Trolls[t_randomizeAnglesIndex].activeFlagClients[client] & 2) bounds = 100.0;
else if(Trolls[t_randomizeAnglesIndex].activeFlagClients[client] & 4) bounds = 200.0;
else if(Trolls[t_randomizeAnglesIndex].activeFlagClients[client] & 8) bounds = 500.0;
else if(Trolls[t_randomizeAnglesIndex].activeFlagClients[client] & 16) bounds = 1000.0;
if(Trolls[t_randomizeVelocityIndex].activeFlagClients[client] & 2) bounds = 100.0;
else if(Trolls[t_randomizeVelocityIndex].activeFlagClients[client] & 4) bounds = 200.0;
else if(Trolls[t_randomizeVelocityIndex].activeFlagClients[client] & 8) bounds = 500.0;
else if(Trolls[t_randomizeVelocityIndex].activeFlagClients[client] & 16) bounds = 1000.0;
float vel[3];
GetEntPropVector(client, Prop_Data, "m_vecVelocity", vel);
vel[0] += GetRandomFloat(-bounds, bounds);
vel[1] += GetRandomFloat(-bounds, bounds);
vel[2] += GetRandomFloat(-100.0, 150.0);
vel[2] += GetRandomFloat(-20.0, 100.0);
SetAbsVelocity(client, vel);
return Plugin_Continue;
}
public Action Timer_Main(Handle timer) {
Action Timer_Main(Handle timer) {
static int loopTick;
static int slowDrainIndex;
@ -81,7 +81,7 @@ public Action Timer_Main(Handle timer) {
}
}
if(Trolls[tempHealthQuickDrainIndex].IsActive(i)) {
if(loopTick % 2 == 0) {
if(loopTick % 3 == 0) {
float bufferTime = GetEntPropFloat(i, Prop_Send, "m_healthBufferTime");
float tempHealth = L4D_GetTempHealth(i);
if(tempHealth > 0.0) {
@ -115,6 +115,46 @@ public Action Timer_Main(Handle timer) {
SetEntProp(primaryWpn, Prop_Send, "m_iClip1", GetRandomInt(0, maxCap));
}
}
if(Trolls[t_vomitPlayerIndex].IsActive(i)) {
if(loopTick % 4 == 0) {
L4D_CTerrorPlayer_OnVomitedUpon(i, i);
}
}
if(Trolls[t_shakeyCameraIndex].IsActive(i)) {
float amplitude = 1.0;
float freq = 1.0;
if(Trolls[t_shakeyCameraIndex].activeFlagClients[i] & 1) {
amplitude = 1.0;
freq = 1.0;
} else if(Trolls[t_shakeyCameraIndex].activeFlagClients[i] & 2) {
amplitude = 5.0;
freq = 5.0;
} else if(Trolls[t_shakeyCameraIndex].activeFlagClients[i] & 4) {
amplitude = 20.0;
freq = 20.0;
} else if(Trolls[t_shakeyCameraIndex].activeFlagClients[i] & 8) {
amplitude = 50.0;
freq = 50.0;
} else if(Trolls[t_shakeyCameraIndex].activeFlagClients[i] & 16) {
amplitude = 100.0;
freq = 200.0;
}
ShakePlayer(i, amplitude, freq, MAIN_TIMER_INTERVAL_S + 1.0);
}
if(Trolls[t_slotRouletteIndex].IsActive(i) && Trolls[t_slotRouletteIndex].activeFlagClients[i] & 8) {
float chance = 1.0;
if(Trolls[t_slotRouletteIndex].activeFlagClients[i] & 16) {
chance = 0.2;
} else if(Trolls[t_slotRouletteIndex].activeFlagClients[i] & 32) {
chance = 0.4;
} else if(Trolls[t_slotRouletteIndex].activeFlagClients[i] & 64) {
chance = 0.6;
}
if(GetURandomFloat() < chance) {
SetSlot(i, -1);
}
}
}
}
if(++loopTick >= 60) {
@ -122,8 +162,28 @@ public Action Timer_Main(Handle timer) {
}
return Plugin_Continue;
}
Action Timer_SlotRoulette(Handle h, int client) {
if(!IsClientConnected(client)) {
Trolls[t_slotRouletteIndex].timerHandles[client] = null;
return Plugin_Stop;
}
if(Trolls[t_slotRouletteIndex].activeFlagClients[client] & 8) {
float chance = 1.0;
if(Trolls[t_slotRouletteIndex].activeFlagClients[client] & 16) {
chance = 0.1;
} else if(Trolls[t_slotRouletteIndex].activeFlagClients[client] & 32) {
chance = 0.3;
} else if(Trolls[t_slotRouletteIndex].activeFlagClients[client] & 64) {
chance = 0.5;
}
public Action Timer_GivePistol(Handle timer, int user) {
if(GetURandomFloat() < chance) {
SetSlot(client, -1);
}
}
return Plugin_Continue;
}
Action Timer_GivePistol(Handle timer, int user) {
int client = GetClientOfUserId(user);
if(client > 0) {
int flags = GetCommandFlags("give");
@ -134,7 +194,7 @@ public Action Timer_GivePistol(Handle timer, int user) {
return Plugin_Handled;
}
public Action Timer_ThrowWeapon(Handle timer, Handle pack) {
Action Timer_ThrowWeapon(Handle timer, Handle pack) {
ResetPack(pack);
float dest[3];
dest[0] = ReadPackFloat(pack);
@ -161,7 +221,7 @@ public Action Timer_ThrowWeapon(Handle timer, Handle pack) {
return Plugin_Handled;
}
public Action Timer_ResetAutoPunish(Handle timer, int user) {
Action Timer_ResetAutoPunish(Handle timer, int user) {
int client = GetClientOfUserId(user);
if(client) {
if(hAutoPunish.IntValue & 2 == 2)
@ -172,7 +232,7 @@ public Action Timer_ResetAutoPunish(Handle timer, int user) {
return Plugin_Handled;
}
public Action Timer_NextWitchSet(Handle timer, DataPack pack) {
Action Timer_NextWitchSet(Handle timer, DataPack pack) {
pack.Reset();
int client = GetClientOfUserId(pack.ReadCell());
int witch = pack.ReadCell();
@ -180,20 +240,20 @@ public Action Timer_NextWitchSet(Handle timer, DataPack pack) {
return Plugin_Handled;
}
public Action Timer_KickBot(Handle timer, int client) {
if(IsClientInGame(client) && (!IsClientInKickQueue(client))) {
if(IsFakeClient(client)) KickClient(client);
Action Timer_KickBot(Handle timer, int client) {
if(IsClientInGame(client) && !IsClientInKickQueue(client) && IsFakeClient(client)) {
KickClient(client);
}
return Plugin_Handled;
}
public Action Timer_Delete(Handle h, int id) {
Action Timer_Delete(Handle h, int id) {
if(IsValidEntity(id))
AcceptEntityInput(id, "Kill");
return Plugin_Handled;
}
public Action Timer_ShootReverse(Handle h, DataPack pack) {
Action Timer_ShootReverse(Handle h, DataPack pack) {
pack.Reset();
int attacker = pack.ReadCell();
int target = pack.ReadCell();
@ -221,8 +281,9 @@ public Action Timer_ShootReverse(Handle h, DataPack pack) {
return Plugin_Stop;
}
}
public Action Timer_CheckSpecialSpawned(Handle h, int id) {
// We check if the special never spawned (g_iSpId never advances, and run the next in queue)
// Prevents the queue from stalling
Action Timer_CheckSpecialSpawned(Handle h, int id) {
if(g_iSpId == id) {
PrintToServer("[FTT] Special did not spawn in time, continuing.");
g_iSpId++;
@ -231,7 +292,7 @@ public Action Timer_CheckSpecialSpawned(Handle h, int id) {
return Plugin_Handled;
}
public Action Timer_CheckIsInSpit(Handle h, int userid) {
Action Timer_CheckIsInSpit(Handle h, int userid) {
int client = GetClientOfUserId(userid);
if(client && GetGameTime() - pdata[userid].lastInSpitTime > 3.0) {
SetEntPropFloat(client, Prop_Send, "m_flLaggedMovementValue", 1.0);
@ -244,7 +305,7 @@ public Action Timer_CheckIsInSpit(Handle h, int userid) {
float CHARGER_CHECK_MIN[3] = { -15.0, -15.0, 2.0};
float CHARGER_CHECK_MAX[3] = { 15.0, 15.0, 20.0 };
public Action Timer_CheckForChargerOpportunity(Handle h, int userid) {
Action Timer_CheckForChargerOpportunity(Handle h, int userid) {
int client = GetClientOfUserId(userid);
if(client) {
int activator = GetClientOfUserId(pdata[client].smartChargeActivator);
@ -262,7 +323,7 @@ public Action Timer_CheckForChargerOpportunity(Handle h, int userid) {
GetHorizontalPositionFromOrigin(pos, ang, 500.0, endPos);
TR_TraceHullFilter(endPos, pos, CHARGER_CHECK_MIN, CHARGER_CHECK_MAX, MASK_SOLID, Filter_CheckChargerValid, client);
if(!TR_DidHit()) {
SpawnSpecialAtPosition(Special_Charger, spawnPos, ang, client);
SpawnSpecialAtPosition(Special_Charger, spawnPos, ang, client, view_as<int>(Special_AlwaysTarget));
if(activator) PrintToChat(activator, "Auto charge %N successfully after %d tries", client, pdata[client].smartChargeAttempts);
pdata[client].smartChargeAttempts = 0;
pdata[client].smartChargeActivator = 0;
@ -311,14 +372,14 @@ Action Timer_SpawnHealBots(Handle h, int max) {
if(count < max) {
if(!AddSurvivor()) {
count = 0;
CreateTimer(0.3, Timer_SpawnHealBotsPost);
CreateTimer(0.5, Timer_SpawnHealBotsPost);
return Plugin_Stop;
}
count++;
return Plugin_Continue;
}
count = 0;
CreateTimer(0.3, Timer_SpawnHealBotsPost);
CreateTimer(0.5, Timer_SpawnHealBotsPost);
return Plugin_Stop;
}
@ -330,12 +391,11 @@ Action Timer_SpawnHealBotsPost(Handle h) {
int kit = GetPlayerWeaponSlot(i, 3);
if(kit > 0) {
GetEntityClassname(kit, classname, sizeof(classname));
if(!StrEqual(classname, "weapon_first_aid_kit")) {
GiveClientWeapon(i, "weapon_first_aid_kit");
if(StrEqual(classname, "weapon_first_aid_kit")) {
continue;
}
} else {
GiveClientWeapon(i, "weapon_first_aid_kit");
}
GiveClientWeapon(i, "weapon_first_aid_kit");
pdata[i].flags &= view_as<int>(Flag_IsTargettingHealer);
}
}
@ -411,7 +471,7 @@ Action Timer_WaitForApex(Handle h, int entref) {
Action Timer_ResetGravity(Handle h, int entref) {
if(IsValidEntity(entref)) {
int entity = EntRefToEntIndex(entref);
SetEntityGravity(entity, 800.0);
SetEntityGravity(entity, 800.0); // could pull from sv_gravity but no ones gonna notice
}
return Plugin_Handled;
}

View file

@ -5,6 +5,9 @@ int stickyGooIndex = 0;
int invertedTrollIndex;
int t_randomizeAnglesIndex;
int t_randomizeVelocityIndex;
int t_vomitPlayerIndex;
int t_shakeyCameraIndex;
int t_slotRouletteIndex;
// int fireSpitMagnetTrollIndex;
void SetupTrolls() {
@ -33,7 +36,7 @@ void SetupTrolls() {
/// CATEGORY: Infected
SetCategory("Infected");
SetupTroll("Swarm", "Swarms a player with zombies. Requires swarm plugin", TrollMod_Instant | TrollMod_Constant);
SetupTroll("Vomit Player", "Shortcut to sm_vomitplayer. vomits the player.", TrollMod_Instant);
t_vomitPlayerIndex = SetupTroll("Vomit Player", "Shortcut to sm_vomitplayer. vomits the player.", TrollMod_Instant | TrollMod_Constant);
SetupTroll("Inface Special", "Shortcut to sm_inface", TrollMod_Instant);
SetupTroll("Insta Special", "Shortcut to sm_insta", TrollMod_Instant);
SetupTroll("Goo", "Spawns a spitter puddle underneath them", TrollMod_Instant);
@ -85,7 +88,6 @@ void SetupTrolls() {
// CATEGORY: Items
SetCategory("Items");
SetupTroll("Throw It All", "Player throws their item(s) periodically to a nearby player", TrollMod_Instant);
index = SetupTroll("Spicy Gas", "Gascans player picks up just ignite. Magic.", TrollMod_Constant);
Trolls[index].AddFlagPrompt(false);
Trolls[index].AddFlag("Always (100%)", false);
@ -112,6 +114,19 @@ void SetupTrolls() {
SetupTroll("Nerf Gun", "When they shoot it does no damage.", TrollMod_Constant);
SetupTroll("Randomize Clip Ammo", "Randomly changes their clip ammo downwards", TrollMod_Constant | TrollMod_Instant);
SetupTroll("CameTooEarly", "When they shoot, random chance they empty whole clip", TrollMod_Constant);
index = SetupTroll("Slot Roulette", "Randomize their slots", TrollMod_Constant);
Trolls[index].AddCustomFlagPrompt("Activations:", true);
Trolls[index].AddFlag("On Vomitted", false); // 1
Trolls[index].AddFlag("On Damage", false); // 2
Trolls[index].AddFlag("On Vocalize", false); // 4
Trolls[index].AddFlag("Periodically", true); //8
Trolls[index].AddCustomFlagPrompt("Frequency:", false, 8);
Trolls[index].AddFlag("Subtle", false); // 16
Trolls[index].AddFlag("Confusing", false); // 32
Trolls[index].AddFlag("Annoying", false); // 64
Trolls[index].AddFlag("Unusable", false); // 128
Trolls[index].SetTimer(0.3, Timer_SlotRoulette, 8);
t_slotRouletteIndex = index;
/// CATEGORY: Chat
@ -170,7 +185,7 @@ void SetupTrolls() {
Trolls[index].AddFlag("3", false); // 16
Trolls[index].AddFlag("4", true); // 32
Trolls[index].AddFlag("5", false); // 64
Trolls[index].AddCustomFlagPrompt("Auto Timeout", false);
Trolls[index].AddCustomFlagPrompt("Auto Timeout", false, 0);
Trolls[index].AddFlag("Until Healed / Map Change", false); // 128
Trolls[index].AddFlag("15 seconds", true); // 255
Trolls[index].AddFlag("30 seconds", false); // 512
@ -228,13 +243,24 @@ void SetupTrolls() {
Trolls[index].AddFlag("Incap On Touch", false);
Trolls[index].AddFlag("Slay On Touch", false);
Trolls[index].AddFlag("0.8x Speed", false);
// TODO: setup instant
index = SetupTroll("Shakey Camera", "Horrible", TrollMod_Constant);
Trolls[index].AddFlagPrompt(false);
// add flag: vomit on touch
Trolls[index].AddFlag("Annoying but playable", false);
Trolls[index].AddFlag("Bad", true);
Trolls[index].AddFlag("Sickness", false);
Trolls[index].AddFlag("Violent", false);
Trolls[index].AddFlag("Violent XX", false);
t_shakeyCameraIndex = index;
index = SetupTroll("Meta: Inverse", "Uhm you are not supposed to see this...", TrollMod_Instant);
Trolls[index].hidden = true;
Trolls[index].AddFlagPrompt(false);
Trolls[index].AddFlag("100%", true);
Trolls[index].AddFlag("50%", false);
Trolls[index].AddFlag("10%", false);
index = SetupTroll("Meta: Random", "Picks a random troll", TrollMod_Instant);
@ -297,7 +323,7 @@ bool ApplyAffect(int victim, const Troll troll, int activator, trollModifier mod
static char wpn[32];
GetClientWeaponName(victim, 4, wpn, sizeof(wpn));
if(StrEqual(wpn, "weapon_adrenaline") || StrEqual(wpn, "weapon_pain_pills")) {
ClientCommand(victim, "slot5");
SetSlot(victim, 5);
pdata[victim].flags |= view_as<int>(Flag_PendingItemGive);
}else{
ReplyToCommand(activator, "User does not have pills or adrenaline");
@ -419,7 +445,7 @@ bool ApplyAffect(int victim, const Troll troll, int activator, trollModifier mod
StopHealingBots();
return true;
} else if(healTargetPlayer != 0) {
if(IsValidHandle(stopHealingTimer)) {
if(stopHealingTimer != null) {
TriggerTimer(stopHealingTimer);
}
return true;
@ -493,6 +519,24 @@ bool ApplyAffect(int victim, const Troll troll, int activator, trollModifier mod
CreateTimer(1.0, Timer_CheckForChargerOpportunity, GetClientUserId(victim), TIMER_REPEAT | TIMER_FLAG_NO_MAPCHANGE);
} else if(StrEqual(troll.name, "No Rushing Us")) {
SetEntPropFloat(victim, Prop_Send, "m_flLaggedMovementValue", 1.0);
} else if(StrEqual(troll.name, "Meta: Random")) {
int rndTroll = GetRandomInt(0, MAX_TROLLS);
int rndFlags = 0;
int maxFlags = Trolls[rndTroll].GetFlagCount();
int numFlags = GetRandomInt(0, maxFlags);
while(numFlags > 0) {
// Apply a random flag
rndFlags |= GetRandomInt(0, maxFlags)
maxFlags--;
}
trollModifier rndMod = Trolls[rndTroll].GetDefaultMod();
if(Trolls[rndTroll].HasMod(TrollMod_Constant) && GetURandomFloat() > 0.5) {
rndMod = TrollMod_Constant;
}
if(Trolls[rndTroll].HasMod(TrollMod_Instant) && GetURandomFloat() > 0.5) {
rndMod |= TrollMod_Instant;
}
Trolls[rndTroll].Activate(victim, activator, rndMod, rndFlags);
} else if(~modifier & TrollMod_Constant) {
PrintToServer("[FTT] Warn: Possibly invalid troll, no apply action defined for \"%s\"", troll.name);
#if defined DEBUG

View file

@ -71,7 +71,6 @@ public void OnPluginStart() {
hThrowItemInterval.AddChangeHook(Change_ThrowInterval);
hAutoPunish = CreateConVar("sm_ftt_autopunish_action", "0", "Setup automatic punishment of players. Add bits together\n0=Disabled, 1=Tank magnet, 2=Special magnet, 4=Swarm, 8=InstantVomit", FCVAR_NONE, true, 0.0);
hAutoPunishExpire = CreateConVar("sm_ftt_autopunish_expire", "0", "How many minutes of gametime until autopunish is turned off? 0 for never.", FCVAR_NONE, true, 0.0);
hMagnetChance = CreateConVar("sm_ftt_magnet_chance", "1.0", "% of the time that the magnet will work on a player.", FCVAR_NONE, true, 0.0, true, 1.0);
hMagnetTargetMode = CreateConVar("sm_ftt_magnet_targetting", "6", "How does the specials target players. Add bits together\n0=Incapped are ignored, 1=Specials targets incapped, 2=Tank targets incapped 4=Witch targets incapped");
hShoveFailChance = CreateConVar("sm_ftt_shove_fail_chance", "0.65", "The % chance that a shove fails", FCVAR_NONE, true, 0.0, true, 1.0);
hBadThrowHitSelf = CreateConVar("sm_ftt_badthrow_fail_chance", "1", "The % chance that on a throw, they will instead hit themselves. 0 to disable", FCVAR_NONE, true, 0.0, true, 1.0);
@ -103,6 +102,8 @@ public void OnPluginStart() {
RegAdminCmd("sm_bots_attack", Command_BotsAttack, ADMFLAG_BAN, "Instructs all bots to attack a player until they have X health.");
RegAdminCmd("sm_scharge", Command_SmartCharge, ADMFLAG_BAN, "Auto Smart charge");
RegAdminCmd("sm_healbots", Command_HealTarget, ADMFLAG_BAN, "Make bots heal a player");
RegAdminCmd("sm_rff", Command_SetReverseFF, ADMFLAG_KICK, "Set reverse FF on player");
RegAdminCmd("sm_magnet", Command_SetMagnetShortcut, ADMFLAG_KICK, "");
HookEvent("player_spawn", Event_PlayerSpawn);
HookEvent("player_first_spawn", Event_PlayerFirstSpawn);
@ -116,6 +117,7 @@ public void OnPluginStart() {
HookEvent("entered_spit", Event_EnteredSpit);
HookEvent("bot_player_replace", Event_BotPlayerSwap);
HookEvent("heal_success", Event_HealSuccess);
HookEvent("player_incapacitated", Event_Incapped);
AddNormalSoundHook(SoundHook);
@ -148,7 +150,6 @@ public void Change_BotDefend(ConVar convar, const char[] oldValue, const char[]
// METHODS - Old methods, some are also in feedthetrolls/misc.inc
///////////////////////////////////////////////////////////////////////////////
void ThrowAllItems(int victim) {
float vicPos[3], destPos[3];
int clients[4];