extensions/sdktools/vsound.cpp
author David Anderson <dvander@alliedmods.net>
Sun Jul 06 04:49:55 2008 +0000 (2008-07-06)
branchrefac-jit
changeset 2168 c24a6fa957db
permissions -rw-r--r--
jit refactoring branch
     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 size_t SoundHooks::_FillInPlayers(int *pl_array, IRecipientFilter *pFilter)
    48 {
    49 	size_t size = static_cast<size_t>(pFilter->GetRecipientCount());
    50 
    51 	for (size_t i=0; i<size; i++)
    52 	{
    53 		pl_array[i] = pFilter->GetRecipientIndex(i);
    54 	}
    55 
    56 	return size;
    57 }
    58 
    59 void SoundHooks::_IncRefCounter(int type)
    60 {
    61 	if (type == NORMAL_SOUND_HOOK)
    62 	{
    63 		if (m_NormalCount++ == 0)
    64 		{
    65 			SH_ADD_HOOK_MEMFUNC(IEngineSound, EmitSound, engsound, this, &SoundHooks::OnEmitSound, false);
    66 			SH_ADD_HOOK_MEMFUNC(IEngineSound, EmitSound, engsound, this, &SoundHooks::OnEmitSound2, false);
    67 		}
    68 	}
    69 	else if (type == AMBIENT_SOUND_HOOK)
    70 	{
    71 		if (m_AmbientCount++ == 0)
    72 		{
    73 			SH_ADD_HOOK_MEMFUNC(IVEngineServer, EmitAmbientSound, engine, this, &SoundHooks::OnEmitAmbientSound, false);
    74 		}
    75 	}
    76 }
    77 
    78 void SoundHooks::_DecRefCounter(int type)
    79 {
    80 	if (type == NORMAL_SOUND_HOOK)
    81 	{
    82 		if (--m_NormalCount == 0)
    83 		{
    84 			SH_REMOVE_HOOK_MEMFUNC(IEngineSound, EmitSound, engsound, this, &SoundHooks::OnEmitSound, false);
    85 			SH_REMOVE_HOOK_MEMFUNC(IEngineSound, EmitSound, engsound, this, &SoundHooks::OnEmitSound2, false);
    86 		}
    87 	}
    88 	else if (type == AMBIENT_SOUND_HOOK)
    89 	{
    90 		if (--m_AmbientCount == 0)
    91 		{
    92 			SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, EmitAmbientSound, engine, this, &SoundHooks::OnEmitAmbientSound, false);
    93 		}
    94 	}
    95 }
    96 
    97 void SoundHooks::Initialize()
    98 {
    99 	plsys->AddPluginsListener(this);
   100 }
   101 
   102 void SoundHooks::Shutdown()
   103 {
   104 	plsys->RemovePluginsListener(this);
   105 	if (m_NormalCount)
   106 	{
   107 		SH_REMOVE_HOOK_MEMFUNC(IEngineSound, EmitSound, engsound, this, &SoundHooks::OnEmitSound, false);
   108 		SH_REMOVE_HOOK_MEMFUNC(IEngineSound, EmitSound, engsound, this, &SoundHooks::OnEmitSound2, false);
   109 	}
   110 	if (m_AmbientCount)
   111 	{
   112 		SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, EmitAmbientSound, engine, this, &SoundHooks::OnEmitAmbientSound, false);
   113 	}
   114 }
   115 
   116 void SoundHooks::OnPluginUnloaded(IPlugin *plugin)
   117 {
   118 	SoundHookIter iter;
   119 	IPluginContext *pContext = plugin->GetBaseContext();
   120 
   121 	if (m_AmbientCount)
   122 	{
   123 		for (iter=m_AmbientFuncs.begin(); iter!=m_AmbientFuncs.end(); )
   124 		{
   125 			if ((*iter)->GetParentContext() == pContext)
   126 			{
   127 				iter = m_AmbientFuncs.erase(iter);
   128 				_DecRefCounter(AMBIENT_SOUND_HOOK);
   129 			}
   130 			else
   131 			{
   132 				iter++;
   133 			}
   134 		}
   135 	}
   136 	if (m_NormalCount)
   137 	{
   138 		for (iter=m_NormalFuncs.begin(); iter!=m_NormalFuncs.end(); )
   139 		{
   140 			if ((*iter)->GetParentContext() == pContext)
   141 			{
   142 				iter = m_NormalFuncs.erase(iter);
   143 				_DecRefCounter(NORMAL_SOUND_HOOK);
   144 			}
   145 			else
   146 			{
   147 				iter++;
   148 			}
   149 		}
   150 	}
   151 }
   152 
   153 void SoundHooks::AddHook(int type, IPluginFunction *pFunc)
   154 {
   155 	if (type == NORMAL_SOUND_HOOK)
   156 	{
   157 		m_NormalFuncs.push_back(pFunc);
   158 		_IncRefCounter(NORMAL_SOUND_HOOK);
   159 	}
   160 	else if (type == AMBIENT_SOUND_HOOK)
   161 	{
   162 		m_AmbientFuncs.push_back(pFunc);
   163 		_IncRefCounter(AMBIENT_SOUND_HOOK);
   164 	}
   165 }
   166 
   167 bool SoundHooks::RemoveHook(int type, IPluginFunction *pFunc)
   168 {
   169 	SoundHookIter iter;
   170 	if (type == NORMAL_SOUND_HOOK)
   171 	{
   172 		if ((iter=m_NormalFuncs.find(pFunc)) != m_NormalFuncs.end())
   173 		{
   174 			m_NormalFuncs.erase(iter);
   175 			_DecRefCounter(NORMAL_SOUND_HOOK);
   176 			return true;
   177 		}
   178 		else
   179 		{
   180 			return false;
   181 		}
   182 	}
   183 	else if (type == AMBIENT_SOUND_HOOK)
   184 	{
   185 		if ((iter=m_AmbientFuncs.find(pFunc)) != m_AmbientFuncs.end())
   186 		{
   187 			m_AmbientFuncs.erase(iter);
   188 			_DecRefCounter(AMBIENT_SOUND_HOOK);
   189 			return true;
   190 		}
   191 		else
   192 		{
   193 			return false;
   194 		}
   195 	}
   196 
   197 	return false;
   198 }
   199 
   200 void SoundHooks::OnEmitAmbientSound(int entindex, const Vector &pos, const char *samp, float vol, 
   201 									soundlevel_t soundlevel, int fFlags, int pitch, float delay)
   202 {
   203 	SoundHookIter iter;
   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);
   209 
   210 	for (iter=m_AmbientFuncs.begin(); iter!=m_AmbientFuncs.end(); iter++)
   211 	{
   212 		pFunc = (*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;
   224 
   225 		switch (res)
   226 		{
   227 		case Pl_Handled:
   228 		case Pl_Stop:
   229 			{
   230 				RETURN_META(MRES_SUPERCEDE);
   231 			}
   232 		case Pl_Changed:
   233 			{
   234 				Vector vec2;
   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));
   240 			}
   241 		}
   242 	}
   243 }
   244 
   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)
   249 {
   250 	SoundHookIter iter;
   251 	IPluginFunction *pFunc;
   252 	cell_t res = static_cast<ResultType>(Pl_Continue);
   253 	char buffer[PLATFORM_MAX_PATH];
   254 	strcpy(buffer, pSample);
   255 
   256 	for (iter=m_NormalFuncs.begin(); iter!=m_NormalFuncs.end(); iter++)
   257 	{
   258 		int players[64], size;
   259 		size = _FillInPlayers(players, &filter);
   260 		pFunc = (*iter);
   261 
   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;
   274 
   275 		switch (res)
   276 		{
   277 		case Pl_Handled:
   278 		case Pl_Stop:
   279 			{
   280 				RETURN_META(MRES_SUPERCEDE);
   281 			}
   282 		case Pl_Changed:
   283 			{
   284 				CellRecipientFilter crf;
   285 				crf.Initialize(players, size);
   286 				RETURN_META_NEWPARAMS(
   287 					MRES_IGNORED,
   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)
   292 					);
   293 			}
   294 		}
   295 	}
   296 }
   297 
   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)
   302 {
   303 	SoundHookIter iter;
   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);
   309 
   310 	for (iter=m_NormalFuncs.begin(); iter!=m_NormalFuncs.end(); iter++)
   311 	{
   312 		int players[64], size;
   313 		size = _FillInPlayers(players, &filter);
   314 		pFunc = (*iter);
   315 
   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;
   328 
   329 		switch (res)
   330 		{
   331 		case Pl_Handled:
   332 		case Pl_Stop:
   333 			{
   334 				RETURN_META(MRES_SUPERCEDE);
   335 			}
   336 		case Pl_Changed:
   337 			{
   338 				CellRecipientFilter crf;
   339 				crf.Initialize(players, size);
   340 				RETURN_META_NEWPARAMS(
   341 					MRES_IGNORED,
   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)
   346 					);
   347 			}
   348 		}
   349 	}
   350 }
   351 
   352 /************************
   353 *                       *
   354 * Sound Related Natives *
   355 *                       *
   356 *************************/
   357 
   358 SoundHooks s_SoundHooks;
   359 
   360 static cell_t PrefetchSound(IPluginContext *pContext, const cell_t *params)
   361 {
   362 	char *name;
   363 	pContext->LocalToString(params[1], &name);
   364 
   365 	engsound->PrefetchSound(name);
   366 
   367 	return 1;
   368 }
   369 
   370 static cell_t GetSoundDuration(IPluginContext *pContext, const cell_t *params)
   371 {
   372 	char *name;
   373 	pContext->LocalToString(params[1], &name);
   374 
   375 	return sp_ftoc(engsound->GetSoundDuration(name));
   376 }
   377 
   378 static cell_t EmitAmbientSound(IPluginContext *pContext, const cell_t *params)
   379 {
   380 	cell_t entity;
   381 	Vector pos;
   382 	char *name;
   383 	float vol, delay;
   384 	int pitch, flags, level;
   385 
   386 	entity = params[3];
   387 
   388 	cell_t *addr;
   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]);
   393 
   394 	pContext->LocalToString(params[1], &name);
   395 
   396 	vol = sp_ctof(params[6]);
   397 	level = params[4];
   398 	flags = params[5];
   399 	pitch = params[7];
   400 	delay = sp_ctof(params[8]);
   401 
   402 	if (g_InSoundHook)
   403 	{
   404 		ENGINE_CALL(EmitAmbientSound)(entity, pos, name, vol, (soundlevel_t)level, flags, pitch, delay);
   405 	}
   406 	else
   407 	{
   408 		engine->EmitAmbientSound(entity, pos, name, vol, (soundlevel_t)level, flags, pitch, delay);
   409 	}
   410 
   411 	return 1;
   412 }
   413 
   414 static cell_t FadeClientVolume(IPluginContext *pContext, const cell_t *params)
   415 {
   416 	int client = params[1];
   417 	if (client < 1 || client > playerhelpers->GetMaxClients())
   418 	{
   419 		return pContext->ThrowNativeError("Client index %d is not valid", client);
   420 	}
   421 
   422 	IGamePlayer *player = playerhelpers->GetGamePlayer(client);
   423 	if (!player->IsInGame())
   424 	{
   425 		return pContext->ThrowNativeError("Client index %d is not in game", client);
   426 	}
   427 
   428 	engine->FadeClientVolume(player->GetEdict(),
   429 		sp_ctof(params[2]),
   430 		sp_ctof(params[3]),
   431 		sp_ctof(params[4]),
   432 		sp_ctof(params[5]));
   433 
   434 	return 1;
   435 }
   436 
   437 static cell_t StopSound(IPluginContext *pContext, const cell_t *params)
   438 {
   439 	int entity = params[1];
   440 	int channel = params[2];
   441 
   442 	char *name;
   443 	pContext->LocalToString(params[3], &name);
   444 
   445 	engsound->StopSound(entity, channel, name);
   446 
   447 	return 1;
   448 }
   449 
   450 static cell_t EmitSound(IPluginContext *pContext, const cell_t *params)
   451 {
   452 	cell_t *addr, *cl_array;
   453 	CellRecipientFilter crf;
   454 	unsigned int numClients;
   455 	int client;
   456 	IGamePlayer *pPlayer = NULL;
   457 
   458 	pContext->LocalToPhysAddr(params[1], &cl_array);
   459 	numClients = params[2];
   460 
   461 	/* Client validation */
   462 	for (unsigned int i = 0; i < numClients; i++)
   463 	{
   464 		client = cl_array[i];
   465 		pPlayer = playerhelpers->GetGamePlayer(client);
   466 
   467 		if (!pPlayer)
   468 		{
   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);
   472 		}
   473 	}
   474 
   475 	crf.Initialize(cl_array, numClients);
   476 
   477 	char *sample;
   478 	pContext->LocalToString(params[3], &sample);
   479 	
   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];
   487 
   488 	Vector *pOrigin = NULL, origin;
   489 	Vector *pDir = NULL, dir;
   490 
   491 	pContext->LocalToPhysAddr(params[11], &addr);
   492 	if (addr != pContext->GetNullRef(SP_NULL_VECTOR))
   493 	{
   494 		pOrigin = &origin;
   495 		origin.x = sp_ctof(addr[0]);
   496 		origin.y = sp_ctof(addr[1]);
   497 		origin.z = sp_ctof(addr[2]);
   498 	}
   499 
   500 	pContext->LocalToPhysAddr(params[12], &addr);
   501 	if (addr != pContext->GetNullRef(SP_NULL_VECTOR))
   502 	{
   503 		pDir = &dir;
   504 		dir.x = sp_ctof(addr[0]);
   505 		dir.y = sp_ctof(addr[1]);
   506 		dir.z = sp_ctof(addr[2]);
   507 	}
   508 
   509 	bool updatePos = params[13] ? true : false;
   510 	float soundtime = sp_ctof(params[14]);
   511 
   512 	CUtlVector<Vector> *pOrigVec = NULL;
   513 	CUtlVector<Vector> origvec;
   514 	if (params[0] > 14)
   515 	{
   516 		pOrigVec = &origvec;
   517 		for (cell_t i = 15; i <= params[0]; i++)
   518 		{
   519 			Vector vec;
   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);
   525 		}
   526 	}
   527 
   528 	/* If we're going to a "local player" and this is a dedicated server,
   529 	 * intelligently redirect each sound.
   530 	 */
   531 
   532 	if (entity == -2 && engine->IsDedicatedServer())
   533 	{
   534 		for (unsigned int i = 0; i < numClients; i++)
   535 		{
   536 			cell_t player[1];
   537 			player[0] = cl_array[i];
   538 			crf.Reset();
   539 			crf.Initialize(player, 1);
   540 			if (g_InSoundHook)
   541 			{
   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))
   546 					(crf, 
   547 					player[0], 
   548 					channel, 
   549 					sample, 
   550 					vol, 
   551 					(soundlevel_t)level, 
   552 					flags, 
   553 					pitch, 
   554 					pOrigin,
   555 					pDir,
   556 					pOrigVec,
   557 					updatePos,
   558 					soundtime,
   559 					speakerentity);
   560 			}
   561 			else
   562 			{
   563 				engsound->EmitSound(crf, 
   564 					player[0], 
   565 					channel, 
   566 					sample, 
   567 					vol, 
   568 					(soundlevel_t)level, 
   569 					flags, 
   570 					pitch, 
   571 					pOrigin,
   572 					pDir,
   573 					pOrigVec,
   574 					updatePos,
   575 					soundtime,
   576 					speakerentity);
   577 			}
   578 		}
   579 	} else {
   580 		if (g_InSoundHook)
   581 		{
   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))
   586 				(crf, 
   587 				entity, 
   588 				channel, 
   589 				sample, 
   590 				vol, 
   591 				(soundlevel_t)level, 
   592 				flags, 
   593 				pitch, 
   594 				pOrigin,
   595 				pDir,
   596 				pOrigVec,
   597 				updatePos,
   598 				soundtime,
   599 				speakerentity);
   600 		}
   601 		else
   602 		{
   603 			engsound->EmitSound(crf, 
   604 				entity, 
   605 				channel, 
   606 				sample, 
   607 				vol, 
   608 				(soundlevel_t)level, 
   609 				flags, 
   610 				pitch, 
   611 				pOrigin,
   612 				pDir,
   613 				pOrigVec,
   614 				updatePos,
   615 				soundtime,
   616 				speakerentity);
   617 		}
   618 	}
   619 
   620 	return 1;
   621 }
   622 
   623 static cell_t EmitSentence(IPluginContext *pContext, const cell_t *params)
   624 {
   625 	cell_t *addr;
   626 	CellRecipientFilter crf;
   627 	unsigned int numClients;
   628 	int client;
   629 	IGamePlayer *pPlayer = NULL;
   630 
   631 	pContext->LocalToPhysAddr(params[1], &addr);
   632 	numClients = params[2];
   633 
   634 	/* Client validation */
   635 	for (unsigned int i = 0; i < numClients; i++)
   636 	{
   637 		client = addr[i];
   638 		pPlayer = playerhelpers->GetGamePlayer(client);
   639 
   640 		if (!pPlayer)
   641 		{
   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);
   645 		}
   646 	}
   647 
   648 	crf.Initialize(addr, numClients);
   649 
   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];
   658 
   659 	Vector *pOrigin = NULL, origin;
   660 	Vector *pDir = NULL, dir;
   661 
   662 	pContext->LocalToPhysAddr(params[11], &addr);
   663 	if (addr != pContext->GetNullRef(SP_NULL_VECTOR))
   664 	{
   665 		pOrigin = &origin;
   666 		origin.x = sp_ctof(addr[0]);
   667 		origin.y = sp_ctof(addr[1]);
   668 		origin.z = sp_ctof(addr[2]);
   669 	}
   670 
   671 	pContext->LocalToPhysAddr(params[12], &addr);
   672 	if (addr != pContext->GetNullRef(SP_NULL_VECTOR))
   673 	{
   674 		pDir = &dir;
   675 		dir.x = sp_ctof(addr[0]);
   676 		dir.y = sp_ctof(addr[1]);
   677 		dir.z = sp_ctof(addr[2]);
   678 	}
   679 
   680 	bool updatePos = params[13] ? true : false;
   681 	float soundtime = sp_ctof(params[14]);
   682 
   683 	CUtlVector<Vector> *pOrigVec = NULL;
   684 	CUtlVector<Vector> origvec;
   685 	if (params[0] > 14)
   686 	{
   687 		pOrigVec = &origvec;
   688 		for (cell_t i = 15; i <= params[0]; i++)
   689 		{
   690 			Vector vec;
   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);
   696 		}
   697 	}
   698 
   699 	engsound->EmitSentenceByIndex(crf, 
   700 		entity, 
   701 		channel, 
   702 		sentence, 
   703 		vol, 
   704 		(soundlevel_t)level, 
   705 		flags, 
   706 		pitch, 
   707 		pOrigin,
   708 		pDir,
   709 		pOrigVec,
   710 		updatePos,
   711 		soundtime,
   712 		speakerentity);
   713 
   714 	return 1;
   715 }
   716 
   717 static cell_t smn_AddAmbientSoundHook(IPluginContext *pContext, const cell_t *params)
   718 {
   719 	IPluginFunction *pFunc = pContext->GetFunctionById(params[1]);
   720 	if (!pFunc)
   721 	{
   722 		return pContext->ThrowNativeError("Invalid function id (%X)", params[1]);
   723 	}
   724 
   725 	s_SoundHooks.AddHook(AMBIENT_SOUND_HOOK, pFunc);
   726 
   727 	return 1;
   728 }
   729 
   730 static cell_t smn_AddNormalSoundHook(IPluginContext *pContext, const cell_t *params)
   731 {
   732 	IPluginFunction *pFunc = pContext->GetFunctionById(params[1]);
   733 	if (!pFunc)
   734 	{
   735 		return pContext->ThrowNativeError("Invalid function id (%X)", params[1]);
   736 	}
   737 
   738 	s_SoundHooks.AddHook(NORMAL_SOUND_HOOK, pFunc);
   739 
   740 	return 1;
   741 }
   742 
   743 static cell_t smn_RemoveAmbientSoundHook(IPluginContext *pContext, const cell_t *params)
   744 {
   745 	IPluginFunction *pFunc = pContext->GetFunctionById(params[1]);
   746 	if (!pFunc)
   747 	{
   748 		return pContext->ThrowNativeError("Invalid function id (%X)", params[1]);
   749 	}
   750 
   751 	if (!s_SoundHooks.RemoveHook(AMBIENT_SOUND_HOOK, pFunc))
   752 	{
   753 		return pContext->ThrowNativeError("Invalid hooked function");
   754 	}
   755 
   756 	return 1;
   757 }
   758 
   759 static cell_t smn_RemoveNormalSoundHook(IPluginContext *pContext, const cell_t *params)
   760 {
   761 	IPluginFunction *pFunc = pContext->GetFunctionById(params[1]);
   762 	if (!pFunc)
   763 	{
   764 		return pContext->ThrowNativeError("Invalid function id (%X)", params[1]);
   765 	}
   766 
   767 	if (!s_SoundHooks.RemoveHook(NORMAL_SOUND_HOOK, pFunc))
   768 	{
   769 		return pContext->ThrowNativeError("Invalid hooked function");
   770 	}
   771 
   772 	return 1;
   773 }
   774 
   775 sp_nativeinfo_t g_SoundNatives[] = 
   776 {
   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},
   788 	{NULL,						NULL},
   789 };