diff --git a/plugins/l4d2_feedthetrolls.smx b/plugins/l4d2_feedthetrolls.smx index 097eee0..3bae44c 100644 Binary files a/plugins/l4d2_feedthetrolls.smx and b/plugins/l4d2_feedthetrolls.smx differ diff --git a/scripting/include/feedthetrolls/base.inc b/scripting/include/feedthetrolls/base.inc index b3fcefb..7face95 100644 --- a/scripting/include/feedthetrolls/base.inc +++ b/scripting/include/feedthetrolls/base.inc @@ -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 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) { diff --git a/scripting/include/feedthetrolls/combos.inc b/scripting/include/feedthetrolls/combos.inc index 246a036..2f89c15 100644 --- a/scripting/include/feedthetrolls/combos.inc +++ b/scripting/include/feedthetrolls/combos.inc @@ -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); } \ No newline at end of file diff --git a/scripting/include/feedthetrolls/commands.inc b/scripting/include/feedthetrolls/commands.inc index 03b092e..785260c 100644 --- a/scripting/include/feedthetrolls/commands.inc +++ b/scripting/include/feedthetrolls/commands.inc @@ -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(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(Special_OnTarget))) { + if(SpawnSpecialForTarget(specialType, target, view_as(Special_OnTarget) | view_as(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 [# 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 [# of bots or 0 for all]"); } return Plugin_Handled; +} +Action Command_SetReverseFF(int client, int args) { + if(args < 2) { + ReplyToCommand(client, "Usage: sm_rff [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 "); + 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; } \ No newline at end of file diff --git a/scripting/include/feedthetrolls/events.inc b/scripting/include/feedthetrolls/events.inc index 64911b6..32cd05a 100644 --- a/scripting/include/feedthetrolls/events.inc +++ b/scripting/include/feedthetrolls/events.inc @@ -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(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(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(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(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(Special_OnTarget)); + SpawnSpecialForTarget(Special_Boomer, activator, view_as(Special_OnTarget) | view_as(Special_AlwaysTarget)); } else if(StrContains(sceneFile, "warnhunter") > -1) { - SpawnSpecialForTarget(Special_Hunter, activator, view_as(Special_OnTarget)); + SpawnSpecialForTarget(Special_Hunter, activator, view_as(Special_OnTarget) | view_as(Special_AlwaysTarget)); } else if(StrContains(sceneFile, "warnsmoker") > -1) { - SpawnSpecialForTarget(Special_Smoker, activator, view_as(Special_OnTarget)); + SpawnSpecialForTarget(Special_Smoker, activator, view_as(Special_OnTarget) | view_as(Special_AlwaysTarget)); } else if(StrContains(sceneFile, "warnspitter") > -1) { - SpawnSpecialForTarget(Special_Spitter, activator, view_as(Special_OnTarget)); + SpawnSpecialForTarget(Special_Spitter, activator, view_as(Special_OnTarget) | view_as(Special_AlwaysTarget)); } else if(StrContains(sceneFile, "warnjockey") > -1) { - SpawnSpecialForTarget(Special_Jockey, activator, view_as(Special_OnTarget)); + SpawnSpecialForTarget(Special_Jockey, activator, view_as(Special_OnTarget) | view_as(Special_AlwaysTarget)); } else if(StrContains(sceneFile, "warncharger") > -1) { - SpawnSpecialForTarget(Special_Charger, activator, view_as(Special_OnTarget)); + SpawnSpecialForTarget(Special_Charger, activator, view_as(Special_OnTarget) | view_as(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(GetRandomInt(1,6)); fLastAntiRushEvent[client] = GetGameTime(); - SpawnSpecialForTarget(special, client, view_as(Special_SpawnDirectOnFailure | Special_OnTarget)); + SpawnSpecialForTarget(special, client, view_as(Special_SpawnDirectOnFailure | Special_OnTarget | Special_AlwaysTarget)); PrintToConsoleAll("[FTT] Spawning anti-rush special on %N (dist=%f) (special=%s)", client, distance, SPECIAL_NAMES[view_as(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(SPI_KillOnTargetIncap)) { + ForcePlayerSuicide(attacker); + } + } +} \ No newline at end of file diff --git a/scripting/include/feedthetrolls/menus.inc b/scripting/include/feedthetrolls/menus.inc index 5a34c4d..d5f87f8 100644 --- a/scripting/include/feedthetrolls/menus.inc +++ b/scripting/include/feedthetrolls/menus.inc @@ -44,14 +44,14 @@ public int Insta_SpecialHandler(Menu menu, MenuAction action, int client, int pa } SpecialType special = view_as(specialInt); if(inFace) { - if(SpawnSpecialForTarget(special, target, view_as(Special_OnTarget | Special_SpawnDirectOnFailure))) { + if(SpawnSpecialForTarget(special, target, view_as(Special_OnTarget) | view_as(Special_SpawnDirectOnFailure) | view_as(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(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); } } diff --git a/scripting/include/feedthetrolls/misc.inc b/scripting/include/feedthetrolls/misc.inc index 2b04ceb..4e13a4f 100644 --- a/scripting/include/feedthetrolls/misc.inc +++ b/scripting/include/feedthetrolls/misc.inc @@ -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); +} \ No newline at end of file diff --git a/scripting/include/feedthetrolls/specials.inc b/scripting/include/feedthetrolls/specials.inc index 027726d..e7e7e0b 100644 --- a/scripting/include/feedthetrolls/specials.inc +++ b/scripting/include/feedthetrolls/specials.inc @@ -26,7 +26,7 @@ ArrayList g_spSpawnQueue; // target client index stock bool SpawnSpecialForTarget(SpecialType specialType, int target, int spawnFlags = view_as(Special_Anywhere)) { static float pos[3]; - int internalFlags = view_as(SPI_AlwaysTarget); + if(spawnFlags & view_as(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(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(Special_SpawnDirectOnFailure)) - GetClientAbsOrigin(target, pos); - else - L4D_GetRandomPZSpawnPosition(target, view_as(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(Special_SpawnDirectOnFailure)) + GetClientAbsOrigin(target, testPos); + else + L4D_GetRandomPZSpawnPosition(target, view_as(specialType), 10, testPos); + } } pos = testPos; } pos[2] += 1.0; - return SpawnSpecialAtPosition(specialType, pos, ang, target, internalFlags); - } else { - if(L4D_GetRandomPZSpawnPosition(target, view_as(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(specialType), 10, pos)) { + if(spawnFlags & view_as(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(Special_AlwaysTarget)) { + internalFlags |= view_as(SPI_AlwaysTarget); + } + if(spawnFlags & view_as(Special_KillOnIncap)) { + internalFlags |= view_as(SPI_KillOnTargetIncap); + } + if(spawnFlags & view_as(Special_OnTarget)) { + if(specialType == Special_Boomer || specialType == Special_Spitter) + internalFlags |= view_as(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(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(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))) { diff --git a/scripting/include/feedthetrolls/timers.inc b/scripting/include/feedthetrolls/timers.inc index 5f46bfc..6507755 100644 --- a/scripting/include/feedthetrolls/timers.inc +++ b/scripting/include/feedthetrolls/timers.inc @@ -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(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(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; } \ No newline at end of file diff --git a/scripting/include/feedthetrolls/trolls.inc b/scripting/include/feedthetrolls/trolls.inc index ca2bcf3..2ad4208 100644 --- a/scripting/include/feedthetrolls/trolls.inc +++ b/scripting/include/feedthetrolls/trolls.inc @@ -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(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 diff --git a/scripting/l4d2_feedthetrolls.sp b/scripting/l4d2_feedthetrolls.sp index 39ed440..9aa505f 100644 --- a/scripting/l4d2_feedthetrolls.sp +++ b/scripting/l4d2_feedthetrolls.sp @@ -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];