diff --git a/extensions/sdktools/extension.cpp b/extensions/sdktools/extension.cpp index 3efac8dc..cdd7a895 100644 --- a/extensions/sdktools/extension.cpp +++ b/extensions/sdktools/extension.cpp @@ -36,6 +36,7 @@ IServerGameEnts *gameents = NULL; IBinTools *g_pBinTools = NULL; IGameConfig *g_pGameConf = NULL; IGameHelpers *g_pGameHelpers = NULL; +IEngineSound *engsound = NULL; HandleType_t g_CallHandle = 0; SMEXT_LINK(&g_SdkTools); @@ -49,6 +50,7 @@ bool SDKTools::SDK_OnLoad(char *error, size_t maxlength, bool late) sharesys->AddNatives(myself, g_CallNatives); sharesys->AddNatives(myself, g_Natives); sharesys->AddNatives(myself, g_TENatives); + sharesys->AddNatives(myself, g_SoundNatives); SM_GET_IFACE(GAMEHELPERS, g_pGameHelpers); @@ -89,7 +91,8 @@ void SDKTools::SDK_OnUnload() bool SDKTools::SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlen, bool late) { - GET_V_IFACE_CURRENT(serverFactory, gameents, IServerGameEnts, INTERFACEVERSION_SERVERGAMEENTS); + GET_V_IFACE_ANY(serverFactory, gameents, IServerGameEnts, INTERFACEVERSION_SERVERGAMEENTS); + GET_V_IFACE_ANY(engineFactory, engsound, IEngineSound, IENGINESOUND_SERVER_INTERFACE_VERSION); return true; } diff --git a/extensions/sdktools/extension.h b/extensions/sdktools/extension.h index e40ac9b4..191ed3d1 100644 --- a/extensions/sdktools/extension.h +++ b/extensions/sdktools/extension.h @@ -33,6 +33,7 @@ #include #include #include +#include /** * @brief Implementation of the SDK Tools extension. @@ -63,5 +64,6 @@ extern IBinTools *g_pBinTools; extern IGameConfig *g_pGameConf; extern IGameHelpers *g_pGameHelpers; extern HandleType_t g_CallHandle; +extern IEngineSound *engsound; #endif //_INCLUDE_SOURCEMOD_EXTENSION_PROPER_H_ diff --git a/extensions/sdktools/msvc8/sdktools.vcproj b/extensions/sdktools/msvc8/sdktools.vcproj index 92f602e3..84939d57 100644 --- a/extensions/sdktools/msvc8/sdktools.vcproj +++ b/extensions/sdktools/msvc8/sdktools.vcproj @@ -1,7 +1,7 @@ + + g_RegCalls; extern sp_nativeinfo_t g_Natives[]; +extern sp_nativeinfo_t g_SoundNatives[]; #endif //_INCLUDE_SDKTOOLS_VNATIVES_H_ diff --git a/extensions/sdktools/vsound.cpp b/extensions/sdktools/vsound.cpp new file mode 100644 index 00000000..ba97ba18 --- /dev/null +++ b/extensions/sdktools/vsound.cpp @@ -0,0 +1,278 @@ +#include "extension.h" +#include "CellRecipientFilter.h" + +static cell_t PrefetchSound(IPluginContext *pContext, const cell_t *params) +{ + char *name; + pContext->LocalToString(params[1], &name); + + engsound->PrefetchSound(name); + + return 1; +} + +static cell_t GetSoundDuration(IPluginContext *pContext, const cell_t *params) +{ + char *name; + pContext->LocalToString(params[1], &name); + + return sp_ftoc(engsound->GetSoundDuration(name)); +} + +static cell_t EmitAmbientSound(IPluginContext *pContext, const cell_t *params) +{ + cell_t entity; + Vector pos; + char *name; + float vol, delay; + int pitch, flags, level; + + entity = params[3]; + + cell_t *addr; + pContext->LocalToPhysAddr(params[2], &addr); + pos.x = sp_ctof(addr[0]); + pos.y = sp_ctof(addr[1]); + pos.z = sp_ctof(addr[2]); + + pContext->LocalToString(params[1], &name); + + vol = sp_ctof(params[6]); + level = params[4]; + flags = params[5]; + pitch = params[7]; + delay = sp_ctof(params[8]); + + engine->EmitAmbientSound(entity, pos, name, vol, (soundlevel_t)level, flags, pitch, delay); + + return 1; +} + +static cell_t FadeClientVolume(IPluginContext *pContext, const cell_t *params) +{ + int client = params[1]; + if (client < 1 || client > playerhelpers->GetMaxClients()) + { + return pContext->ThrowNativeError("Client index %d is not valid", client); + } + + IGamePlayer *player = playerhelpers->GetGamePlayer(client); + if (!player->IsInGame()) + { + return pContext->ThrowNativeError("Client index %d is not in game", client); + } + + engine->FadeClientVolume(player->GetEdict(), + sp_ctof(params[2]), + sp_ctof(params[3]), + sp_ctof(params[4]), + sp_ctof(params[5])); + + return 1; +} + +static cell_t StopSound(IPluginContext *pContext, const cell_t *params) +{ + int entity = params[1]; + int channel = params[2]; + + char *name; + pContext->LocalToString(params[3], &name); + + engsound->StopSound(entity, channel, name); + + return 1; +} + +static cell_t EmitSound(IPluginContext *pContext, const cell_t *params) +{ + cell_t *addr, *pl_addr; + + CellRecipientFilter crf; + pContext->LocalToPhysAddr(params[1], &pl_addr); + crf.Initialize(pl_addr, params[2]); + + char *sample; + pContext->LocalToString(params[3], &sample); + + int entity = params[4]; + int channel = params[5]; + int level = params[6]; + int flags = params[7]; + float vol = sp_ctof(params[8]); + int pitch = params[9]; + int speakerentity = params[10]; + + Vector *pOrigin = NULL, origin; + Vector *pDir = NULL, dir; + + pContext->LocalToPhysAddr(params[11], &addr); + if (addr != pContext->GetNullRef(SP_NULL_VECTOR)) + { + pOrigin = &origin; + origin.x = sp_ctof(addr[0]); + origin.y = sp_ctof(addr[1]); + origin.z = sp_ctof(addr[2]); + } + + pContext->LocalToPhysAddr(params[12], &addr); + if (addr != pContext->GetNullRef(SP_NULL_VECTOR)) + { + pDir = &dir; + dir.x = sp_ctof(addr[0]); + dir.y = sp_ctof(addr[1]); + dir.z = sp_ctof(addr[2]); + } + + bool updatePos = params[13] ? true : false; + float soundtime = sp_ctof(params[14]); + + CUtlVector *pOrigVec = NULL; + CUtlVector origvec; + if (params[0] > 14) + { + pOrigVec = &origvec; + for (cell_t i = 15; i <= params[0]; i++) + { + Vector vec; + pContext->LocalToPhysAddr(params[i], &addr); + vec.x = sp_ctof(addr[0]); + vec.y = sp_ctof(addr[1]); + vec.z = sp_ctof(addr[2]); + origvec.AddToTail(vec); + } + } + + /* If we're going to a "local player" and this is a dedicated server, + * intelligently redirect each sound. + */ + + if (entity == SOUND_FROM_LOCAL_PLAYER && engine->IsDedicatedServer()) + { + for (cell_t i=0; iEmitSound(crf, + player[0], + channel, + sample, + vol, + (soundlevel_t)level, + flags, + pitch, + pOrigin, + pDir, + pOrigVec, + updatePos, + soundtime, + speakerentity); + } + } else { + engsound->EmitSound(crf, + entity, + channel, + sample, + vol, + (soundlevel_t)level, + flags, + pitch, + pOrigin, + pDir, + pOrigVec, + updatePos, + soundtime, + speakerentity); + } + + return 1; +} + +static cell_t EmitSentence(IPluginContext *pContext, const cell_t *params) +{ + cell_t *addr; + + CellRecipientFilter crf; + pContext->LocalToPhysAddr(params[1], &addr); + crf.Initialize(addr, params[2]); + + int sentence = params[3]; + int entity = params[4]; + int channel = params[5]; + int level = params[6]; + int flags = params[7]; + float vol = sp_ctof(params[8]); + int pitch = params[9]; + int speakerentity = params[10]; + + Vector *pOrigin = NULL, origin; + Vector *pDir = NULL, dir; + + pContext->LocalToPhysAddr(params[11], &addr); + if (addr != pContext->GetNullRef(SP_NULL_VECTOR)) + { + pOrigin = &origin; + origin.x = sp_ctof(addr[0]); + origin.y = sp_ctof(addr[1]); + origin.z = sp_ctof(addr[2]); + } + + pContext->LocalToPhysAddr(params[12], &addr); + if (addr != pContext->GetNullRef(SP_NULL_VECTOR)) + { + pDir = &dir; + dir.x = sp_ctof(addr[0]); + dir.y = sp_ctof(addr[1]); + dir.z = sp_ctof(addr[2]); + } + + bool updatePos = params[13] ? true : false; + float soundtime = sp_ctof(params[14]); + + CUtlVector *pOrigVec = NULL; + CUtlVector origvec; + if (params[0] > 14) + { + pOrigVec = &origvec; + for (cell_t i = 15; i <= params[0]; i++) + { + Vector vec; + pContext->LocalToPhysAddr(params[i], &addr); + vec.x = sp_ctof(addr[0]); + vec.y = sp_ctof(addr[1]); + vec.z = sp_ctof(addr[2]); + origvec.AddToTail(vec); + } + } + + engsound->EmitSentenceByIndex(crf, + entity, + channel, + sentence, + vol, + (soundlevel_t)level, + flags, + pitch, + pOrigin, + pDir, + pOrigVec, + updatePos, + soundtime, + speakerentity); + + return 1; +} + +sp_nativeinfo_t g_SoundNatives[] = +{ + {"EmitAmbientSound", EmitAmbientSound}, + {"EmitSentence", EmitSentence}, + {"EmitSound", EmitSound}, + {"FadeClientVolume", FadeClientVolume}, + {"GetSoundDuration", GetSoundDuration}, + {"PrefetchSound", PrefetchSound}, + {"StopSound", StopSound}, + {NULL, NULL}, +}; diff --git a/plugins/include/sdktools.inc b/plugins/include/sdktools.inc index 685c3b4f..fa05a0b5 100644 --- a/plugins/include/sdktools.inc +++ b/plugins/include/sdktools.inc @@ -22,6 +22,7 @@ #include #include #include +#include enum SDKCallType { diff --git a/plugins/include/sdktools_sound.inc b/plugins/include/sdktools_sound.inc new file mode 100644 index 00000000..4cd2e22d --- /dev/null +++ b/plugins/include/sdktools_sound.inc @@ -0,0 +1,347 @@ +/** + * vim: set ts=4 : + * =============================================================== + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * =============================================================== + * + * This file is part of the SourceMod/SourcePawn SDK. This file may only be used + * or modified under the Terms and Conditions of its License Agreement, which is found + * in LICENSE.txt. The Terms and Conditions for making SourceMod extensions/plugins + * may change at any time. To view the latest information, see: + * http://www.sourcemod.net/license.php + * + * Version: $Id$ + */ + +#if defined _sdktools_sound_included + #endinput +#endif +#define _sdktools_sound_included + +/** + * Sound is from the client. + */ +#define SOUND_FROM_PLAYER -1 + +/** + * Sound is from the world. + */ +#define SOUND_FROM_WORLD 0 + +/** + * Sound channels. + */ +enum +{ + SNDCHAN_REPLACE = -1, /**< Unknown */ + SNDCHAN_AUTO = 0, /**< Auto */ + SNDCHAN_WEAPON = 1, /**< Weapons */ + SNDCHAN_VOICE = 2, /**< Voices */ + SNDCHAN_ITEM = 3, /**< Items */ + SNDCHAN_BODY = 4, /**< Player? */ + SNDCHAN_STREAM = 5, /**< "Stream channel from the static or dynamic area" */ + SNDCHAN_STATIC = 6, /**< "Stream channel from the static area" */ + SNDCHAN_VOICE_BASE = 7, /**< "Channel for network voice data" */ + SNDCHAN_USER_BASE = 135 /**< Anything >= this is allocated to game code */ +}; + +/** + * Sound flags for the sound emitter system. + */ +enum +{ + SND_NOFLAGS= 0, /**< Nothing */ + SND_CHANGEVOL = 1, /**< Change sound volume */ + SND_CHANGEPITCH = 2, /**< Change sound pitch */ + SND_STOP = 3, /**< Stop the sound */ + SND_SPAWNING = 4, /**< Used in some cases for ambients */ + SND_DELAY = 5, /**< Sound has an initial delay */ + SND_STOPLOOPING = 6, /**< Stop looping all sounds on the entity */ + SND_SPEAKER = 7, /**< Being played by a mic through a speaker */ + SND_SHOULDPAUSE = 8, /**< Pause if game is paused */ +}; + +/** + * Various predefined sound levels in dB. + */ +enum +{ + SNDLEVEL_NONE = 0, /**< None */ + SNDLEVEL_RUSTLE = 20, /**< Rustling leaves */ + SNDLEVEL_WHISPER = 25, /**< Whispering */ + SNDLEVEL_LIBRARY = 30, /**< In a library */ + SNDLEVEL_FRIDGE = 45, /**< Refridgerator */ + SNDLEVEL_HOME = 50, /**< Average home (3.9 attn) */ + SNDLEVEL_CONVO = 60, /**< Normal conversation (2.0 attn) */ + SNDLEVEL_DRYER = 60, /**< Clothes dryer */ + SNDLEVEL_DISHWASHER = 65, /**< Dishwasher/washing machine (1.5 attn) */ + SNDLEVEL_CAR = 70, /**< Car or vacuum cleaner (1.0 attn) */ + SNDLEVEL_NORMAL = 75, /**< Normal sound level */ + SNDLEVEL_TRAFFIC = 75, /**< Busy traffic (0.8 attn) */ + SNDLEVEL_MINIBIKE = 80, /**< Mini-bike, alarm clock (0.7 attn) */ + SNDLEVEL_SCREAMING = 90, /**< Screaming child (0.5 attn) */ + SNDLEVEL_TRAIN = 100, /**< Subway train, pneumatic drill (0.4 attn) */ + SNDLEVEL_HELICOPTER = 105, /**< Helicopter */ + SNDLEVEL_SNOWMOBILE = 110, /**< Snow mobile */ + SNDLEVEL_AIRCRAFT = 120, /**< Auto horn, aircraft */ + SNDLEVEL_RAIDSIREN = 130, /**< Air raid siren */ + SNDLEVEL_GUNFIRE = 140, /**< Gunshot, jet engine (0.27 attn) */ + SNDLEVEL_ROCKET = 180, /**< Rocket launching (0.2 attn) */ +}; + +#define SNDVOL_NORMAL 1.0 /**< Normal volume */ +#define SNDPITCH_NORMAL 100 /**< Normal pitch */ +#define SNDPITCH_LOW 95 /**< A low pitch */ +#define SNDPITCH_HIGH 120 /**< A high pitch */ +#define SNDATTN_NONE 0.0 /**< No attenuation */ +#define SNDATTN_NORMAL 0.8 /**< Normal attenuation */ +#define SNDATTN_STATIC 1.25 /**< Static attenuation? */ +#define SNDATTN_RICOCHET 1.5 /**< Ricochet effect */ +#define SNDATTN_IDLE 2.0 /**< Idle attenuation? */ + +/** + * Prefetches a sound. + * + * @param name Sound file name. + * @noreturn + */ +native PrefetchSound(const String:nane[]); + +/** + * Gets a sound's duration (does not work for mp3s). + * + * @param name Sound file name. + * @return Duration in seconds. + */ +native Float:GetSoundDuration(const String:name[]); + +/** + * Emits an ambient sound. + * + * @param entity Entity index to associate sound with. + * @param pos Origin of sound. + * @param name Sound file name. + * @param level Sound level (from 0 to 255). + * @param flags Sound flags. + * @param vol Volume (from 0.0 to 1.0). + * @param pitch Pitch (from 0 to 255). + * @param delay Play delay. + * @noreturn + */ +native EmitAmbientSound(const String:name[], + const Float:pos[3], + entity = SOUND_FROM_WORLD, + level = SNDLEVEL_NORMAL, + flags = SND_NOFLAGS, + Float:vol = SNDVOL_NORMAL, + pitch = SNDPITCH_NORMAL, + Float:delay = 0.0); + +/** + * Fades a client's volume level toward silence or a given percentage. + * + * @param client Client index. + * @param percent Fade percentage. + * @param outtime Fade out time, in seconds. + * @param holdtime Hold time, in seconds. + * @param intime Fade in time, in seconds. + * @noreturn + * @error Invalid client index or client not in game. + */ +native FadeClientVolume(client, Float:percent, Float:outtime, Float:holdtime, Float:intime); + +/** + * Stops a sound. + * + * @param entity Entity index. + * @param channel Channel number. + * @param name Sound name. + * @noreturn + */ +native StopSound(entity, channel, const String:name[]); + +/** + * Emits a sound to a list of clients. + * + * @param clients Array of client indexes. + * @param numClients Number of clients in the array. + * @param entity Entity to emit from. + * @param channel Channel to emit with. + * @param sample Sound name. + * @param level Sound level. + * @param flags Sound flags. + * @param volume Sound volume. + * @param pitch Sound pitch. + * @param speakerentity Unknown. + * @param origin Sound origin. + * @param dir Sound direction. + * @param updatePos Unknown (updates positions?) + * @param soundtime Alternate time to play sound for. + * @param ... Optional list of Float[3] arrays to specify additional origins. + * @noreturn + * @error Invalid client index. + */ +native EmitSound(const clients[], + numClients, + const String:sample[], + entity = SOUND_FROM_WORLD, + channel = SNDCHAN_AUTO, + level = SNDLEVEL_NORMAL, + flags = SND_NOFLAGS, + Float:volume = SNDVOL_NORMAL, + pitch = SNDPITCH_NORMAL, + speakerentity = -1, + const Float:origin[3] = NULL_VECTOR, + const Float:dir[3] = NULL_VECTOR, + bool:updatePos = true, + Float:soundtime = 0.0, + any:...); + +/** + * Emits a sentence to a list of clients. + * + * @param clients Array of client indexes. + * @param numClients Number of clients in the array. + * @param entity Entity to emit from. + * @param channel Channel to emit with. + * @param sentence Sentence index (from PrecacheSenteFile). + * @param level Sound level. + * @param flags Sound flags. + * @param volume Sound volume. + * @param pitch Sound pitch. + * @param speakerentity Unknown. + * @param origin Sound origin. + * @param dir Sound direction. + * @param updatePos Unknown (updates positions?) + * @param soundtime Alternate time to play sound for. + * @param ... Optional list of Float[3] arrays to specify additional origins. + * @noreturn + * @error Invalid client index. + */ +native EmitSentence(const clients[], + numClients, + sentence, + entity = SOUND_FROM_WORLD, + channel = SNDCHAN_AUTO, + level = SNDLEVEL_NORMAL, + flags = SND_NOFLAGS, + Float:volume = SNDVOL_NORMAL, + pitch = SNDPITCH_NORMAL, + speakerentity = -1, + const Float:origin[3] = NULL_VECTOR, + const Float:dir[3] = NULL_VECTOR, + bool:updatePos = true, + Float:soundtime = 0.0, + any:...); + + +/** + * Wrapper to emit sound to one client. + * + * @param client Client index. + * @param entity Entity to emit from. + * @param channel Channel to emit with. + * @param sample Sound name. + * @param level Sound level. + * @param flags Sound flags. + * @param volume Sound volume. + * @param pitch Sound pitch. + * @param speakerentity Unknown. + * @param origin Sound origin. + * @param dir Sound direction. + * @param updatePos Unknown (updates positions?) + * @param soundtime Alternate time to play sound for. + * @noreturn + * @error Invalid client index. + */ +stock EmitSoundToClient(client, + const String:sample[], + entity = SOUND_FROM_PLAYER, + channel = SNDCHAN_AUTO, + level = SNDLEVEL_NORMAL, + flags = SND_NOFLAGS, + Float:volume = SNDVOL_NORMAL, + pitch = SNDPITCH_NORMAL, + speakerentity = -1, + const Float:origin[3] = NULL_VECTOR, + const Float:dir[3] = NULL_VECTOR, + bool:updatePos = true, + Float:soundtime = 0.0) +{ + new clients[1]; + clients[0] = client; + /* Save some work for SDKTools and remove SOUND_FROM_PLAYER references */ + entity = (entity == SOUND_FROM_PLAYER) ? client : entity; + EmitSound(clients, 1, sample, entity, channel, + level, flags, volume, pitch, speakerentity, + origin, dir, updatePos, soundtime); +} + +/** + * Wrapper to emit sound to all clients. + * + * @param entity Entity to emit from. + * @param channel Channel to emit with. + * @param sample Sound name. + * @param level Sound level. + * @param flags Sound flags. + * @param volume Sound volume. + * @param pitch Sound pitch. + * @param speakerentity Unknown. + * @param origin Sound origin. + * @param dir Sound direction. + * @param updatePos Unknown (updates positions?) + * @param soundtime Alternate time to play sound for. + * @noreturn + * @error Invalid client index. + */ +stock EmitSoundToAll(const String:sample[], + entity = SOUND_FROM_PLAYER, + channel = SNDCHAN_AUTO, + level = SNDLEVEL_NORMAL, + flags = SND_NOFLAGS, + Float:volume = SNDVOL_NORMAL, + pitch = SNDPITCH_NORMAL, + speakerentity = -1, + const Float:origin[3] = NULL_VECTOR, + const Float:dir[3] = NULL_VECTOR, + bool:updatePos = true, + Float:soundtime = 0.0) +{ + new maxClients = GetMaxClients(); + new clients[maxClients]; + new total = 0; + + for (new i=1; i<=maxClients; i++) + { + if (IsClientInGame(i)) + { + clients[total++] = i; + } + } + + if (!total) + { + return; + } + + EmitSound(clients, total, sample, entity, channel, + level, flags, volume, pitch, speakerentity, + origin, dir, updatePos, soundtime); +} + +/** + * Converts an attenuation value to a sound level. + * This function is from the HL2SDK. + * + * @param attn Attenuation value. + * @return Integer sound level. + */ +stock ATTN_TO_SNDLEVEL(Float:attn) +{ + if (attn > 0.0) + { + return RoundFloat(50.0 + (20.0 / attn)); + } + return 0; +}