csgo-plugins/MapMusic/scripting/MapMusic_interface.sp
2020-03-25 20:09:12 +02:00

393 lines
11 KiB
SourcePawn

#include <sourcemod>
#include <sdktools>
#include <sdkhooks>
#include <regex>
#include <dhooks>
#include <soundlib2>
#include <hooksoundmsg>
#include <Basic>
#include <CSound>
#pragma semicolon 1
#pragma newdecls required
bool g_bBlockAll = false;
bool g_bDisabled[MAXPLAYERS+1] = false;
float g_fSoundVolume[MAXPLAYERS+1] = {1.0, ...};
float g_fMusicVolume[MAXPLAYERS+1] = {1.0, ...};
Handle hOnStartSoundForward = INVALID_HANDLE;
Basic g_aSounds;
//----------------------------------------------------------------------------------------------------
// Purpose: MyInfo
//----------------------------------------------------------------------------------------------------
public Plugin myinfo = {
name = "Map Music Control Interface",
author = "Mitch & SHUFEN from POSSESSION.tokyo & New Methods by PerfectLaugh from POSSESSION.tokyo & Testing by CrazyKid",
description = "Allows clients to adjust ambient sounds played by the map",
version = "4.0",
url = "http://www.sourcemod.net/"
};
//----------------------------------------------------------------------------------------------------
// Purpose: API
//----------------------------------------------------------------------------------------------------
public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) {
RegPluginLibrary("mapmusic_interface");
CreateNative("MapMusicInterface_GetSoundVolume", Native_GetSoundVolume);
CreateNative("MapMusicInterface_GetMusicVolume", Native_GetMusicVolume);
CreateNative("MapMusicInterface_SetSoundVolume", Native_SetSoundVolume);
CreateNative("MapMusicInterface_SetMusicVolume", Native_SetMusicVolume);
CreateNative("MapMusicInterface_IsDisabled", Native_IsDisabled);
CreateNative("MapMusicInterface_SetDisabled", Native_SetDisabled);
CreateNative("MapMusicInterface_GetBlockAll", Native_GetBlockAll);
CreateNative("MapMusicInterface_SetBlockAll", Native_SetBlockAll);
CreateNative("MapMusicInterface_HasExceedingLengthSound", Native_HasExceedingLengthSound);
return APLRes_Success;
}
public any Native_GetSoundVolume(Handle myself, int numParams) {
return GetSoundVolume(GetNativeCell(1));
}
public any Native_GetMusicVolume(Handle myself, int numParams) {
return GetMusicVolume(GetNativeCell(1));
}
public any Native_SetSoundVolume(Handle myself, int numParams) {
SetSoundVolume(GetNativeCell(1), GetNativeCell(2));
}
public any Native_SetMusicVolume(Handle myself, int numParams) {
SetMusicVolume(GetNativeCell(1), GetNativeCell(2));
}
public any Native_IsDisabled(Handle myself, int numParams) {
return IsClientDisabled(GetNativeCell(1));
}
public any Native_SetDisabled(Handle myself, int numParams) {
SetClientDisabled(GetNativeCell(1), GetNativeCell(2));
}
public any Native_GetBlockAll(Handle myself, int numParams)
{
return g_bBlockAll;
}
public any Native_SetBlockAll(Handle myself, int numParams)
{
g_bBlockAll = GetNativeCell(1);
}
public any Native_HasExceedingLengthSound(Handle myself, int numParams) {
return HasExceedingLengthSound(GetNativeCell(1));
}
//----------------------------------------------------------------------------------------------------
// Purpose: General
//----------------------------------------------------------------------------------------------------
public void OnPluginStart()
{
hOnStartSoundForward = CreateGlobalForward("MapMusicInterface_OnStartSound", ET_Hook, Param_String, Param_Float, Param_CellByRef, Param_CellByRef);
g_aSounds = new Basic();
for (int client = 1; client <= MaxClients; client++) {
g_bDisabled[client] = false;
g_fSoundVolume[client] = 1.0;
g_fMusicVolume[client] = 1.0;
}
}
//----------------------------------------------------------------------------------------------------
// Purpose: Clients
//----------------------------------------------------------------------------------------------------
public void OnClientConnected(int client) {
g_bDisabled[client] = false;
g_fSoundVolume[client] = 1.0;
g_fMusicVolume[client] = 1.0;
}
public void OnClientDisconnect_Post(int client) {
g_bDisabled[client] = false;
g_fSoundVolume[client] = 1.0;
g_fMusicVolume[client] = 1.0;
}
CSound GetCSound(int entity, int soundnum, bool is_ambient)
{
char key[64];
FormatEx(key, sizeof(key), "%x_%x_%d", entity, soundnum, is_ambient? 1 : 0);
CSound soundmap = view_as<CSound>(g_aSounds.GetHandle(key));
if (soundmap == null) {
soundmap = new CSound();
g_aSounds.SetHandle(key, soundmap);
}
return soundmap;
}
CSound GetCSoundFromEntity(int entity)
{
char prefix[64];
FormatEx(prefix, sizeof(prefix), "%x_", entity);
CSound soundmap = null;
StringMapSnapshot snap = g_aSounds.Snapshot();
char elem_key[64];
for (int i = 0; i < snap.Length; i++) {
snap.GetKey(i, elem_key, sizeof(elem_key));
if (strncmp(prefix, elem_key, strlen(prefix)) == 0) {
soundmap = view_as<CSound>(g_aSounds.GetHandle(elem_key));
break;
}
}
delete snap;
return soundmap;
}
#define EMIT_SND_NOFLAGS 0
#define EMIT_SND_CHANGE_VOL (1 << 0)
#define EMIT_SND_STOP (1 << 2)
#define EMIT_SND_STOP_LOOPING (1 << 5)
//----------------------------------------------------------------------------------------------------
// Purpose: HookSoundMsg
//----------------------------------------------------------------------------------------------------
public Action OnSoundEmittingToClient(int client, int origin[3], int& volume, float& delay, int& seq, int& entity, int& channel, int& pitch, int& flags, int& soundnum, int& soundnum_handle, int& speaker_entity, int& sound_level, bool is_sentence, bool is_ambient) {
if (is_sentence || soundnum == -1) {
return Plugin_Continue;
}
CSound soundmap = GetCSound(entity, soundnum, is_ambient);
if (flags & EMIT_SND_STOP || flags & EMIT_SND_STOP_LOOPING) {
volume = 0;
} else if (volume == -1) {
volume = 100;
}
if (soundmap.iPassTickCount != GetGameTickCount()) {
char sample[256];
char soundfile[256];
ReadStringTable(FindStringTable("soundprecache"), soundnum, sample, sizeof(sample));
if (sample[0] == '*' || sample[0] == '#')
strcopy(soundfile, sizeof(soundfile), sample[1]);
else
strcopy(soundfile, sizeof(soundfile), sample);
if (soundmap.fLength <= 0.0) {
soundmap.fLength = GetSoundLengthFloat(soundfile);
}
if (flags == EMIT_SND_NOFLAGS ||
((flags & EMIT_SND_CHANGE_VOL) && soundmap.fEndTime > 0.0 && soundmap.fEndTime <= GetTickedTime() && volume > 0)) {
bool isMusic = false;
Action res = APIOnStartSoundForward(soundfile, soundmap.fLength, isMusic, volume);
if (res != Plugin_Continue) {
return Plugin_Handled;
}
if (isMusic && g_bBlockAll) {
return Plugin_Handled;
}
soundmap.fEndTime = GetTickedTime() + soundmap.fLength;
soundmap.bIsMusic = isMusic;
}
else if (flags & EMIT_SND_STOP || flags & EMIT_SND_STOP_LOOPING) {
soundmap.fEndTime = GetTickedTime();
}
// Volumes should be binded on entity, rather than sound file path.
// This is the reason of why sounds were bugged on ze_deadcore.
// Airvulpes uses multiple entities with same sound file to play with huge volume.
soundmap.iVolume = volume;
}
if ((flags & EMIT_SND_CHANGE_VOL) && soundmap.iPassTickCount == GetGameTickCount()) {
if (soundmap.iPassClient != client) {
// When called AcceptEntityInput(entity, "Volume"); via void UpdateAllGenericSound(int client),
// that should be ignored because it was called even though needless except caller client.
return Plugin_Handled;
}
}
volume = GetAdjustedVolumeOfClient(client, volume, soundmap.bIsMusic);
return Plugin_Changed;
}
int GetAdjustedVolumeOfClient(int client, int sourcevol, bool isMusic) {
if (isMusic && IsClientDisabled(client)) {
return 0;
}
float fvolume;
if (isMusic) {
fvolume = GetMusicVolume(client);
}
else {
fvolume = GetSoundVolume(client);
}
if (fvolume <= 0.0) {
return 0;
}
return RoundToCeil(float(sourcevol) * fvolume);
}
Action APIOnStartSoundForward(const char[] sample, float length, bool& isMusic, int& volume) {
Call_StartForward(hOnStartSoundForward);
Call_PushString(sample);
Call_PushFloat(length);
Call_PushCellRef(isMusic);
Call_PushCellRef(volume);
Action res = Plugin_Continue;
Call_Finish(res);
return res;
}
//----------------------------------------------------------------------------------------------------
// Purpose: Internal
//----------------------------------------------------------------------------------------------------
float GetSoundVolume(int client)
{
return g_fSoundVolume[client];
}
float GetMusicVolume(int client)
{
return g_fMusicVolume[client];
}
void SetSoundVolume(int client, float volume) {
if (volume < 0.0) {
volume = 0.0;
}
else if (volume > 1.0) {
volume = 1.0;
}
g_fSoundVolume[client] = volume;
if (IsClientInGame(client)) {
UpdateAllGenericSound(client);
}
}
void SetMusicVolume(int client, float volume) {
SetClientDisabled(client, false);
if (volume < 0.0) {
volume = 0.0;
}
else if (volume > 1.0) {
volume = 1.0;
}
g_fMusicVolume[client] = volume;
if (IsClientInGame(client)) {
UpdateAllGenericSound(client);
}
}
bool IsClientDisabled(int client) {
return g_bDisabled[client];
}
void SetClientDisabled(int client, bool disabled) {
g_bDisabled[client] = disabled;
if (IsClientInGame(client)) {
UpdateAllGenericSound(client);
}
}
bool HasExceedingLengthSound(float searchlength) {
int entity = INVALID_ENT_REFERENCE;
while ((entity = FindEntityByClassname(entity, "ambient_generic")) != INVALID_ENT_REFERENCE) {
char soundfile[256];
GetEntPropString(entity, Prop_Data, "m_iszSound", soundfile, sizeof(soundfile));
if (soundfile[0] == '*' || soundfile[0] == '#')
strcopy(soundfile, sizeof(soundfile), soundfile[1]);
float length = GetSoundLengthFloat(soundfile);
if (searchlength > 0.0 && length >= searchlength) {
return true;
}
}
return false;
}
float GetSoundLengthFloat(const char[] soundFile) {
char path[PLATFORM_MAX_PATH];
FormatEx(path, sizeof(path), "sound/%s", soundFile);
if (!FileExists(path, true)) {
return 0.0;
}
SoundFile hSoundFile = new SoundFile(path, true);
if (hSoundFile == null)
return 0.0;
float result = float(hSoundFile.LengthInMilliseconds) / 1000.0;
delete hSoundFile;
return result;
}
public void OnClientDisconnect(int client)
{
OnEntityDestroyed(client);
}
public void OnEntityDestroyed(int entity) {
char prefix[32];
FormatEx(prefix, sizeof(prefix), "%x_", entity);
StringMapSnapshot snap = g_aSounds.Snapshot();
char elem_key[32];
for (int i = 0; i < snap.Length; i++) {
snap.GetKey(i, elem_key, sizeof(elem_key));
if (strncmp(prefix, elem_key, strlen(prefix)) == 0) {
delete g_aSounds.GetHandle(elem_key);
g_aSounds.Remove(elem_key);
}
}
delete snap;
}
void UpdateAllGenericSound(int client) {
int entity = INVALID_ENT_REFERENCE;
while ((entity = FindEntityByClassname(entity, "ambient_generic")) != INVALID_ENT_REFERENCE) {
CSound soundmap = GetCSoundFromEntity(entity);
if (soundmap == null) {
continue;
}
soundmap.iPassTickCount = GetGameTickCount();
soundmap.iPassClient = client;
SetVariantInt(soundmap.iVolume / 10);
AcceptEntityInput(entity, "Volume");
}
}