#pragma semicolon 1 #pragma newdecls required #include #include #include #define PLUGIN_VERSION "1.3" #define MAX_SONGS 128 #define MAX_TITLE_LENGTH 128 #define CHAT_PREFIX "\x01ARCANE \x02GAME" char g_SongTitles[MAX_SONGS][MAX_TITLE_LENGTH]; char g_SongFiles[MAX_SONGS][PLATFORM_MAX_PATH]; int g_SongCount; int g_NextSongIndex; bool g_MusicEnabled[MAXPLAYERS + 1]; int g_VolumePercent[MAXPLAYERS + 1]; int g_LastSongIndex[MAXPLAYERS + 1]; Cookie g_CookieEnabled; Cookie g_CookieVolume; public Plugin myinfo = { name = "Arcane Round End Music", author = "Codex", description = "End round music with personal toggle and volume control.", version = PLUGIN_VERSION, url = "" }; public void OnPluginStart() { RegConsoleCmd("sm_res", Command_ResMenu); RegAdminCmd("sm_res_reload", Command_ResReload, ADMFLAG_GENERIC); RegAdminCmd("sm_res_test", Command_ResTest, ADMFLAG_GENERIC); HookEvent("round_end", Event_RoundEnd, EventHookMode_PostNoCopy); HookEvent("round_start", Event_RoundStart, EventHookMode_PostNoCopy); g_CookieEnabled = RegClientCookie("arcane_music_enabled", "Round end music enabled", CookieAccess_Private); g_CookieVolume = RegClientCookie("arcane_music_volume", "Round end music volume", CookieAccess_Private); for (int client = 1; client <= MaxClients; client++) { if (IsClientInGame(client)) { ResetClientSettings(client); if (!IsFakeClient(client) && AreClientCookiesCached(client)) { LoadClientSettings(client); } } } LoadSongs(); } public void OnMapStart() { LoadSongs(); } public void OnClientPutInServer(int client) { ResetClientSettings(client); if (!IsFakeClient(client) && AreClientCookiesCached(client)) { LoadClientSettings(client); } } public void OnClientDisconnect(int client) { g_LastSongIndex[client] = -1; } public void OnClientCookiesCached(int client) { if (!IsValidMusicClient(client)) { return; } LoadClientSettings(client); } public Action Command_ResMenu(int client, int args) { if (!IsValidMusicClient(client)) { return Plugin_Handled; } if (g_SongCount <= 0) { PrintMusicChat(client, "Песни не загружены. Проверь конфиг и путь к wav."); } ShowResMenu(client); return Plugin_Handled; } public Action Command_ResReload(int client, int args) { LoadSongs(); ReplyToCommand(client, "[ArcaneGameMusic] Songs loaded: %d", g_SongCount); return Plugin_Handled; } public Action Command_ResTest(int client, int args) { if (!IsValidMusicClient(client)) { ReplyToCommand(client, "[ArcaneGameMusic] Command is available only for real players in game."); return Plugin_Handled; } if (g_SongCount <= 0) { ReplyToCommand(client, "[ArcaneGameMusic] No songs loaded. Check config and file paths."); return Plugin_Handled; } int songIndex = GetNextSongIndex(); PlaySongToClient(client, songIndex); ReplyToCommand(client, "[ArcaneGameMusic] Test song sent."); return Plugin_Handled; } public void Event_RoundEnd(Event event, const char[] name, bool dontBroadcast) { if (g_SongCount <= 0) { LogError("[ArcaneGameMusic] round_end fired, but no songs were loaded."); return; } int songIndex = GetNextSongIndex(); for (int client = 1; client <= MaxClients; client++) { if (!IsValidMusicClient(client) || !g_MusicEnabled[client]) { continue; } PlaySongToClient(client, songIndex); } } public void Event_RoundStart(Event event, const char[] name, bool dontBroadcast) { for (int client = 1; client <= MaxClients; client++) { if (IsClientInGame(client)) { StopClientSong(client); } } } public int MenuHandler_Res(Menu menu, MenuAction action, int client, int item) { if (action == MenuAction_End) { delete menu; return 0; } if (action != MenuAction_Select || !IsValidMusicClient(client)) { return 0; } char info[16]; menu.GetItem(item, info, sizeof(info)); if (StrEqual(info, "toggle")) { g_MusicEnabled[client] = !g_MusicEnabled[client]; if (!g_MusicEnabled[client]) { StopClientSong(client); PrintMusicChat(client, "Музыка \x02выключена"); } else { PrintMusicChat(client, "Музыка \x04включена"); } } else if (StrEqual(info, "down")) { int oldVolume = g_VolumePercent[client]; g_VolumePercent[client] = ClampVolume(g_VolumePercent[client] - 10); if (oldVolume == g_VolumePercent[client]) { PrintMusicChat(client, "Громкость уже на минимуме"); } else { PrintMusicChat(client, "Громкость: \x09%d%%", g_VolumePercent[client]); } } else if (StrEqual(info, "up")) { int oldVolume = g_VolumePercent[client]; g_VolumePercent[client] = ClampVolume(g_VolumePercent[client] + 10); if (oldVolume == g_VolumePercent[client]) { PrintMusicChat(client, "Громкость уже на максимуме"); } else { PrintMusicChat(client, "Громкость: \x09%d%%", g_VolumePercent[client]); } } SaveClientSettings(client); ShowResMenu(client); return 0; } void ShowResMenu(int client) { Menu menu = new Menu(MenuHandler_Res); char title[128]; FormatEx(title, sizeof(title), "ArcaneGameMusic\nВключено: %s\nГромкость: %d%%", g_MusicEnabled[client] ? "Да" : "Нет", g_VolumePercent[client]); menu.SetTitle(title); menu.AddItem("toggle", g_MusicEnabled[client] ? "Выключить музыку" : "Включить музыку"); menu.AddItem("down", "Уменьшить громкость (-10%)"); menu.AddItem("up", "Увеличить громкость (+10%)"); menu.ExitButton = true; menu.Display(client, 20); } void ResetClientSettings(int client) { g_MusicEnabled[client] = true; g_VolumePercent[client] = 40; g_LastSongIndex[client] = -1; } void LoadClientSettings(int client) { ResetClientSettings(client); char value[16]; GetClientCookie(client, g_CookieEnabled, value, sizeof(value)); if (value[0] != '\0') { g_MusicEnabled[client] = StringToInt(value) != 0; } GetClientCookie(client, g_CookieVolume, value, sizeof(value)); if (value[0] != '\0') { int volume = StringToInt(value); if (volume >= 0 && volume <= 100) { g_VolumePercent[client] = volume; } } } void SaveClientSettings(int client) { char value[16]; IntToString(g_MusicEnabled[client] ? 1 : 0, value, sizeof(value)); SetClientCookie(client, g_CookieEnabled, value); IntToString(g_VolumePercent[client], value, sizeof(value)); SetClientCookie(client, g_CookieVolume, value); } void LoadSongs() { g_SongCount = 0; char configPath[PLATFORM_MAX_PATH]; BuildPath(Path_SM, configPath, sizeof(configPath), "configs/arcane_round_end_music.cfg"); KeyValues kv = new KeyValues("RoundEndMusic"); if (!kv.ImportFromFile(configPath)) { LogError("[ArcaneGameMusic] Could not open config: %s", configPath); delete kv; return; } if (!kv.JumpToKey("songs")) { LogError("[ArcaneGameMusic] Missing section 'songs' in: %s", configPath); delete kv; return; } if (!kv.GotoFirstSubKey(false)) { LogError("[ArcaneGameMusic] No songs found in: %s", configPath); delete kv; return; } do { if (g_SongCount >= MAX_SONGS) { LogError("[ArcaneGameMusic] Song limit reached (%d)", MAX_SONGS); break; } kv.GetString("title", g_SongTitles[g_SongCount], MAX_TITLE_LENGTH); kv.GetString("file", g_SongFiles[g_SongCount], PLATFORM_MAX_PATH); TrimString(g_SongTitles[g_SongCount]); TrimString(g_SongFiles[g_SongCount]); if (g_SongTitles[g_SongCount][0] == '\0' || g_SongFiles[g_SongCount][0] == '\0') { continue; } char downloadPath[PLATFORM_MAX_PATH]; FormatEx(downloadPath, sizeof(downloadPath), "sound/%s", g_SongFiles[g_SongCount]); if (!FileExists(downloadPath)) { LogError("[ArcaneGameMusic] File not found: %s", downloadPath); continue; } if (!PrecacheSound(g_SongFiles[g_SongCount], false)) { LogError("[ArcaneGameMusic] Could not precache sound: %s", g_SongFiles[g_SongCount]); continue; } AddFileToDownloadsTable(downloadPath); g_SongCount++; } while (kv.GotoNextKey(false)); if (g_SongCount <= 0 || g_NextSongIndex >= g_SongCount) { g_NextSongIndex = 0; } LogMessage("[ArcaneGameMusic] Loaded songs: %d", g_SongCount); delete kv; } void PlaySongToClient(int client, int songIndex) { if (songIndex < 0 || songIndex >= g_SongCount) { return; } StopClientSong(client); float volume = float(g_VolumePercent[client]) / 100.0; EmitSoundToClient(client, g_SongFiles[songIndex], SOUND_FROM_PLAYER, SNDCHAN_STATIC, SNDLEVEL_NONE, SND_NOFLAGS, volume); g_LastSongIndex[client] = songIndex; PrintMusicChat(client, "Сейчас играет: \x09%s", g_SongTitles[songIndex]); PrintMusicChat(client, "Вы можете управлять музыкой по команде \x04!res"); } void StopClientSong(int client) { int songIndex = g_LastSongIndex[client]; if (songIndex < 0 || songIndex >= g_SongCount) { return; } StopSound(client, SNDCHAN_STATIC, g_SongFiles[songIndex]); g_LastSongIndex[client] = -1; } bool IsValidMusicClient(int client) { return client > 0 && client <= MaxClients && IsClientInGame(client) && !IsFakeClient(client); } int ClampVolume(int value) { if (value < 0) { return 0; } if (value > 100) { return 100; } return value; } int GetNextSongIndex() { if (g_SongCount <= 0) { return -1; } int songIndex = g_NextSongIndex; g_NextSongIndex++; if (g_NextSongIndex >= g_SongCount) { g_NextSongIndex = 0; } return songIndex; } void PrintMusicChat(int client, const char[] format, any ...) { char message[256]; VFormat(message, sizeof(message), format, 3); PrintToChat(client, "%s\x01 | %s", CHAT_PREFIX, message); }