extensions/sdktools/vsound.cpp
author javalia <javalia.javalia@gmail.com>
Sat Sep 03 14:18:08 2011 -0400 (2011-09-03)
changeset 3384 24c6057b2fda
parent 3132 176125f4c329
child 3385 6bac913d978f
permissions -rw-r--r--
Added GetDistGainFromSoundLevel native to SDKTools (bug 5066, r=dvander).
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod SDKTools Extension
     5  * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
     6  * =============================================================================
     7  *
     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.
    11  * 
    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
    15  * details.
    16  *
    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/>.
    19  *
    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>.
    28  *
    29  * Version: $Id$
    30  */
    31 
    32 #include "vsound.h"
    33 #include <IForwardSys.h>
    34 
    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);
    38 
    39 bool g_InSoundHook = false;
    40 
    41 /***************************
    42 *                          *
    43 * Sound Related Hook Class *
    44 *                          *
    45 ****************************/
    46 
    47 cell_t SoundReferenceToIndex(cell_t ref)
    48 {
    49 	if (ref == 0 || ref == -1 || ref == -2)
    50 	{
    51 		return ref;
    52 	}
    53 
    54 	return gamehelpers->ReferenceToIndex(ref);
    55 }
    56 
    57 size_t SoundHooks::_FillInPlayers(int *pl_array, IRecipientFilter *pFilter)
    58 {
    59 	size_t size = static_cast<size_t>(pFilter->GetRecipientCount());
    60 
    61 	for (size_t i=0; i<size; i++)
    62 	{
    63 		pl_array[i] = pFilter->GetRecipientIndex(i);
    64 	}
    65 
    66 	return size;
    67 }
    68 
    69 void SoundHooks::_IncRefCounter(int type)
    70 {
    71 	if (type == NORMAL_SOUND_HOOK)
    72 	{
    73 		if (m_NormalCount++ == 0)
    74 		{
    75 			SH_ADD_HOOK_MEMFUNC(IEngineSound, EmitSound, engsound, this, &SoundHooks::OnEmitSound, false);
    76 			SH_ADD_HOOK_MEMFUNC(IEngineSound, EmitSound, engsound, this, &SoundHooks::OnEmitSound2, false);
    77 		}
    78 	}
    79 	else if (type == AMBIENT_SOUND_HOOK)
    80 	{
    81 		if (m_AmbientCount++ == 0)
    82 		{
    83 			SH_ADD_HOOK_MEMFUNC(IVEngineServer, EmitAmbientSound, engine, this, &SoundHooks::OnEmitAmbientSound, false);
    84 		}
    85 	}
    86 }
    87 
    88 void SoundHooks::_DecRefCounter(int type)
    89 {
    90 	if (type == NORMAL_SOUND_HOOK)
    91 	{
    92 		if (--m_NormalCount == 0)
    93 		{
    94 			SH_REMOVE_HOOK_MEMFUNC(IEngineSound, EmitSound, engsound, this, &SoundHooks::OnEmitSound, false);
    95 			SH_REMOVE_HOOK_MEMFUNC(IEngineSound, EmitSound, engsound, this, &SoundHooks::OnEmitSound2, false);
    96 		}
    97 	}
    98 	else if (type == AMBIENT_SOUND_HOOK)
    99 	{
   100 		if (--m_AmbientCount == 0)
   101 		{
   102 			SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, EmitAmbientSound, engine, this, &SoundHooks::OnEmitAmbientSound, false);
   103 		}
   104 	}
   105 }
   106 
   107 void SoundHooks::Initialize()
   108 {
   109 	plsys->AddPluginsListener(this);
   110 }
   111 
   112 void SoundHooks::Shutdown()
   113 {
   114 	plsys->RemovePluginsListener(this);
   115 	if (m_NormalCount)
   116 	{
   117 		SH_REMOVE_HOOK_MEMFUNC(IEngineSound, EmitSound, engsound, this, &SoundHooks::OnEmitSound, false);
   118 		SH_REMOVE_HOOK_MEMFUNC(IEngineSound, EmitSound, engsound, this, &SoundHooks::OnEmitSound2, false);
   119 	}
   120 	if (m_AmbientCount)
   121 	{
   122 		SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, EmitAmbientSound, engine, this, &SoundHooks::OnEmitAmbientSound, false);
   123 	}
   124 }
   125 
   126 void SoundHooks::OnPluginUnloaded(IPlugin *plugin)
   127 {
   128 	SoundHookIter iter;
   129 	IPluginContext *pContext = plugin->GetBaseContext();
   130 
   131 	if (m_AmbientCount)
   132 	{
   133 		for (iter=m_AmbientFuncs.begin(); iter!=m_AmbientFuncs.end(); )
   134 		{
   135 			if ((*iter)->GetParentContext() == pContext)
   136 			{
   137 				iter = m_AmbientFuncs.erase(iter);
   138 				_DecRefCounter(AMBIENT_SOUND_HOOK);
   139 			}
   140 			else
   141 			{
   142 				iter++;
   143 			}
   144 		}
   145 	}
   146 	if (m_NormalCount)
   147 	{
   148 		for (iter=m_NormalFuncs.begin(); iter!=m_NormalFuncs.end(); )
   149 		{
   150 			if ((*iter)->GetParentContext() == pContext)
   151 			{
   152 				iter = m_NormalFuncs.erase(iter);
   153 				_DecRefCounter(NORMAL_SOUND_HOOK);
   154 			}
   155 			else
   156 			{
   157 				iter++;
   158 			}
   159 		}
   160 	}
   161 }
   162 
   163 void SoundHooks::AddHook(int type, IPluginFunction *pFunc)
   164 {
   165 	if (type == NORMAL_SOUND_HOOK)
   166 	{
   167 		m_NormalFuncs.push_back(pFunc);
   168 		_IncRefCounter(NORMAL_SOUND_HOOK);
   169 	}
   170 	else if (type == AMBIENT_SOUND_HOOK)
   171 	{
   172 		m_AmbientFuncs.push_back(pFunc);
   173 		_IncRefCounter(AMBIENT_SOUND_HOOK);
   174 	}
   175 }
   176 
   177 bool SoundHooks::RemoveHook(int type, IPluginFunction *pFunc)
   178 {
   179 	SoundHookIter iter;
   180 	if (type == NORMAL_SOUND_HOOK)
   181 	{
   182 		if ((iter=m_NormalFuncs.find(pFunc)) != m_NormalFuncs.end())
   183 		{
   184 			m_NormalFuncs.erase(iter);
   185 			_DecRefCounter(NORMAL_SOUND_HOOK);
   186 			return true;
   187 		}
   188 		else
   189 		{
   190 			return false;
   191 		}
   192 	}
   193 	else if (type == AMBIENT_SOUND_HOOK)
   194 	{
   195 		if ((iter=m_AmbientFuncs.find(pFunc)) != m_AmbientFuncs.end())
   196 		{
   197 			m_AmbientFuncs.erase(iter);
   198 			_DecRefCounter(AMBIENT_SOUND_HOOK);
   199 			return true;
   200 		}
   201 		else
   202 		{
   203 			return false;
   204 		}
   205 	}
   206 
   207 	return false;
   208 }
   209 
   210 void SoundHooks::OnEmitAmbientSound(int entindex, const Vector &pos, const char *samp, float vol, 
   211 									soundlevel_t soundlevel, int fFlags, int pitch, float delay)
   212 {
   213 	SoundHookIter iter;
   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);
   219 
   220 	for (iter=m_AmbientFuncs.begin(); iter!=m_AmbientFuncs.end(); iter++)
   221 	{
   222 		pFunc = (*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;
   234 
   235 		switch (res)
   236 		{
   237 		case Pl_Handled:
   238 		case Pl_Stop:
   239 			{
   240 				RETURN_META(MRES_SUPERCEDE);
   241 			}
   242 		case Pl_Changed:
   243 			{
   244 				Vector vec2;
   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));
   250 			}
   251 		}
   252 	}
   253 }
   254 
   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)
   259 {
   260 	SoundHookIter iter;
   261 	IPluginFunction *pFunc;
   262 	cell_t res = static_cast<ResultType>(Pl_Continue);
   263 	char buffer[PLATFORM_MAX_PATH];
   264 	strcpy(buffer, pSample);
   265 
   266 	for (iter=m_NormalFuncs.begin(); iter!=m_NormalFuncs.end(); iter++)
   267 	{
   268 		int players[64], size;
   269 		size = _FillInPlayers(players, &filter);
   270 		pFunc = (*iter);
   271 
   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;
   284 
   285 		switch (res)
   286 		{
   287 		case Pl_Handled:
   288 		case Pl_Stop:
   289 			{
   290 				RETURN_META(MRES_SUPERCEDE);
   291 			}
   292 		case Pl_Changed:
   293 			{
   294 				CellRecipientFilter crf;
   295 				crf.Initialize(players, size);
   296 				RETURN_META_NEWPARAMS(
   297 					MRES_IGNORED,
   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)
   302 					);
   303 			}
   304 		}
   305 	}
   306 }
   307 
   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)
   312 {
   313 	SoundHookIter iter;
   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);
   319 
   320 	for (iter=m_NormalFuncs.begin(); iter!=m_NormalFuncs.end(); iter++)
   321 	{
   322 		int players[64], size;
   323 		size = _FillInPlayers(players, &filter);
   324 		pFunc = (*iter);
   325 
   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;
   338 
   339 		switch (res)
   340 		{
   341 		case Pl_Handled:
   342 		case Pl_Stop:
   343 			{
   344 				RETURN_META(MRES_SUPERCEDE);
   345 			}
   346 		case Pl_Changed:
   347 			{
   348 				CellRecipientFilter crf;
   349 				crf.Initialize(players, size);
   350 				RETURN_META_NEWPARAMS(
   351 					MRES_IGNORED,
   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)
   356 					);
   357 			}
   358 		}
   359 	}
   360 }
   361 
   362 /************************
   363 *                       *
   364 * Sound Related Natives *
   365 *                       *
   366 *************************/
   367 
   368 SoundHooks s_SoundHooks;
   369 
   370 static cell_t PrefetchSound(IPluginContext *pContext, const cell_t *params)
   371 {
   372 	char *name;
   373 	pContext->LocalToString(params[1], &name);
   374 
   375 	engsound->PrefetchSound(name);
   376 
   377 	return 1;
   378 }
   379 
   380 static cell_t GetSoundDuration(IPluginContext *pContext, const cell_t *params)
   381 {
   382 	char *name;
   383 	pContext->LocalToString(params[1], &name);
   384 
   385 	return sp_ftoc(engsound->GetSoundDuration(name));
   386 }
   387 
   388 static cell_t EmitAmbientSound(IPluginContext *pContext, const cell_t *params)
   389 {
   390 	cell_t entity;
   391 	Vector pos;
   392 	char *name;
   393 	float vol, delay;
   394 	int pitch, flags, level;
   395 
   396 	entity = SoundReferenceToIndex(params[3]);
   397 
   398 	cell_t *addr;
   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]);
   403 
   404 	pContext->LocalToString(params[1], &name);
   405 
   406 	vol = sp_ctof(params[6]);
   407 	level = params[4];
   408 	flags = params[5];
   409 	pitch = params[7];
   410 	delay = sp_ctof(params[8]);
   411 
   412 	if (g_InSoundHook)
   413 	{
   414 		ENGINE_CALL(EmitAmbientSound)(entity, pos, name, vol, (soundlevel_t)level, flags, pitch, delay);
   415 	}
   416 	else
   417 	{
   418 		engine->EmitAmbientSound(entity, pos, name, vol, (soundlevel_t)level, flags, pitch, delay);
   419 	}
   420 
   421 	return 1;
   422 }
   423 
   424 static cell_t FadeClientVolume(IPluginContext *pContext, const cell_t *params)
   425 {
   426 	int client = params[1];
   427 	if (client < 1 || client > playerhelpers->GetMaxClients())
   428 	{
   429 		return pContext->ThrowNativeError("Client index %d is not valid", client);
   430 	}
   431 
   432 	IGamePlayer *player = playerhelpers->GetGamePlayer(client);
   433 	if (!player->IsInGame())
   434 	{
   435 		return pContext->ThrowNativeError("Client index %d is not in game", client);
   436 	}
   437 
   438 	engine->FadeClientVolume(player->GetEdict(),
   439 		sp_ctof(params[2]),
   440 		sp_ctof(params[3]),
   441 		sp_ctof(params[4]),
   442 		sp_ctof(params[5]));
   443 
   444 	return 1;
   445 }
   446 
   447 static cell_t StopSound(IPluginContext *pContext, const cell_t *params)
   448 {
   449 	int entity = SoundReferenceToIndex(params[1]);
   450 	int channel = params[2];
   451 
   452 	char *name;
   453 	pContext->LocalToString(params[3], &name);
   454 
   455 	engsound->StopSound(entity, channel, name);
   456 
   457 	return 1;
   458 }
   459 
   460 static cell_t EmitSound(IPluginContext *pContext, const cell_t *params)
   461 {
   462 	cell_t *addr, *cl_array;
   463 	CellRecipientFilter crf;
   464 	unsigned int numClients;
   465 	int client;
   466 	IGamePlayer *pPlayer = NULL;
   467 
   468 	pContext->LocalToPhysAddr(params[1], &cl_array);
   469 	numClients = params[2];
   470 
   471 	/* Client validation */
   472 	for (unsigned int i = 0; i < numClients; i++)
   473 	{
   474 		client = cl_array[i];
   475 		pPlayer = playerhelpers->GetGamePlayer(client);
   476 
   477 		if (!pPlayer)
   478 		{
   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);
   482 		}
   483 	}
   484 
   485 	crf.Initialize(cl_array, numClients);
   486 
   487 	char *sample;
   488 	pContext->LocalToString(params[3], &sample);
   489 	
   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];
   497 
   498 	Vector *pOrigin = NULL, origin;
   499 	Vector *pDir = NULL, dir;
   500 
   501 	pContext->LocalToPhysAddr(params[11], &addr);
   502 	if (addr != pContext->GetNullRef(SP_NULL_VECTOR))
   503 	{
   504 		pOrigin = &origin;
   505 		origin.x = sp_ctof(addr[0]);
   506 		origin.y = sp_ctof(addr[1]);
   507 		origin.z = sp_ctof(addr[2]);
   508 	}
   509 
   510 	pContext->LocalToPhysAddr(params[12], &addr);
   511 	if (addr != pContext->GetNullRef(SP_NULL_VECTOR))
   512 	{
   513 		pDir = &dir;
   514 		dir.x = sp_ctof(addr[0]);
   515 		dir.y = sp_ctof(addr[1]);
   516 		dir.z = sp_ctof(addr[2]);
   517 	}
   518 
   519 	bool updatePos = params[13] ? true : false;
   520 	float soundtime = sp_ctof(params[14]);
   521 
   522 	CUtlVector<Vector> *pOrigVec = NULL;
   523 	CUtlVector<Vector> origvec;
   524 	if (params[0] > 14)
   525 	{
   526 		pOrigVec = &origvec;
   527 		for (cell_t i = 15; i <= params[0]; i++)
   528 		{
   529 			Vector vec;
   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);
   535 		}
   536 	}
   537 
   538 	/* If we're going to a "local player" and this is a dedicated server,
   539 	 * intelligently redirect each sound.
   540 	 */
   541 
   542 	if (entity == -2 && engine->IsDedicatedServer())
   543 	{
   544 		for (unsigned int i = 0; i < numClients; i++)
   545 		{
   546 			cell_t player[1];
   547 			player[0] = cl_array[i];
   548 			crf.Reset();
   549 			crf.Initialize(player, 1);
   550 			if (g_InSoundHook)
   551 			{
   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))
   556 					(crf, 
   557 					player[0], 
   558 					channel, 
   559 					sample, 
   560 					vol, 
   561 					(soundlevel_t)level, 
   562 					flags, 
   563 					pitch, 
   564 					pOrigin,
   565 					pDir,
   566 					pOrigVec,
   567 					updatePos,
   568 					soundtime,
   569 					speakerentity);
   570 			}
   571 			else
   572 			{
   573 				engsound->EmitSound(crf, 
   574 					player[0], 
   575 					channel, 
   576 					sample, 
   577 					vol, 
   578 					(soundlevel_t)level, 
   579 					flags, 
   580 					pitch, 
   581 					pOrigin,
   582 					pDir,
   583 					pOrigVec,
   584 					updatePos,
   585 					soundtime,
   586 					speakerentity);
   587 			}
   588 		}
   589 	} else {
   590 		if (g_InSoundHook)
   591 		{
   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))
   596 				(crf, 
   597 				entity, 
   598 				channel, 
   599 				sample, 
   600 				vol, 
   601 				(soundlevel_t)level, 
   602 				flags, 
   603 				pitch, 
   604 				pOrigin,
   605 				pDir,
   606 				pOrigVec,
   607 				updatePos,
   608 				soundtime,
   609 				speakerentity);
   610 		}
   611 		else
   612 		{
   613 			engsound->EmitSound(crf, 
   614 				entity, 
   615 				channel, 
   616 				sample, 
   617 				vol, 
   618 				(soundlevel_t)level, 
   619 				flags, 
   620 				pitch, 
   621 				pOrigin,
   622 				pDir,
   623 				pOrigVec,
   624 				updatePos,
   625 				soundtime,
   626 				speakerentity);
   627 		}
   628 	}
   629 
   630 	return 1;
   631 }
   632 
   633 static cell_t EmitSentence(IPluginContext *pContext, const cell_t *params)
   634 {
   635 	cell_t *addr;
   636 	CellRecipientFilter crf;
   637 	unsigned int numClients;
   638 	int client;
   639 	IGamePlayer *pPlayer = NULL;
   640 
   641 	pContext->LocalToPhysAddr(params[1], &addr);
   642 	numClients = params[2];
   643 
   644 	/* Client validation */
   645 	for (unsigned int i = 0; i < numClients; i++)
   646 	{
   647 		client = addr[i];
   648 		pPlayer = playerhelpers->GetGamePlayer(client);
   649 
   650 		if (!pPlayer)
   651 		{
   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);
   655 		}
   656 	}
   657 
   658 	crf.Initialize(addr, numClients);
   659 
   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];
   668 
   669 	Vector *pOrigin = NULL, origin;
   670 	Vector *pDir = NULL, dir;
   671 
   672 	pContext->LocalToPhysAddr(params[11], &addr);
   673 	if (addr != pContext->GetNullRef(SP_NULL_VECTOR))
   674 	{
   675 		pOrigin = &origin;
   676 		origin.x = sp_ctof(addr[0]);
   677 		origin.y = sp_ctof(addr[1]);
   678 		origin.z = sp_ctof(addr[2]);
   679 	}
   680 
   681 	pContext->LocalToPhysAddr(params[12], &addr);
   682 	if (addr != pContext->GetNullRef(SP_NULL_VECTOR))
   683 	{
   684 		pDir = &dir;
   685 		dir.x = sp_ctof(addr[0]);
   686 		dir.y = sp_ctof(addr[1]);
   687 		dir.z = sp_ctof(addr[2]);
   688 	}
   689 
   690 	bool updatePos = params[13] ? true : false;
   691 	float soundtime = sp_ctof(params[14]);
   692 
   693 	CUtlVector<Vector> *pOrigVec = NULL;
   694 	CUtlVector<Vector> origvec;
   695 	if (params[0] > 14)
   696 	{
   697 		pOrigVec = &origvec;
   698 		for (cell_t i = 15; i <= params[0]; i++)
   699 		{
   700 			Vector vec;
   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);
   706 		}
   707 	}
   708 
   709 	engsound->EmitSentenceByIndex(crf, 
   710 		entity, 
   711 		channel, 
   712 		sentence, 
   713 		vol, 
   714 		(soundlevel_t)level, 
   715 		flags, 
   716 		pitch, 
   717 		pOrigin,
   718 		pDir,
   719 		pOrigVec,
   720 		updatePos,
   721 		soundtime,
   722 		speakerentity);
   723 
   724 	return 1;
   725 }
   726 
   727 static cell_t smn_AddAmbientSoundHook(IPluginContext *pContext, const cell_t *params)
   728 {
   729 	IPluginFunction *pFunc = pContext->GetFunctionById(params[1]);
   730 	if (!pFunc)
   731 	{
   732 		return pContext->ThrowNativeError("Invalid function id (%X)", params[1]);
   733 	}
   734 
   735 	s_SoundHooks.AddHook(AMBIENT_SOUND_HOOK, pFunc);
   736 
   737 	return 1;
   738 }
   739 
   740 static cell_t smn_AddNormalSoundHook(IPluginContext *pContext, const cell_t *params)
   741 {
   742 	IPluginFunction *pFunc = pContext->GetFunctionById(params[1]);
   743 	if (!pFunc)
   744 	{
   745 		return pContext->ThrowNativeError("Invalid function id (%X)", params[1]);
   746 	}
   747 
   748 	s_SoundHooks.AddHook(NORMAL_SOUND_HOOK, pFunc);
   749 
   750 	return 1;
   751 }
   752 
   753 static cell_t smn_RemoveAmbientSoundHook(IPluginContext *pContext, const cell_t *params)
   754 {
   755 	IPluginFunction *pFunc = pContext->GetFunctionById(params[1]);
   756 	if (!pFunc)
   757 	{
   758 		return pContext->ThrowNativeError("Invalid function id (%X)", params[1]);
   759 	}
   760 
   761 	if (!s_SoundHooks.RemoveHook(AMBIENT_SOUND_HOOK, pFunc))
   762 	{
   763 		return pContext->ThrowNativeError("Invalid hooked function");
   764 	}
   765 
   766 	return 1;
   767 }
   768 
   769 static cell_t smn_RemoveNormalSoundHook(IPluginContext *pContext, const cell_t *params)
   770 {
   771 	IPluginFunction *pFunc = pContext->GetFunctionById(params[1]);
   772 	if (!pFunc)
   773 	{
   774 		return pContext->ThrowNativeError("Invalid function id (%X)", params[1]);
   775 	}
   776 
   777 	if (!s_SoundHooks.RemoveHook(NORMAL_SOUND_HOOK, pFunc))
   778 	{
   779 		return pContext->ThrowNativeError("Invalid hooked function");
   780 	}
   781 
   782 	return 1;
   783 }
   784 
   785 static cell_t smn_GetDistGainFromSoundLevel(IPluginContext *pContext, const cell_t *params)
   786 {
   787 	int decibel = params[1];
   788 	float distance = sp_ctof(params[2]);
   789 
   790 	return sp_ftoc(engsound->GetDistGainFromSoundLevel(soundlevel_t)decibel, distance));
   791 }
   792 
   793 sp_nativeinfo_t g_SoundNatives[] = 
   794 {
   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},
   807 	{NULL,						NULL},
   808 };