diff --git a/addons/sourcemod/plugins/fixes/bequiet.smx b/addons/sourcemod/plugins/fixes/bequiet.smx index f5e28e71d..7867d90ad 100644 Binary files a/addons/sourcemod/plugins/fixes/bequiet.smx and b/addons/sourcemod/plugins/fixes/bequiet.smx differ diff --git a/addons/sourcemod/scripting/bequiet.sp b/addons/sourcemod/scripting/bequiet.sp index befeb6dc5..1511d722f 100644 --- a/addons/sourcemod/scripting/bequiet.sp +++ b/addons/sourcemod/scripting/bequiet.sp @@ -1,124 +1,245 @@ #pragma semicolon 1 +#pragma newdecls required #include -#pragma newdecls required #include +#undef REQUIRE_PLUGIN +#include +#include +#define REQUIRE_PLUGIN + +#define CHAT_SYMBOL '@' -ConVar hCvarCvarChange, hCvarNameChange, hCvarSpecNameChange, hCvarSpecSeeChat; -bool bCvarChange, bNameChange, bSpecNameChange, bSpecSeeChat; +ConVar + g_cvarCvarChange, + g_cvarNameChange, + g_cvarSpecSeeChat, + g_cvarBaseChat; -public Plugin myinfo = +bool + g_bSourceComms, + g_bBaseComm, + g_bLateload; + +enum L4DTeam { - name = "BeQuiet", - author = "Sir", - description = "Please be Quiet!", - version = "1.33.7", - url = "https://github.com/SirPlease/SirCoding" + L4DTeam_Unassigned = 0, + L4DTeam_Spectator = 1, + L4DTeam_Survivor = 2, + L4DTeam_Infected = 3 } -public void OnPluginStart() +public Plugin myinfo = +{ + name = "BeQuiet", + author = "Sir", + description = "Please be Quiet!", + version = "1.4", + url = "https://github.com/SirPlease/L4D2-Competitive-Rework" +} + +public APLRes APLResAskPluginLoad2(Handle hMyself, bool bLate, char[] sError, int iErr_max) +{ + g_bLateload = bLate; + return APLRes_Success; +} + +public void OnAllPluginsLoaded() { - AddCommandListener(Say_Callback, "say"); - AddCommandListener(TeamSay_Callback, "say_team"); - - //Server CVar - HookEvent("server_cvar", Event_ServerConVar, EventHookMode_Pre); - HookEvent("player_changename", Event_NameChange, EventHookMode_Pre); - - //Cvars - hCvarCvarChange = CreateConVar("bq_cvar_change_suppress", "1", "Silence Server Cvars being changed, this makes for a clean chat with no disturbances."); - hCvarNameChange = CreateConVar("bq_name_change_suppress", "1", "Silence Player name Changes."); - hCvarSpecNameChange = CreateConVar("bq_name_change_spec_suppress", "1", "Silence Spectating Player name Changes."); - hCvarSpecSeeChat = CreateConVar("bq_show_player_team_chat_spec", "1", "Show Spectators Survivors and Infected Team chat?"); - - bCvarChange = GetConVarBool(hCvarCvarChange); - bNameChange = GetConVarBool(hCvarNameChange); - bSpecNameChange = GetConVarBool(hCvarSpecNameChange); - bSpecSeeChat = GetConVarBool(hCvarSpecSeeChat); - - hCvarCvarChange.AddChangeHook(cvarChanged); - hCvarNameChange.AddChangeHook(cvarChanged); - hCvarSpecNameChange.AddChangeHook(cvarChanged); - hCvarSpecSeeChat.AddChangeHook(cvarChanged); - - AutoExecConfig(true); - LoadTranslations("bequiet.phrases"); + g_bSourceComms = LibraryExists("sourcecomms++"); + g_bBaseComm = LibraryExists("basecomm"); } -Action Say_Callback(int client, char[] command, int args) +public void OnLibraryAdded(const char[] sName) { - char sayWord[MAX_NAME_LENGTH]; - GetCmdArg(1, sayWord, sizeof(sayWord)); - - if(sayWord[0] == '!' || sayWord[0] == '/') - { - return Plugin_Handled; - } - return Plugin_Continue; + if (StrEqual(sName, "sourcecomms++")) + g_bSourceComms = true; + else if (StrEqual(sName, "basecomm")) + g_bBaseComm = true; } -Action TeamSay_Callback(int client, char[] command, int args) +public void OnLibraryRemoved(const char[] sName) { - char sayWord[MAX_NAME_LENGTH]; - GetCmdArg(1, sayWord, sizeof(sayWord)); - - if(sayWord[0] == '!' || sayWord[0] == '/') - { - return Plugin_Handled; - } - - if (bSpecSeeChat && GetClientTeam(client) != 1) - { - char sChat[256]; - GetCmdArgString(sChat, 256); - StripQuotes(sChat); - int i = 1; - while (i <= 65) - { - if (IsValidClient(i) && GetClientTeam(i) == 1) - { - if (GetClientTeam(client) == 2) - { - CPrintToChat(i, "%t", "SurvivorSay", client, sChat); - } - else CPrintToChat(i, "%t", "InfectedSay", client, sChat); - } - i++; - } - } - return Plugin_Continue; + if (StrEqual(sName, "sourcecomms++")) + g_bSourceComms = false; + else if (StrEqual(sName, "basecomm")) + g_bBaseComm = false; +} + +public void OnPluginStart() +{ + LoadTranslation("bequiet.phrases"); + AddCommandListener(TeamSay_Callback, "say_team"); + + HookEvent("server_cvar", Event_ServerConVar, EventHookMode_Pre); + HookUserMessage(GetUserMessageId("SayText2"), TextMsg, true); + + g_cvarCvarChange = CreateConVar("bq_cvar_change_suppress", "1", "Silence Server Cvars being changed, this makes for a clean chat with no disturbances.", FCVAR_NONE, true, 0.0, true, 1.0); + g_cvarNameChange = CreateConVar("bq_name_change_suppress", "1", "Silence Player name Changes.", FCVAR_NONE, true, 0.0, true, 1.0); + g_cvarSpecSeeChat = CreateConVar("bq_show_player_team_chat_spec", "1", "Show Spectators Survivors and Infected Team chat?", FCVAR_NONE, true, 0.0, true, 1.0); + g_cvarBaseChat = CreateConVar("bq_basechat", "1", "basechat support?", FCVAR_NONE, true, 0.0, true, 1.0); + + AutoExecConfig(true, "bequiet"); + + if (!g_bLateload) + return; + + g_bSourceComms = LibraryExists("sourcecomms++"); + g_bBaseComm = LibraryExists("basecomm"); +} + +Action TeamSay_Callback(int iClient, char[] command, int argss) +{ + if (!g_cvarSpecSeeChat.BoolValue) + return Plugin_Continue; + + if (IsPunishedPlayer(iClient)) + return Plugin_Handled; + + char sayWord[MAX_NAME_LENGTH]; + GetCmdArg(1, sayWord, sizeof(sayWord)); + + if (sayWord[0] == '/') + return Plugin_Handled; + + if (g_cvarBaseChat.BoolValue && sayWord[0] == CHAT_SYMBOL) + return Plugin_Handled; + + char sChat[256]; + GetCmdArgString(sChat, 256); + StripQuotes(sChat); + SpecSeeTeamChat(iClient, sChat); + return Plugin_Handled; } Action Event_ServerConVar(Event event, const char[] name, bool dontBroadcast) { - if (bCvarChange) return Plugin_Handled; - return Plugin_Continue; + if (g_cvarCvarChange.BoolValue) + return Plugin_Handled; + return Plugin_Continue; } -Action Event_NameChange(Event event, const char[] name, bool dontBroadcast) +public Action TextMsg(UserMsg msg_id, BfRead msg, const int[] players, int playersNum, bool reliable, bool init) { - int clientid = event.GetInt("userid"); - int client = GetClientOfUserId(clientid); - - if (IsValidClient(client)) - { - if (GetClientTeam(client) == 1 && bSpecNameChange) return Plugin_Handled; - else if (bNameChange) return Plugin_Handled; - } - return Plugin_Continue; + msg.ReadByte(); // Skip first parameter + msg.ReadByte(); // Skip second parameter + + char buffer[100]; + buffer[0] = '\0'; + msg.ReadString(buffer, sizeof(buffer), false); + + // left4dead2/resource/left4dead2_english.txt, found "Cstrike_Name_Change" + if (StrContains(buffer, "Cstrike_Name_Change") == -1) + return Plugin_Continue; + + if (g_cvarNameChange.BoolValue) + return Plugin_Handled; + + return Plugin_Continue; } -void cvarChanged(Handle convar, const char[] oldValue, const char[] newValue) +/** + * This function handles the display of team chat messages to spectators in the game. + * It formats the chat message with the team name and author, and then sends it to the appropriate clients. + * + * @param iAuthor The client index of the message author. + * @param sChat The chat message to be displayed. + */ +void SpecSeeTeamChat(int iAuthor, char[] sChat) { - bCvarChange = hCvarCvarChange.BoolValue; - bNameChange = hCvarNameChange.BoolValue; - bSpecNameChange = hCvarSpecNameChange.BoolValue; - bSpecSeeChat = hCvarSpecSeeChat.BoolValue; + char + sTeamAuthor[16], + sMessage[500]; + + L4DTeam + L4DTeamAuthor = L4D_GetClientTeam(iAuthor); + + switch (L4DTeamAuthor) + { + case L4DTeam_Survivor: + Format(sTeamAuthor, sizeof(sTeamAuthor), "%t", "Team_Survivor"); + case L4DTeam_Infected: + Format(sTeamAuthor, sizeof(sTeamAuthor), "%t", "Team_Infected"); + case L4DTeam_Spectator: + Format(sTeamAuthor, sizeof(sTeamAuthor), "%t", "Team_Spectator"); + } + + Format(sMessage, sizeof(sMessage), "(%s) %N: %s", sTeamAuthor, iAuthor, sChat); + CRemoveTags(sMessage, sizeof(sMessage)); + PrintToServer("%s", sMessage); + + sMessage[0] = '\0'; + Format(sMessage, sizeof(sMessage), "(%s) \x03%N\x01: %s", sTeamAuthor, iAuthor, sChat); + for (int iTarget = 1; iTarget <= MaxClients; iTarget++) + { + if (IsClientConnected(iTarget) && (IsClientSourceTV(iTarget) || IsClientReplay(iTarget))) + { + CPrintToChatEx(iTarget, iAuthor, sMessage); + continue; + } + + if (!IsClientInGame(iTarget) || IsFakeClient(iTarget)) + continue; + + L4DTeam L4DTeamTarget = L4D_GetClientTeam(iTarget); + + // Don't show spectator chat to survivors or infected + if (L4DTeamAuthor == L4DTeam_Spectator && (L4DTeamTarget == L4DTeam_Survivor || L4DTeamTarget == L4DTeam_Infected)) + continue; + + // Don't show infected chat to survivors and vice versa + if ((L4DTeamAuthor == L4DTeam_Survivor && L4DTeamTarget == L4DTeam_Infected) || (L4DTeamAuthor == L4DTeam_Infected && L4DTeamTarget == L4DTeam_Survivor)) + continue; + + CPrintToChatEx(iTarget, iAuthor, sMessage); + } } -bool IsValidClient(int client) -{ - if (client <= 0 || client > MaxClients || !IsClientConnected(client) || !IsClientInGame(client)) return false; - return true; +/** + * Checks if a player is punished. + * + * @param iClient The iClient index of the player to check. + * @return True if the player is punished, false otherwise. + */ +bool IsPunishedPlayer(int iClient) +{ + if (g_bSourceComms) + return bNot != SourceComms_GetClientGagType(iClient); + + if (g_bBaseComm) + return BaseComm_IsClientGagged(iClient); + + return false; +} + +/** + * Returns the iClients team using L4DTeam. + * + * @param iClient Player's index. + * @return Current L4DTeam of player. + * @error Invalid iClient index. + */ +stock L4DTeam L4D_GetClientTeam(int iClient) +{ + return view_as(GetClientTeam(iClient)); +} + +/** + * Loads a translation file for the specified translation. + * + * @param sTranslation The name of the translation to load. + */ +void LoadTranslation(char[] sTranslation) +{ + char + sPath[PLATFORM_MAX_PATH], + sName[64]; + + Format(sName, sizeof(sName), "translations/%s.txt", sTranslation); + BuildPath(Path_SM, sPath, sizeof(sPath), sName); + + if (!FileExists(sPath)) + SetFailState("Missing translation file %s.txt", sTranslation); + LoadTranslations(sTranslation); } diff --git a/addons/sourcemod/scripting/include/sourcecomms.inc b/addons/sourcemod/scripting/include/sourcecomms.inc new file mode 100644 index 000000000..905cbdd99 --- /dev/null +++ b/addons/sourcemod/scripting/include/sourcecomms.inc @@ -0,0 +1,138 @@ +// ************************************************************************* +// This file is part of SourceBans++. +// +// Copyright (C) 2014-2024 SourceBans++ Dev Team +// +// SourceBans++ is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, per version 3 of the License. +// +// SourceBans++ is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with SourceBans++. If not, see . +// +// This file based off work(s) covered by the following copyright(s): +// +// SourceComms 0.9.266 +// Copyright (C) 2013-2014 Alexandr Duplishchev +// Licensed under GNU GPL version 3, or later. +// Page: - +// +// ************************************************************************* + +#if defined _sourcecomms_included +#endinput +#endif +#define _sourcecomms_included + +/** + * @section Int definitions for punishments types. + */ + +#define TYPE_MUTE 1 /**< Voice Mute */ +#define TYPE_GAG 2 /**< Gag (text chat) */ +#define TYPE_SILENCE 3 /**< Silence (mute + gag) */ +#define TYPE_UNMUTE 4 /**< Voice Unmute*/ +#define TYPE_UNGAG 5 /**< Ungag*/ +#define TYPE_UNSILENCE 6 /**< Unsilence */ +#define TYPE_TEMP_UNMUTE 14 /**< Temp mute removed */ +#define TYPE_TEMP_UNGAG 15 /**< Temp gag removed */ +#define TYPE_TEMP_UNSILENCE 16 /**< Temp silence removed */ + +/* Punishments types */ +enum bType { + bNot = 0, // Player chat or voice is not blocked + bSess, // ... blocked for player session (until reconnect) + bTime, // ... blocked for some time + bPerm // ... permanently blocked +} + +/** + * Sets a client's mute state. + * + * @param client Client index. + * @param muteState True to mute client, false to unmute. + * -------------------------------------Parameters below this line are used only for muteState=true------------------------------------- + * ----------------------------------for muteState=false these parameters are ignored (saveToDB=false)---------------------------------- + * @param muteLength Length of punishment in minutes. Value < 0 muting client for session. Permanent (0) is not allowed at this time. + * @param saveToDB If true, punishment will be saved in database. + * @param reason Reason for punishment. + * @return True if this caused a change in mute state, false otherwise. + */ +native bool SourceComms_SetClientMute(int client, bool muteState, int muteLength = -1, bool saveToDB = false, const char[] reason = "Muted through natives"); + +/** + * Sets a client's gag state. + * + * @param client Client index. + * @param gagState True to gag client, false to ungag. + * --------------------------------------Parameters below this line are used only for gagState=true-------------------------------------- + * -----------------------------------for gagState=false these parameters are ignored (saveToDB=false)----------------------------------- + * @param gagLength Length of punishment in minutes. Value < 0 gagging client for session. Permanent (0) is not allowed at this time. + * @param saveToDB If true, punishment will be saved in database. + * @param reason Reason for punishment. + * @return True if this caused a change in gag state, false otherwise. + */ +native bool SourceComms_SetClientGag(int client, bool gagState, int gagLength = -1, bool saveToDB = false, const char[] reason = "Gagged through natives"); + +/** + * Returns the client's mute type + * + * @param client The client index of the player to check mute status + * @return The client's current mute type index (see enum bType in the begin). + */ +native bType SourceComms_GetClientMuteType(int client); + + +/** + * Returns the client's gag type + * + * @param client The client index of the player to check gag status + * @return The client's current gag type index (see enum bType in the begin). + */ +native bType SourceComms_GetClientGagType(int client); + +/** + * Called when added communication block for player. + * + * @param client The client index of the admin who is blocking the client. + * @param target The client index of the player to blocked. + * @param time The time to blocked the player for (in minutes, 0 = permanent). + * @param type The type of block. See section "Int definitions for punishments types". + * @param reason The reason to block the player. + */ +forward void SourceComms_OnBlockAdded(int client, int target, int time, int type, char[] reason); + +/** + * Called when removed communication block for player. + * + * @param client The client index of the admin who is unblocking the client. + * @param target The client index of the player to blocked. + * @param type The type of unblock. See section "Int definitions for punishments types". + * @param reason The reason to unblock the player. + */ +forward void SourceComms_OnBlockRemoved(int client, int target, int type, char[] reason); + +public SharedPlugin __pl_sourcecomms = +{ + name = "sourcecomms++", + file = "sbpp_comms.smx", + #if defined REQUIRE_PLUGIN + required = 1 + #else + required = 0 + #endif +}; + +public void __pl_sourcecomms_SetNTVOptional() +{ + MarkNativeAsOptional("SourceComms_SetClientMute"); + MarkNativeAsOptional("SourceComms_SetClientGag"); + MarkNativeAsOptional("SourceComms_GetClientMuteType"); + MarkNativeAsOptional("SourceComms_GetClientGagType"); + +} diff --git a/addons/sourcemod/translations/bequiet.phrases.txt b/addons/sourcemod/translations/bequiet.phrases.txt index e1f6c3b5f..a6f928bcc 100644 --- a/addons/sourcemod/translations/bequiet.phrases.txt +++ b/addons/sourcemod/translations/bequiet.phrases.txt @@ -1,14 +1,15 @@ "Phrases" { - "SurvivorSay" - { - "#format" "{1:N},{2:s}" // 1:client, 2:sChat(the content the client said) - "en" "{default}(Survivor) {blue}{1} {default}: {2}" - } - - "InfectedSay" - { - "#format" "{1:N},{2:s}" - "en" "{default}(Infected) {red}{1} {default}: {2}" - } + "Team_Survivor" + { + "en" "Survivor" + } + "Team_Infected" + { + "en" "Infected" + } + "Team_Spectator" + { + "en" "Spected" + } } \ No newline at end of file diff --git a/addons/sourcemod/translations/chi/bequiet.phrases.txt b/addons/sourcemod/translations/chi/bequiet.phrases.txt index ab99f4820..4c231ebab 100644 --- a/addons/sourcemod/translations/chi/bequiet.phrases.txt +++ b/addons/sourcemod/translations/chi/bequiet.phrases.txt @@ -1,14 +1,15 @@ "Phrases" { - "SurvivorSay" - { - "#format" "{1:N},{2:s}" // 1:client, 2:sChat(the content the client said) - "chi" "{default}(生还者) {blue}{1} {default}: {2}" - } - - "InfectedSay" - { - "#format" "{1:N},{2:s}" - "chi" "{default}(感染者) {red}{1} {default}: {2}" - } + "Team_Survivor" + { + "chi" "生还者" + } + "Team_Infected" + { + "chi" "感染者" + } + "Team_Spectator" + { + "chi" "观众" + } } \ No newline at end of file diff --git a/addons/sourcemod/translations/es/bequiet.phrases.txt b/addons/sourcemod/translations/es/bequiet.phrases.txt new file mode 100644 index 000000000..4c231ebab --- /dev/null +++ b/addons/sourcemod/translations/es/bequiet.phrases.txt @@ -0,0 +1,15 @@ +"Phrases" +{ + "Team_Survivor" + { + "chi" "生还者" + } + "Team_Infected" + { + "chi" "感染者" + } + "Team_Spectator" + { + "chi" "观众" + } +} \ No newline at end of file