diff --git a/StopSound/scripting/StopSound.sp b/StopSound/scripting/StopSound.sp index 536231b0..f98102c1 100644 --- a/StopSound/scripting/StopSound.sp +++ b/StopSound/scripting/StopSound.sp @@ -1,22 +1,24 @@ #pragma semicolon 1 +#pragma newdecls required #include #include -#include #include #include #include -#pragma newdecls required - -#define PLUGIN_VERSION "3.0" -#define MAX_MAPMUSIC_ENTITIES 2048 +#define PLUGIN_VERSION "3.1.0" bool g_bStopWeaponSounds[MAXPLAYERS+1] = { false, ... }; +bool g_bStopMapMusic[MAXPLAYERS+1] = { false, ... }; bool g_bStopWeaponSoundsHooked = false; +bool g_bStopMapMusicHooked = false; + +StringMap g_MapMusic; Handle g_hCookieStopSound = null; +Handle g_hCookieStopMapMusic = null; public Plugin myinfo = { @@ -44,26 +46,54 @@ public void OnPluginStart() LoadTranslations("plugin.stopsound.phrases"); LoadTranslations("common.phrases"); // For On/Off buttons in Cookies Menu - // Detect game and hook appropriate tempent. - AddTempEntHook("Shotgun Shot", CSS_Hook_ShotgunShot); + g_MapMusic = new StringMap(); + // Detect game and hook appropriate tempent. + AddTempEntHook("Shotgun Shot", Hook_ShotgunShot); + + // Ambient sounds + AddAmbientSoundHook(Hook_AmbientSound); + + // Map music will be caught here + HookEvent("round_end", Event_RoundEnd); HookEvent("player_spawn", Event_PlayerSpawn); + 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"); + RegConsoleCmd("sm_stopmusic", Command_StopMusic, "Toggle hearing map music"); + RegConsoleCmd("sm_music", Command_StopMusic, "Toggle hearing map music"); // Cookies g_hCookieStopSound = RegClientCookie("weaponsound_blocked", "Are weapon sounds enabled", CookieAccess_Protected); + g_hCookieStopMapMusic = RegClientCookie("mapmusic_blocked", "Are map music enabled", CookieAccess_Protected); SetCookieMenuItem(CookieMenuHandler_StopSounds, 0, "Stop sounds"); // Suppress reload sound effects UserMsg ReloadEffect = GetUserMessageId("ReloadEffect"); - AddNormalSoundHook(Hook_NormalSound_CSS); + // 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_CSS, true); + 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++) @@ -86,15 +116,39 @@ public void OnPluginEnd() } // Remove tempent hook - RemoveTempEntHook("Shotgun Shot", CSS_Hook_ShotgunShot); + RemoveTempEntHook("Shotgun Shot", Hook_ShotgunShot); + + // Remove ambient sound hook + RemoveAmbientSoundHook(Hook_AmbientSound); // Find ReloadEffect UserMsg ReloadEffect = GetUserMessageId("ReloadEffect"); - RemoveNormalSoundHook(Hook_NormalSound_CSS); + // Remove game-specific + if(GetEngineVersion() == Engine_CSGO) + { + RemoveNormalSoundHook(Hook_NormalSound_CSGO); - if(ReloadEffect != INVALID_MESSAGE_ID) - UnhookUserMessage(ReloadEffect, Hook_ReloadEffect_CSS, true); + 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 OnMapStart() +{ + g_MapMusic.Clear(); +} + +public void Event_RoundEnd(Event event, const char[] name, bool dontBroadcast) +{ + g_MapMusic.Clear(); } public void Event_PlayerSpawn(Event event, const char[] name, bool dontBroadcast) @@ -106,6 +160,9 @@ public void Event_PlayerSpawn(Event event, const char[] name, bool dontBroadcast if(g_bStopWeaponSounds[client]) CPrintToChat(client, "%t %t", "Chat Prefix", "Weapon sounds disabled"); + + if(g_bStopMapMusic[client]) + CPrintToChat(client, "%t %t", "Chat Prefix", "Map music disabled"); } public Action Command_StopSound(int client, int args) @@ -133,6 +190,32 @@ public Action Command_StopSound(int client, int args) return Plugin_Handled; } +public Action Command_StopMusic(int client, int args) +{ + if(client == 0) + { + ReplyToCommand(client, "[SM] Cannot use command from server console."); + return Plugin_Handled; + } + + g_bStopMapMusic[client] = !g_bStopMapMusic[client]; + CheckMapMusicHooks(); + + if(g_bStopMapMusic[client]) + { + SetClientCookie(client, g_hCookieStopMapMusic, "1"); + CReplyToCommand(client, "%t %t", "Chat Prefix", "Map music disabled"); + StopMapMusic(client); + } + else + { + SetClientCookie(client, g_hCookieStopMapMusic, ""); + CReplyToCommand(client, "%t %t", "Chat Prefix", "Map music enabled"); + } + + return Plugin_Handled; +} + public void OnClientCookiesCached(int client) { char sBuffer[2]; @@ -147,13 +230,26 @@ public void OnClientCookiesCached(int client) } else g_bStopWeaponSounds[client] = false; + + // Map Music cookie + GetClientCookie(client, g_hCookieStopMapMusic, sBuffer, sizeof(sBuffer)); + + if(sBuffer[0] != '\0') + { + g_bStopMapMusic[client] = true; + g_bStopMapMusicHooked = true; + } + else + g_bStopMapMusic[client] = false; } public void OnClientDisconnect(int client) { g_bStopWeaponSounds[client] = false; + g_bStopMapMusic[client] = false; CheckWeaponSoundsHooks(); + CheckMapMusicHooks(); } void CheckWeaponSoundsHooks() @@ -173,6 +269,48 @@ void CheckWeaponSoundsHooks() g_bStopWeaponSoundsHooked = bShouldHook; } +void CheckMapMusicHooks() +{ + bool bShouldHook = false; + + for(int i = 1; i <= MaxClients; i++) + { + if(g_bStopMapMusic[i]) + { + bShouldHook = true; + break; + } + } + + // Fake (un)hook because toggling actual hooks will cause server instability. + g_bStopMapMusicHooked = bShouldHook; +} + +void StopMapMusic(int client) +{ + int entity = INVALID_ENT_REFERENCE; + + char sEntity[16]; + char sSample[PLATFORM_MAX_PATH]; + + StringMapSnapshot MapMusicSnap = g_MapMusic.Snapshot(); + for(int i = 0; i < MapMusicSnap.Length; i++) + { + MapMusicSnap.GetKey(i, sEntity, sizeof(sEntity)); + + if((entity = EntRefToEntIndex(StringToInt(sEntity))) == INVALID_ENT_REFERENCE) + { + g_MapMusic.Remove(sEntity); + continue; + } + + g_MapMusic.GetString(sEntity, sSample, sizeof(sSample)); + + EmitSoundToClient(client, sSample, entity, SNDCHAN_STATIC, SNDLEVEL_NONE, SND_STOPLOOPING, SNDVOL_NORMAL, SNDPITCH_NORMAL); + } + delete MapMusicSnap; +} + public void CookieMenuHandler_StopSounds(int client, CookieMenuAction action, any info, char[] buffer, int maxlen) { if(action == CookieMenuAction_DisplayOption) @@ -196,6 +334,9 @@ void ShowStopSoundsSettingsMenu(int client) Format(sBuffer, sizeof(sBuffer), "%T%T", "Weapon Sounds", client, g_bStopWeaponSounds[client] ? "Disabled" : "Enabled", client); menu.AddItem("0", sBuffer); + Format(sBuffer, sizeof(sBuffer), "%T%T", "Map Sounds", client, g_bStopMapMusic[client] ? "Disabled" : "Enabled", client); + menu.AddItem("1", sBuffer); + menu.ExitBackButton = true; menu.Display(client, MENU_TIME_FOREVER); } @@ -225,6 +366,23 @@ public int MenuHandler_StopSoundsSettings(Menu menu, MenuAction action, int clie } } + else if(selection == 1) + { + g_bStopMapMusic[client] = !g_bStopMapMusic[client]; + CheckMapMusicHooks(); + + if(g_bStopMapMusic[client]) + { + SetClientCookie(client, g_hCookieStopMapMusic, "1"); + CPrintToChat(client, "%t %t", "Chat Prefix", "Map music disabled"); + StopMapMusic(client); + } + else + { + SetClientCookie(client, g_hCookieStopMapMusic, ""); + CPrintToChat(client, "%t %t", "Chat Prefix", "Map music enabled"); + } + } ShowStopSoundsSettingsMenu(client); } @@ -266,7 +424,39 @@ public Action Hook_NormalSound_CSS(int clients[MAXPLAYERS], int &numClients, cha return (numClients > 0) ? Plugin_Changed : Plugin_Stop; } -public Action CSS_Hook_ShotgunShot(const char[] te_name, const int[] Players, int numClients, float delay) +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; @@ -295,19 +485,38 @@ public Action CSS_Hook_ShotgunShot(const char[] te_name, const int[] Players, in } // Re-broadcast to clients that still need it. - 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); + 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; } @@ -357,6 +566,51 @@ public Action Hook_ReloadEffect_CSS(UserMsg msg_id, BfRead msg, const int[] play 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(); @@ -389,3 +643,51 @@ public void OnReloadEffect(DataPack pack) EndMessage(); } + +public Action Hook_AmbientSound(char sample[PLATFORM_MAX_PATH], int &entity, float &volume, int &level, int &pitch, float pos[3], int &flags, float &delay) +{ + // Are we playing music? + if(!strncmp(sample, "music", 5, false) && !strncmp(sample, "#", 1, false)) + return Plugin_Continue; + + char sEntity[16]; + IntToString(EntIndexToEntRef(entity), sEntity, sizeof(sEntity)); + + g_MapMusic.SetString(sEntity, sample, true); + + if(!g_bStopMapMusicHooked) + return Plugin_Continue; + + switch(flags) + { + case(SND_NOFLAGS): + { + // Starting sound.. + for(int client = 1; client <= MaxClients; client++) + { + if (!IsClientInGame(client) || g_bStopMapMusic[client]) + continue; + + // Stop the old sound.. + EmitSoundToClient(client, sample, entity, SNDCHAN_STATIC, SNDLEVEL_NONE, SND_STOPLOOPING, SNDVOL_NORMAL, SNDPITCH_NORMAL); + + // Pass through the new sound.. + EmitSoundToClient(client, sample, entity, SNDCHAN_STATIC, level, flags, volume, pitch); + } + } + default: + { + // Nothing special going on.. Pass it through.. + for(int client = 1; client <= MaxClients; client++) + { + if (!IsClientInGame(client) || g_bStopMapMusic[client]) + continue; + + EmitSoundToClient(client, sample, entity, SNDCHAN_STATIC, level, flags, volume, pitch); + } + } + } + + // Block the default sound.. + return Plugin_Handled; +} \ No newline at end of file diff --git a/StopSound/scripting/include/multicolors.inc b/StopSound/scripting/include/multicolors.inc new file mode 100644 index 00000000..cb69a30d --- /dev/null +++ b/StopSound/scripting/include/multicolors.inc @@ -0,0 +1 @@ +../../../includes/multicolors.inc \ No newline at end of file diff --git a/StopSound/translations/plugin.stopsound.phrases.txt b/StopSound/translations/plugin.stopsound.phrases.txt index cfa4ad93..5f28ef2e 100644 --- a/StopSound/translations/plugin.stopsound.phrases.txt +++ b/StopSound/translations/plugin.stopsound.phrases.txt @@ -65,4 +65,4 @@ "en" ": Enabled" "ru" " [Включено]" } -} +} \ No newline at end of file