From 3c88c010ade8dd273ef378443d7a39004830bb26 Mon Sep 17 00:00:00 2001 From: Jackz Date: Wed, 19 Jan 2022 14:52:52 -0600 Subject: [PATCH] scripts --- scripting/globalbans.sp | 10 +- scripting/include/feedthetrolls/base.inc | 4 +- scripting/include/feedthetrolls/commands.inc | 82 +++++---- scripting/include/feedthetrolls/events.inc | 133 +++++++++++---- scripting/include/feedthetrolls/misc.inc | 119 +++++++++++++ scripting/include/feedthetrolls/timers.inc | 29 ++++ scripting/include/feedthetrolls/trolls.inc | 28 +-- scripting/include/ftt.inc | 8 + scripting/l4d2_TKStopper.sp | 171 +++++++++---------- scripting/l4d2_ai_minigun.sp | 4 +- scripting/l4d2_extraplayeritems.sp | 26 ++- scripting/l4d2_feedthetrolls.sp | 19 ++- scripting/l4d2_hideandseek.sp | 14 +- scripting/l4d2_population_control.sp | 2 +- scripting/l4d2_tank_priority.sp | 24 ++- scripting/l4d_survivor_identity_fix.sp | 52 +++--- scripting/sm_player_notes.sp | 10 +- 17 files changed, 503 insertions(+), 232 deletions(-) diff --git a/scripting/globalbans.sp b/scripting/globalbans.sp index edf46bb..f996629 100644 --- a/scripting/globalbans.sp +++ b/scripting/globalbans.sp @@ -62,7 +62,7 @@ public void OnClientAuthorized(int client, const char[] auth) { if(!StrEqual(auth, "BOT", true)) { static char query[256], ip[32]; GetClientIP(client, ip, sizeof(ip)); - Format(query, sizeof(query), "SELECT `reason`, `steamid`, `expired` FROM `bans` WHERE `steamid` = 'STEAM_%:%:%s' OR ip = '?'", auth[10], ip); + Format(query, sizeof(query), "SELECT `reason`, `steamid`, `expired` FROM `bans` WHERE `steamid` LIKE 'STEAM_%:%:%s' OR ip = '?'", auth[10], ip); g_db.Query(DB_OnConnectCheck, query, GetClientUserId(client), DBPrio_High); } } @@ -114,13 +114,13 @@ public Action OnBanClient(int client, int time, int flags, const char[] reason, static char query[255]; static char expiresDate[64]; if(time > 0) { - Format(expiresDate, sizeof(expiresDate), "%d", GetTime() + (time * 60000)); - }else{ + Format(expiresDate, sizeof(expiresDate), "%d", GetTime() + (time * 60)); + } else { Format(expiresDate, sizeof(expiresDate), "NULL"); } Format(query, sizeof(query), "INSERT INTO bans" ..."(steamid, ip, reason, expires, executor, ip_banned)" - ..."VALUES ('%s', '%s', '%s', %s, '%s', 0)", + ..."VALUES ('%s', '%s', '%s', FROM_UNIXTIME(%s), '%s', 0)", identity, ip, reason, @@ -180,7 +180,7 @@ public void DB_OnConnectCheck(Database db, DBResultSet results, const char[] err g_db.Format(query, sizeof(query), "UPDATE bans SET times_tried=times_tried+1 WHERE steamid = '%s'", steamid); g_db.Query(DB_OnBanQuery, query); }else{ - DeleteBan(steamid); + PrintChatToAdmins("%N has a previously expired ban of reason \"%s\"", client, reason); } } } diff --git a/scripting/include/feedthetrolls/base.inc b/scripting/include/feedthetrolls/base.inc index a146a0b..92fff17 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 33 + #define MAX_TROLLS 34 #endif enum trollModifier { @@ -224,7 +224,7 @@ void ResetClient(int victim, bool wipe = true) { Trolls[i].activeFlagClients[victim] = -1; } } - + BaseComm_SetClientMute(victim, false); SetEntityGravity(victim, 1.0); SetEntPropFloat(victim, Prop_Send, "m_flLaggedMovementValue", 1.0); SDKUnhook(victim, SDKHook_WeaponCanUse, Event_ItemPickup); diff --git a/scripting/include/feedthetrolls/commands.inc b/scripting/include/feedthetrolls/commands.inc index a1cb430..dbd3b11 100644 --- a/scripting/include/feedthetrolls/commands.inc +++ b/scripting/include/feedthetrolls/commands.inc @@ -213,9 +213,11 @@ public Action Command_ResetUser(int client, int args) { } for (int i = 0; i < target_count; i++) { + if(IsAnyTrollActive(target_list[i])) { + LogAction(client, target_list[i], "\"%L\" reset all troll effects for \"%L\"", client, target_list[i]); + ShowActivityEx(client, "[FTT] ", "reset troll effects for \"%N\". ", target_list[i]); + } ResetClient(target_list[i], true); - LogAction(client, target_list[i], "\"%L\" reset all troll effects for \"%L\"", client, target_list[i]); - ShowActivityEx(client, "[FTT] ", "reset troll effects for \"%N\". ", target_list[i]); } } return Plugin_Handled; @@ -494,38 +496,6 @@ public Action Command_MarkPendingTroll(int client, int args) { return Plugin_Handled; } -public Action Command_MarkNoob(int client, int args) { - if(args == 0) { - ReplyToCommand(client, "sm_noob "); - return Plugin_Handled; - } - static char target_name[MAX_TARGET_LENGTH]; - GetCmdArg(1, target_name, sizeof(target_name)); - - int target_list[MAXPLAYERS], target_count; - bool tn_is_ml; - if ((target_count = ProcessTargetString( - target_name, - client, - target_list, - 1, - COMMAND_FILTER_NO_MULTI, /* Only allow alive players */ - target_name, - sizeof(target_name), - tn_is_ml)) <= 0 - ) { - /* This function replies to the admin with a failure message */ - ReplyToTargetError(client, target_count); - return Plugin_Handled; - } - int target = target_list[0]; - //Todo: Check if marked as noob or not, undo if so, add if not - - ShowActivityEx(client, "[FTT] ", "marked %N as a noob", target_name); - LogAction(client, target, "\"%L\" marked \"%L\" as a noob", client, target); - return Plugin_Handled; -} - public Action Command_FeedTheTrollMenu(int client, int args) { ReplyToCommand(client, "sm_ftl [player(s)] - Lists all the active trolls on players. Will show flag names if a player is specified."); ReplyToCommand(client, "sm_ftm - Lists all available trolls & descriptions"); @@ -536,3 +506,47 @@ public Action Command_FeedTheTrollMenu(int client, int args) { ReplyToCommand(client, "sm_mark - Marks the user to be banned on disconnect, prevents their FF."); return Plugin_Handled; } + +public Action Command_BotsAttack(int client, int args) { + if(args > 0) { + static char arg1[32], arg2[4]; + GetCmdArg(1, arg1, sizeof(arg1)); + GetCmdArg(2, arg2, sizeof(arg2)); + + int targetHP; + if(StringToIntEx(arg2, targetHP) == 0 || targetHP < 0 || targetHP > 100) { + ReplyToCommand(client, "Invalid target HP value. Must be between 0 and 100"); + return Plugin_Handled; + } + + int target_list[MAXPLAYERS], target_count; + static char target_name[MAX_TARGET_LENGTH]; + bool tn_is_ml; + if ((target_count = ProcessTargetString( + arg1, + client, + target_list, + 1, + COMMAND_FILTER_ALIVE | COMMAND_FILTER_NO_MULTI, + target_name, + sizeof(target_name), + tn_is_ml)) <= 0 + ) { + /* This function replies to the admin with a failure message */ + ReplyToTargetError(client, target_count); + return Plugin_Handled; + } + int target = target_list[0]; + for(int i = 1; i <= MaxClients; i++) { + if(IsClientConnected(i) && IsClientInGame(i) && IsFakeClient(i) && GetClientTeam(i) == 2) { + if(!SetBotTarget(target, i, targetHP, 80)) { + ReplyToCommand(client, "%N could not target %s", i, target_name); + } + } + } + ShowActivity(client, "set all bots to attack %s", target_name); + } else { + ReplyToCommand(client, "syntax: sm_bots_attack [target-hp]"); + } + return Plugin_Handled; +} \ No newline at end of file diff --git a/scripting/include/feedthetrolls/events.inc b/scripting/include/feedthetrolls/events.inc index 73725e7..0ddf67f 100644 --- a/scripting/include/feedthetrolls/events.inc +++ b/scripting/include/feedthetrolls/events.inc @@ -5,6 +5,7 @@ public void OnMapEnd() { UnhookEntityOutput("func_button", "OnPressed", Event_ButtonPress); } public void OnMapStart() { + if(hBotReverseFFDefend.IntValue > 0) hSbFriendlyFire.BoolValue = true; AddFileToDownloadsTable("sound/custom/meow1.mp3"); PrecacheSound("custom/meow1.mp3"); @@ -18,6 +19,9 @@ public void OnMapStart() { } public void OnClientPutInServer(int client) { g_PendingBanTroll[client] = 0; + shootAtTarget[client] = 0; + if(IsTrollActive(client, "Voice Mute")) + BaseComm_SetClientMute(client, true); SDKHook(client, SDKHook_OnTakeDamage, Event_TakeDamage); } public void Event_PlayerSpawn(Event event, const char[] name, bool dontBroadcast) { @@ -35,13 +39,14 @@ public Action Timer_CheckSpecial(Handle h, int specialID) { TeleportEntity(special, gInstaSpecialSpawnPos, gInstaSpecialSpawnAng, NULL_VECTOR); if(gInstaSpecialInstaKill) { - SDKHooks_TakeDamage(special, gInstaSpecialTarget, gInstaSpecialTarget, 1000.0); + RequestFrame(Frame_Boom, special); } } } } -public void Frame_InstaSpawned(int special) { - +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")); @@ -63,6 +68,9 @@ public void Event_PlayerDisconnect(Event event, const char[] name, bool dontBroa } steamids[client][0] = '\0'; g_iAttackerTarget[client] = 0; + shootAtTarget[client] = 0; + shootAtTargetLoops[client] = 0; + shootAtTargetHP[client] = 0; } public Action Event_PlayerDeath(Event event, const char[] name, bool dontBroadcast) { int client = GetClientOfUserId(event.GetInt("userid")); @@ -70,7 +78,7 @@ public Action Event_PlayerDeath(Event event, const char[] name, bool dontBroadca int target = GetClientOfUserId(g_iAttackerTarget[client]); gInstaSpecialMagnet[target]--; if(gInstaSpecialMagnet[target] == 0) { - PrintToServer("[FTT] gInstaSpecialMagnet droped below 0"); + PrintToServer("[FTT] gInstaSpecialMagnet dropped below 0"); gInstaSpecialMagnet[target] = 0; } } @@ -113,15 +121,13 @@ public void RushPlayer(int user) { L4D2_RunScript("RushVictim(GetPlayerFromUserID(%d), %d)", user, 15000); } public Action L4D2_OnChooseVictim(int attacker, int &curTarget) { - // ========================= - // OVERRIDE VICTIM - // ========================= static int spMagnetID, tankMagnetID; 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(g_iAttackerTarget[attacker]); if(existingTarget > 0 && IsPlayerAlive(existingTarget)) { if(gInstaSpecialMagnet[existingTarget] > 0) { @@ -136,21 +142,24 @@ public Action L4D2_OnChooseVictim(int attacker, int &curTarget) { } } + // If no existing target, find closest valid victim float closestDistance, survPos[3], spPos[3]; GetClientAbsOrigin(attacker, spPos); int closestClient = -1; for(int i = 1; i <= MaxClients; i++) { if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2 && IsPlayerAlive(i)) { //Ignore incapped players if turned on: - if(IsPlayerIncapped(i)) { - if((class == L4D2Infected_Tank && hMagnetTargetMode.IntValue & 2) || (class != L4D2Infected_Tank && hMagnetTargetMode.IntValue & 1)) continue; - } + if(class == L4D2Infected_Tank && Trolls[tankMagnetID].IsActive(i) || (class != L4D2Infected_Tank && Trolls[spMagnetID].IsActive(i))) { if(class == L4D2Infected_Tank) { if(!WillMagnetRun(Trolls[tankMagnetID], i)) return Plugin_Continue; } else if(!WillMagnetRun(Trolls[spMagnetID], i)) return Plugin_Continue; + if(IsPlayerIncapped(i)) { + if((class == L4D2Infected_Tank && hMagnetTargetMode.IntValue & 2) || (class != L4D2Infected_Tank && hMagnetTargetMode.IntValue & 1)) continue; + } + GetClientAbsOrigin(i, survPos); float dist = GetVectorDistance(survPos, spPos, true); if(closestClient == -1 || dist < closestDistance) { @@ -160,7 +169,7 @@ public Action L4D2_OnChooseVictim(int attacker, int &curTarget) { } } } - + // If found, set, else just let game decide if(closestClient > 0) { g_iAttackerTarget[attacker] = GetClientUserId(closestClient); curTarget = closestClient; @@ -179,20 +188,24 @@ bool WillMagnetRun(const Troll troll, int i) { cChance = 0.1; return GetRandomFloat() <= cChance; } + public Action L4D2_OnEntityShoved(int client, int entity, int weapon, float vecDir[3], bool bIsHighPounce) { if(client > 0 && client <= MaxClients && IsTrollActive(client, "No Shove") && hShoveFailChance.FloatValue > GetRandomFloat()) { return Plugin_Handled; } return Plugin_Continue; } + public Action OnClientSayCommand(int client, const char[] command, const char[] sArgs) { - if(sArgs[0] == '@') return Plugin_Continue; + if(sArgs[0] == '@') return Plugin_Continue; //Ignore admin chat + static int honkID; static int profanityID; if(honkID == 0) honkID = GetTrollID("Honk / Meow"); if(profanityID == 0) profanityID = GetTrollID("No Profanity"); if(Trolls[honkID].IsActive(client) && Trolls[honkID].activeFlagClients[client] & 1) { + // Honk Processing static char strings[32][7]; int words = ExplodeString(sArgs, " ", strings, sizeof(strings), 5); for(int i = 0; i < words; i++) { @@ -308,8 +321,8 @@ public Action OnClientSayCommand(int client, const char[] command, const char[] CPrintToChatAll("{blue}%N {default}: %s", client, newMessage); return Plugin_Handled; }else if(Trolls[profanityID].IsActive(client)) { - static char strings[32][MAX_PHRASE_LENGTH]; - ArrayList phrases; + char strings[32][MAX_PHRASE_LENGTH]; + static ArrayList phrases; bool foundWord = false; int words = ExplodeString(sArgs, " ", strings, 32, MAX_PHRASE_LENGTH); // Replace all swear words @@ -324,14 +337,17 @@ public Action OnClientSayCommand(int client, const char[] command, const char[] char[] message = new char[length]; if(foundWord) { + // Found at least one word, keep modified intact ImplodeStrings(strings, 32, " ", message, length); - } else if(Trolls[profanityID].activeFlagClients[client] & 2) { //Replace full messages if flag enabled + } else if(Trolls[profanityID].activeFlagClients[client] & 2) { + // Replace full message content if flag enabled if(!fullMessagePhraseList) { PrintToServer("[FTT] Error: Could not find full message phrases!!!"); return Plugin_Continue; } fullMessagePhraseList.GetString(GetRandomInt(0, fullMessagePhraseList.Length - 1), message, MAX_PHRASE_LENGTH); } else { + // Flag off, keep original text return Plugin_Continue; } @@ -351,6 +367,8 @@ public Action OnClientSayCommand(int client, const char[] command, const char[] } return Plugin_Continue; } +static char SMG[8] = "smg"; +static char AWP[16] = "sniper_awp"; public Action Event_ItemPickup(int client, int weapon) { if(IsTrollActive(client, "No Pickup")) { @@ -358,38 +376,45 @@ public Action Event_ItemPickup(int client, int weapon) { }else{ static char wpnName[64]; GetEdictClassname(weapon, wpnName, sizeof(wpnName)); - if(StrContains(wpnName, "rifle") > -1 - || StrContains(wpnName, "smg") > -1 - || StrContains(wpnName, "weapon_grenade_launcher") > -1 - || StrContains(wpnName, "sniper") > -1 + if(strcmp(wpnName[7], "rifle") >= 0 + || strcmp(wpnName[7], "smg") >= 0 + || StrEqual(wpnName[7], "grenade_launcher") + || 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(IsTrollActive(client, "UziRules")) { + if(Trolls[UziRulesIndex].IsActive(client)) { + static char comp[16]; + if(Trolls[UziRulesIndex].activeFlagClients[client] & 1) + strcopy(comp, sizeof(comp), SMG); + else + strcopy(comp, sizeof(comp), AWP); static char currentWpn[32]; GetClientWeaponName(client, 0, currentWpn, sizeof(currentWpn)); - if(StrEqual(wpnName, "weapon_smg", true)) { + if(StrEqual(wpnName[7], comp)) { return Plugin_Continue; - } else if(StrEqual(currentWpn, "weapon_smg", true)) { + } else if(StrEqual(currentWpn[7], comp)) { return Plugin_Stop; - }else{ + } else { int flags = GetCommandFlags("give"); SetCommandFlags("give", flags & ~FCVAR_CHEAT); - FakeClientCommand(client, "give smg"); + FakeClientCommand(client, "give %s", comp); SetCommandFlags("give", flags); return Plugin_Stop; } - }else if(IsTrollActive(client, "Primary Disable")) { + } else if(IsTrollActive(client, "Primary Disable")) { return Plugin_Stop; } - return Plugin_Continue; - }else{ - return Plugin_Continue; } + + 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]) { + // If 'KillMeSoftly' activated: if(g_bPendingItemGive[client] && !(buttons & IN_ATTACK2)) { int target = GetClientAimTarget(client, true); if(target > -1) { @@ -399,6 +424,24 @@ public Action OnPlayerRunCmd(int client, int& buttons, int& impulse, float vel[3 } return Plugin_Continue; } + + if (shootAtTarget[client] > 0 && (buttons & IN_ATTACK) == 0) { + // If so, block their crouching (+duck) + if(GetClientAimTarget(client, true) == shootAtTarget[client]) { + if(!IsActorBusy(client)) + PerformScene(client, "PlayerLaugh"); + buttons |= IN_ATTACK; + return Plugin_Changed; + } else { + if(!IsClientConnected(shootAtTarget[client])) { + shootAtTarget[client] = 0; + } else { + LookAtClient(client, shootAtTarget[client]); + } + } + } + + // Inverted control code: static int invertedTrollIndex; if(invertedTrollIndex == 0) invertedTrollIndex = GetTrollID("Inverted Controls"); if(Trolls[invertedTrollIndex].IsActive(client)) { @@ -432,7 +475,9 @@ 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 && IsClientInGame(attacker) && IsPlayerAlive(attacker)) { + if(GetClientTeam(attacker) == 4 && IsFakeClient(attacker)) return Plugin_Stop; + 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; } @@ -455,15 +500,33 @@ public Action Event_TakeDamage(int victim, int& attacker, int& inflictor, float& damage = 0.0; return Plugin_Changed; } + + if(damagetype & DMG_BURN || damagetype & DMG_BLAST) return Plugin_Continue; + if(hBotReverseFFDefend.IntValue > 0 && IsFakeClient(attacker) && shootAtTarget[attacker] == 0 && GetClientTeam(attacker) == 2 && GetClientTeam(victim) == 2) return Plugin_Stop; + if(attacker != victim && hBotReverseFFDefend.IntValue > 0 && hBotReverseFFDefend.IntValue == 2 || GetUserAdmin(attacker) == INVALID_ADMIN_ID) { + if(IsFakeClient(victim) && !IsFakeClient(attacker) && GetClientTeam(attacker) == 2 && GetClientTeam(victim) == 2) { + + if(shootAtTarget[victim] == attacker) { + shootAtTargetHP[attacker] -= RoundFloat(damage); + shootAtTargetLoops[victim] += 4; + return Plugin_Continue; + } else if(shootAtTarget[victim] > 0) { + // Don't switch, wait for timer to stop + return Plugin_Continue; + } + SetBotTarget(attacker, victim, GetClientRealHealth(attacker) - RoundFloat(damage)); + } + } } return Plugin_Continue; } public Action SoundHook(int[] clients, int& numClients, char sample[PLATFORM_MAX_PATH], int& entity, int& channel, float& volume, int& level, int& pitch, int& flags, char[] soundEntry, int& seed) { static int honkID; - if(honkID == 0) honkID = GetTrollID("Honk / Meow"); static int vocalGagID; + if(honkID == 0) honkID = GetTrollID("Honk / Meow"); if(vocalGagID == 0) vocalGagID = GetTrollID("Vocalize Gag"); + if(lastButtonUser > -1 && StrEqual(sample, "npc/mega_mob/mega_mob_incoming.wav")) { PrintToConsoleAll("CRESCENDO STARTED BY %N", lastButtonUser); #if defined DEBUG @@ -500,10 +563,14 @@ public Action SoundHook(int[] clients, int& numClients, char sample[PLATFORM_MAX } public Action Event_WitchVictimSet(Event event, const char[] name, bool dontBroadcast) { + static int witchTrollID; + if(witchTrollID == 0) witchTrollID = GetTrollID("Witch Magnet"); + int witch = event.GetInt("witchid"); float closestDistance, survPos[3], witchPos[3]; GetEntPropVector(witch, Prop_Send, "m_vecOrigin", witchPos); int closestClient = -1; + for(int i = 1; i <= MaxClients; i++) { if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2 && IsPlayerAlive(i)) { //Ignore incapped players if hWitchIgnoreIncapp turned on: @@ -511,7 +578,7 @@ public Action Event_WitchVictimSet(Event event, const char[] name, bool dontBroa continue; } - if(IsTrollActive(i, "Witch Magnet")) { + if(Trolls[witchTrollID].IsActive(i)) { GetClientAbsOrigin(i, survPos); float dist = GetVectorDistance(survPos, witchPos, true); if(closestClient == -1 || dist < closestDistance) { @@ -538,6 +605,7 @@ public void OnEntityCreated(int entity, const char[] classname) { RequestFrame(EntityCreateCallback, entity); } } + void EntityCreateCallback(int entity) { if(!HasEntProp(entity, Prop_Send, "m_hOwnerEntity") || !IsValidEntity(entity)) return; static char class[16]; @@ -584,7 +652,6 @@ void EntityCreateCallback(int entity) { } } -//Dull Melee public Action L4D2_MeleeGetDamageForVictim(int client, int weapon, int victim, float &damage) { static int dullMeleeID; if(!dullMeleeID) dullMeleeID = GetTrollID("Dull Melee"); @@ -610,4 +677,4 @@ int FindClosestVisibleClient(int source) { public bool TraceEntityFilterPlayer(int entity, int mask, any data) { return data != entity && entity <= MaxClients && GetClientTeam(entity) == 2 && IsPlayerAlive(entity); -} +} \ No newline at end of file diff --git a/scripting/include/feedthetrolls/misc.inc b/scripting/include/feedthetrolls/misc.inc index 16a464c..32889f8 100644 --- a/scripting/include/feedthetrolls/misc.inc +++ b/scripting/include/feedthetrolls/misc.inc @@ -248,4 +248,123 @@ void DropItem(int victim, int slot) { if(slot != 1 || DoesClientHaveMelee(victim)) { SDKHooks_DropWeapon(victim, wpn, NULL_VECTOR); } +} + +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]) +{ + fOutPut[0] = Cosine(fVecAngle[1] / (180 / FLOAT_PI)); + fOutPut[1] = Sine(fVecAngle[1] / (180 / FLOAT_PI)); + fOutPut[2] = -Sine(fVecAngle[0] / (180 / FLOAT_PI)); +} +stock void LookAtClient(int iClient, int iTarget) { + static float fTargetPos[3]; + static float fTargetAngles[3]; + static float fClientPos[3]; + static float fFinalPos[3]; + + GetClientEyePosition(iClient, fClientPos); + GetClientEyePosition(iTarget, fTargetPos); + GetClientEyeAngles(iTarget, fTargetAngles); + + float fVecFinal[3]; + AddInFrontOf(fTargetPos, fTargetAngles, 7.0, fVecFinal); + MakeVectorFromPoints(fClientPos, fVecFinal, fFinalPos); + + GetVectorAngles(fFinalPos, fFinalPos); + + TeleportEntity(iClient, NULL_VECTOR, fFinalPos, NULL_VECTOR); +} + + +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)) + { + return -1; + } + + //If the client is not on the survivors team, then just return the normal client health. + 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 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); + } + + //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); +} + + +/// Returns TRUE if set, FALSE if not (if no weapon to shoot) +bool SetBotTarget(int bot, int target, int targetHP, int loops = 15) { + if(shootAtTarget[target] == bot) { + return false; + } else if(shootAtTarget[target] > 0) { + return false; + } + LookAtClient(target, bot); + int weapon = GetPlayerWeaponSlot(target, 0); + if(weapon > -1) { + shootAtTarget[target] = bot; + shootAtTargetLoops[target] = loops; + shootAtTargetHP[bot] = targetHP; + int ammo = GetEntProp(weapon, Prop_Send, "m_iClip1"); + DataPack pack = new DataPack(); + // Reverse target and bot: + pack.WriteCell(target); + pack.WriteCell(bot); + pack.WriteCell(weapon); + pack.WriteCell(ammo); + CreateTimer(0.1, Timer_ShootReverse, pack, TIMER_REPEAT | TIMER_FLAG_NO_MAPCHANGE); + return true; + } else { + return false; + } } \ No newline at end of file diff --git a/scripting/include/feedthetrolls/timers.inc b/scripting/include/feedthetrolls/timers.inc index 590ea9b..fcbd383 100644 --- a/scripting/include/feedthetrolls/timers.inc +++ b/scripting/include/feedthetrolls/timers.inc @@ -107,4 +107,33 @@ public Action Timer_InstaFailed(Handle h) { gInstaSpecialType = -1; gInstaSpecialTarget = 0; } +} + +public Action Timer_ShootReverse(Handle h, DataPack pack) { + pack.Reset(); + int attacker = pack.ReadCell(); + int target = pack.ReadCell(); + int weapon = pack.ReadCell(); + int ammo = pack.ReadCell(); + + + if(!IsClientConnected(target) || !IsClientConnected(attacker) || attacker > MaxClients || target > MaxClients) return Plugin_Stop; + static float targetPos[3], botAngles[3], botPosition[3]; + GetClientAbsOrigin(attacker, targetPos); + GetClientAbsAngles(attacker, botAngles); + GetClientAbsOrigin(attacker, botPosition); + + botAngles[1] = RadToDeg(ArcTangent2( botPosition[1] - targetPos[1], botPosition[0] - targetPos[0])) + 180.0; + TeleportEntity(attacker, NULL_VECTOR, botAngles, NULL_VECTOR); + shootAtTargetLoops[attacker]--; + if(IsValidEntity(weapon)) + SetEntProp(weapon, Prop_Send, "m_iClip1", ammo); + if(shootAtTargetLoops[attacker] > 0 && GetClientRealHealth(target) > shootAtTargetHP[target]) { + return Plugin_Continue; + } else { + shootAtTargetLoops[attacker] = 0; + shootAtTarget[attacker] = 0; + shootAtTargetHP[target] = 0; + return Plugin_Stop; + } } \ No newline at end of file diff --git a/scripting/include/feedthetrolls/trolls.inc b/scripting/include/feedthetrolls/trolls.inc index d1879b2..9bb1ed9 100644 --- a/scripting/include/feedthetrolls/trolls.inc +++ b/scripting/include/feedthetrolls/trolls.inc @@ -27,13 +27,16 @@ void SetupTrolls() { // Trolls[index].AddFlag("Throw to Admin", true); // Trolls[index].AddFlag("Drop At Feet", false); // Trolls[index].AddFlag("Drop At Admin", false); - index = SetupTroll("Bad Throw", "Player drops throwables on throw, and biles/molotovs themselves", TrollMod_Constant | TrollMod_PlayerOnly); + index = SetupTroll("Bad Throw", "Player drops throwables on throw, and biles/molotovs themselves", TrollMod_Constant); Trolls[index].AddFlagPrompt(true); Trolls[index].AddFlag("Biles", true); Trolls[index].AddFlag("Molotovs", true); Trolls[index].AddFlag("Pipebombs", true); SetupTroll("No Pickup", "Prevents a player from picking up ANY (new) item. Use ThrowItAll to make them drop", TrollMod_Constant); - SetupTroll("UziRules", "Picking up a weapon gives them a UZI instead", TrollMod_Constant); + 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); + Trolls[index].AddFlag("AWP Only", false); SetupTroll("Primary Disable", "Player cannot pickup any weapons, only melee/pistols", TrollMod_Constant); index = SetupTroll("Dull Melee", "Player's melee weapon does 0 damage (based on %). Headshots still work", TrollMod_Constant); Trolls[index].AddFlagPrompt(false); @@ -44,7 +47,7 @@ void SetupTrolls() { 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 | TrollMod_PlayerOnly); + index = SetupTroll("No Profanity", "Replaces some words with random phrases", TrollMod_Constant); Trolls[index].AddFlagPrompt(false); Trolls[index].AddFlag("Only Replace Swears", false); Trolls[index].AddFlag("Replace Full Messages", true); @@ -63,7 +66,8 @@ void SetupTrolls() { Trolls[index].AddFlag("Show Modified to Them", true); Trolls[index].AddFlag("Show Original to Them", false); Trolls[index].AddFlag("Show Modified Only To Them", false); - SetupTroll("Reversed", "Reserves their message", TrollMod_Constant | TrollMod_PlayerOnly); + SetupTroll("Reversed", "Reserves their message", TrollMod_Constant); + SetupTroll("Voice Mute", "Mutes from voice", TrollMod_Constant); SetCategory("Health"); SetupTroll("Damage Boost", "Makes a player take more damage than normal", TrollMod_Constant); @@ -83,16 +87,16 @@ void SetupTrolls() { 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 | TrollMod_PlayerOnly); - SetupTroll("Inverted Controls", "Well, aint it obvious", TrollMod_Constant | TrollMod_PlayerOnly); + 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("Meta: Inverse", "Uhm you are not supposed to see this...", TrollMod_Instant | TrollMod_PlayerOnly); + 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); - //INFO: UP MAX_TROLLS when adding new trolls! + // Initialize the default flag values to -1 @@ -129,16 +133,16 @@ bool ApplyAffect(int victim, const Troll troll, int activator, trollModifier mod else if(StrEqual(troll.name, "Half Primary Ammo")) { int current = GetPrimaryReserveAmmo(victim); SetPrimaryReserveAmmo(victim, current / 2); - } else if(StrEqual(troll.name, "UziRules")) { + } 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"); + DisableTroll(victim, "UziRules / AwpSmells"); DisableTroll(victim, "No Pickup"); SDKHook(victim, SDKHook_WeaponCanUse, Event_ItemPickup); } else if(StrEqual(troll.name, "No Pickup")) { - DisableTroll(victim, "UziRules"); + DisableTroll(victim, "UziRules / AwpSmells"); DisableTroll(victim, "Primary Disable"); SDKHook(victim, SDKHook_WeaponCanUse, Event_ItemPickup); } else if(StrEqual(troll.name, "CameTooEarly")) { @@ -189,6 +193,8 @@ bool ApplyAffect(int victim, const Troll troll, int activator, trollModifier mod L4D2_SpitterPrj(victim, pos, ang); } else if(StrEqual(troll.name, "Stagger")) { L4D_StaggerPlayer(victim, victim, NULL_VECTOR); + } else if(StrEqual(troll.name, "Voice Mute")) { + BaseComm_SetClientMute(victim, !isActive); } 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 7c80c7a..3d50246 100644 --- a/scripting/include/ftt.inc +++ b/scripting/include/ftt.inc @@ -23,6 +23,9 @@ ConVar hShoveFailChance; ConVar hAutoPunishExpire; ConVar hMagnetTargetMode; ConVar hBadThrowHitSelf; +ConVar hBotReverseFFDefend; +ConVar hSbFriendlyFire; + bool g_bPendingItemGive[MAXPLAYERS+1]; @@ -43,6 +46,10 @@ int gInstaSpecialMagnet[MAXPLAYERS+1]; char steamids[MAXPLAYERS+1][64]; +int shootAtTarget[MAXPLAYERS+1]; +int shootAtTargetLoops[MAXPLAYERS+1]; +int shootAtTargetHP[MAXPLAYERS+1]; + #include #include #include @@ -52,3 +59,4 @@ char steamids[MAXPLAYERS+1][64]; #include #include #include + diff --git a/scripting/l4d2_TKStopper.sp b/scripting/l4d2_TKStopper.sp index 877b85b..88ffc2b 100644 --- a/scripting/l4d2_TKStopper.sp +++ b/scripting/l4d2_TKStopper.sp @@ -4,9 +4,6 @@ //#define DEBUG #define PLUGIN_VERSION "1.0" -#define FF_BAN_THRESHOLD 100.0 -#define FF_BAN_JOIN_MINUTES_THRESHOLD 2 -#define FF_BAN_MINUTES 60 #include #include @@ -14,17 +11,19 @@ #include #include -bool lateLoaded, IsFinaleEnding, isPlayerTroll[MAXPLAYERS+1], isImmune[MAXPLAYERS+1], isUnderAttack[MAXPLAYERS+1]; -int iJoinTime[MAXPLAYERS+1], iIdleStartTime[MAXPLAYERS+1], iJumpAttempts[MAXPLAYERS+1]; -float playerTotalDamageFF[MAXPLAYERS+1]; -int lastFF[MAXPLAYERS+1]; +bool lateLoaded, isFinaleEnding; +bool isPlayerTroll[MAXPLAYERS+1], isImmune[MAXPLAYERS+1], isUnderAttack[MAXPLAYERS+1]; +int iJoinTime[MAXPLAYERS+1]; +int iIdleStartTime[MAXPLAYERS+1]; +int iLastFFTime[MAXPLAYERS+1]; +int iJumpAttempts[MAXPLAYERS+1]; +float playerTotalDamageFF[MAXPLAYERS+1]; float autoFFScaleFactor[MAXPLAYERS+1]; ConVar hForgivenessTime, hBanTime, hThreshold, hJoinTime, hTKAction, hSuicideAction, hSuicideLimit, hFFAutoScaleAmount, hFFAutoScaleForgivenessAmount, hFFAutoScaleMaxRatio, hFFAutoScaleIgnoreAdmins; -public Plugin myinfo = -{ +public Plugin myinfo = { name = "TK Stopper", author = "jackzmc", description = "", @@ -38,28 +37,26 @@ public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max } } -public void OnPluginStart() -{ +public void OnPluginStart() { EngineVersion g_Game = GetEngineVersion(); - if(g_Game != Engine_Left4Dead && g_Game != Engine_Left4Dead2) - { + if(g_Game != Engine_Left4Dead && g_Game != Engine_Left4Dead2) { SetFailState("This plugin is for L4D/L4D2 only."); } hForgivenessTime = CreateConVar("l4d2_tk_forgiveness_time", "15", "The minimum amount of time to pass (in seconds) where a player's previous accumulated FF is forgiven", FCVAR_NONE, true, 0.0); - hBanTime = CreateConVar("l4d2_tk_bantime", "60", "How long in minutes should a player be banned for? 0 for permanently", FCVAR_NONE, true, 0.0); + hBanTime = CreateConVar("l4d2_tk_bantime", "120", "How long in minutes should a player be banned for? 0 for permanently", FCVAR_NONE, true, 0.0); hThreshold = CreateConVar("l4d2_tk_ban_ff_threshold", "75.0", "How much damage does a player need to do before being instantly banned", FCVAR_NONE, true, 0.0); hJoinTime = CreateConVar("l4d2_tk_ban_join_time", "2", "Upto how many minutes should any new player be subjected to instant bans on any FF", FCVAR_NONE, true, 0.0); hTKAction = CreateConVar("l4d2_tk_action", "3", "How should the TK be punished?\n0 = No action (No message), 1 = Kick, 2 = Instant Ban, 3 = Ban on disconnect", FCVAR_NONE, true, 0.0, true, 3.0); hSuicideAction = CreateConVar("l4d2_suicide_action", "3", "How should a suicider be punished?\n0 = No action (No message), 1 = Kick, 2 = Instant Ban, 3 = Ban on disconnect", FCVAR_NONE, true, 0.0, true, 3.0); hSuicideLimit = CreateConVar("l4d2_suicide_limit", "1", "How many attempts does a new joined player have until action is taken for suiciding?", FCVAR_NONE, true, 0.0); - - hFFAutoScaleAmount = CreateConVar("l4d2_tk_auto_ff_rate", "0.04", "The rate at which auto reverse-ff is scaled by.", FCVAR_NONE, true, 0.0); + // Reverse FF Auto Scale + hFFAutoScaleAmount = CreateConVar("l4d2_tk_auto_ff_rate", "0.01", "The rate at which auto reverse-ff is scaled by.", FCVAR_NONE, true, 0.0); hFFAutoScaleMaxRatio = CreateConVar("l4d2_tk_auto_ff_max_ratio", "5.0", "The maximum amount that the reverse ff can go. 0.0 for unlimited", FCVAR_NONE, true, 0.0); - hFFAutoScaleForgivenessAmount = CreateConVar("l4d2_tk_auto_ff_forgive_rate", "0.008", "This amount times amount of minutes since last ff is removed from ff rate", FCVAR_NONE, true, 0.0); + hFFAutoScaleForgivenessAmount = CreateConVar("l4d2_tk_auto_ff_forgive_rate", "0.02", "This amount times amount of minutes since last ff is removed from ff rate", FCVAR_NONE, true, 0.0); hFFAutoScaleIgnoreAdmins = CreateConVar("l4d2_tk_auto_ff_ignore_admins", "1", "Should automatic reverse ff ignore admins? 0 = Admins are subjected\n1 = Admins are excempt", FCVAR_NONE, true, 0.0, true, 1.0); - //AutoExecConfig(true, "l4d2_tkstopper"); + AutoExecConfig(true, "l4d2_tkstopper"); HookEvent("finale_vehicle_ready", Event_FinaleVehicleReady); HookEvent("player_disconnect", Event_PlayerDisconnect); @@ -83,7 +80,6 @@ public void OnPluginStart() RegAdminCmd("sm_ignore", Command_IgnorePlayer, ADMFLAG_KICK, "Makes a player immune for any anti trolling detection for a session"); - RegAdminCmd("sm_tkinfo", Command_TKInfo, ADMFLAG_KICK, "Debug info for TKSTopper"); if(lateLoaded) { @@ -93,29 +89,14 @@ public void OnPluginStart() } } } - // CreateTimer(60.0, Timer_Forgive, _, TIMER_REPEAT); } - -/*public Action Timer_Forgive(Handle h) { - for(int i = 1; i <= MaxClients; i++) { - if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2 && ) { - - } - } - return Plugin_Continue; -}*/ - /////////////////////////////////////////////////////////////////////////////// // Special Infected Events /////////////////////////////////////////////////////////////////////////////// public Action Event_ChargerCarry(Event event, const char[] name, bool dontBroadcast) { int victim = GetClientOfUserId(event.GetInt("victim")); if(victim) { - if(StrEqual(name, "charger_carry_start")) { - isUnderAttack[victim] = true; - }else{ - isUnderAttack[victim] = false; - } + isUnderAttack[victim] = StrEqual(name, "charger_carry_start"); } return Plugin_Continue; } @@ -123,11 +104,7 @@ public Action Event_ChargerCarry(Event event, const char[] name, bool dontBroadc public Action Event_HunterPounce(Event event, const char[] name, bool dontBroadcast) { int victim = GetClientOfUserId(event.GetInt("victim")); if(victim) { - if(StrEqual(name, "lunge_pounce")) { - isUnderAttack[victim] = true; - }else{ - isUnderAttack[victim] = false; - } + isUnderAttack[victim] = StrEqual(name, "lunge_pounce"); } return Plugin_Continue; } @@ -135,41 +112,35 @@ public Action Event_HunterPounce(Event event, const char[] name, bool dontBroadc public Action Event_SmokerChoke(Event event, const char[] name, bool dontBroadcast) { int victim = GetClientOfUserId(event.GetInt("victim")); if(victim) { - if(StrEqual(name, "choke_start")) { - isUnderAttack[victim] = true; - }else{ - isUnderAttack[victim] = false; - } + isUnderAttack[victim] = StrEqual(name, "choke_start"); } return Plugin_Continue; } public Action Event_JockeyRide(Event event, const char[] name, bool dontBroadcast) { int victim = GetClientOfUserId(event.GetInt("victim")); if(victim) { - if(StrEqual(name, "jockey_ride")) { - isUnderAttack[victim] = true; - }else{ - isUnderAttack[victim] = false; - } + isUnderAttack[victim] = StrEqual(name, "jockey_ride"); } return Plugin_Continue; } /////////////////////////////////////////////////////////////////////////////// // IDLE /////////////////////////////////////////////////////////////////////////////// -public Action Event_BotToPlayer(Handle event, const char[] name, bool dontBroadcast) { - int player = GetClientOfUserId(GetEventInt(event, "player")); +public Action Event_BotToPlayer(Event event, const char[] name, bool dontBroadcast) { + int player = GetClientOfUserId(event.GetInt("player")); - if (!IsValidClient(player) || (GetClientTeam(player) != 2 && GetClientTeam(player) != 3) || IsFakeClient(player)) return Plugin_Continue; // ignore fake players (side product of creating bots) + // ignore fake players (side product of creating bots) + if (!IsValidClient(player) || (GetClientTeam(player) != 2 && GetClientTeam(player) != 3) || IsFakeClient(player)) return Plugin_Continue; - // If a player has been idle for over 600s (10 min), reset to them "first joining" + // If a player has been idle for over 600s (10 min), reset to them "just joined" + // Purpose: Some trolls idle till end and then attack @ escape, or "gain trust" if(GetTime() - iIdleStartTime[player] >= 600) { iJoinTime[player] = GetTime(); } return Plugin_Continue; } -public Action Event_PlayerToBot(Handle event, char[] name, bool dontBroadcast) { - int player = GetClientOfUserId(GetEventInt(event, "player")); +public Action Event_PlayerToBot(Event event, char[] name, bool dontBroadcast) { + int player = GetClientOfUserId(event.GetInt("player")); iIdleStartTime[player] = GetTime(); return Plugin_Continue; } @@ -177,7 +148,7 @@ public Action Event_PlayerToBot(Handle event, char[] name, bool dontBroadcast) { // Misc events /////////////////////////////////////////////////////////////////////////////// public void Event_FinaleVehicleReady(Event event, const char[] name, bool dontBroadcast) { - IsFinaleEnding = true; + isFinaleEnding = true; for(int i = 1; i <= MaxClients; i++) { if(isPlayerTroll[i] && IsClientConnected(i) && IsClientInGame(i)) { PrintChatToAdmins("Note: %N is still marked as troll and will be banned after this game. Use /ignore to ignore them.", i); @@ -186,12 +157,12 @@ public void Event_FinaleVehicleReady(Event event, const char[] name, bool dontBr } public void OnMapEnd() { - IsFinaleEnding = false; + isFinaleEnding = false; } public void OnClientPutInServer(int client) { iJoinTime[client] = GetTime(); - lastFF[client] = GetTime(); + iLastFFTime[client] = GetTime(); SDKHook(client, SDKHook_OnTakeDamage, Event_OnTakeDamage); } @@ -200,7 +171,6 @@ public void OnClientDisconnect(int client) { playerTotalDamageFF[client] = 0.0; isUnderAttack[client] = false; iJumpAttempts[client] = 0; - isUnderAttack[client] = false; } // Only clear things when they fully left on their own accord: @@ -214,20 +184,27 @@ public void Event_PlayerDisconnect(Event event, const char[] name, bool dontBroa } public Action Event_OnTakeDamage(int victim, int& attacker, int& inflictor, float& damage, int& damagetype, int& weapon, float damageForce[3], float damagePosition[3]) { - if(damage > 0.0 && victim <= MaxClients && attacker <= MaxClients && attacker > 0 && victim > 0) { - if(damagetype & DMG_BURN && IsFakeClient(attacker)) { + if(damage > 0.0 && victim <= MaxClients && attacker <= MaxClients && attacker > 0 && victim > 0 && attacker != victim) { + if(GetClientTeam(victim) != GetClientTeam(attacker) || attacker == victim) return Plugin_Continue; + else if(damagetype & DMG_BURN && IsFakeClient(attacker)) { + // Ignore damage from fire caused by bots (players who left after causing fire) damage = 0.0; return Plugin_Changed; } + // Otherwise if attacker was ignored or is a bot, stop here and let vanilla handle it + else if(isImmune[attacker] || IsFakeClient(attacker)) return Plugin_Continue; + bool isAdmin = GetUserAdmin(attacker) != INVALID_ADMIN_ID; - bool ignore = hFFAutoScaleIgnoreAdmins.BoolValue && isAdmin; - if(isImmune[attacker] || IsFakeClient(attacker)) return Plugin_Continue; - if(GetClientTeam(victim) != 2 || GetClientTeam(attacker) != 2 || attacker == victim) return Plugin_Continue; + //Allow friendly firing BOTS that aren't idle players: //if(IsFakeClient(victim) && !HasEntProp(attacker, Prop_Send, "m_humanSpectatorUserID") || GetEntProp(attacker, Prop_Send, "m_humanSpectatorUserID") == 0) return Plugin_Continue; // Stop all damage early if already marked as troll - if(isPlayerTroll[attacker]) return Plugin_Stop; + if(isPlayerTroll[attacker]) { + SDKHooks_TakeDamage(attacker, attacker, attacker, autoFFScaleFactor[attacker] * damage); + + return Plugin_Stop; + } // Allow vanilla-damage if being attacked by special (example, charger carry) if(isUnderAttack[victim]) return Plugin_Continue; @@ -258,33 +235,34 @@ public Action Event_OnTakeDamage(int victim, int& attacker, int& inflictor, flo } // Forgive player based on threshold, resetting accumlated damage - if(time - lastFF[attacker] > hForgivenessTime.IntValue) { + if(time - iLastFFTime[attacker] > hForgivenessTime.IntValue) { playerTotalDamageFF[attacker] = 0.0; } playerTotalDamageFF[attacker] += damage; // Auto reverse ff logic - lastFF[attacker] = time; - if(isDamageDirect && !ignore) { - // Decrement any recovered FF - float minutesSinceLastFF = (time - lastFF[attacker]) / 60.0; - autoFFScaleFactor[attacker] -= minutesSinceLastFF * hFFAutoScaleForgivenessAmount.FloatValue; + iLastFFTime[attacker] = time; + if(isDamageDirect && (!hFFAutoScaleIgnoreAdmins.BoolValue || !isAdmin)) { + // Decrement any forgiven ratio (computed on demand) + float minutesSinceiLastFFTime = (time - iLastFFTime[attacker]) / 60.0; + autoFFScaleFactor[attacker] -= minutesSinceiLastFFTime * hFFAutoScaleForgivenessAmount.FloatValue; if(autoFFScaleFactor[attacker] < 0.0) { autoFFScaleFactor[attacker] = 0.0; } - // Then increment + // Then calculate a new reverse ff ratio autoFFScaleFactor[attacker] += hFFAutoScaleAmount.FloatValue * damage; - if(hFFAutoScaleMaxRatio.FloatValue > 0.0 && autoFFScaleFactor[attacker] > hFFAutoScaleMaxRatio.FloatValue) { - autoFFScaleFactor[attacker] = hFFAutoScaleMaxRatio.FloatValue; + if(isPlayerTroll[attacker]) { + autoFFScaleFactor[attacker] *= 2; } - if(minutesSinceLastFF > 3.0) { - PrintToConsoleAdmins("%N new reverse ratio: %f", attacker, autoFFScaleFactor[attacker]); + + if(!isPlayerTroll[attacker] && hFFAutoScaleMaxRatio.FloatValue > 0.0 && autoFFScaleFactor[attacker] > hFFAutoScaleMaxRatio.FloatValue) { + autoFFScaleFactor[attacker] = hFFAutoScaleMaxRatio.FloatValue; } } // Check for excessive friendly fire damage in short timespan - if(!isAdmin && playerTotalDamageFF[attacker] > hThreshold.IntValue && !IsFinaleEnding && isDamageDirect) { - LogAction(-1, attacker, "Excessive FF (%.2f HP)", playerTotalDamageFF[attacker]); + if(!isAdmin && playerTotalDamageFF[attacker] > hThreshold.IntValue && !isFinaleEnding && isDamageDirect) { + LogAction(-1, attacker, "Excessive FF (%.2f HP) (%.2f RFF Rate)", playerTotalDamageFF[attacker], autoFFScaleFactor[attacker]); if(hTKAction.IntValue == 1) { LogMessage("[NOTICE] Kicking %N for excessive FF (%.2f HP) for %d minutes.", attacker, playerTotalDamageFF[attacker], hBanTime.IntValue); NotifyAllAdmins("[Notice] Kicking %N for excessive FF (%.2f HP) for %d minutes.", attacker, playerTotalDamageFF[attacker], hBanTime.IntValue); @@ -304,39 +282,51 @@ public Action Event_OnTakeDamage(int victim, int& attacker, int& inflictor, flo // Modify damages based on criteria if(iJumpAttempts[victim] > 0 || L4D_IsInFirstCheckpoint(victim) || L4D_IsInLastCheckpoint(victim) || time - iJoinTime[attacker] <= hJoinTime.IntValue * 60) { - // If the amount of MS is <= join time threshold * 60000 ms then cancel - // Or if the player is in a saferoom - // Or if the player tried to suicide jump + /* + If the amount of seconds since they joined is <= the minimum join time cvar (min) threshold + or if the player is in a saferoom + or if the player tried to suicide jump + Then cancel all damage: + */ damage = 0.0; return Plugin_Handled; - }else if(IsFinaleEnding) { + }else if(isFinaleEnding) { + // Keep admins immune if escape vehicle out if(isAdmin) return Plugin_Continue; SDKHooks_TakeDamage(attacker, attacker, attacker, damage * 2.0); damage = 0.0; return Plugin_Changed; - }else if(!isDamageDirect) { // Ignore fire and propane damage, mistakes can happen - SDKHooks_TakeDamage(attacker, attacker, attacker, float(RoundToCeil(autoFFScaleFactor[attacker] * damage))); - damage /= 2.1; + }else if(isDamageDirect) { // Ignore fire and propane damage, mistakes can happen + // Apply their reverse ff damage, and have victim take a decreasing amount + SDKHooks_TakeDamage(attacker, attacker, attacker, autoFFScaleFactor[attacker] * damage); + if(isPlayerTroll[attacker]) return Plugin_Stop; + if(autoFFScaleFactor[attacker] > 1.0) + damage /= autoFFScaleFactor[attacker]; + else + damage /= 2.0; return Plugin_Changed; } } return Plugin_Continue; } +/// COMMANDS + public Action Command_TKInfo(int client, int args) { int time = GetTime(); for(int i = 1; i <= MaxClients; i++) { if(IsClientConnected(i) && IsClientInGame(i) && !IsFakeClient(i)) { - float minutesSinceLastFF = (time - lastFF[i]) / 60.0; - float activeRate = autoFFScaleFactor[i] - (minutesSinceLastFF * hFFAutoScaleForgivenessAmount.FloatValue); + float minutesSinceiLastFFTime = (time - iLastFFTime[i]) / 60.0; + float activeRate = autoFFScaleFactor[i] - (minutesSinceiLastFFTime * hFFAutoScaleForgivenessAmount.FloatValue); if(activeRate < 0.0) { activeRate = 0.0; } - ReplyToCommand(client, "%N: %f TK-FF buffer | %f (active: %f), reverse FF rate | %f ff min ago | %d suicide jumps", i, playerTotalDamageFF[i], autoFFScaleFactor[i], activeRate, minutesSinceLastFF, iJumpAttempts[i]); + ReplyToCommand(client, "%N: %f TK-FF buffer | %.3f (buf %f), reverse FF rate | last ff %.1f min ago | %d suicide jumps", i, playerTotalDamageFF[i], activeRate, autoFFScaleFactor[i], minutesSinceiLastFFTime, iJumpAttempts[i]); } } return Plugin_Handled; } + public Action Command_IgnorePlayer(int client, int args) { char arg1[32]; GetCmdArg(1, arg1, sizeof(arg1)); @@ -379,6 +369,9 @@ public Action Command_IgnorePlayer(int client, int args) { return Plugin_Handled; } + +/// STOCKS + stock bool GetNearestPlayerPosition(int client, float pos[3]) { static float targetPos[3], lowestDist; int lowestID = -1; diff --git a/scripting/l4d2_ai_minigun.sp b/scripting/l4d2_ai_minigun.sp index 0a2435c..d017d8a 100644 --- a/scripting/l4d2_ai_minigun.sp +++ b/scripting/l4d2_ai_minigun.sp @@ -108,7 +108,7 @@ public Action Command_SpawnMinigunBot(int client, int args) { int survivor = GetClientOfUserId(SpawnSurvivor(vPos, vAng, model, true)); if(survivor > 0) { - GiveClientWeapon(survivor, "rifle_ak47", true); + GiveClientWeaponLasers(survivor, "rifle_ak47"); }else{ ReplyToCommand(client, "Failed to spawn survivor."); } @@ -152,7 +152,7 @@ public Action Command_SpawnHoldoutBot(int client, int args) { int survivor = SpawnSurvivor(vPos, vAng, model, false); if(survivor > 0) { - GiveClientWeapon(survivor, wpn, true); + GiveClientWeaponLasers(survivor, wpn); SetEntProp(survivor, Prop_Send, "m_survivorCharacter", survivorId); }else{ ReplyToCommand(client, "Failed to spawn survivor."); diff --git a/scripting/l4d2_extraplayeritems.sp b/scripting/l4d2_extraplayeritems.sp index 30f4183..d6c464f 100644 --- a/scripting/l4d2_extraplayeritems.sp +++ b/scripting/l4d2_extraplayeritems.sp @@ -118,7 +118,7 @@ public void OnPluginStart() { HookEvent("round_freeze_end", Event_RoundFreezeEnd); HookEvent("tank_spawn", Event_TankSpawn); - hExtraItemBasePercentage = CreateConVar("l4d2_extraitem_chance", "0.056", "The base chance (multiplied by player count) of an extra item being spawned.", FCVAR_NONE, true, 0.0, true, 1.0); + hExtraItemBasePercentage = CreateConVar("l4d2_extraitems_chance", "0.056", "The base chance (multiplied by player count) of an extra item being spawned.", FCVAR_NONE, true, 0.0, true, 1.0); hAddExtraKits = CreateConVar("l4d2_extraitems_kitmode", "0", "Decides how extra kits should be added.\n0 -> Overwrites previous extra kits, 1 -> Adds onto previous extra kits", FCVAR_NONE, true, 0.0, true, 1.0); hUpdateMinPlayers = CreateConVar("l4d2_extraitems_updateminplayers", "1", "Should the plugin update abm\'s cvar min_players convar to the player count?\n 0 -> NO, 1 -> YES", FCVAR_NONE, true, 0.0, true, 1.0); hMinPlayersSaferoomDoor = CreateConVar("l4d2_extraitems_doorunlock_percent", "0.75", "The percent of players that need to be loaded in before saferoom door is opened.\n 0 to disable", FCVAR_NONE, true, 0.0, true, 1.0); @@ -442,8 +442,17 @@ public void Frame_SetupNewClient(int client) { } static float spawnPos[3]; // TODO: Fix null - if(GetCenterPositionInSurvivorFlow(client, spawnPos)) + if(GetIdealPositionInSurvivorFlow(client, spawnPos)) TeleportEntity(client, spawnPos, NULL_VECTOR, NULL_VECTOR); + CreateTimer(1.5, Timer_RemoveInvincibility, client); + SDKHook(client, SDKHook_OnTakeDamage, OnInvincibleDamageTaken); +} +public Action Timer_RemoveInvincibility(Handle h, int client) { + SDKUnhook(client, SDKHook_OnTakeDamage, OnInvincibleDamageTaken); +} +public Action OnInvincibleDamageTaken(int victim, int& attacker, int& inflictor, float& damage, int& damagetype, int& weapon, float damageForce[3], float damagePosition[3]) { + damage = 0.0; + return Plugin_Stop; } public Action Timer_GiveClientKit(Handle hdl, int user) { int client = GetClientOfUserId(user); @@ -736,8 +745,8 @@ public Action OnUpgradePackUse(int entity, int activator, int caller, UseType ty GetEntityClassname(primaryWeapon, classname, sizeof(classname)); if(!weaponMaxClipSizes.GetValue(classname, ammo)) { - if(StrEqual(classname, "weapon_grenade_launcher", true)) ammo = 1; - else if(StrEqual(classname, "weapon_rifle_m60", true)) ammo = 150; + if(StrEqual(classname[7], "grenade_launcher", true)) ammo = 1; + else if(StrEqual(classname[7], "rifle_m60", true)) ammo = 150; else { int currentAmmo = GetEntProp(primaryWeapon, Prop_Send, "m_iClip1"); if(currentAmmo > 10) ammo = 10; @@ -1124,13 +1133,13 @@ stock void RunVScriptLong(const char[] sCode, any ...) { } // Gets a position (from a nav area) -stock bool GetCenterPositionInSurvivorFlow(int target, float pos[3]) { +stock bool GetIdealPositionInSurvivorFlow(int target, float pos[3]) { static float ang[3]; int client = GetHighestFlowSurvivor(target); if(client > 0) { GetClientAbsOrigin(client, pos); GetClientAbsAngles(client, ang); - pos[2] = -pos[2]; + ang[2] = -ang[2]; TR_TraceRayFilter(pos, ang, MASK_SHOT, RayType_Infinite, Filter_GroundOnly); if(TR_DidHit()) { TR_GetEndPosition(pos); @@ -1174,9 +1183,10 @@ stock int GetHighestFlowSurvivor(int ignoreTarget = 0) { float highestFlow = L4D2Direct_GetFlowDistance(client); for(int i = 1; i <= MaxClients; i++) { if(ignoreTarget != i && IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2 && IsPlayerAlive(i)) { - if(L4D2Direct_GetFlowDistance(i) > highestFlow) { + float dist = L4D2Direct_GetFlowDistance(i); + if(dist > highestFlow) { client = i; - highestFlow = L4D2Direct_GetFlowDistance(i); + highestFlow = dist; } } } diff --git a/scripting/l4d2_feedthetrolls.sp b/scripting/l4d2_feedthetrolls.sp index ebfda63..a78f12f 100644 --- a/scripting/l4d2_feedthetrolls.sp +++ b/scripting/l4d2_feedthetrolls.sp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -66,6 +67,13 @@ public void OnPluginStart() { 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); + hBotReverseFFDefend = CreateConVar("sm_ftt_bot_defend", "1", "Should bots defend themselves?\n0 = OFF\n1 = Will retaliate against non-admins\n2 = Anyone", FCVAR_NONE, true, 0.0, true, 2.0); + + hSbFriendlyFire = FindConVar("sb_friendlyfire"); + if(hBotReverseFFDefend.IntValue > 0) hSbFriendlyFire.BoolValue = true; + + + hBotReverseFFDefend.AddChangeHook(Change_BotDefend); RegAdminCmd("sm_ftl", Command_ListTheTrolls, ADMFLAG_KICK, "Lists all the trolls currently ingame."); RegAdminCmd("sm_ftm", Command_ListModes, ADMFLAG_KICK, "Lists all the troll modes and their description"); @@ -80,7 +88,7 @@ public void OnPluginStart() { RegAdminCmd("sm_insta", Command_InstaSpecial, ADMFLAG_KICK, "Spawns a special that targets them, close to them."); RegAdminCmd("sm_instaface", Command_InstaSpecialFace, ADMFLAG_KICK, "Spawns a special that targets them, right in their face."); RegAdminCmd("sm_inface", Command_InstaSpecialFace, ADMFLAG_KICK, "Spawns a special that targets them, right in their face."); - RegAdminCmd("sm_noob", Command_MarkNoob, ADMFLAG_KICK, "Marks a player as a noob. stored in a database"); + RegAdminCmd("sm_bots_attack", Command_BotsAttack, ADMFLAG_CHEATS, "Instructs all bots to attack a player until they have X health."); HookEvent("player_spawn", Event_PlayerSpawn); HookEvent("player_disconnect", Event_PlayerDisconnect); @@ -93,7 +101,10 @@ public void OnPluginStart() { AutoExecConfig(true, "l4d2_feedthetrolls"); - + for(int i = 1; i <= MaxClients; i++) { + if(IsClientConnected(i) && IsClientInGame(i)) + SDKHook(i, SDKHook_OnTakeDamage, Event_TakeDamage); + } } /////////////////////////////////////////////////////////////////////////////// // CVAR CHANGES @@ -107,6 +118,10 @@ public void Change_ThrowInterval(ConVar convar, const char[] oldValue, const cha } } +public void Change_BotDefend(ConVar convar, const char[] oldValue, const char[] newValue) { + hSbFriendlyFire.IntValue = convar.IntValue != 0; +} + /////////////////////////////////////////////////////////////////////////////// // METHODS - Old methods, some are also in feedthetrolls/misc.inc /////////////////////////////////////////////////////////////////////////////// diff --git a/scripting/l4d2_hideandseek.sp b/scripting/l4d2_hideandseek.sp index 5731d96..944c910 100644 --- a/scripting/l4d2_hideandseek.sp +++ b/scripting/l4d2_hideandseek.sp @@ -406,7 +406,7 @@ public void Event_GamemodeChange(ConVar cvar, const char[] oldValue, const char[ } -public Action Event_PlayerDeath(Event event, const char[] name, bool dontBroadcast) { +public void Event_PlayerDeath(Event event, const char[] name, bool dontBroadcast) { int client = GetClientOfUserId(event.GetInt("userid")); if(!gameOver && client && GetClientTeam(client) == 2) { int alive = 0; @@ -435,7 +435,6 @@ public Action Event_PlayerDeath(Event event, const char[] name, bool dontBroadca } } } - return Plugin_Continue; } public void OnClientPutInServer(int client) { @@ -447,7 +446,7 @@ public void OnClientPutInServer(int client) { } } -public Action Event_ItemPickup(Event event, const char[] name, bool dontBroadcast) { +public void Event_ItemPickup(Event event, const char[] name, bool dontBroadcast) { int client = GetClientOfUserId(event.GetInt("userid")); if(client && client > 0 && currentSeeker != client) { static char item[32]; @@ -469,11 +468,9 @@ public Action Event_ItemPickup(Event event, const char[] name, bool dontBroadcas } } } - return Plugin_Continue; - } -public Action Event_RoundStart(Event event, const char[] name, bool dontBroadcast) { +public void Event_RoundStart(Event event, const char[] name, bool dontBroadcast) { if(hasSpawnpoint) { for(int i = 1; i <= MaxClients; i++) { if(IsClientConnected(i) && IsClientInGame(i)) { @@ -483,11 +480,10 @@ public Action Event_RoundStart(Event event, const char[] name, bool dontBroadcas } SetupEntities(); CreateTimer(15.0, Timer_RoundStart); - return Plugin_Continue; } -public Action Event_RoundEnd(Event event, const char[] name, bool dontBroadcast) { +public void Event_RoundEnd(Event event, const char[] name, bool dontBroadcast) { currentSeeker = 0; static float tpLoc[3]; for(int i = 1; i <= MaxClients; i++) { @@ -508,8 +504,6 @@ public Action Event_RoundEnd(Event event, const char[] name, bool dontBroadcast) isPendingPlay[i] = false; } } - return Plugin_Continue; - } public Action Timer_CheckPlayers(Handle h) { diff --git a/scripting/l4d2_population_control.sp b/scripting/l4d2_population_control.sp index aa7d537..d9d4e78 100644 --- a/scripting/l4d2_population_control.sp +++ b/scripting/l4d2_population_control.sp @@ -176,7 +176,7 @@ public Action Hook_SpawnPost(int entity) { // } public void OnEntityDestroyed(int entity) { - if(commonType[entity] > 0) { + if(entity > 0 && entity <= 2048 && commonType[entity] > 0) { commonType[entity] = 0; if(commonType[entity] == 2) { --clownCommonsSpawned; diff --git a/scripting/l4d2_tank_priority.sp b/scripting/l4d2_tank_priority.sp index 8a636a0..457d96e 100644 --- a/scripting/l4d2_tank_priority.sp +++ b/scripting/l4d2_tank_priority.sp @@ -21,6 +21,7 @@ public Plugin myinfo = #define TANK_CLASS_ID 8 static int tankChooseVictimTicks[MAXPLAYERS+1]; //Per tank +static int tankChosenVictim[MAXPLAYERS+1]; static int totalTankDamage[MAXPLAYERS+1]; //Per survivor static ArrayList clients; @@ -45,17 +46,17 @@ public Action L4D2_OnChooseVictim(int attacker, int &curTarget) { if(++tankChooseVictimTicks[attacker] >= 200) { tankChooseVictimTicks[attacker] = 0; clients.Clear(); - float tankPos[3], clientPos[3]; + static float tankPos[3], clientPos[3]; GetClientAbsOrigin(attacker, tankPos); for(int i = 1; i <= MaxClients; i++) { - if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2 && IsPlayerAlive(i)) { + if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2 && IsPlayerAlive(i) && !IsPlayerIncapacitated(i)) { //If a player does less than 50 damage, and has green health add them to list if(totalTankDamage[i] < 100 && GetClientHealth(i) > 40) { GetClientAbsOrigin(i, clientPos); float dist = GetVectorDistance(clientPos, tankPos); // Only add targets who are far enough away from tank - if(dist > 5000.0) { + if(dist > 3000.0) { PrintToConsoleAll("Adding player %N to possible victim list. Dist=%f, Dmg=%d", i, dist, totalTankDamage[i]); int index = clients.Push(i); clients.Set(index, dist, 1); @@ -67,10 +68,19 @@ public Action L4D2_OnChooseVictim(int attacker, int &curTarget) { if(clients.Length == 0) return Plugin_Continue; clients.SortCustom(Sort_TankTargetter); - /*curTarget = clients.Get(0);*/ + curTarget = clients.Get(0); + tankChosenVictim[attacker] = curTarget; PrintToConsoleAll("[TankPriority] Player Selected to target: %N", curTarget); //TODO: Possibly clear totalTankDamage - //return Plugin_Changed; + return Plugin_Changed; + } + if(tankChosenVictim[attacker] > 0) { + if(IsClientConnected(tankChosenVictim[attacker]) && IsClientInGame(tankChosenVictim[attacker])) { + curTarget = tankChosenVictim[attacker]; + return Plugin_Changed; + } else { + tankChosenVictim[attacker] = 0; + } } return Plugin_Continue; } @@ -104,4 +114,8 @@ public void Event_TankSpawn(Event event, const char[] name, bool dontBroadcast) if(tank > 0 && IsFakeClient(tank)) { tankChooseVictimTicks[tank] = -20; } +} + +bool IsPlayerIncapacitated(int client) { + return (GetEntProp(client, Prop_Send, "m_isIncapacitated") == 1); } \ No newline at end of file diff --git a/scripting/l4d_survivor_identity_fix.sp b/scripting/l4d_survivor_identity_fix.sp index de85b98..a9a129e 100644 --- a/scripting/l4d_survivor_identity_fix.sp +++ b/scripting/l4d_survivor_identity_fix.sp @@ -35,7 +35,7 @@ static Handle hDHookSetModel = null, hModelPrefCookie; static ConVar hCookiesEnabled; static bool isLateLoad, cookieModelsSet, isL4D1Survivors; static int survivors; -static bool IsTemporarilyL4D2[MAXPLAYERS]; //Use index 0 to state if its activated +static bool IsTemporarilyL4D2[MAXPLAYERS]; static char currentMap[16]; Handle cookieModelTimer; @@ -289,11 +289,8 @@ public void OnMapStart() { } GetCurrentMap(currentMap, sizeof(currentMap)); - if(StrEqual(currentMap, "c6m3_port")) { - HookEvent("door_open", Event_DoorOpen); - } - } + //Either use preferred model OR find the least-used. public Action Event_PlayerFirstSpawn(Event event, const char[] name, bool dontBroadcast) { if(hCookiesEnabled.IntValue > 0) @@ -344,12 +341,17 @@ public Action Timer_SetAllCookieModels(Handle h) { public void Event_PlayerSpawn(Event event, const char[] name, bool dontBroadcast) { int client = GetClientOfUserId(event.GetInt("userid")); - if(StrEqual(currentMap, "c6m1_riverbank") && GetClientTeam(client) == 2) { - //If player died as l4d1 character on first map, revert it - RevertL4D1Survivor(client); - }else if(StrEqual(currentMap, "c6m3_port") && GetClientTeam(client) == 2) { - //If player not swapped (joined, or via prev. map, switch) - RequestFrame(Frame_SwapSurvivor, client); + if(client > 0 && client <= MaxClients && GetClientTeam(client) == 2) { + if(StrEqual(currentMap, "c6m1_riverbank")) { + //If player died as l4d1 character on first map, revert it + RevertSwappedSurvivor(client); + }else if(StrEqual(currentMap, "c6m3_port")) { + //If player not swapped (joined, or via prev. map, switch) + if(IsTemporarilyL4D2[client]) + RequestFrame(Frame_RevertSwappedSurvivor, client); + else + RequestFrame(Frame_SwapSurvivor, client); + } } } public Action Event_PlayerDeath(Event event, const char[] name, bool dontBroadcast) { @@ -357,46 +359,46 @@ public Action Event_PlayerDeath(Event event, const char[] name, bool dontBroadca if(StrEqual(currentMap, "c6m3_port") || StrEqual(currentMap, "c6m1_riverbank")) { int client = GetClientOfUserId(event.GetInt("userid")); if(client > 0 && GetClientTeam(client) == 2) { - SwapL4D1Survivor(client); + SwapL4D1Survivor(client, false); } } return Plugin_Continue; } - -public void Event_DoorOpen(Event event, const char[] name, bool dontBroadcast) { - for(int i = 1; i <= MaxClients; i++) { - if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2 && !IsTemporarilyL4D2[i]) { - SwapL4D1Survivor(i); - } +public void OnClientPutInServer(int client) { + if(GetClientTeam(client) == 2 && StrEqual(currentMap, "c6m3_port")) { + } - UnhookEvent("door_open", Event_DoorOpen); } + //On finale start: Set back to their L4D1 character. public Action Event_FinaleStart(Event event, const char[] name, bool dontBroadcast) { if(StrEqual(currentMap, "c6m3_port")) { for(int i = 1; i <= MaxClients; i++) { if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2) { - RevertL4D1Survivor(i); + RevertSwappedSurvivor(i); } } } } public void Frame_SwapSurvivor(int client) { - SwapL4D1Survivor(client); + SwapL4D1Survivor(client, true); } -public void Frame_RevertSurvivor(int client) { - RevertL4D1Survivor(client); +public void Frame_RevertSwappedSurvivor(int client) { + RevertSwappedSurvivor(client); } -void SwapL4D1Survivor(int client) { +void SwapL4D1Survivor(int client, bool showMessage) { int playerType = GetEntProp(client, Prop_Send, "m_survivorCharacter"); //If character is L4D1 Character (4: bill, etc..) then swap if(playerType > 3) { SetEntProp(client, Prop_Send, "m_survivorCharacter", playerType - 4); IsTemporarilyL4D2[client] = true; + if(showMessage && GetUserAdmin(client) != INVALID_ADMIN_ID) { + PrintToChat(client, "Your survivor is temporarily swapped. Please do not change back, it should auto-revert after the elevator is done. This is to prevent a game bug with L4D1 Survivors on this map."); + } } } -void RevertL4D1Survivor(int client) { +void RevertSwappedSurvivor(int client) { if(IsTemporarilyL4D2[client]) { int playerType = GetEntProp(client, Prop_Send, "m_survivorCharacter"); SetEntProp(client, Prop_Send, "m_survivorCharacter", playerType + 4); diff --git a/scripting/sm_player_notes.sp b/scripting/sm_player_notes.sp index cd835d2..1b3c6b4 100644 --- a/scripting/sm_player_notes.sp +++ b/scripting/sm_player_notes.sp @@ -57,7 +57,7 @@ public Action Command_AddNoteDisconnected(int client, int args) { } Menu menu = new Menu(Menu_Disconnected); menu.SetTitle("Add Note For Disconnected"); - for(int i = lastPlayers.Length + 1; i >= 0; i--) { + for(int i = lastPlayers.Length - 1; i >= 0; i--) { PlayerData data; lastPlayers.GetArray(i, data, sizeof(data)); menu.AddItem(data.id, data.name); @@ -76,7 +76,7 @@ public int Menu_Disconnected(Menu menu, MenuAction action, int client, int item) } public Action OnClientSayCommand(int client, const char[] command, const char[] sArgs) { - if(WaitingForNotePlayer == client) { + if(client > 0 && WaitingForNotePlayer == client) { WaitingForNotePlayer = 0; static char buffer[32]; GetClientAuthId(client, AuthId_Steam2, buffer, sizeof(buffer)); @@ -181,7 +181,7 @@ bool ConnectDB() { } public Action Event_FirstSpawn(Event event, const char[] name, bool dontBroadcast) { - int client = GetClientUserId(event.GetInt("userid")); + int client = GetClientOfUserId(event.GetInt("userid")); if(client > 0 && client <= MaxClients && !IsFakeClient(client)) { static char auth[32]; GetClientAuthId(client, AuthId_Steam2, auth, sizeof(auth)); @@ -223,9 +223,9 @@ public void DB_FindNotes(Database db, DBResultSet results, const char[] error, a } //initialize variables int client = GetClientOfUserId(data); - if(client && results.RowCount > 0) { + if(client > 0 && results.RowCount > 0) { static char noteCreator[32]; - PrintChatToAdmins("Notes for %s", client); + PrintChatToAdmins("Notes for %N", client); while(results.FetchRow()) { results.FetchString(0, reason, sizeof(reason)); results.FetchString(1, noteCreator, sizeof(noteCreator));