441 lines
11 KiB
SourcePawn
441 lines
11 KiB
SourcePawn
#pragma semicolon 1
|
|
#pragma newdecls required
|
|
|
|
#include <sourcemod>
|
|
#include <sdktools>
|
|
#include <clientprefs>
|
|
|
|
#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);
|
|
}
|