3 * =============================================================================
4 * SourceMod SDKTools Extension
5 * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
6 * =============================================================================
8 * This program is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU General Public License, version 3.0, as published by the
10 * Free Software Foundation.
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
17 * You should have received a copy of the GNU General Public License along with
18 * this program. If not, see <http://www.gnu.org/licenses/>.
20 * As a special exception, AlliedModders LLC gives you permission to link the
21 * code of this program (as well as its derivative works) to "Half-Life 2," the
22 * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
23 * by the Valve Corporation. You must obey the GNU General Public License in
24 * all respects for all other code used. Additionally, AlliedModders LLC grants
25 * this exception to all derivative works. AlliedModders LLC defines further
26 * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
27 * or <http://www.sourcemod.net/license.php>.
33 #include <IForwardSys.h>
35 SH_DECL_HOOK8_void(IVEngineServer, EmitAmbientSound, SH_NOATTRIB, 0, int, const Vector &, const char *, float, soundlevel_t, int, int, float);
36 SH_DECL_HOOK14_void(IEngineSound, EmitSound, SH_NOATTRIB, 0, IRecipientFilter &, int, int, const char *, float, float, int, int, const Vector *, const Vector *, CUtlVector<Vector> *, bool, float, int);
37 SH_DECL_HOOK14_void(IEngineSound, EmitSound, SH_NOATTRIB, 1, IRecipientFilter &, int, int, const char *, float, soundlevel_t, int, int, const Vector *, const Vector *, CUtlVector<Vector> *, bool, float, int);
39 bool g_InSoundHook = false;
41 /***************************
43 * Sound Related Hook Class *
45 ****************************/
47 cell_t SoundReferenceToIndex(cell_t ref)
49 if (ref == 0 || ref == -1 || ref == -2)
54 return gamehelpers->ReferenceToIndex(ref);
57 size_t SoundHooks::_FillInPlayers(int *pl_array, IRecipientFilter *pFilter)
59 size_t size = static_cast<size_t>(pFilter->GetRecipientCount());
61 for (size_t i=0; i<size; i++)
63 pl_array[i] = pFilter->GetRecipientIndex(i);
69 void SoundHooks::_IncRefCounter(int type)
71 if (type == NORMAL_SOUND_HOOK)
73 if (m_NormalCount++ == 0)
75 SH_ADD_HOOK_MEMFUNC(IEngineSound, EmitSound, engsound, this, &SoundHooks::OnEmitSound, false);
76 SH_ADD_HOOK_MEMFUNC(IEngineSound, EmitSound, engsound, this, &SoundHooks::OnEmitSound2, false);
79 else if (type == AMBIENT_SOUND_HOOK)
81 if (m_AmbientCount++ == 0)
83 SH_ADD_HOOK_MEMFUNC(IVEngineServer, EmitAmbientSound, engine, this, &SoundHooks::OnEmitAmbientSound, false);
88 void SoundHooks::_DecRefCounter(int type)
90 if (type == NORMAL_SOUND_HOOK)
92 if (--m_NormalCount == 0)
94 SH_REMOVE_HOOK_MEMFUNC(IEngineSound, EmitSound, engsound, this, &SoundHooks::OnEmitSound, false);
95 SH_REMOVE_HOOK_MEMFUNC(IEngineSound, EmitSound, engsound, this, &SoundHooks::OnEmitSound2, false);
98 else if (type == AMBIENT_SOUND_HOOK)
100 if (--m_AmbientCount == 0)
102 SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, EmitAmbientSound, engine, this, &SoundHooks::OnEmitAmbientSound, false);
107 void SoundHooks::Initialize()
109 plsys->AddPluginsListener(this);
112 void SoundHooks::Shutdown()
114 plsys->RemovePluginsListener(this);
117 SH_REMOVE_HOOK_MEMFUNC(IEngineSound, EmitSound, engsound, this, &SoundHooks::OnEmitSound, false);
118 SH_REMOVE_HOOK_MEMFUNC(IEngineSound, EmitSound, engsound, this, &SoundHooks::OnEmitSound2, false);
122 SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, EmitAmbientSound, engine, this, &SoundHooks::OnEmitAmbientSound, false);
126 void SoundHooks::OnPluginUnloaded(IPlugin *plugin)
129 IPluginContext *pContext = plugin->GetBaseContext();
133 for (iter=m_AmbientFuncs.begin(); iter!=m_AmbientFuncs.end(); )
135 if ((*iter)->GetParentContext() == pContext)
137 iter = m_AmbientFuncs.erase(iter);
138 _DecRefCounter(AMBIENT_SOUND_HOOK);
148 for (iter=m_NormalFuncs.begin(); iter!=m_NormalFuncs.end(); )
150 if ((*iter)->GetParentContext() == pContext)
152 iter = m_NormalFuncs.erase(iter);
153 _DecRefCounter(NORMAL_SOUND_HOOK);
163 void SoundHooks::AddHook(int type, IPluginFunction *pFunc)
165 if (type == NORMAL_SOUND_HOOK)
167 m_NormalFuncs.push_back(pFunc);
168 _IncRefCounter(NORMAL_SOUND_HOOK);
170 else if (type == AMBIENT_SOUND_HOOK)
172 m_AmbientFuncs.push_back(pFunc);
173 _IncRefCounter(AMBIENT_SOUND_HOOK);
177 bool SoundHooks::RemoveHook(int type, IPluginFunction *pFunc)
180 if (type == NORMAL_SOUND_HOOK)
182 if ((iter=m_NormalFuncs.find(pFunc)) != m_NormalFuncs.end())
184 m_NormalFuncs.erase(iter);
185 _DecRefCounter(NORMAL_SOUND_HOOK);
193 else if (type == AMBIENT_SOUND_HOOK)
195 if ((iter=m_AmbientFuncs.find(pFunc)) != m_AmbientFuncs.end())
197 m_AmbientFuncs.erase(iter);
198 _DecRefCounter(AMBIENT_SOUND_HOOK);
210 void SoundHooks::OnEmitAmbientSound(int entindex, const Vector &pos, const char *samp, float vol,
211 soundlevel_t soundlevel, int fFlags, int pitch, float delay)
214 IPluginFunction *pFunc;
215 cell_t vec[3] = {sp_ftoc(pos.x), sp_ftoc(pos.y), sp_ftoc(pos.z)};
216 cell_t res = static_cast<ResultType>(Pl_Continue);
217 char buffer[PLATFORM_MAX_PATH];
218 strcpy(buffer, samp);
220 for (iter=m_AmbientFuncs.begin(); iter!=m_AmbientFuncs.end(); iter++)
223 pFunc->PushStringEx(buffer, sizeof(buffer), SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK);
224 pFunc->PushCellByRef(&entindex);
225 pFunc->PushFloatByRef(&vol);
226 pFunc->PushCellByRef(reinterpret_cast<cell_t *>(&soundlevel));
227 pFunc->PushCellByRef(&pitch);
228 pFunc->PushArray(vec, 3, SM_PARAM_COPYBACK);
229 pFunc->PushCellByRef(&fFlags);
230 pFunc->PushFloatByRef(&delay);
231 g_InSoundHook = true;
232 pFunc->Execute(&res);
233 g_InSoundHook = false;
240 RETURN_META(MRES_SUPERCEDE);
245 vec2.x = sp_ctof(vec[0]);
246 vec2.y = sp_ctof(vec[1]);
247 vec2.z = sp_ctof(vec[2]);
248 RETURN_META_NEWPARAMS(MRES_IGNORED, &IVEngineServer::EmitAmbientSound,
249 (entindex, vec2, buffer, vol, soundlevel, fFlags, pitch, delay));
255 void SoundHooks::OnEmitSound(IRecipientFilter &filter, int iEntIndex, int iChannel, const char *pSample,
256 float flVolume, soundlevel_t iSoundlevel, int iFlags, int iPitch, const Vector *pOrigin,
257 const Vector *pDirection, CUtlVector<Vector> *pUtlVecOrigins, bool bUpdatePositions,
258 float soundtime, int speakerentity)
261 IPluginFunction *pFunc;
262 cell_t res = static_cast<ResultType>(Pl_Continue);
263 char buffer[PLATFORM_MAX_PATH];
264 strcpy(buffer, pSample);
266 for (iter=m_NormalFuncs.begin(); iter!=m_NormalFuncs.end(); iter++)
268 int players[64], size;
269 size = _FillInPlayers(players, &filter);
272 pFunc->PushArray(players, 64, SM_PARAM_COPYBACK);
273 pFunc->PushCellByRef(&size);
274 pFunc->PushStringEx(buffer, sizeof(buffer), SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK);
275 pFunc->PushCellByRef(&iEntIndex);
276 pFunc->PushCellByRef(&iChannel);
277 pFunc->PushFloatByRef(&flVolume);
278 pFunc->PushCellByRef(reinterpret_cast<cell_t *>(&iSoundlevel));
279 pFunc->PushCellByRef(&iPitch);
280 pFunc->PushCellByRef(&iFlags);
281 g_InSoundHook = true;
282 pFunc->Execute(&res);
283 g_InSoundHook = false;
290 RETURN_META(MRES_SUPERCEDE);
294 CellRecipientFilter crf;
295 crf.Initialize(players, size);
296 RETURN_META_NEWPARAMS(
298 static_cast<void (IEngineSound::*)(IRecipientFilter &, int, int, const char*, float, soundlevel_t,
299 int, int, const Vector *, const Vector *, CUtlVector<Vector> *, bool, float, int)>(&IEngineSound::EmitSound),
300 (crf, iEntIndex, iChannel, buffer, flVolume, iSoundlevel, iFlags, iPitch, pOrigin,
301 pDirection, pUtlVecOrigins, bUpdatePositions, soundtime, speakerentity)
308 void SoundHooks::OnEmitSound2(IRecipientFilter &filter, int iEntIndex, int iChannel, const char *pSample,
309 float flVolume, float flAttenuation, int iFlags, int iPitch, const Vector *pOrigin,
310 const Vector *pDirection, CUtlVector<Vector> *pUtlVecOrigins, bool bUpdatePositions,
311 float soundtime, int speakerentity)
314 IPluginFunction *pFunc;
315 cell_t res = static_cast<ResultType>(Pl_Continue);
316 cell_t sndlevel = static_cast<cell_t>(ATTN_TO_SNDLVL(flAttenuation));
317 char buffer[PLATFORM_MAX_PATH];
318 strcpy(buffer, pSample);
320 for (iter=m_NormalFuncs.begin(); iter!=m_NormalFuncs.end(); iter++)
322 int players[64], size;
323 size = _FillInPlayers(players, &filter);
326 pFunc->PushArray(players, 64, SM_PARAM_COPYBACK);
327 pFunc->PushCellByRef(&size);
328 pFunc->PushStringEx(buffer, sizeof(buffer), SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK);
329 pFunc->PushCellByRef(&iEntIndex);
330 pFunc->PushCellByRef(&iChannel);
331 pFunc->PushFloatByRef(&flVolume);
332 pFunc->PushCellByRef(&sndlevel);
333 pFunc->PushCellByRef(&iPitch);
334 pFunc->PushCellByRef(&iFlags);
335 g_InSoundHook = true;
336 pFunc->Execute(&res);
337 g_InSoundHook = false;
344 RETURN_META(MRES_SUPERCEDE);
348 CellRecipientFilter crf;
349 crf.Initialize(players, size);
350 RETURN_META_NEWPARAMS(
352 static_cast<void (IEngineSound::*)(IRecipientFilter &, int, int, const char*, float, float,
353 int, int, const Vector *, const Vector *, CUtlVector<Vector> *, bool, float, int)>(&IEngineSound::EmitSound),
354 (crf, iEntIndex, iChannel, buffer, flVolume, SNDLVL_TO_ATTN(static_cast<soundlevel_t>(sndlevel)),
355 iFlags, iPitch, pOrigin, pDirection, pUtlVecOrigins, bUpdatePositions, soundtime, speakerentity)
362 /************************
364 * Sound Related Natives *
366 *************************/
368 SoundHooks s_SoundHooks;
370 static cell_t PrefetchSound(IPluginContext *pContext, const cell_t *params)
373 pContext->LocalToString(params[1], &name);
375 engsound->PrefetchSound(name);
380 static cell_t GetSoundDuration(IPluginContext *pContext, const cell_t *params)
383 pContext->LocalToString(params[1], &name);
385 return sp_ftoc(engsound->GetSoundDuration(name));
388 static cell_t EmitAmbientSound(IPluginContext *pContext, const cell_t *params)
394 int pitch, flags, level;
396 entity = SoundReferenceToIndex(params[3]);
399 pContext->LocalToPhysAddr(params[2], &addr);
400 pos.x = sp_ctof(addr[0]);
401 pos.y = sp_ctof(addr[1]);
402 pos.z = sp_ctof(addr[2]);
404 pContext->LocalToString(params[1], &name);
406 vol = sp_ctof(params[6]);
410 delay = sp_ctof(params[8]);
414 ENGINE_CALL(EmitAmbientSound)(entity, pos, name, vol, (soundlevel_t)level, flags, pitch, delay);
418 engine->EmitAmbientSound(entity, pos, name, vol, (soundlevel_t)level, flags, pitch, delay);
424 static cell_t FadeClientVolume(IPluginContext *pContext, const cell_t *params)
426 int client = params[1];
427 if (client < 1 || client > playerhelpers->GetMaxClients())
429 return pContext->ThrowNativeError("Client index %d is not valid", client);
432 IGamePlayer *player = playerhelpers->GetGamePlayer(client);
433 if (!player->IsInGame())
435 return pContext->ThrowNativeError("Client index %d is not in game", client);
438 engine->FadeClientVolume(player->GetEdict(),
447 static cell_t StopSound(IPluginContext *pContext, const cell_t *params)
449 int entity = SoundReferenceToIndex(params[1]);
450 int channel = params[2];
453 pContext->LocalToString(params[3], &name);
455 engsound->StopSound(entity, channel, name);
460 static cell_t EmitSound(IPluginContext *pContext, const cell_t *params)
462 cell_t *addr, *cl_array;
463 CellRecipientFilter crf;
464 unsigned int numClients;
466 IGamePlayer *pPlayer = NULL;
468 pContext->LocalToPhysAddr(params[1], &cl_array);
469 numClients = params[2];
471 /* Client validation */
472 for (unsigned int i = 0; i < numClients; i++)
474 client = cl_array[i];
475 pPlayer = playerhelpers->GetGamePlayer(client);
479 return pContext->ThrowNativeError("Client index %d is invalid", client);
480 } else if (!pPlayer->IsInGame()) {
481 return pContext->ThrowNativeError("Client %d is not connected", client);
485 crf.Initialize(cl_array, numClients);
488 pContext->LocalToString(params[3], &sample);
490 int entity = SoundReferenceToIndex(params[4]);
491 int channel = params[5];
492 int level = params[6];
493 int flags = params[7];
494 float vol = sp_ctof(params[8]);
495 int pitch = params[9];
496 int speakerentity = params[10];
498 Vector *pOrigin = NULL, origin;
499 Vector *pDir = NULL, dir;
501 pContext->LocalToPhysAddr(params[11], &addr);
502 if (addr != pContext->GetNullRef(SP_NULL_VECTOR))
505 origin.x = sp_ctof(addr[0]);
506 origin.y = sp_ctof(addr[1]);
507 origin.z = sp_ctof(addr[2]);
510 pContext->LocalToPhysAddr(params[12], &addr);
511 if (addr != pContext->GetNullRef(SP_NULL_VECTOR))
514 dir.x = sp_ctof(addr[0]);
515 dir.y = sp_ctof(addr[1]);
516 dir.z = sp_ctof(addr[2]);
519 bool updatePos = params[13] ? true : false;
520 float soundtime = sp_ctof(params[14]);
522 CUtlVector<Vector> *pOrigVec = NULL;
523 CUtlVector<Vector> origvec;
527 for (cell_t i = 15; i <= params[0]; i++)
530 pContext->LocalToPhysAddr(params[i], &addr);
531 vec.x = sp_ctof(addr[0]);
532 vec.y = sp_ctof(addr[1]);
533 vec.z = sp_ctof(addr[2]);
534 origvec.AddToTail(vec);
538 /* If we're going to a "local player" and this is a dedicated server,
539 * intelligently redirect each sound.
542 if (entity == -2 && engine->IsDedicatedServer())
544 for (unsigned int i = 0; i < numClients; i++)
547 player[0] = cl_array[i];
549 crf.Initialize(player, 1);
552 SH_CALL(enginesoundPatch,
553 static_cast<void (IEngineSound::*)(IRecipientFilter &, int, int, const char*, float,
554 soundlevel_t, int, int, const Vector *, const Vector *, CUtlVector<Vector> *, bool, float, int)>
555 (&IEngineSound::EmitSound))
573 engsound->EmitSound(crf,
592 SH_CALL(enginesoundPatch,
593 static_cast<void (IEngineSound::*)(IRecipientFilter &, int, int, const char*, float,
594 soundlevel_t, int, int, const Vector *, const Vector *, CUtlVector<Vector> *, bool, float, int)>
595 (&IEngineSound::EmitSound))
613 engsound->EmitSound(crf,
633 static cell_t EmitSentence(IPluginContext *pContext, const cell_t *params)
636 CellRecipientFilter crf;
637 unsigned int numClients;
639 IGamePlayer *pPlayer = NULL;
641 pContext->LocalToPhysAddr(params[1], &addr);
642 numClients = params[2];
644 /* Client validation */
645 for (unsigned int i = 0; i < numClients; i++)
648 pPlayer = playerhelpers->GetGamePlayer(client);
652 return pContext->ThrowNativeError("Client index %d is invalid", client);
653 } else if (!pPlayer->IsInGame()) {
654 return pContext->ThrowNativeError("Client %d is not connected", client);
658 crf.Initialize(addr, numClients);
660 int sentence = params[3];
661 int entity = SoundReferenceToIndex(params[4]);
662 int channel = params[5];
663 int level = params[6];
664 int flags = params[7];
665 float vol = sp_ctof(params[8]);
666 int pitch = params[9];
667 int speakerentity = params[10];
669 Vector *pOrigin = NULL, origin;
670 Vector *pDir = NULL, dir;
672 pContext->LocalToPhysAddr(params[11], &addr);
673 if (addr != pContext->GetNullRef(SP_NULL_VECTOR))
676 origin.x = sp_ctof(addr[0]);
677 origin.y = sp_ctof(addr[1]);
678 origin.z = sp_ctof(addr[2]);
681 pContext->LocalToPhysAddr(params[12], &addr);
682 if (addr != pContext->GetNullRef(SP_NULL_VECTOR))
685 dir.x = sp_ctof(addr[0]);
686 dir.y = sp_ctof(addr[1]);
687 dir.z = sp_ctof(addr[2]);
690 bool updatePos = params[13] ? true : false;
691 float soundtime = sp_ctof(params[14]);
693 CUtlVector<Vector> *pOrigVec = NULL;
694 CUtlVector<Vector> origvec;
698 for (cell_t i = 15; i <= params[0]; i++)
701 pContext->LocalToPhysAddr(params[i], &addr);
702 vec.x = sp_ctof(addr[0]);
703 vec.y = sp_ctof(addr[1]);
704 vec.z = sp_ctof(addr[2]);
705 origvec.AddToTail(vec);
709 engsound->EmitSentenceByIndex(crf,
727 static cell_t smn_AddAmbientSoundHook(IPluginContext *pContext, const cell_t *params)
729 IPluginFunction *pFunc = pContext->GetFunctionById(params[1]);
732 return pContext->ThrowNativeError("Invalid function id (%X)", params[1]);
735 s_SoundHooks.AddHook(AMBIENT_SOUND_HOOK, pFunc);
740 static cell_t smn_AddNormalSoundHook(IPluginContext *pContext, const cell_t *params)
742 IPluginFunction *pFunc = pContext->GetFunctionById(params[1]);
745 return pContext->ThrowNativeError("Invalid function id (%X)", params[1]);
748 s_SoundHooks.AddHook(NORMAL_SOUND_HOOK, pFunc);
753 static cell_t smn_RemoveAmbientSoundHook(IPluginContext *pContext, const cell_t *params)
755 IPluginFunction *pFunc = pContext->GetFunctionById(params[1]);
758 return pContext->ThrowNativeError("Invalid function id (%X)", params[1]);
761 if (!s_SoundHooks.RemoveHook(AMBIENT_SOUND_HOOK, pFunc))
763 return pContext->ThrowNativeError("Invalid hooked function");
769 static cell_t smn_RemoveNormalSoundHook(IPluginContext *pContext, const cell_t *params)
771 IPluginFunction *pFunc = pContext->GetFunctionById(params[1]);
774 return pContext->ThrowNativeError("Invalid function id (%X)", params[1]);
777 if (!s_SoundHooks.RemoveHook(NORMAL_SOUND_HOOK, pFunc))
779 return pContext->ThrowNativeError("Invalid hooked function");
785 static cell_t smn_GetDistGainFromSoundLevel(IPluginContext *pContext, const cell_t *params)
787 int decibel = params[1];
788 float distance = sp_ctof(params[2]);
790 return sp_ftoc(engsound->GetDistGainFromSoundLevel(soundlevel_t)decibel, distance));
793 sp_nativeinfo_t g_SoundNatives[] =
795 {"EmitAmbientSound", EmitAmbientSound},
796 {"EmitSentence", EmitSentence},
797 {"EmitSound", EmitSound},
798 {"FadeClientVolume", FadeClientVolume},
799 {"GetSoundDuration", GetSoundDuration},
800 {"PrefetchSound", PrefetchSound},
801 {"StopSound", StopSound},
802 {"AddAmbientSoundHook", smn_AddAmbientSoundHook},
803 {"AddNormalSoundHook", smn_AddNormalSoundHook},
804 {"RemoveAmbientSoundHook", smn_RemoveAmbientSoundHook},
805 {"RemoveNormalSoundHook", smn_RemoveNormalSoundHook},
806 {"GetDistGainFromSoundLevel", smn_GetDistGainFromSoundLevel},