diff --git a/plugins/l4d2_feedthetrolls.smx b/plugins/l4d2_feedthetrolls.smx index 21bda80..cef53fa 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 d45ceec..e86aa12 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 34 + #define MAX_TROLLS 37 #endif enum trollModifier { @@ -35,6 +35,10 @@ bool SilentMenuSelected[MAXPLAYERS+1]; static int g_trollAddPromptIndex; ArrayList gRandomClients; +char SPECIAL_NAMES[][] = { + "Smoker", "Boomer", "Hunter", "Spitter", "Jockey", "Charger", "Witch", "Tank" +}; + enum struct TrollFlagPrompt { char promptText[MAX_TROLL_FLAG_LENGTH]; int flags; @@ -53,6 +57,15 @@ enum struct TrollFlagPrompt { } } +int GetIndexFromPower(int powerOfTwo) { + for(int i = 0; i < 16; i++) { + if(1 << i == powerOfTwo) { + return i; + } + } + return -1; +} + enum struct Troll { int id; int categoryID; @@ -112,9 +125,11 @@ enum struct Troll { int AddFlag(const char[] name, bool defaultOn) { if(this.flagNames == null) this.flagNames = new ArrayList(MAX_TROLL_FLAG_LENGTH); + // Check if flag already added int flagIndex = this.GetFlagIndex(name); if(flagIndex == -1) flagIndex = this.flagNames.PushString(name); + // Grab the prompt static TrollFlagPrompt prompt; this.flagPrompts.GetArray(g_trollAddPromptIndex, prompt); @@ -346,20 +361,33 @@ void ApplyTroll(int victim, const char[] name, int activator, trollModifier modi ShowActivityEx(activator, "[FTT] ", "deactivated troll \"%s\" on %N. ", troll.name, victim); LogAction(activator, victim, "\"%L\" deactivated troll \"%s\" on \"%L\"", activator, troll.name, victim); } else { + static char flagName[MAX_TROLL_FLAG_LENGTH]; + if(flags > 0 && flags & flags - 1 == 0) { + // Get the flag name if there is only one flag set + troll.GetFlagName(GetIndexFromPower(flags), flagName, sizeof(flagName)); + } if(modifier & TrollMod_Constant) { if(flags > 0) { - ShowActivityEx(activator, "[FTT] ", "activated constant troll \"%s\" (%d) for %N. ", troll.name, flags, victim); + if(flagName[0] != '\0') { + ShowActivityEx(activator, "[FTT] ", "activated constant troll \"%s\" (%s) for %N. ", troll.name, flagName, victim); + } else { + ShowActivityEx(activator, "[FTT] ", "activated constant troll \"%s\" (%d) for %N. ", troll.name, flags, victim); + } } else ShowActivityEx(activator, "[FTT] ", "activated constant troll \"%s\" for %N. ", troll.name, victim); - } else if(flags > 0) - ShowActivityEx(activator, "[FTT] ", "activated troll \"%s\" (%d) for %N. ", troll.name, flags, victim); - else + } else if(flags > 0) { + if(flagName[0] != '\0') { + ShowActivityEx(activator, "[FTT] ", "activated troll \"%s\" (%s) for %N. ", troll.name, flagName, victim); + } else { + ShowActivityEx(activator, "[FTT] ", "activated troll \"%s\" (%d) for %N. ", troll.name, flags, victim); + } + } else ShowActivityEx(activator, "[FTT] ", "activated troll \"%s\" for %N. ", troll.name, victim); LogAction(activator, victim, "\"%L\" activated troll \"%s\" (%d) for \"%L\"", activator, troll.name, flags, victim); } } else { - ReplyToCommand(activator, "apply troll \"%s\" flags=%d on %N", troll.name, flags, victim); + ReplyToCommand(activator, "ftt: Applied Troll \"%s\" on %N with flags=%d", troll.name, victim, flags); } // Toggle on flags for client, if it's not a single run. if(modifier & TrollMod_Constant) { diff --git a/scripting/include/feedthetrolls/commands.inc b/scripting/include/feedthetrolls/commands.inc index 47c3789..13a5da6 100644 --- a/scripting/include/feedthetrolls/commands.inc +++ b/scripting/include/feedthetrolls/commands.inc @@ -13,10 +13,6 @@ public Action Command_InstaSpecial(int client, int args) { menu.ExitButton = true; menu.Display(client, 0); } else { - if(gInstaSpecialType > -1) { - ReplyToCommand(client, "Please wait for last Insta to spawn."); - return Plugin_Handled; - } char arg1[32], arg2[32] = "jockey"; GetCmdArg(1, arg1, sizeof(arg1)); if(args >= 2) { @@ -49,7 +45,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(SpawnSpecialNear(target, specialType)) { + if(SpawnSpecialForTarget(specialType, target)) { LogAction(client, target, "\"%L\" spawned Insta-%s™ nearby \"%L\"", client, arg2, target); successes++; } else { @@ -82,10 +78,6 @@ public Action Command_InstaSpecialFace(int client, int args) { menu.ExitButton = true; menu.Display(client, 0); } else { - if(gInstaSpecialType > -1) { - ReplyToCommand(client, "Please wait for last Insta to spawn."); - return Plugin_Handled; - } char arg1[32], arg2[32] = "jockey"; GetCmdArg(1, arg1, sizeof(arg1)); if(args >= 2) { @@ -118,7 +110,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(SpawnSpecialInFace(target, specialType)) { + if(SpawnSpecialForTarget(specialType, target, Special_OnTarget)) { LogAction(client, target, "\"%L\" spawned Insta-%s™ at player \"%L\"", client, arg2, target); successes++; } else { diff --git a/scripting/include/feedthetrolls/events.inc b/scripting/include/feedthetrolls/events.inc index 4c174a0..a5e53b5 100644 --- a/scripting/include/feedthetrolls/events.inc +++ b/scripting/include/feedthetrolls/events.inc @@ -15,6 +15,9 @@ public void OnMapStart() { PrecacheSound("player/footsteps/clown/concrete1.wav"); PrecacheSound("weapons/ceda_jar/ceda_jar_explode.wav"); PrecacheSound("weapons/molotov/molotov_detonate_1.wav"); + + g_spSpawnQueue.Clear(); + spIsActive = false; //CreateTimer(30.0, Timer_AutoPunishCheck, _, TIMER_REPEAT | TIMER_FLAG_NO_MAPCHANGE); } public void OnClientPutInServer(int client) { @@ -26,36 +29,47 @@ public void OnClientPutInServer(int client) { } public void Event_PlayerSpawn(Event event, const char[] name, bool dontBroadcast) { int userid = event.GetInt("userid"); - CreateTimer(0.1, Timer_CheckSpecial, userid); + if(spIsActive) + CreateTimer(0.1, Timer_CheckSpecial, userid); +} +public void Event_DoorToggle(Event event, const char[] name, bool dontBroadcast) { + int client = GetClientOfUserId(event.GetInt("userid")); + if(client && Trolls[slipperyShoesIndex].IsActive(client) && Trolls[slipperyShoesIndex].activeFlagClients[client] & 2) { + L4D_StaggerPlayer(client, client, NULL_VECTOR); + } +} +public 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); + } } public Action Timer_CheckSpecial(Handle h, int specialID) { int special = GetClientOfUserId(specialID); // Check if new player is the spawned special: - if(special > 0 && gInstaSpecialType > -1 && IsFakeClient(special) && GetClientTeam(special) == 3) { - int type = GetEntProp(special, Prop_Send, "m_zombieClass"); - // Verify type of special is spawned special - if(type == gInstaSpecialType) { - // Ignore 'ManualDirectorBot' or abm bots + if(spIsActive && special > 0 && IsFakeClient(special) && 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) { - gInstaSpecialType = -1; - // Set special to only attack them: - g_iAttackerTarget[special] = gInstaSpecialTarget; - // Incremenet count of specials targetting player: - gInstaSpecialMagnet[GetClientOfUserId(gInstaSpecialTarget)]++; + if(spActiveRequest.targetUserId) + g_iAttackerTarget[special] = spActiveRequest.targetUserId; + gInstaSpecialMagnet[GetClientOfUserId(spActiveRequest.targetUserId)]++; - TeleportEntity(special, gInstaSpecialSpawnPos, gInstaSpecialSpawnAng, NULL_VECTOR); - if(gInstaSpecialInstaKill) { + TeleportEntity(special, spActiveRequest.position, spActiveRequest.angle, NULL_VECTOR); + if(spActiveRequest.flags & view_as(SPI_KillOnSpawn)) { RequestFrame(Frame_Boom, special); } + + ProcessSpecialQueue(); } } - } + } } public void Frame_Boom(int special) { SDKHooks_TakeDamage(special, special, special, 1000.0); - gInstaSpecialInstaKill = false; } public void Event_PlayerFirstSpawn(Event event, const char[] name, bool dontBroadcast) { int client = GetClientOfUserId(event.GetInt("userid")); @@ -136,6 +150,12 @@ public void Event_CarAlarm(Event event, const char[] name, bool dontBroadcast) { //Ignore car alarms for autopunish lastButtonUser = -1; } +public void Event_EnteredSpit(Event event, const char[] name, bool dontBroadcast) { + int client = GetClientOfUserId(event.GetInt("userid")); + if(client) { + g_iInSpit[client] = true; + } +} public Action RushPlayer(Handle h, int user) { L4D2_RunScript("RushVictim(GetPlayerFromUserID(%d), %d)", user, 15000); } @@ -407,6 +427,11 @@ static char AWP[16] = "sniper_awp"; public Action Event_ItemPickup(int client, int weapon) { static int NoPickupIndex; if(NoPickupIndex == 0) NoPickupIndex = GetTrollID("No Pickup"); + static int SpicyGasIndex; + if(SpicyGasIndex == 0) SpicyGasIndex = GetTrollID("Spicy Gas"); + static int UziRulesIndex; + if(UziRulesIndex == 0) UziRulesIndex = GetTrollID("UziRules / AwpSmells"); + static char wpnName[64]; if(Trolls[NoPickupIndex].IsActive(client)) { int flags = Trolls[NoPickupIndex].activeFlagClients[client]; @@ -425,9 +450,10 @@ public Action Event_ItemPickup(int client, int weapon) { } else if(flags & 16 && GetPlayerWeaponSlot(client, view_as(L4DWeaponSlot_Pills)) == weapon) { // No Pills / Adr return Plugin_Handled; + } else if(flags & 32 && GetEntityClassname(weapon, wpnName, sizeof(wpnName)) && StrEqual(wpnName, "weapon_gascan")) { + return Plugin_Handled; } - return Plugin_Continue; - } else { + } else if(Trolls[UziRulesIndex].IsActive(client) || IsTrollActive(client, "Primary Disable")) { GetEdictClassname(weapon, wpnName, sizeof(wpnName)); if(strcmp(wpnName[7], "rifle") >= 0 || strcmp(wpnName[7], "smg") >= 0 @@ -435,8 +461,6 @@ public Action Event_ItemPickup(int client, int weapon) { || strcmp(wpnName[7], "sniper") > -1 || StrContains(wpnName, "shotgun") > -1 ) { - static int UziRulesIndex; - if(UziRulesIndex == 0) UziRulesIndex = GetTrollID("UziRules / AwpSmells"); //If 4: Only UZI, if 5: Can't switch. if(Trolls[UziRulesIndex].IsActive(client)) { static char comp[16]; @@ -461,9 +485,22 @@ public Action Event_ItemPickup(int client, int weapon) { return Plugin_Stop; } } - - return Plugin_Continue; + } else if(Trolls[SpicyGasIndex].IsActive(client)) { + PrintToChat(client, "spice active"); + if(GetEntityClassname(weapon, wpnName, sizeof(wpnName))) { + float max = 1.0; + if(Trolls[SpicyGasIndex].activeFlagClients[client] & 2) max = 0.5; + else if(Trolls[SpicyGasIndex].activeFlagClients[client] & 4) max = 0.1; + if(GetRandomFloat() <= max) { + if(StrEqual(wpnName, "weapon_gascan")) { + AcceptEntityInput(weapon, "Ignite", client, client); + } else if(StrEqual(wpnName, "weapon_propanetank") || StrEqual(wpnName, "weapon_oxygentank")) { + ExplodeProjectile(weapon); + } + } + } } + return Plugin_Continue; } public Action OnPlayerRunCmd(int client, int& buttons, int& impulse, float vel[3], float angles[3], int& weapon, int& subtype, int& cmdnum, int& tickcount, int& seed, int mouse[2]) { @@ -529,17 +566,29 @@ public Action Event_TakeDamage(int victim, int& attacker, int& inflictor, float& //Stop FF from marked: static int reverseFF; if(reverseFF == 0) reverseFF = GetTrollID("Reverse FF"); - if(attacker > 0 && attacker <= MaxClients && GetClientTeam(attacker) == 4 && IsFakeClient(attacker)) return Plugin_Stop; + if(attacker > 0 && attacker <= MaxClients && GetClientTeam(attacker) == 4 && IsFakeClient(attacker)) return Plugin_Continue; + + if (victim > 0 && victim <= MaxClients && damagetype != 263168 && damagetype != 265216) { + PrintToChat(victim, "you are in spit."); + } + if(attacker > 0 && victim <= MaxClients && attacker <= MaxClients && IsClientInGame(attacker) && IsPlayerAlive(attacker)) { if(shootAtTarget[attacker] == victim) return Plugin_Continue; if(g_PendingBanTroll[attacker] > 0 && GetClientTeam(attacker) == 2 && GetClientTeam(victim) == 2) { return Plugin_Stop; } + + + if(damage > 0.0 && victim != attacker && Trolls[slipperyShoesIndex].IsActive(victim) && Trolls[slipperyShoesIndex].activeFlagClients[victim] & 16) { + L4D_StaggerPlayer(victim, victim, NULL_VECTOR); + } + if(IsTrollActive(victim, "Damage Boost")) { damage * 2; return Plugin_Changed; } else if(Trolls[reverseFF].IsActive(attacker) && damagetype != DMG_BURN && attacker != victim && GetClientTeam(attacker) == GetClientTeam(victim)) { + float returnDmg = damage; //default is 1:1 if(Trolls[reverseFF].activeFlagClients[attacker] & 4) { returnDmg /= 2.0; @@ -668,9 +717,7 @@ void EntityCreateCallback(int entity) { static char class[16]; static int badThrowID; - if(badThrowID == 0) { - badThrowID = GetTrollID("Bad Throw"); - } + if(badThrowID == 0) badThrowID = GetTrollID("Bad Throw"); GetEntityClassname(entity, class, sizeof(class)); int entOwner = GetEntPropEnt(entity, Prop_Send, "m_hOwnerEntity"); @@ -701,10 +748,16 @@ void EntityCreateCallback(int entity) { AcceptEntityInput(entity, "Kill"); } } else if(Trolls[badThrowID].IsFlagActive(entOwner, Flag_3) && StrContains(class, "pipe_bomb", true) > -1) { - if(hBadThrowHitSelf.FloatValue > 0.0 && GetRandomFloat() <= hBadThrowHitSelf.FloatValue) + if(hBadThrowHitSelf.FloatValue > 0.0 && GetRandomFloat() <= hBadThrowHitSelf.FloatValue) { TeleportEntity(entity, pos, NULL_VECTOR, NULL_VECTOR); + ExplodeProjectile(entity); + } SpawnItem("pipe_bomb", pos); } + } else if(Trolls[slipperyShoesIndex].IsActive(entOwner)) { + if(Trolls[slipperyShoesIndex].activeFlagClients[entOwner] & 4) { + L4D_StaggerPlayer(entOwner, entOwner, NULL_VECTOR); + } } } } @@ -738,13 +791,12 @@ public bool TraceEntityFilterPlayer(int entity, int mask, any data) { float iLastAntiRushEvent[MAXPLAYERS+1]; public Action OnAntiRush(int client, int &type, float distance) { - PrintToConsoleAll("[FTT] Antirush: %N (dist=%d) (GameTime=%f)", client, distance, GetGameTime()); if(type == 3 && IsPlayerAlive(client) && !IsPlayerIncapped(client)) { if(GetGameTime() - iLastAntiRushEvent[client] > 30.0) { - SpecialType special = view_as(GetRandomInt(0,6)); + SpecialType special = view_as(GetRandomInt(1,6)); iLastAntiRushEvent[client] = GetGameTime(); - SpawnSpecialNear(client, special); - PrintToConsoleAll("[FTT] Spawning anti-rush special on %N (dist=%f) (special=%d)", client, distance, special); + SpawnSpecialForTarget(special, client); + PrintToConsoleAll("[FTT] Spawning anti-rush special on %N (dist=%f) (special=%N)", client, distance, SPECIAL_NAMES[view_as(special)-1]); } } } \ No newline at end of file diff --git a/scripting/include/feedthetrolls/menus.inc b/scripting/include/feedthetrolls/menus.inc index 5762eff..8f0cd73 100644 --- a/scripting/include/feedthetrolls/menus.inc +++ b/scripting/include/feedthetrolls/menus.inc @@ -38,14 +38,14 @@ public int Insta_SpecialHandler(Menu menu, MenuAction action, int client, int pa } SpecialType special = view_as(specialInt); if(inFace) { - if(SpawnSpecialInFace(target, special)) { + if(SpawnSpecialForTarget(special, target, Special_OnTarget)) { LogAction(client, target, "\"%L\" spawned Insta-%s™ on \"%L\"", client, SPECIAL_NAMES[specialInt-1], target); ShowActivityEx(client, "[FTT] ", "spawned Insta-%s™ on %N", SPECIAL_NAMES[specialInt-1], target); } else { ReplyToCommand(client, "Could not spawn special."); } } else { - if(SpawnSpecialNear(target, special)) { + if(SpawnSpecialForTarget(special, target)) { LogAction(client, target, "\"%L\" spawned Insta-%s™ near \"%L\"", client, SPECIAL_NAMES[specialInt-1], target); ShowActivityEx(client, "[FTT] ", "spawned Insta-%s™ near %N", SPECIAL_NAMES[specialInt-1], target); } else { @@ -488,7 +488,7 @@ void ShowThrowItAllMenu(int client, int userid) { } // Grab all the items the player has, add to menu - for(int slot = 0; slot <= 4; slot++) { + for(int slot = 0; slot <= 5; slot++) { int item = GetPlayerWeaponSlot(victim, slot); if(item > -1) { GetEdictClassname(item, itmName, sizeof(itmName)); diff --git a/scripting/include/feedthetrolls/misc.inc b/scripting/include/feedthetrolls/misc.inc index 32889f8..3c854df 100644 --- a/scripting/include/feedthetrolls/misc.inc +++ b/scripting/include/feedthetrolls/misc.inc @@ -367,4 +367,12 @@ bool SetBotTarget(int bot, int target, int targetHP, int loops = 15) { } else { return false; } -} \ No newline at end of file +} + +// Taken from https://forums.alliedmods.net/showthread.php?t=220132&page=2 +stock void ExplodeProjectile(int entity) { + SetEntProp(entity, Prop_Data, "m_takedamage", 2); + SetEntProp(entity, Prop_Data, "m_iHealth", 1); + SDKHooks_TakeDamage(entity, 0, 0, 1.0); + SetEntProp(entity, Prop_Data, "m_nNextThinkTick", 1); //for smoke +} diff --git a/scripting/include/feedthetrolls/specials.inc b/scripting/include/feedthetrolls/specials.inc index fbc9218..40c0a54 100644 --- a/scripting/include/feedthetrolls/specials.inc +++ b/scripting/include/feedthetrolls/specials.inc @@ -1,6 +1,4 @@ -char SPECIAL_NAMES[][] = { - "Smoker", "Boomer", "Hunter", "Spitter", "Jockey", "Charger", "Witch", "Tank" -}; + enum SpecialType { Special_Invalid = -1, Special_Smoker = 1, @@ -13,6 +11,104 @@ enum SpecialType { Special_Tank } + +enum struct SpecialSpawnRequest { + SpecialType type; + int targetUserId; + float position[3]; + float angle[3]; + int flags; +} + +ArrayList g_spSpawnQueue; + +// Finds position, sends to SpawnSpecialAtPosition +// target client index +stock bool SpawnSpecialForTarget(SpecialType specialType, int target, SpecialSpawnFlags spawnFlags = Special_Anywhere) { + static float pos[3]; + int internalFlags = 0; + if(spawnFlags == Special_OnTarget) { + static float ang[3]; + static float testPos[3]; + testPos = pos; + + GetClientAbsOrigin(target, pos); + GetClientEyeAngles(target, ang); + + if(specialType == Special_Boomer) + internalFlags |= view_as(SPI_KillOnSpawn); + + if(specialType == Special_Jockey || specialType == Special_Boomer) { // Else spawn a little bit off, and above (above for jockeys) + pos[2] += 25.0; + pos[0] += 5.0; + } else { //If not jockey/hunter find a suitable area that is at least 5 m away + float minDistance = GetIdealMinDistance(specialType); + GetHorizontalPositionFromOrigin(pos, ang, minDistance, testPos); + if(!FindSuitablePosition(pos, testPos, minDistance, 100)) { + 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 false; +} + +// Target is optional +stock bool SpawnSpecialAtPosition(SpecialType special, const float destination[3], const float angle[3], int target = 0, int flags) { + SpecialSpawnRequest request; + request.type = special; + if(target) + request.targetUserId = GetClientUserId(target); + request.flags = flags; + request.position = destination; + request.angle = angle; + g_spSpawnQueue.PushArray(request); + if(!spIsActive) ProcessSpecialQueue(); + return true; +} + +SpecialSpawnRequest spActiveRequest; + +bool ProcessSpecialQueue() { + if(g_spSpawnQueue.Length > 0) { + if(g_spSpawnQueue.GetArray(0, spActiveRequest, sizeof(spActiveRequest))) { + spIsActive = true; + } + g_spSpawnQueue.Erase(0); + + int target = GetClientOfUserId(spActiveRequest.targetUserId); + if(view_as(spActiveRequest.type) <= 6) { + // CreateTimer(2.0, Timer_CheckSpecialSpawn, spCurrentId); + int bot = CreateFakeClient("FTTSpecialBot"); + if (bot != 0) { + ChangeClientTeam(bot, 3); + CreateTimer(0.1, Timer_KickBot, bot); + } + CheatCommand(target, "z_spawn_old", SPECIAL_NAMES[view_as(spActiveRequest.type) - 1], "auto"); + } else if(spActiveRequest.type == Special_Witch) { + int witch = L4D2_SpawnWitch(spActiveRequest.position, spActiveRequest.angle); + if(witch != -1) + SetWitchTarget(witch, target); + return ProcessSpecialQueue(); + } else if(spActiveRequest.type == Special_Tank) { + // BypassLimit(); + int tank = L4D2_SpawnTank(spActiveRequest.position, spActiveRequest.angle); + if(tank > 0 && IsClientConnected(tank)) + g_iAttackerTarget[tank] = spActiveRequest.targetUserId; + return ProcessSpecialQueue(); + } + return true; + } + spIsActive = false; + return false; +} + stock SpecialType GetSpecialType(const char[] input) { for(int i = 0; i < 8; i++) { if(strcmp(SPECIAL_NAMES[i], input, false) == 0) return view_as(i + 1); @@ -47,85 +143,6 @@ float GetIdealMinDistance(SpecialType specialType) { } } -bool SpawnSpecialInFace(int target, SpecialType specialType) { - static float pos[3], ang[3]; - static float testPos[3]; - testPos = pos; - - GetClientAbsOrigin(target, pos); - GetClientEyeAngles(target, ang); - - if(specialType == Special_Boomer) - gInstaSpecialInstaKill = true; - - if(specialType == Special_Jockey || specialType == Special_Boomer) { // Else spawn a little bit off, and above (above for jockeys) - pos[2] += 25.0; - pos[0] += 5.0; - } else { //If not jockey/hunter find a suitable area that is at least 5 m away - float minDistance = GetIdealMinDistance(specialType); - GetHorizontalPositionFromOrigin(pos, ang, minDistance, testPos); - if(!FindSuitablePosition(pos, testPos, minDistance, 100)) { - L4D_GetRandomPZSpawnPosition(target, view_as(specialType), 10, testPos); - } - pos = testPos; - } - pos[2] += 1.0; - // NegateVector(ang); - - return SpawnSpecialInternal(specialType, target, pos, NULL_VECTOR) != -1; -} - -bool SpawnSpecialNear(int target, SpecialType type) { - gInstaSpecialInstaKill = false; - static float pos[3]; - if(L4D_GetRandomPZSpawnPosition(target, view_as(type), 10, pos)) { - return SpawnSpecialInternal(type, target, pos, NULL_VECTOR) != -1; - } - return false; -} - -// doesnt seem to work with l4dhooks methods -void BypassLimit() { - int bot = CreateFakeClient("Infected Bot"); - if (bot != 0) { - ChangeClientTeam(bot, 3); - CreateTimer(0.1, Timer_KickBot, bot); - } -} - -int SpawnSpecialInternal(SpecialType type, int target, float pos[3], float ang[3]) { - if(view_as(type) <= 6) { - // Bypass limit: - gInstaSpecialType = view_as(type); - gInstaSpecialTarget = GetClientUserId(target); - gInstaSpecialSpawnPos = pos; - gInstaSpecialSpawnAng = pos; - CreateTimer(2.0, Timer_InstaFailed); - int bot = CreateFakeClient("ManualDirectorBot"); - if (bot != 0) { - ChangeClientTeam(bot, 3); - CreateTimer(0.1, Timer_KickBot, bot); - } - CheatCommand(target, "z_spawn_old", SPECIAL_NAMES[view_as(type) - 1], "auto"); - return 0; - } - else if(type == Special_Witch) { - int witch = L4D2_SpawnWitch(pos, ang); - if(witch != -1) - SetWitchTarget(witch, target); - return witch; - } - else if(type == Special_Tank) { - // BypassLimit(); - int tank = L4D2_SpawnTank(pos, ang); - if(tank <= 0 || !IsClientConnected(tank)) return -1; - if(tank != -1) - g_iAttackerTarget[tank] = GetClientUserId(target); - return tank; - } - else return -1; -} - stock bool GetGroundBehind(int client, float vPos[3], float vAng[3]) { GetClientEyePosition(client, vPos); GetClientEyeAngles(client, vAng); diff --git a/scripting/include/feedthetrolls/timers.inc b/scripting/include/feedthetrolls/timers.inc index fcbd383..bd8468e 100644 --- a/scripting/include/feedthetrolls/timers.inc +++ b/scripting/include/feedthetrolls/timers.inc @@ -11,18 +11,25 @@ public Action Timer_ThrowTimer(Handle timer) { } public Action Timer_Main(Handle timer) { - static int loop; + static int loopTick; + static int slowDrainIndex; + if(!slowDrainIndex) slowDrainIndex = GetTrollID("Slow Drain"); + static int tempHealthQuickDrainIndex; + if(!tempHealthQuickDrainIndex) tempHealthQuickDrainIndex = GetTrollID("Temp Health Quick Drain"); + static int swarmIndex; + if(!swarmIndex) swarmIndex = GetTrollID("Swarm"); + for(int i = 1; i <= MaxClients; i++) { if(IsClientConnected(i) && IsClientInGame(i) && IsPlayerAlive(i)) { - if(IsTrollActive(i, "Slow Drain")) { - if(loop % 4 == 0) { + if(Trolls[slowDrainIndex].IsActive(i)) { + if(loopTick % 4 == 0) { int hp = GetClientHealth(i); if(hp > 50) { SetEntProp(i, Prop_Send, "m_iHealth", hp - 1); } } - }else if(IsTrollActive(i, "Temp Health Quick Drain")) { - if(loop % 2 == 0) { + }else if(Trolls[tempHealthQuickDrainIndex].IsActive(i)) { + if(loopTick % 2 == 0) { float bufferTime = GetEntPropFloat(i, Prop_Send, "m_healthBufferTime"); float buffer = GetEntPropFloat(i, Prop_Send, "m_healthBuffer"); float tempHealth = GetTempHealth(i); @@ -32,13 +39,17 @@ public Action Timer_Main(Handle timer) { SetEntPropFloat(i, Prop_Send, "m_healthBufferTime", bufferTime - 7.0); } } - }else if(IsTrollActive(i, "Swarm")) { + }else if(Trolls[swarmIndex].IsActive(i)) { L4D2_RunScript("RushVictim(GetPlayerFromUserID(%d), %d)", GetClientUserId(i), 15000); + } else if(Trolls[slipperyShoesIndex].IsActive(i) && Trolls[slipperyShoesIndex].activeFlagClients[i] & 1) { + if(GetRandomFloat() <= 0.4) { + L4D_StaggerPlayer(i, i, NULL_VECTOR); + } } } } - if(++loop >= 60) { - loop = 0; + if(++loopTick >= 60) { + loopTick = 0; } return Plugin_Continue; } @@ -102,13 +113,6 @@ public Action Timer_KickBot(Handle timer, int client) { } } -public Action Timer_InstaFailed(Handle h) { - if(gInstaSpecialType != -1) { - gInstaSpecialType = -1; - gInstaSpecialTarget = 0; - } -} - public Action Timer_ShootReverse(Handle h, DataPack pack) { pack.Reset(); int attacker = pack.ReadCell(); diff --git a/scripting/include/feedthetrolls/trolls.inc b/scripting/include/feedthetrolls/trolls.inc index 7b5486e..14b0a7e 100644 --- a/scripting/include/feedthetrolls/trolls.inc +++ b/scripting/include/feedthetrolls/trolls.inc @@ -1,5 +1,7 @@ // UP THE VALUE 'MAX_TROLLS' in base.inc before adding new ones! +int slipperyShoesIndex = 0; + void SetupTrolls() { trollKV = new StringMap(); categories = new ArrayList(ByteCountToCells(16)); @@ -7,6 +9,7 @@ void SetupTrolls() { int index; SetupTroll("Reset User", "Resets the user, removes all troll effects", TrollMod_Instant); + /// CATEGORY: Magnets SetCategory("Magnets"); index = SetupTroll("Special Magnet", "Attracts ALL specials to any alive target with this troll enabled", TrollMod_Constant); AddMagnetFlags(index); @@ -14,13 +17,23 @@ void SetupTrolls() { AddMagnetFlags(index); index = SetupTroll("Witch Magnet", "All witches when startled will target any player with this troll", TrollMod_Constant); + /// 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); 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); + index = SetupTroll("Amazon Special Combo", "Choose multiple different specials to spawn", TrollMod_Instant); + Trolls[index].AddFlagPrompt(true); + for(int i = 0; i < 8; i++) { + Trolls[index].AddFlag(SPECIAL_NAMES[i], false); // 1 << 7 max + } + Trolls[index].AddFlagPrompt(false); + Trolls[index].AddFlag("Spawn On Face", false); // 1 << 8 + Trolls[index].AddFlag("Spawn Near", false); // 1 << 9 + // CATEGORY: Items SetCategory("Items"); index = SetupTroll("Throw It All", "Player throws their item(s) periodically to a nearby player", TrollMod_Instant); //Can't add directly, is custom troll: @@ -32,6 +45,11 @@ void SetupTrolls() { Trolls[index].AddFlag("Biles", true); Trolls[index].AddFlag("Molotovs", true); Trolls[index].AddFlag("Pipebombs", true); + index = SetupTroll("Spicy Gas", "Gascans player picks up just ignite. Magic.", TrollMod_Constant); + Trolls[index].AddFlagPrompt(false); + Trolls[index].AddFlag("Always (100%)", false); + Trolls[index].AddFlag("Half Time (50%)", true); + Trolls[index].AddFlag("Rare (10%)", false); index = SetupTroll("No Pickup", "Prevents a player from picking up ANY (new) item. Use ThrowItAll to make them drop", TrollMod_Constant); Trolls[index].AddFlagPrompt(true); Trolls[index].AddFlag("No Primary", false); @@ -39,6 +57,7 @@ void SetupTrolls() { Trolls[index].AddFlag("No Throwables", true); Trolls[index].AddFlag("No Kits", true); Trolls[index].AddFlag("No Pills / Adr", true); + Trolls[index].AddFlag("No GASCANS", true); index = SetupTroll("UziRules / AwpSmells", "Picking up a weapon gives them a UZI or AWP instead", TrollMod_Constant); Trolls[index].AddFlagPrompt(false); Trolls[index].AddFlag("UZI Only", true); @@ -51,6 +70,7 @@ void SetupTrolls() { Trolls[index].AddFlag("Rare (10%)", false); SetupTroll("Half Primary Ammo", "Cuts their primary reserve ammo in half", TrollMod_Instant); + /// CATEGORY: Chat SetCategory("Chat"); SetupTroll("iCantSpellNoMore", "Chat messages letter will randomly changed with wrong letters", TrollMod_Constant); index = SetupTroll("No Profanity", "Replaces some words with random phrases", TrollMod_Constant); @@ -75,6 +95,7 @@ void SetupTrolls() { SetupTroll("Reversed", "Reserves their message", TrollMod_Constant); SetupTroll("Voice Mute", "Mutes from voice", TrollMod_Constant); + /// CATEGORY: Health SetCategory("Health"); SetupTroll("Damage Boost", "Makes a player take more damage than normal", TrollMod_Constant); SetupTroll("Temp Health Quick Drain", "Makes a player's temporarily health drain very quickly", TrollMod_Constant); @@ -87,15 +108,28 @@ void SetupTrolls() { Trolls[index].AddFlag("0.5x Ratio", false); //4 Trolls[index].AddFlag("0.0x Ratio (None)", false); //8 Trolls[index].AddFlag("3x Ratio", false); //16 + Trolls[index].AddFlag("-2x Ratio", false); //16 - SetCategory("Misc"); - SetupTroll("Gun Jam", "On reload, small chance their gun gets jammed - Can't reload.", TrollMod_Constant); + /// CATEGORY: Movement + SetCategory("Movement"); SetupTroll("Slow Speed", "Sets player speed to 0.8x of normal speed", TrollMod_Constant); SetupTroll("Higher Gravity", "Sets player gravity to 1.3x of normal gravity", TrollMod_Constant); - SetupTroll("No Shove", "Prevents a player from shoving", TrollMod_Constant); - SetupTroll("CameTooEarly", "When they shoot, random chance they empty whole clip", TrollMod_Constant); SetupTroll("Inverted Controls", "Well, aint it obvious", TrollMod_Constant); SetupTroll("Stagger", "Like a slap, but different", TrollMod_Instant); + index = SetupTroll("Slippery Shoes", "Periodically stumbles around.", TrollMod_Constant); + Trolls[index].AddFlagPrompt(true); + Trolls[index].AddFlag("Periodically", true); + Trolls[index].AddFlag("When using doors", false); + Trolls[index].AddFlag("On throwable use", false); + Trolls[index].AddFlag("On pills/adrenaline use", false); + Trolls[index].AddFlag("On zombie bite", false); + slipperyShoesIndex = index; + + /// CATEGORY: MISC + SetCategory("Misc"); + SetupTroll("Gun Jam", "On reload, small chance their gun gets jammed - Can't reload.", TrollMod_Constant); + SetupTroll("No Shove", "Prevents a player from shoving", TrollMod_Constant); + SetupTroll("CameTooEarly", "When they shoot, random chance they empty whole clip", TrollMod_Constant); index = SetupTroll("Meta: Inverse", "Uhm you are not supposed to see this...", TrollMod_Instant); Trolls[index].hidden = true; Trolls[index].AddFlagPrompt(false); @@ -142,7 +176,6 @@ bool ApplyAffect(int victim, const Troll troll, int activator, trollModifier mod } else if(StrEqual(troll.name, "UziRules / AwpSmells")) { DisableTroll(victim, "No Pickup"); DisableTroll(victim, "Primary Disable"); - SDKHook(victim, SDKHook_WeaponCanUse, Event_ItemPickup); } else if(StrEqual(troll.name, "Primary Disable")) { DisableTroll(victim, "UziRules / AwpSmells"); DisableTroll(victim, "No Pickup"); @@ -201,6 +234,17 @@ bool ApplyAffect(int victim, const Troll troll, int activator, trollModifier mod L4D_StaggerPlayer(victim, victim, NULL_VECTOR); } else if(StrEqual(troll.name, "Voice Mute")) { BaseComm_SetClientMute(victim, !isActive); + } else if(StrEqual(troll.name, "Spicy Gas")) { + SDKHook(victim, SDKHook_WeaponCanUse, Event_ItemPickup); + } else if(StrEqual(troll.name, "Amazon Special Combo")) { + SpecialSpawnFlags spawnFlag = Special_Anywhere; + if(flags & (1 << 8)) spawnFlag = Special_OnTarget; + PrintToChatAll("spawnFlag: %d", spawnFlag); + for(int i = 1; i < 7; i++) { + if(flags & (1 << i)) { + SpawnSpecialForTarget(view_as(i), victim, spawnFlag); + } + } } 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/include/ftt.inc b/scripting/include/ftt.inc index 60f7fe8..5b71762 100644 --- a/scripting/include/ftt.inc +++ b/scripting/include/ftt.inc @@ -37,12 +37,9 @@ int lastButtonUser; int lastCrescendoUser; int g_iAttackerTarget[MAXPLAYERS+1]; int g_PendingBanTroll[MAXPLAYERS+1]; +int g_iInSpit[MAXPLAYERS+1]; +Timer g_inSpitTimer; -int gInstaSpecialType = -1; -int gInstaSpecialTarget; -float gInstaSpecialSpawnPos[3]; -float gInstaSpecialSpawnAng[3]; -bool gInstaSpecialInstaKill; int gInstaSpecialMagnet[MAXPLAYERS+1]; char steamids[MAXPLAYERS+1][64]; @@ -51,6 +48,17 @@ int shootAtTarget[MAXPLAYERS+1]; int shootAtTargetLoops[MAXPLAYERS+1]; int shootAtTargetHP[MAXPLAYERS+1]; +bool spIsActive; +enum SpecialSpawnFlags { + Special_Anywhere = 0, + Special_OnTarget = 1 +} + +enum SpecialInternalFlags { + SPI_KillOnSpawn = 1 +} + + #include #include #include diff --git a/scripting/l4d2_feedthetrolls.sp b/scripting/l4d2_feedthetrolls.sp index e7fb647..9a9f97d 100644 --- a/scripting/l4d2_feedthetrolls.sp +++ b/scripting/l4d2_feedthetrolls.sp @@ -20,6 +20,17 @@ #include +/* +TODO IDEAS: +1. [x] Instant pipebomb explosion +2. [x] Spicy gas (instant ignite), ignite propane/gas canisters +3. [x] Amazon Special Combo (flags, choose combinations) (NEED QUEUE) +4. Random weapons (on interval, possible second option of random capacity) +5. [x] Slippery shoes (periodic stagger, or on certain events) +6. (on hold) Sticky goo (slow user down in goo, or freezes) +7. Follow goo +*/ + public Plugin myinfo = { name = "L4D2 Feed The Trolls", @@ -47,6 +58,8 @@ public void OnPluginStart() { SetupTrolls(); SetupsTrollCombos(); + g_spSpawnQueue = new ArrayList(sizeof(SpecialSpawnRequest)); + // Witch target overwrite stuff: GameData data = new GameData("l4d2_behavior"); @@ -96,14 +109,19 @@ public void OnPluginStart() { HookEvent("player_death", Event_PlayerDeath); HookEvent("triggered_car_alarm", Event_CarAlarm); HookEvent("witch_harasser_set", Event_WitchVictimSet); + HookEvent("door_open", Event_DoorToggle); + HookEvent("door_close", Event_DoorToggle); + HookEvent("adrenaline_used", Event_SecondaryHealthUsed); + HookEvent("pills_used", Event_SecondaryHealthUsed); AddNormalSoundHook(view_as(SoundHook)); AutoExecConfig(true, "l4d2_feedthetrolls"); for(int i = 1; i <= MaxClients; i++) { - if(IsClientConnected(i) && IsClientInGame(i)) + if(IsClientConnected(i) && IsClientInGame(i)) { SDKHook(i, SDKHook_OnTakeDamage, Event_TakeDamage); + } } } ///////////////////////////////////////////////////////////////////////////////