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 size_t SoundHooks::_FillInPlayers(int *pl_array, IRecipientFilter *pFilter)
49 size_t size = static_cast<size_t>(pFilter->GetRecipientCount());
51 for (size_t i=0; i<size; i++)
53 pl_array[i] = pFilter->GetRecipientIndex(i);
59 void SoundHooks::_IncRefCounter(int type)
61 if (type == NORMAL_SOUND_HOOK)
63 if (m_NormalCount++ == 0)
65 SH_ADD_HOOK_MEMFUNC(IEngineSound, EmitSound, engsound, this, &SoundHooks::OnEmitSound, false);
66 SH_ADD_HOOK_MEMFUNC(IEngineSound, EmitSound, engsound, this, &SoundHooks::OnEmitSound2, false);
69 else if (type == AMBIENT_SOUND_HOOK)
71 if (m_AmbientCount++ == 0)
73 SH_ADD_HOOK_MEMFUNC(IVEngineServer, EmitAmbientSound, engine, this, &SoundHooks::OnEmitAmbientSound, false);
78 void SoundHooks::_DecRefCounter(int type)
80 if (type == NORMAL_SOUND_HOOK)
82 if (--m_NormalCount == 0)
84 SH_REMOVE_HOOK_MEMFUNC(IEngineSound, EmitSound, engsound, this, &SoundHooks::OnEmitSound, false);
85 SH_REMOVE_HOOK_MEMFUNC(IEngineSound, EmitSound, engsound, this, &SoundHooks::OnEmitSound2, false);
88 else if (type == AMBIENT_SOUND_HOOK)
90 if (--m_AmbientCount == 0)
92 SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, EmitAmbientSound, engine, this, &SoundHooks::OnEmitAmbientSound, false);
97 void SoundHooks::Initialize()
99 plsys->AddPluginsListener(this);
102 void SoundHooks::Shutdown()
104 plsys->RemovePluginsListener(this);
107 SH_REMOVE_HOOK_MEMFUNC(IEngineSound, EmitSound, engsound, this, &SoundHooks::OnEmitSound, false);
108 SH_REMOVE_HOOK_MEMFUNC(IEngineSound, EmitSound, engsound, this, &SoundHooks::OnEmitSound2, false);
112 SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, EmitAmbientSound, engine, this, &SoundHooks::OnEmitAmbientSound, false);
116 void SoundHooks::OnPluginUnloaded(IPlugin *plugin)
119 IPluginContext *pContext = plugin->GetBaseContext();
123 for (iter=m_AmbientFuncs.begin(); iter!=m_AmbientFuncs.end(); )
125 if ((*iter)->GetParentContext() == pContext)
127 iter = m_AmbientFuncs.erase(iter);
128 _DecRefCounter(AMBIENT_SOUND_HOOK);
138 for (iter=m_NormalFuncs.begin(); iter!=m_NormalFuncs.end(); )
140 if ((*iter)->GetParentContext() == pContext)
142 iter = m_NormalFuncs.erase(iter);
143 _DecRefCounter(NORMAL_SOUND_HOOK);
153 void SoundHooks::AddHook(int type, IPluginFunction *pFunc)
155 if (type == NORMAL_SOUND_HOOK)
157 m_NormalFuncs.push_back(pFunc);
158 _IncRefCounter(NORMAL_SOUND_HOOK);
160 else if (type == AMBIENT_SOUND_HOOK)
162 m_AmbientFuncs.push_back(pFunc);
163 _IncRefCounter(AMBIENT_SOUND_HOOK);
167 bool SoundHooks::RemoveHook(int type, IPluginFunction *pFunc)
170 if (type == NORMAL_SOUND_HOOK)
172 if ((iter=m_NormalFuncs.find(pFunc)) != m_NormalFuncs.end())
174 m_NormalFuncs.erase(iter);
175 _DecRefCounter(NORMAL_SOUND_HOOK);
183 else if (type == AMBIENT_SOUND_HOOK)
185 if ((iter=m_AmbientFuncs.find(pFunc)) != m_AmbientFuncs.end())
187 m_AmbientFuncs.erase(iter);
188 _DecRefCounter(AMBIENT_SOUND_HOOK);
200 void SoundHooks::OnEmitAmbientSound(int entindex, const Vector &pos, const char *samp, float vol,
201 soundlevel_t soundlevel, int fFlags, int pitch, float delay)
204 IPluginFunction *pFunc;
205 cell_t vec[3] = {sp_ftoc(pos.x), sp_ftoc(pos.y), sp_ftoc(pos.z)};
206 cell_t res = static_cast<ResultType>(Pl_Continue);
207 char buffer[PLATFORM_MAX_PATH];
208 strcpy(buffer, samp);
210 for (iter=m_AmbientFuncs.begin(); iter!=m_AmbientFuncs.end(); iter++)
213 pFunc->PushStringEx(buffer, sizeof(buffer), SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK);
214 pFunc->PushCellByRef(&entindex);
215 pFunc->PushFloatByRef(&vol);
216 pFunc->PushCellByRef(reinterpret_cast<cell_t *>(&soundlevel));
217 pFunc->PushCellByRef(&pitch);
218 pFunc->PushArray(vec, 3, SM_PARAM_COPYBACK);
219 pFunc->PushCellByRef(&fFlags);
220 pFunc->PushFloatByRef(&delay);
221 g_InSoundHook = true;
222 pFunc->Execute(&res);
223 g_InSoundHook = false;
230 RETURN_META(MRES_SUPERCEDE);
235 vec2.x = sp_ctof(vec[0]);
236 vec2.y = sp_ctof(vec[1]);
237 vec2.z = sp_ctof(vec[2]);
238 RETURN_META_NEWPARAMS(MRES_IGNORED, &IVEngineServer::EmitAmbientSound,
239 (entindex, vec2, buffer, vol, soundlevel, fFlags, pitch, delay));
245 void SoundHooks::OnEmitSound(IRecipientFilter &filter, int iEntIndex, int iChannel, const char *pSample,
246 float flVolume, soundlevel_t iSoundlevel, int iFlags, int iPitch, const Vector *pOrigin,
247 const Vector *pDirection, CUtlVector<Vector> *pUtlVecOrigins, bool bUpdatePositions,
248 float soundtime, int speakerentity)
251 IPluginFunction *pFunc;
252 cell_t res = static_cast<ResultType>(Pl_Continue);
253 char buffer[PLATFORM_MAX_PATH];
254 strcpy(buffer, pSample);
256 for (iter=m_NormalFuncs.begin(); iter!=m_NormalFuncs.end(); iter++)
258 int players[64], size;
259 size = _FillInPlayers(players, &filter);
262 pFunc->PushArray(players, 64, SM_PARAM_COPYBACK);
263 pFunc->PushCellByRef(&size);
264 pFunc->PushStringEx(buffer, sizeof(buffer), SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK);
265 pFunc->PushCellByRef(&iEntIndex);
266 pFunc->PushCellByRef(&iChannel);
267 pFunc->PushFloatByRef(&flVolume);
268 pFunc->PushCellByRef(reinterpret_cast<cell_t *>(&iSoundlevel));
269 pFunc->PushCellByRef(&iPitch);
270 pFunc->PushCellByRef(&iFlags);
271 g_InSoundHook = true;
272 pFunc->Execute(&res);
273 g_InSoundHook = false;
280 RETURN_META(MRES_SUPERCEDE);
284 CellRecipientFilter crf;
285 crf.Initialize(players, size);
286 RETURN_META_NEWPARAMS(
288 static_cast<void (IEngineSound::*)(IRecipientFilter &, int, int, const char*, float, soundlevel_t,
289 int, int, const Vector *, const Vector *, CUtlVector<Vector> *, bool, float, int)>(&IEngineSound::EmitSound),
290 (crf, iEntIndex, iChannel, buffer, flVolume, iSoundlevel, iFlags, iPitch, pOrigin,
291 pDirection, pUtlVecOrigins, bUpdatePositions, soundtime, speakerentity)
298 void SoundHooks::OnEmitSound2(IRecipientFilter &filter, int iEntIndex, int iChannel, const char *pSample,
299 float flVolume, float flAttenuation, int iFlags, int iPitch, const Vector *pOrigin,
300 const Vector *pDirection, CUtlVector<Vector> *pUtlVecOrigins, bool bUpdatePositions,
301 float soundtime, int speakerentity)
304 IPluginFunction *pFunc;
305 cell_t res = static_cast<ResultType>(Pl_Continue);
306 cell_t sndlevel = static_cast<cell_t>(ATTN_TO_SNDLVL(flAttenuation));
307 char buffer[PLATFORM_MAX_PATH];
308 strcpy(buffer, pSample);
310 for (iter=m_NormalFuncs.begin(); iter!=m_NormalFuncs.end(); iter++)
312 int players[64], size;
313 size = _FillInPlayers(players, &filter);
316 pFunc->PushArray(players, 64, SM_PARAM_COPYBACK);
317 pFunc->PushCellByRef(&size);
318 pFunc->PushStringEx(buffer, sizeof(buffer), SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK);
319 pFunc->PushCellByRef(&iEntIndex);
320 pFunc->PushCellByRef(&iChannel);
321 pFunc->PushFloatByRef(&flVolume);
322 pFunc->PushCellByRef(&sndlevel);
323 pFunc->PushCellByRef(&iPitch);
324 pFunc->PushCellByRef(&iFlags);
325 g_InSoundHook = true;
326 pFunc->Execute(&res);
327 g_InSoundHook = false;
334 RETURN_META(MRES_SUPERCEDE);
338 CellRecipientFilter crf;
339 crf.Initialize(players, size);
340 RETURN_META_NEWPARAMS(
342 static_cast<void (IEngineSound::*)(IRecipientFilter &, int, int, const char*, float, float,
343 int, int, const Vector *, const Vector *, CUtlVector<Vector> *, bool, float, int)>(&IEngineSound::EmitSound),
344 (crf, iEntIndex, iChannel, buffer, flVolume, SNDLVL_TO_ATTN(static_cast<soundlevel_t>(sndlevel)),
345 iFlags, iPitch, pOrigin, pDirection, pUtlVecOrigins, bUpdatePositions, soundtime, speakerentity)
352 /************************
354 * Sound Related Natives *
356 *************************/
358 SoundHooks s_SoundHooks;
360 static cell_t PrefetchSound(IPluginContext *pContext, const cell_t *params)
363 pContext->LocalToString(params[1], &name);
365 engsound->PrefetchSound(name);
370 static cell_t GetSoundDuration(IPluginContext *pContext, const cell_t *params)
373 pContext->LocalToString(params[1], &name);
375 return sp_ftoc(engsound->GetSoundDuration(name));
378 static cell_t EmitAmbientSound(IPluginContext *pContext, const cell_t *params)
384 int pitch, flags, level;
389 pContext->LocalToPhysAddr(params[2], &addr);
390 pos.x = sp_ctof(addr[0]);
391 pos.y = sp_ctof(addr[1]);
392 pos.z = sp_ctof(addr[2]);
394 pContext->LocalToString(params[1], &name);
396 vol = sp_ctof(params[6]);
400 delay = sp_ctof(params[8]);
404 ENGINE_CALL(EmitAmbientSound)(entity, pos, name, vol, (soundlevel_t)level, flags, pitch, delay);
408 engine->EmitAmbientSound(entity, pos, name, vol, (soundlevel_t)level, flags, pitch, delay);
414 static cell_t FadeClientVolume(IPluginContext *pContext, const cell_t *params)
416 int client = params[1];
417 if (client < 1 || client > playerhelpers->GetMaxClients())
419 return pContext->ThrowNativeError("Client index %d is not valid", client);
422 IGamePlayer *player = playerhelpers->GetGamePlayer(client);
423 if (!player->IsInGame())
425 return pContext->ThrowNativeError("Client index %d is not in game", client);
428 engine->FadeClientVolume(player->GetEdict(),
437 static cell_t StopSound(IPluginContext *pContext, const cell_t *params)
439 int entity = params[1];
440 int channel = params[2];
443 pContext->LocalToString(params[3], &name);
445 engsound->StopSound(entity, channel, name);
450 static cell_t EmitSound(IPluginContext *pContext, const cell_t *params)
452 cell_t *addr, *cl_array;
453 CellRecipientFilter crf;
454 unsigned int numClients;
456 IGamePlayer *pPlayer = NULL;
458 pContext->LocalToPhysAddr(params[1], &cl_array);
459 numClients = params[2];
461 /* Client validation */
462 for (unsigned int i = 0; i < numClients; i++)
464 client = cl_array[i];
465 pPlayer = playerhelpers->GetGamePlayer(client);
469 return pContext->ThrowNativeError("Client index %d is invalid", client);
470 } else if (!pPlayer->IsInGame()) {
471 return pContext->ThrowNativeError("Client %d is not connected", client);
475 crf.Initialize(cl_array, numClients);
478 pContext->LocalToString(params[3], &sample);
480 int entity = params[4];
481 int channel = params[5];
482 int level = params[6];
483 int flags = params[7];
484 float vol = sp_ctof(params[8]);
485 int pitch = params[9];
486 int speakerentity = params[10];
488 Vector *pOrigin = NULL, origin;
489 Vector *pDir = NULL, dir;
491 pContext->LocalToPhysAddr(params[11], &addr);
492 if (addr != pContext->GetNullRef(SP_NULL_VECTOR))
495 origin.x = sp_ctof(addr[0]);
496 origin.y = sp_ctof(addr[1]);
497 origin.z = sp_ctof(addr[2]);
500 pContext->LocalToPhysAddr(params[12], &addr);
501 if (addr != pContext->GetNullRef(SP_NULL_VECTOR))
504 dir.x = sp_ctof(addr[0]);
505 dir.y = sp_ctof(addr[1]);
506 dir.z = sp_ctof(addr[2]);
509 bool updatePos = params[13] ? true : false;
510 float soundtime = sp_ctof(params[14]);
512 CUtlVector<Vector> *pOrigVec = NULL;
513 CUtlVector<Vector> origvec;
517 for (cell_t i = 15; i <= params[0]; i++)
520 pContext->LocalToPhysAddr(params[i], &addr);
521 vec.x = sp_ctof(addr[0]);
522 vec.y = sp_ctof(addr[1]);
523 vec.z = sp_ctof(addr[2]);
524 origvec.AddToTail(vec);
528 /* If we're going to a "local player" and this is a dedicated server,
529 * intelligently redirect each sound.
532 if (entity == -2 && engine->IsDedicatedServer())
534 for (unsigned int i = 0; i < numClients; i++)
537 player[0] = cl_array[i];
539 crf.Initialize(player, 1);
542 SH_CALL(enginesoundPatch,
543 static_cast<void (IEngineSound::*)(IRecipientFilter &, int, int, const char*, float,
544 soundlevel_t, int, int, const Vector *, const Vector *, CUtlVector<Vector> *, bool, float, int)>
545 (&IEngineSound::EmitSound))
563 engsound->EmitSound(crf,
582 SH_CALL(enginesoundPatch,
583 static_cast<void (IEngineSound::*)(IRecipientFilter &, int, int, const char*, float,
584 soundlevel_t, int, int, const Vector *, const Vector *, CUtlVector<Vector> *, bool, float, int)>
585 (&IEngineSound::EmitSound))
603 engsound->EmitSound(crf,
623 static cell_t EmitSentence(IPluginContext *pContext, const cell_t *params)
626 CellRecipientFilter crf;
627 unsigned int numClients;
629 IGamePlayer *pPlayer = NULL;
631 pContext->LocalToPhysAddr(params[1], &addr);
632 numClients = params[2];
634 /* Client validation */
635 for (unsigned int i = 0; i < numClients; i++)
638 pPlayer = playerhelpers->GetGamePlayer(client);
642 return pContext->ThrowNativeError("Client index %d is invalid", client);
643 } else if (!pPlayer->IsInGame()) {
644 return pContext->ThrowNativeError("Client %d is not connected", client);
648 crf.Initialize(addr, numClients);
650 int sentence = params[3];
651 int entity = params[4];
652 int channel = params[5];
653 int level = params[6];
654 int flags = params[7];
655 float vol = sp_ctof(params[8]);
656 int pitch = params[9];
657 int speakerentity = params[10];
659 Vector *pOrigin = NULL, origin;
660 Vector *pDir = NULL, dir;
662 pContext->LocalToPhysAddr(params[11], &addr);
663 if (addr != pContext->GetNullRef(SP_NULL_VECTOR))
666 origin.x = sp_ctof(addr[0]);
667 origin.y = sp_ctof(addr[1]);
668 origin.z = sp_ctof(addr[2]);
671 pContext->LocalToPhysAddr(params[12], &addr);
672 if (addr != pContext->GetNullRef(SP_NULL_VECTOR))
675 dir.x = sp_ctof(addr[0]);
676 dir.y = sp_ctof(addr[1]);
677 dir.z = sp_ctof(addr[2]);
680 bool updatePos = params[13] ? true : false;
681 float soundtime = sp_ctof(params[14]);
683 CUtlVector<Vector> *pOrigVec = NULL;
684 CUtlVector<Vector> origvec;
688 for (cell_t i = 15; i <= params[0]; i++)
691 pContext->LocalToPhysAddr(params[i], &addr);
692 vec.x = sp_ctof(addr[0]);
693 vec.y = sp_ctof(addr[1]);
694 vec.z = sp_ctof(addr[2]);
695 origvec.AddToTail(vec);
699 engsound->EmitSentenceByIndex(crf,
717 static cell_t smn_AddAmbientSoundHook(IPluginContext *pContext, const cell_t *params)
719 IPluginFunction *pFunc = pContext->GetFunctionById(params[1]);
722 return pContext->ThrowNativeError("Invalid function id (%X)", params[1]);
725 s_SoundHooks.AddHook(AMBIENT_SOUND_HOOK, pFunc);
730 static cell_t smn_AddNormalSoundHook(IPluginContext *pContext, const cell_t *params)
732 IPluginFunction *pFunc = pContext->GetFunctionById(params[1]);
735 return pContext->ThrowNativeError("Invalid function id (%X)", params[1]);
738 s_SoundHooks.AddHook(NORMAL_SOUND_HOOK, pFunc);
743 static cell_t smn_RemoveAmbientSoundHook(IPluginContext *pContext, const cell_t *params)
745 IPluginFunction *pFunc = pContext->GetFunctionById(params[1]);
748 return pContext->ThrowNativeError("Invalid function id (%X)", params[1]);
751 if (!s_SoundHooks.RemoveHook(AMBIENT_SOUND_HOOK, pFunc))
753 return pContext->ThrowNativeError("Invalid hooked function");
759 static cell_t smn_RemoveNormalSoundHook(IPluginContext *pContext, const cell_t *params)
761 IPluginFunction *pFunc = pContext->GetFunctionById(params[1]);
764 return pContext->ThrowNativeError("Invalid function id (%X)", params[1]);
767 if (!s_SoundHooks.RemoveHook(NORMAL_SOUND_HOOK, pFunc))
769 return pContext->ThrowNativeError("Invalid hooked function");
775 sp_nativeinfo_t g_SoundNatives[] =
777 {"EmitAmbientSound", EmitAmbientSound},
778 {"EmitSentence", EmitSentence},
779 {"EmitSound", EmitSound},
780 {"FadeClientVolume", FadeClientVolume},
781 {"GetSoundDuration", GetSoundDuration},
782 {"PrefetchSound", PrefetchSound},
783 {"StopSound", StopSound},
784 {"AddAmbientSoundHook", smn_AddAmbientSoundHook},
785 {"AddNormalSoundHook", smn_AddNormalSoundHook},
786 {"RemoveAmbientSoundHook", smn_RemoveAmbientSoundHook},
787 {"RemoveNormalSoundHook", smn_RemoveNormalSoundHook},