#pragma semicolon 1 #pragma newdecls required #include #include #include #include #include #define PLUGIN_VERSION "3.1.0" bool g_bStopWeaponSounds[MAXPLAYERS+1] = { false, ... }; bool g_bStopWeaponSoundsHooked = false; Handle g_hCookieStopSound = null; public Plugin myinfo = { name = "Toggle Game Sounds", author = "GoD-Tony, edit by Obus + BotoX, Oleg Tsvetkov", description = "Allows clients to stop hearing weapon sounds and map music", version = PLUGIN_VERSION, url = "http://www.sourcemod.net/" }; public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) { if(GetEngineVersion() != Engine_CSGO && GetEngineVersion() != Engine_CSS) { strcopy(error, err_max, "This plugin supports only CS:GO and CS:S!"); return APLRes_Failure; } return APLRes_Success; } public void OnPluginStart() { // Load translations LoadTranslations("plugin.stopsound.phrases"); LoadTranslations("common.phrases"); // For On/Off buttons in Cookies Menu // Detect game and hook appropriate tempent. AddTempEntHook("Shotgun Shot", Hook_ShotgunShot); CreateConVar("sm_stopsound_version", PLUGIN_VERSION, "Toggle Weapon Sounds", FCVAR_NOTIFY|FCVAR_DONTRECORD|FCVAR_REPLICATED); RegConsoleCmd("sm_stopsound", Command_StopSound, "Toggle hearing weapon sounds"); RegConsoleCmd("sm_sound", Command_StopSound, "Toggle hearing weapon sounds"); // Cookies g_hCookieStopSound = RegClientCookie("weaponsound_blocked", "Are weapon sounds enabled", CookieAccess_Protected); SetCookieMenuItem(CookieMenuHandler_StopSounds, 0, "Stop sounds"); // Suppress reload sound effects UserMsg ReloadEffect = GetUserMessageId("ReloadEffect"); // Game-specific setup if(GetEngineVersion() == Engine_CSGO) { // Weapon sounds will be caught here. AddNormalSoundHook(Hook_NormalSound_CSGO); if(ReloadEffect != INVALID_MESSAGE_ID) { HookUserMessage(ReloadEffect, Hook_ReloadEffect_CSGO, true); } } else // CS:S { // Weapon sounds will be caught here. AddNormalSoundHook(Hook_NormalSound_CSS); if(ReloadEffect != INVALID_MESSAGE_ID) { HookUserMessage(ReloadEffect, Hook_ReloadEffect_CSS, true); } } // Late load for(int client = 1; client <= MaxClients; client++) { if(IsClientInGame(client) && AreClientCookiesCached(client)) { OnClientCookiesCached(client); } } } public void OnPluginEnd() { for(int client = 1; client <= MaxClients; client++) { if(IsClientInGame(client)) { OnClientDisconnect(client); } } // Remove tempent hook RemoveTempEntHook("Shotgun Shot", Hook_ShotgunShot); // Find ReloadEffect UserMsg ReloadEffect = GetUserMessageId("ReloadEffect"); // Remove game-specific if(GetEngineVersion() == Engine_CSGO) { RemoveNormalSoundHook(Hook_NormalSound_CSGO); if(ReloadEffect != INVALID_MESSAGE_ID) UnhookUserMessage(ReloadEffect, Hook_ReloadEffect_CSGO, true); } else { RemoveNormalSoundHook(Hook_NormalSound_CSS); if(ReloadEffect != INVALID_MESSAGE_ID) UnhookUserMessage(ReloadEffect, Hook_ReloadEffect_CSS, true); } } public void Event_PlayerSpawn(Event event, const char[] name, bool dontBroadcast) { int client = GetClientOfUserId(event.GetInt("userid")); if(!IsClientInGame(client) || GetClientTeam(client) <= CS_TEAM_SPECTATOR) return; if(g_bStopWeaponSounds[client]) CPrintToChat(client, "%t %t", "Chat Prefix", "Weapon sounds disabled"); } public Action Command_StopSound(int client, int args) { if(client == 0) { ReplyToCommand(client, "[SM] Cannot use command from server console."); return Plugin_Handled; } g_bStopWeaponSounds[client] = !g_bStopWeaponSounds[client]; CheckWeaponSoundsHooks(); if(g_bStopWeaponSounds[client]) { SetClientCookie(client, g_hCookieStopSound, "1"); CReplyToCommand(client, "%t %t", "Chat Prefix", "Weapon sounds disabled"); } else { SetClientCookie(client, g_hCookieStopSound, ""); CReplyToCommand(client, "%t %t", "Chat Prefix", "Weapon sounds enabled"); } return Plugin_Handled; } public void OnClientCookiesCached(int client) { char sBuffer[10]; // Weapon Sounds cookie GetClientCookie(client, g_hCookieStopSound, sBuffer, sizeof(sBuffer)); if(sBuffer[0] != '\0') { g_bStopWeaponSounds[client] = true; g_bStopWeaponSoundsHooked = true; } else g_bStopWeaponSounds[client] = false; } public void OnClientDisconnect(int client) { g_bStopWeaponSounds[client] = false; CheckWeaponSoundsHooks(); } void CheckWeaponSoundsHooks() { bool bShouldHook = false; for(int i = 1; i <= MaxClients; i++) { if(g_bStopWeaponSounds[i]) { bShouldHook = true; break; } } // Fake (un)hook because toggling actual hooks will cause server instability. g_bStopWeaponSoundsHooked = bShouldHook; } public void CookieMenuHandler_StopSounds(int client, CookieMenuAction action, any info, char[] buffer, int maxlen) { if(action == CookieMenuAction_DisplayOption) { Format(buffer, maxlen, "%T", "Cookie Menu Stop Sounds", client); } else if(action == CookieMenuAction_SelectOption) { ShowStopSoundsSettingsMenu(client); } } void ShowStopSoundsSettingsMenu(int client) { Menu menu = new Menu(MenuHandler_StopSoundsSettings); menu.SetTitle("%T", "Cookie Menu Stop Sounds Title", client); char sBuffer[128]; Format(sBuffer, sizeof(sBuffer), "%T%T", "Weapon Sounds", client, g_bStopWeaponSounds[client] ? "Disabled" : "Enabled", client); menu.AddItem("0", sBuffer); menu.ExitBackButton = true; menu.Display(client, MENU_TIME_FOREVER); } public int MenuHandler_StopSoundsSettings(Menu menu, MenuAction action, int client, int selection) { if(action == MenuAction_Cancel) { ShowCookieMenu(client); } else if(action == MenuAction_Select) { if(selection == 0) { g_bStopWeaponSounds[client] = !g_bStopWeaponSounds[client]; CheckWeaponSoundsHooks(); if(g_bStopWeaponSounds[client]) { SetClientCookie(client, g_hCookieStopSound, "1"); CPrintToChat(client, "%t %t", "Chat Prefix", "Weapon sounds disabled"); } else { SetClientCookie(client, g_hCookieStopSound, ""); CPrintToChat(client, "%t %t", "Chat Prefix", "Weapon sounds enabled"); } } ShowStopSoundsSettingsMenu(client); } else if(action == MenuAction_End) { delete menu; } } public Action Hook_NormalSound_CSS(int clients[MAXPLAYERS], int &numClients, char sample[PLATFORM_MAX_PATH], int &entity, int &channel, float &volume, int &level, int &pitch, int &flags, char soundEntry[PLATFORM_MAX_PATH], int &seed) { if(!g_bStopWeaponSoundsHooked) return Plugin_Continue; // Ignore non-weapon sounds. if(channel != SNDCHAN_WEAPON && !(channel == SNDCHAN_AUTO && strncmp(sample, "physics/flesh", 13) == 0) && !(channel == SNDCHAN_VOICE && StrContains(sample, "player/headshot", true) != -1)) { return Plugin_Continue; } int j = 0; for(int i = 0; i < numClients; i++) { int client = clients[i]; if(!g_bStopWeaponSounds[client] && IsClientInGame(client)) { // Keep client. clients[j] = clients[i]; j++; } } numClients = j; return (numClients > 0) ? Plugin_Changed : Plugin_Stop; } public Action Hook_NormalSound_CSGO(int clients[MAXPLAYERS], int &numClients, char sample[PLATFORM_MAX_PATH], int &entity, int &channel, float &volume, int &level, int &pitch, int &flags, char soundEntry[PLATFORM_MAX_PATH], int &seed) { if(!g_bStopWeaponSoundsHooked) return Plugin_Continue; // Ignore non-weapon sounds. if(channel != SNDCHAN_WEAPON && !(channel == SNDCHAN_AUTO && strncmp(sample, "physics/flesh", 13) == 0) && !(channel == SNDCHAN_STATIC && StrContains(sample, "player/headshot", true) != -1)) { return Plugin_Continue; } int j = 0; for(int i = 0; i < numClients; i++) { int client = clients[i]; if(!g_bStopWeaponSounds[client] && IsClientInGame(client)) { // Keep client. clients[j] = clients[i]; j++; } } numClients = j; return (numClients > 0) ? Plugin_Changed : Plugin_Stop; } public Action Hook_ShotgunShot(const char[] te_name, const int[] Players, int numClients, float delay) { if(!g_bStopWeaponSoundsHooked) return Plugin_Continue; // Check which clients need to be excluded. int[] newClients = new int[numClients]; int newTotal = 0; for(int i = 0; i < numClients; i++) { if(!g_bStopWeaponSounds[Players[i]]) { newClients[newTotal++] = Players[i]; } } if(newTotal == numClients) { // No clients were excluded. return Plugin_Continue; } else if(newTotal == 0) { // All clients were excluded and there is no need to broadcast. return Plugin_Stop; } // Re-broadcast to clients that still need it. if(GetEngineVersion() == Engine_CSGO) { float vTemp[3]; TE_Start("Shotgun Shot"); TE_ReadVector("m_vecOrigin", vTemp); TE_WriteVector("m_vecOrigin", vTemp); TE_WriteFloat("m_vecAngles[0]", TE_ReadFloat("m_vecAngles[0]")); TE_WriteFloat("m_vecAngles[1]", TE_ReadFloat("m_vecAngles[1]")); TE_WriteNum("m_weapon", TE_ReadNum("m_weapon")); TE_WriteNum("m_iMode", TE_ReadNum("m_iMode")); TE_WriteNum("m_iSeed", TE_ReadNum("m_iSeed")); TE_WriteNum("m_iPlayer", TE_ReadNum("m_iPlayer")); TE_WriteFloat("m_fInaccuracy", TE_ReadFloat("m_fInaccuracy")); TE_WriteFloat("m_fSpread", TE_ReadFloat("m_fSpread")); TE_Send(newClients, newTotal, delay); } else { float vTemp[3]; TE_Start("Shotgun Shot"); TE_ReadVector("m_vecOrigin", vTemp); TE_WriteVector("m_vecOrigin", vTemp); TE_WriteFloat("m_vecAngles[0]", TE_ReadFloat("m_vecAngles[0]")); TE_WriteFloat("m_vecAngles[1]", TE_ReadFloat("m_vecAngles[1]")); TE_WriteNum("m_iWeaponID", TE_ReadNum("m_iWeaponID")); TE_WriteNum("m_iMode", TE_ReadNum("m_iMode")); TE_WriteNum("m_iSeed", TE_ReadNum("m_iSeed")); TE_WriteNum("m_iPlayer", TE_ReadNum("m_iPlayer")); TE_WriteFloat("m_fInaccuracy", TE_ReadFloat("m_fInaccuracy")); TE_WriteFloat("m_fSpread", TE_ReadFloat("m_fSpread")); TE_Send(newClients, newTotal, delay); } return Plugin_Stop; } public Action Hook_ReloadEffect_CSS(UserMsg msg_id, BfRead msg, const int[] players, int playersNum, bool reliable, bool init) { if(!g_bStopWeaponSoundsHooked) return Plugin_Continue; int client = msg.ReadShort(); // Check which clients need to be excluded. int[] newClients = new int[playersNum]; int newTotal = 0; for(int i = 0; i < playersNum; i++) { int client_ = players[i]; if(IsClientInGame(client_) && !g_bStopWeaponSounds[client_]) { newClients[newTotal++] = client_; } } if(newTotal == playersNum) { // No clients were excluded. return Plugin_Continue; } else if(newTotal == 0) { // All clients were excluded and there is no need to broadcast. return Plugin_Handled; } DataPack pack = new DataPack(); pack.WriteCell(client); pack.WriteCell(newTotal); for(int i = 0; i < newTotal; i++) { pack.WriteCell(newClients[i]); } RequestFrame(OnReloadEffect, pack); return Plugin_Handled; } public Action Hook_ReloadEffect_CSGO(UserMsg msg_id, Protobuf msg, const int[] players, int playersNum, bool reliable, bool init) { if(!g_bStopWeaponSoundsHooked) return Plugin_Continue; int client = PbReadInt(msg, "entidx"); // Check which clients need to be excluded. int[] newClients = new int[playersNum]; int newTotal = 0; for(int i = 0; i < playersNum; i++) { int client_ = players[i]; if(IsClientInGame(client_) && !g_bStopWeaponSounds[client_]) { newClients[newTotal++] = client_; } } if(newTotal == playersNum) { // No clients were excluded. return Plugin_Continue; } else if(newTotal == 0) { // All clients were excluded and there is no need to broadcast. return Plugin_Handled; } DataPack pack = new DataPack(); pack.WriteCell(client); pack.WriteCell(newTotal); for(int i = 0; i < newTotal; i++) { pack.WriteCell(newClients[i]); } RequestFrame(OnReloadEffect, pack); return Plugin_Handled; } public void OnReloadEffect(DataPack pack) { pack.Reset(); int client = pack.ReadCell(); int newTotal = pack.ReadCell(); int[] players = new int[newTotal]; int playersNum = 0; for(int i = 0; i < newTotal; i++) { int client_ = pack.ReadCell(); if(IsClientInGame(client_)) { players[playersNum++] = client_; } } delete pack; Handle ReloadEffect = StartMessage("ReloadEffect", players, playersNum, USERMSG_RELIABLE | USERMSG_BLOCKHOOKS); if(GetFeatureStatus(FeatureType_Native, "GetUserMessageType") == FeatureStatus_Available && GetUserMessageType() == UM_Protobuf) { PbSetInt(ReloadEffect, "entidx", client); } else { BfWriteShort(ReloadEffect, client); } EndMessage(); }