extensions/sdktools/vnatives.cpp
author Nicholas Hastings <skamonkey@gmail.com>
Wed Aug 14 17:07:27 2013 -0400 (2013-08-14)
changeset 4010 e32d0f63c41b
parent 4004 91974ce16453
child 4141 8ff66ae5ffcc
permissions -rw-r--r--
Update GivePlayerItem for new params in CS:GO.
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod SDKTools Extension
     5 * Copyright (C) 2004-2010 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 <stdlib.h>
    33 #include <sh_string.h>
    34 #include "extension.h"
    35 #include "vcallbuilder.h"
    36 #include "vnatives.h"
    37 #include "vhelpers.h"
    38 #include "vglobals.h"
    39 #include "CellRecipientFilter.h"
    40 #include <inetchannel.h>
    41 #include <iclient.h>
    42 #include "iserver.h"
    43 
    44 SourceHook::List<ValveCall *> g_RegCalls;
    45 SourceHook::List<ICallWrapper *> g_CallWraps;
    46 
    47 inline void InitPass(ValvePassInfo &info, ValveType vtype, PassType type, unsigned int flags, unsigned int decflags=0)
    48 {
    49 	info.decflags = decflags;
    50 	info.encflags = 0;
    51 	info.flags = flags;
    52 	info.type = type;
    53 	info.vtype = vtype;
    54 }
    55 
    56 #define START_CALL() \
    57 	unsigned char *vptr = pCall->stk_get();
    58 
    59 #define FINISH_CALL_SIMPLE(vret) \
    60 	pCall->call->Execute(vptr, vret); \
    61 	pCall->stk_put(vptr);
    62 
    63 #define ENCODE_VALVE_PARAM(num, which, vnum) \
    64 	if (EncodeValveParam(pContext, \
    65 			params[num], \
    66 			pCall, \
    67 			&pCall->which[vnum], \
    68 			vptr) \
    69 		== Data_Fail) \
    70 	{ \
    71 		return 0; \
    72 	}
    73 
    74 #define DECODE_VALVE_PARAM(num, which, vnum) \
    75 	if (DecodeValveParam(pContext, \
    76 			params[num], \
    77 			pCall, \
    78 			&pCall->which[vnum], \
    79 			vptr) \
    80 		== Data_Fail) \
    81 	{ \
    82 		return 0; \
    83 	}
    84 
    85 bool CreateBaseCall(const char *name,
    86 						  ValveCallType vcalltype,
    87 						  const ValvePassInfo *retinfo,
    88 						  const ValvePassInfo params[],
    89 						  unsigned int numParams,
    90 						  ValveCall **vaddr)
    91 {
    92 	int offset;
    93 	ValveCall *call;
    94 	if (g_pGameConf->GetOffset(name, &offset))
    95 	{
    96 		call = CreateValveVCall(offset, vcalltype, retinfo, params, numParams);
    97 		if (call)
    98 		{
    99 			g_RegCalls.push_back(call);
   100 		}
   101 		*vaddr = call;
   102 		return true;
   103 	} else {
   104 		void *addr = NULL;
   105 		if (g_pGameConf->GetMemSig(name, &addr) && addr != NULL)
   106 		{
   107 			call = CreateValveCall(addr, vcalltype, retinfo, params, numParams);
   108 			if (call)
   109 			{
   110 				g_RegCalls.push_back(call);
   111 			}
   112 			*vaddr = call;
   113 			return true;
   114 		}
   115 	}
   116 	return false;
   117 }
   118 
   119 static cell_t RemovePlayerItem(IPluginContext *pContext, const cell_t *params)
   120 {
   121 	static ValveCall *pCall = NULL;
   122 	if (!pCall)
   123 	{
   124 		ValvePassInfo pass[2];
   125 		InitPass(pass[0], Valve_CBaseEntity, PassType_Basic, PASSFLAG_BYVAL);
   126 		InitPass(pass[1], Valve_Bool, PassType_Basic, PASSFLAG_BYVAL);
   127 		if (!CreateBaseCall("RemovePlayerItem", ValveCall_Player, &pass[1], pass, 1, &pCall))
   128 		{
   129 			return pContext->ThrowNativeError("\"RemovePlayerItem\" not supported by this mod");
   130 		} else if (!pCall) {
   131 			return pContext->ThrowNativeError("\"RemovePlayerItem\" wrapper failed to initialize");
   132 		}
   133 	}
   134 
   135 	bool ret;
   136 	START_CALL();
   137 	DECODE_VALVE_PARAM(1, thisinfo, 0);
   138 	DECODE_VALVE_PARAM(2, vparams, 0);
   139 	FINISH_CALL_SIMPLE(&ret);
   140 	return ret ? 1 : 0;
   141 }
   142 
   143 #if SOURCE_ENGINE == SE_CSGO
   144 class CEconItemView;
   145 static cell_t GiveNamedItem(IPluginContext *pContext, const cell_t *params)
   146 {
   147 	static ValveCall *pCall = NULL;
   148 	if (!pCall)
   149 	{
   150 		ValvePassInfo pass[5];
   151 		InitPass(pass[0], Valve_String, PassType_Basic, PASSFLAG_BYVAL);
   152 		InitPass(pass[1], Valve_POD, PassType_Basic, PASSFLAG_BYVAL);
   153 		InitPass(pass[2], Valve_POD, PassType_Basic, PASSFLAG_BYVAL);
   154 		InitPass(pass[3], Valve_Bool, PassType_Basic, PASSFLAG_BYVAL);
   155 		InitPass(pass[4], Valve_CBaseEntity, PassType_Basic, PASSFLAG_BYVAL);
   156 		if (!CreateBaseCall("GiveNamedItem", ValveCall_Player, &pass[4], pass, 4, &pCall))
   157 		{
   158 			return pContext->ThrowNativeError("\"GiveNamedItem\" not supported by this mod");
   159 		} else if (!pCall) {
   160 			return pContext->ThrowNativeError("\"GiveNamedItem\" wrapper failed to initialize");
   161 		}
   162 	}
   163 
   164 	CBaseEntity *pEntity = NULL;
   165 	START_CALL();
   166 	DECODE_VALVE_PARAM(1, thisinfo, 0);
   167 	DECODE_VALVE_PARAM(2, vparams, 0);
   168 	DECODE_VALVE_PARAM(3, vparams, 1);
   169 	*(CEconItemView **)(vptr + 12) = NULL;
   170 	*(bool *)(vptr + 16) = false;
   171 	FINISH_CALL_SIMPLE(&pEntity);
   172 
   173 	return gamehelpers->EntityToBCompatRef(pEntity);
   174 }
   175 #else
   176 static cell_t GiveNamedItem(IPluginContext *pContext, const cell_t *params)
   177 {
   178 	static ValveCall *pCall = NULL;
   179 	if (!pCall)
   180 	{
   181 		ValvePassInfo pass[3];
   182 		InitPass(pass[0], Valve_String, PassType_Basic, PASSFLAG_BYVAL);
   183 		InitPass(pass[1], Valve_POD, PassType_Basic, PASSFLAG_BYVAL);
   184 		InitPass(pass[2], Valve_CBaseEntity, PassType_Basic, PASSFLAG_BYVAL);
   185 		if (!CreateBaseCall("GiveNamedItem", ValveCall_Player, &pass[2], pass, 2, &pCall))
   186 		{
   187 			return pContext->ThrowNativeError("\"GiveNamedItem\" not supported by this mod");
   188 		} else if (!pCall) {
   189 			return pContext->ThrowNativeError("\"GiveNamedItem\" wrapper failed to initialize");
   190 		}
   191 	}
   192 
   193 	CBaseEntity *pEntity = NULL;
   194 	START_CALL();
   195 	DECODE_VALVE_PARAM(1, thisinfo, 0);
   196 	DECODE_VALVE_PARAM(2, vparams, 0);
   197 	DECODE_VALVE_PARAM(3, vparams, 1);
   198 	FINISH_CALL_SIMPLE(&pEntity);
   199 
   200 	return gamehelpers->EntityToBCompatRef(pEntity);
   201 }
   202 #endif
   203 
   204 static cell_t GetPlayerWeaponSlot(IPluginContext *pContext, const cell_t *params)
   205 {
   206 	static ValveCall *pCall = NULL;
   207 	if (!pCall)
   208 	{
   209 		ValvePassInfo pass[2];
   210 		InitPass(pass[0], Valve_POD, PassType_Basic, PASSFLAG_BYVAL);
   211 		InitPass(pass[1], Valve_CBaseEntity, PassType_Basic, PASSFLAG_BYVAL);
   212 		if (!CreateBaseCall("Weapon_GetSlot", ValveCall_Player, &pass[1], pass, 1, &pCall))
   213 		{
   214 			return pContext->ThrowNativeError("\"Weapon_GetSlot\" not supported by this mod");
   215 		} else if (!pCall) {
   216 			return pContext->ThrowNativeError("\"Weapon_GetSlot\" wrapper failed to initialize");
   217 		}
   218 	}
   219 
   220 	CBaseEntity *pEntity;
   221 	START_CALL();
   222 	DECODE_VALVE_PARAM(1, thisinfo, 0);
   223 	DECODE_VALVE_PARAM(2, vparams, 0);
   224 	FINISH_CALL_SIMPLE(&pEntity);
   225 
   226 	return gamehelpers->EntityToBCompatRef(pEntity);
   227 }
   228 
   229 #if SOURCE_ENGINE != SE_DARKMESSIAH
   230 static cell_t IgniteEntity(IPluginContext *pContext, const cell_t *params)
   231 {
   232 	static ValveCall *pCall = NULL;
   233 	if (!pCall)
   234 	{
   235 		ValvePassInfo pass[4];
   236 		InitPass(pass[0], Valve_Float, PassType_Float, PASSFLAG_BYVAL);
   237 		InitPass(pass[1], Valve_Bool, PassType_Basic, PASSFLAG_BYVAL);
   238 		InitPass(pass[2], Valve_Float, PassType_Float, PASSFLAG_BYVAL);
   239 		InitPass(pass[3], Valve_Bool, PassType_Basic, PASSFLAG_BYVAL);
   240 		if (!CreateBaseCall("Ignite", ValveCall_Entity, NULL, pass, 4, &pCall))
   241 		{
   242 			return pContext->ThrowNativeError("\"Ignite\" not supported by this mod");
   243 		} else if (!pCall) {
   244 			return pContext->ThrowNativeError("\"Ignite\" wrapper failed to initialize");
   245 		}
   246 	}
   247 
   248 	START_CALL();
   249 	DECODE_VALVE_PARAM(1, thisinfo, 0);
   250 	DECODE_VALVE_PARAM(2, vparams, 0);
   251 	DECODE_VALVE_PARAM(3, vparams, 1);
   252 	DECODE_VALVE_PARAM(4, vparams, 2);
   253 	DECODE_VALVE_PARAM(5, vparams, 3);
   254 	FINISH_CALL_SIMPLE(NULL);
   255 
   256 	return 1;
   257 }
   258 #else
   259 /* Dark Messiah specific version */
   260 static cell_t IgniteEntity(IPluginContext *pContext, const cell_t *params)
   261 {
   262 	static ValveCall *pCall = NULL;
   263 	if (!pCall)
   264 	{
   265 		ValvePassInfo pass[6];
   266 		InitPass(pass[0], Valve_Float, PassType_Float, PASSFLAG_BYVAL);
   267 		InitPass(pass[1], Valve_Bool, PassType_Basic, PASSFLAG_BYVAL);
   268 		InitPass(pass[2], Valve_Float, PassType_Float, PASSFLAG_BYVAL);
   269 		InitPass(pass[3], Valve_Bool, PassType_Basic, PASSFLAG_BYVAL);
   270 		InitPass(pass[4], Valve_POD, PassType_Basic, PASSFLAG_BYVAL);
   271 		InitPass(pass[5], Valve_POD, PassType_Basic, PASSFLAG_BYVAL);
   272 		if (!CreateBaseCall("Ignite", ValveCall_Entity, NULL, pass, 6, &pCall))
   273 		{
   274 			return pContext->ThrowNativeError("\"Ignite\" not supported by this mod");
   275 		} else if (!pCall) {
   276 			return pContext->ThrowNativeError("\"Ignite\" wrapper failed to initialize");
   277 		}
   278 	}
   279 
   280 	START_CALL();
   281 	DECODE_VALVE_PARAM(1, thisinfo, 0);
   282 	DECODE_VALVE_PARAM(2, vparams, 0);
   283 	DECODE_VALVE_PARAM(3, vparams, 1);
   284 	DECODE_VALVE_PARAM(4, vparams, 2);
   285 	DECODE_VALVE_PARAM(5, vparams, 3);
   286 	/* Not sure what these params do, but they appear to be the default values */
   287 	*(int *)(vptr + 14) = 3;
   288 	*(int *)(vptr + 18) = 0;
   289 	FINISH_CALL_SIMPLE(NULL);
   290 
   291 	return 1;
   292 }
   293 #endif
   294 
   295 static cell_t ExtinguishEntity(IPluginContext *pContext, const cell_t *params)
   296 {
   297 	static ValveCall *pCall = NULL;
   298 	if (!pCall)
   299 	{
   300 		if (!CreateBaseCall("Extinguish", ValveCall_Entity, NULL, NULL, 0, &pCall))
   301 		{
   302 			return pContext->ThrowNativeError("\"Extinguish\" not supported by this mod");
   303 		} else if (!pCall) {
   304 			return pContext->ThrowNativeError("\"Extinguish\" wrapper failed to initialize");
   305 		}
   306 	}
   307 
   308 	START_CALL();
   309 	DECODE_VALVE_PARAM(1, thisinfo, 0);
   310 	FINISH_CALL_SIMPLE(NULL);
   311 
   312 	return 1;
   313 }
   314 
   315 static cell_t TeleportEntity(IPluginContext *pContext, const cell_t *params)
   316 {
   317 	static ValveCall *pCall = NULL;
   318 	if (!pCall)
   319 	{
   320 		ValvePassInfo pass[3];
   321 		InitPass(pass[0], Valve_Vector, PassType_Basic, PASSFLAG_BYVAL, VDECODE_FLAG_ALLOWNULL);
   322 		InitPass(pass[1], Valve_QAngle, PassType_Basic, PASSFLAG_BYVAL, VDECODE_FLAG_ALLOWNULL);
   323 		InitPass(pass[2], Valve_Vector, PassType_Basic, PASSFLAG_BYVAL, VDECODE_FLAG_ALLOWNULL);
   324 		if (!CreateBaseCall("Teleport", ValveCall_Entity, NULL, pass, 3, &pCall))
   325 		{
   326 			return pContext->ThrowNativeError("\"Teleport\" not supported by this mod");
   327 		} else if (!pCall) {
   328 			return pContext->ThrowNativeError("\"Teleport\" wrapper failed to initialize");
   329 		}
   330 	}
   331 
   332 	START_CALL();
   333 	DECODE_VALVE_PARAM(1, thisinfo, 0);
   334 	DECODE_VALVE_PARAM(2, vparams, 0);
   335 	DECODE_VALVE_PARAM(3, vparams, 1);
   336 	DECODE_VALVE_PARAM(4, vparams, 2);
   337 	FINISH_CALL_SIMPLE(NULL);
   338 
   339 	return 1;
   340 }
   341 
   342 #if SOURCE_ENGINE >= SE_ORANGEBOX
   343 /* :TODO: This is Team Fortress 2 specific */
   344 static cell_t ForcePlayerSuicide(IPluginContext *pContext, const cell_t *params)
   345 {
   346 	static ValveCall *pCall = NULL;
   347 	if (!pCall)
   348 	{
   349 		ValvePassInfo pass[2];
   350 		InitPass(pass[0], Valve_Bool, PassType_Basic, PASSFLAG_BYVAL);
   351 		InitPass(pass[1], Valve_Bool, PassType_Basic, PASSFLAG_BYVAL);
   352 		if (!CreateBaseCall("CommitSuicide", ValveCall_Player, NULL, pass, 2, &pCall))
   353 		{
   354 			return pContext->ThrowNativeError("\"CommitSuicide\" not supported by this mod");
   355 		}
   356 		else if (!pCall)
   357 		{
   358 			return pContext->ThrowNativeError("\"CommitSuicide\" wrapper failed to initialize");
   359 		}
   360 	}
   361 
   362 	START_CALL();
   363 	DECODE_VALVE_PARAM(1, thisinfo, 0);
   364 	*(bool *)(vptr + 4) = false;
   365 	*(bool *)(vptr + 5) = false;
   366 	FINISH_CALL_SIMPLE(NULL);
   367 
   368 	return 1;
   369 }
   370 #else
   371 static cell_t ForcePlayerSuicide(IPluginContext *pContext, const cell_t *params)
   372 {
   373 	static ValveCall *pCall = NULL;
   374 	if (!pCall)
   375 	{
   376 		if (!CreateBaseCall("CommitSuicide", ValveCall_Player, NULL, NULL, 0, &pCall))
   377 		{
   378 			return pContext->ThrowNativeError("\"CommitSuicide\" not supported by this mod");
   379 		} else if (!pCall) {
   380 			return pContext->ThrowNativeError("\"CommitSuicide\" wrapper failed to initialize");
   381 		}
   382 	}
   383 
   384 	START_CALL();
   385 	DECODE_VALVE_PARAM(1, thisinfo, 0);
   386 	FINISH_CALL_SIMPLE(NULL);
   387 
   388 	return 1;
   389 }
   390 #endif
   391 
   392 static cell_t SetClientViewEntity(IPluginContext *pContext, const cell_t *params)
   393 {
   394 	IGamePlayer *player = playerhelpers->GetGamePlayer(params[1]);
   395 	if (player == NULL)
   396 	{
   397 		return pContext->ThrowNativeError("Invalid client index %d", params[1]);
   398 	}
   399 	if (!player->IsInGame())
   400 	{
   401 		return pContext->ThrowNativeError("Client %d is not in game", params[1]);
   402 	}
   403 
   404 	edict_t *pEdict = PEntityOfEntIndex(gamehelpers->ReferenceToIndex(params[2]));
   405 	if (!pEdict || pEdict->IsFree())
   406 	{
   407 		return pContext->ThrowNativeError("Entity %d is not valid", params[2]);
   408 	}
   409 
   410 #if SOURCE_ENGINE == SE_DOTA
   411 	engine->SetView(params[1], params[2]);
   412 #else
   413 	engine->SetView(player->GetEdict(), pEdict);
   414 #endif
   415 
   416 	return 1;
   417 }
   418 
   419 static SourceHook::String *g_lightstyle[MAX_LIGHTSTYLES] = {NULL};
   420 static cell_t SetLightStyle(IPluginContext *pContext, const cell_t *params)
   421 {
   422 	int style = params[1];
   423 	if (style >= MAX_LIGHTSTYLES)
   424 	{
   425 		return pContext->ThrowNativeError("Light style %d is invalid (range: 0-%d)", style, MAX_LIGHTSTYLES - 1);
   426 	}
   427 
   428 	if (g_lightstyle[style] == NULL)
   429 	{
   430 		/* We allocate and never free this because the Engine wants to hold onto it :\
   431 		 * in theory we could hook light style and know whether we're supposed to free 
   432 		 * this or not on shutdown, but for ~4K of memory MAX, it doesn't seem worth it yet.
   433 		 * So, it's a :TODO:!
   434 		 */
   435 		g_lightstyle[style] = new SourceHook::String();
   436 	}
   437 
   438 	char *str;
   439 	pContext->LocalToString(params[2], &str);
   440 
   441 	g_lightstyle[style]->assign(str);
   442 
   443 	engine->LightStyle(style, g_lightstyle[style]->c_str());
   444 
   445 	return 1;
   446 }
   447 
   448 static cell_t SlapPlayer(IPluginContext *pContext, const cell_t *params)
   449 {
   450 	static bool s_slap_supported = false;
   451 	static bool s_slap_setup = false;
   452 	static ICallWrapper *s_teleport = NULL;
   453 	static int s_health_offs = 0;
   454 	static int s_sound_count = 0;
   455 	static int s_frag_offs = 0;
   456 
   457 	if (!s_slap_setup)
   458 	{
   459 		int tries = 0;
   460 
   461 		s_slap_setup = true;
   462 
   463 		if (IsTeleportSupported())
   464 		{
   465 			tries++;
   466 		}
   467 		if (IsGetVelocitySupported())
   468 		{
   469 			tries++;
   470 		}
   471 
   472 		/* Setup health */
   473 		if (g_pGameConf->GetOffset("m_iHealth", &s_health_offs) && s_health_offs)
   474 		{
   475 			tries++;
   476 		}
   477 
   478 		if (tries == 3)
   479 		{
   480 			s_slap_supported = true;
   481 
   482 			const char *key;
   483 			if ((key = g_pGameConf->GetKeyValue("SlapSoundCount")) != NULL)
   484 			{
   485 				s_sound_count = atoi(key);
   486 			}
   487 		}
   488 	}
   489 
   490 	if (!s_slap_supported)
   491 	{
   492 		return pContext->ThrowNativeError("This function is not supported on this mod");
   493 	}
   494 
   495 	/* First check if the client is valid */
   496 	IGamePlayer *player = playerhelpers->GetGamePlayer(params[1]);
   497 	if (!player)
   498 	{
   499 		return pContext->ThrowNativeError("Client %d is not valid", params[1]);
   500 	} else if (!player->IsInGame()) {
   501 		return pContext->ThrowNativeError("Client %d is not in game", params[1]);
   502 	}
   503 
   504 	edict_t *pEdict = player->GetEdict();
   505 	CBaseEntity *pEntity = pEdict->GetUnknown()->GetBaseEntity();
   506 
   507 	/* See if we should be taking away health */
   508 	bool should_slay = false;
   509 	if (params[2])
   510 	{
   511 #if SOURCE_ENGINE != SE_DARKMESSIAH
   512 		int *health = (int *)((char *)pEntity + s_health_offs);
   513 
   514 		if (*health - params[2] <= 0)
   515 		{
   516 			*health = 1;
   517 			should_slay = true;
   518 		} else {
   519 			*health -= params[2];
   520 		}
   521 #else
   522 		float *health = (float *)((char *)pEntity + s_health_offs);
   523 
   524 		if (*health - (float)params[2] <= 0)
   525 		{
   526 			*health = 1.0f;
   527 			should_slay = true;
   528 		} else {
   529 			*health -= (float)params[2];
   530 		}
   531 #endif
   532 	}
   533 
   534 	/* Teleport in a random direction - thank you, Mani!*/
   535 	Vector velocity;
   536 	GetVelocity(pEntity, &velocity, NULL);
   537 	velocity.x += ((rand() % 180) + 50) * (((rand() % 2) == 1) ?  -1 : 1);
   538 	velocity.y += ((rand() % 180) + 50) * (((rand() % 2) == 1) ?  -1 : 1);
   539 	velocity.z += rand() % 200 + 100;
   540 	Teleport(pEntity, NULL, NULL, &velocity);
   541 
   542 	/* Play a random sound */
   543 	if (params[3] && s_sound_count > 0)
   544 	{
   545 		char name[48];
   546 		const char *sound_name;
   547 		cell_t player_list[256], total_players = 0;
   548 		int maxClients = playerhelpers->GetMaxClients();
   549 
   550 		int r = (rand() % s_sound_count) + 1;
   551 		snprintf(name, sizeof(name), "SlapSound%d", r);
   552 
   553 		if ((sound_name = g_pGameConf->GetKeyValue(name)) != NULL)
   554 		{
   555 			IGamePlayer *other;
   556 			for (int i=1; i<=maxClients; i++)
   557 			{
   558 				other = playerhelpers->GetGamePlayer(i);
   559 				if (other->IsInGame())
   560 				{
   561 					player_list[total_players++] = i;
   562 				}
   563 			}
   564 
   565 			const Vector & pos = pEdict->GetCollideable()->GetCollisionOrigin();
   566 			CellRecipientFilter rf;
   567 			rf.SetToReliable(true);
   568 			rf.Initialize(player_list, total_players);
   569 #if SOURCE_ENGINE == SE_CSS || SOURCE_ENGINE == SE_HL2DM || SOURCE_ENGINE == SE_DODS || SOURCE_ENGINE == SE_TF2
   570 			engsound->EmitSound(rf, params[1], CHAN_AUTO, sound_name, VOL_NORM, ATTN_NORM, 0, PITCH_NORM, 0, &pos);
   571 #elif SOURCE_ENGINE < SE_PORTAL2
   572 			engsound->EmitSound(rf, params[1], CHAN_AUTO, sound_name, VOL_NORM, ATTN_NORM, 0, PITCH_NORM, &pos);
   573 #else
   574 			engsound->EmitSound(rf, params[1], CHAN_AUTO, sound_name, -1, sound_name, VOL_NORM, ATTN_NORM, 0, 0, PITCH_NORM, &pos);
   575 #endif
   576 		}
   577 	}
   578 
   579 	if (!s_frag_offs)
   580 	{
   581 		const char *frag_prop = g_pGameConf->GetKeyValue("m_iFrags");
   582 		if (frag_prop)
   583 		{
   584 			datamap_t *pMap = gamehelpers->GetDataMap(pEntity);
   585 			sm_datatable_info_t info;
   586 			if (gamehelpers->FindDataMapInfo(pMap, frag_prop, &info))
   587 			{
   588 				s_frag_offs = info.actual_offset;
   589 			}
   590 		}
   591 		if (!s_frag_offs)
   592 		{
   593 			s_frag_offs = -1;
   594 		}
   595 	}
   596 
   597 	int old_frags = 0;
   598 	if (s_frag_offs > 0)
   599 	{
   600 		old_frags = *(int *)((char *)pEntity + s_frag_offs);
   601 	}
   602 	
   603 	/* Force suicide */
   604 	if (should_slay)
   605 	{
   606 #if SOURCE_ENGINE == SE_DOTA
   607 		engine->ClientCommand(params[1], "kill\n");
   608 #else
   609 		pluginhelpers->ClientCommand(pEdict, "kill\n");
   610 #endif
   611 	}
   612 
   613 	if (s_frag_offs > 0)
   614 	{
   615 		*(int *)((char *)pEntity + s_frag_offs) = old_frags;
   616 	}
   617 
   618 	return 1;
   619 }
   620 
   621 static cell_t GetClientEyePosition(IPluginContext *pContext, const cell_t *params)
   622 {
   623 	IGamePlayer *player = playerhelpers->GetGamePlayer(params[1]);
   624 	if (player == NULL)
   625 	{
   626 		return pContext->ThrowNativeError("Invalid client index %d", params[1]);
   627 	}
   628 	if (!player->IsInGame())
   629 	{
   630 		return pContext->ThrowNativeError("Client %d is not in game", params[1]);
   631 	}
   632 
   633 	Vector pos;
   634 #if SOURCE_ENGINE == SE_DOTA
   635 	serverClients->ClientEarPosition(params[1], &pos);
   636 #else
   637 	serverClients->ClientEarPosition(player->GetEdict(), &pos);
   638 #endif
   639 
   640 	cell_t *addr;
   641 	pContext->LocalToPhysAddr(params[2], &addr);
   642 	addr[0] = sp_ftoc(pos.x);
   643 	addr[1] = sp_ftoc(pos.y);
   644 	addr[2] = sp_ftoc(pos.z);
   645 
   646 	return 1;
   647 }
   648 
   649 static cell_t GetClientEyeAngles(IPluginContext *pContext, const cell_t *params)
   650 {
   651 	IGamePlayer *pPlayer = playerhelpers->GetGamePlayer(params[1]);
   652 
   653 	if (!pPlayer)
   654 	{
   655 		return pContext->ThrowNativeError("Invalid client index %d", params[1]);
   656 	}
   657 	else if (!pPlayer->IsInGame())
   658 	{
   659 		return pContext->ThrowNativeError("Client %d is not in game", params[1]);
   660 	}
   661 
   662 	edict_t *pEdict = pPlayer->GetEdict();
   663 	CBaseEntity *pEntity = pEdict->GetUnknown() ? pEdict->GetUnknown()->GetBaseEntity() : NULL;
   664 
   665 	/* We always set the angles for backwards compatibility -- 
   666 	 * The original function had no return value.
   667 	 */
   668 	QAngle angles;
   669 	bool got_angles = false;
   670 
   671 	if (pEntity != NULL)
   672 	{
   673 		got_angles = GetEyeAngles(pEntity, &angles);
   674 	}
   675 	
   676 	cell_t *addr;
   677 	pContext->LocalToPhysAddr(params[2], &addr);
   678 	addr[0] = sp_ftoc(angles.x);
   679 	addr[1] = sp_ftoc(angles.y);
   680 	addr[2] = sp_ftoc(angles.z);
   681 
   682 	return got_angles ? 1 : 0;
   683 }
   684 
   685 #if SOURCE_ENGINE >= SE_ORANGEBOX
   686 static cell_t NativeFindEntityByClassname(IPluginContext *pContext, const cell_t *params)
   687 {
   688 	char *searchname;
   689 	CBaseEntity *pEntity;
   690 
   691 	if (params[1] == -1)
   692 	{
   693 		pEntity = (CBaseEntity *)servertools->FirstEntity();
   694 	}
   695 	else
   696 	{
   697 		pEntity = gamehelpers->ReferenceToEntity(params[1]);
   698 		if (!pEntity)
   699 		{
   700 			return pContext->ThrowNativeError("Entity %d (%d) is invalid",
   701 				gamehelpers->ReferenceToIndex(params[1]),
   702 				params[1]);
   703 		}
   704 		pEntity = (CBaseEntity *)servertools->NextEntity(pEntity);
   705 	}
   706 
   707 	// it's tough to find a good ent these days
   708 	if (!pEntity)
   709 	{
   710 		return -1;
   711 	}
   712 
   713 	pContext->LocalToString(params[2], &searchname);
   714 
   715 	const char *classname;
   716 	int lastletterpos;
   717 
   718 	static int offset = -1;
   719 	if (offset == -1)
   720 	{
   721 		sm_datatable_info_t info;
   722 		if (!gamehelpers->FindDataMapInfo(gamehelpers->GetDataMap(pEntity), "m_iClassname", &info))
   723 		{
   724 			return -1;
   725 		}
   726 		
   727 		offset = info.actual_offset;
   728 	}
   729 
   730 	string_t s;
   731 
   732 	while (pEntity)
   733 	{
   734 		if ((s = *(string_t *)((uint8_t *)pEntity + offset)) == NULL_STRING)
   735 		{
   736 			pEntity = (CBaseEntity *)servertools->NextEntity(pEntity);
   737 			continue;
   738 		}
   739 
   740 		classname = STRING(s);
   741 
   742 		lastletterpos = strlen(searchname) - 1;
   743 		if (searchname[lastletterpos] == '*')
   744 		{
   745 			if (strncasecmp(searchname, classname, lastletterpos) == 0)
   746 			{
   747 				return gamehelpers->EntityToBCompatRef(pEntity);
   748 			}
   749 		}
   750 		else if (strcasecmp(searchname, classname) == 0)
   751 		{
   752 			return gamehelpers->EntityToBCompatRef(pEntity);
   753 		}
   754 
   755 		pEntity = (CBaseEntity *)servertools->NextEntity(pEntity);
   756 	}
   757 
   758 	return -1;
   759 }
   760 #endif
   761 
   762 static cell_t FindEntityByClassname(IPluginContext *pContext, const cell_t *params)
   763 {
   764 	static ValveCall *pCall = NULL;
   765 	static bool bProbablyNoFEBC = false;
   766 
   767 #if SOURCE_ENGINE >= SE_ORANGEBOX
   768 	if (bProbablyNoFEBC)
   769 	{
   770 		return NativeFindEntityByClassname(pContext, params);
   771 	}
   772 #endif
   773 
   774 	if (!pCall)
   775 	{
   776 		ValvePassInfo pass[3];
   777 		InitPass(pass[0], Valve_CBaseEntity, PassType_Basic, PASSFLAG_BYVAL, VDECODE_FLAG_ALLOWNULL|VDECODE_FLAG_ALLOWWORLD);
   778 		InitPass(pass[1], Valve_String, PassType_Basic, PASSFLAG_BYVAL);
   779 		InitPass(pass[2], Valve_CBaseEntity, PassType_Basic, PASSFLAG_BYVAL);
   780 
   781 		char error[256];
   782 		error[0] = '\0';
   783 		if (!CreateBaseCall("FindEntityByClassname", ValveCall_EntityList, &pass[2], pass, 2, &pCall))
   784 		{
   785 			g_pSM->Format(error, sizeof(error), "\"FindEntityByClassname\" not supported by this mod");
   786 		} else if (!pCall) {
   787 			g_pSM->Format(error, sizeof(error), "\"FindEntityByClassname\" wrapper failed to initialize");
   788 		}
   789 
   790 		if (error[0] != '\0')
   791 		{
   792 #if SOURCE_ENGINE >= SE_ORANGEBOX
   793 			if (!bProbablyNoFEBC)
   794 			{
   795 				bProbablyNoFEBC = true;
   796 				g_pSM->LogError(myself, "%s, falling back to IServerTools method.", error);
   797 			}
   798 			return NativeFindEntityByClassname(pContext, params);
   799 #else
   800 			return pContext->ThrowNativeError("%s", error);
   801 #endif
   802 		}
   803 	}
   804 
   805 	CBaseEntity *pEntity;
   806 	START_CALL();
   807 	*(void **)vptr = g_EntList; 
   808 	DECODE_VALVE_PARAM(1, vparams, 0);
   809 	DECODE_VALVE_PARAM(2, vparams, 1);
   810 	FINISH_CALL_SIMPLE(&pEntity);
   811 
   812 	return gamehelpers->EntityToBCompatRef(pEntity);
   813 }
   814 
   815 #if SOURCE_ENGINE >= SE_ORANGEBOX
   816 static cell_t CreateEntityByName(IPluginContext *pContext, const cell_t *params)
   817 {
   818 	if (!g_pSM->IsMapRunning())
   819 	{
   820 		return pContext->ThrowNativeError("Cannot create new entity when no map is running");
   821 	}
   822 
   823 	char *classname;
   824 	pContext->LocalToString(params[1], &classname);
   825 	CBaseEntity *pEntity = (CBaseEntity *)servertools->CreateEntityByName(classname);
   826 	return gamehelpers->EntityToBCompatRef(pEntity);
   827 }
   828 #else
   829 static cell_t CreateEntityByName(IPluginContext *pContext, const cell_t *params)
   830 {
   831 	if (!g_pSM->IsMapRunning())
   832 	{
   833 		return pContext->ThrowNativeError("Cannot create new entity when no map is running");
   834 	}
   835 
   836 	static ValveCall *pCall = NULL;
   837 	if (!pCall)
   838 	{
   839 		ValvePassInfo pass[3];
   840 		InitPass(pass[0], Valve_String, PassType_Basic, PASSFLAG_BYVAL);
   841 		InitPass(pass[1], Valve_POD, PassType_Basic, PASSFLAG_BYVAL);
   842 		InitPass(pass[2], Valve_CBaseEntity, PassType_Basic, PASSFLAG_BYVAL);
   843 		if (!CreateBaseCall("CreateEntityByName", ValveCall_Static, &pass[2], pass, 2, &pCall))
   844 		{
   845 			return pContext->ThrowNativeError("\"CreateEntityByName\" not supported by this mod");
   846 		} else if (!pCall) {
   847 			return pContext->ThrowNativeError("\"CreateEntityByName\" wrapper failed to initialize");
   848 		}
   849 	}
   850 
   851 	CBaseEntity *pEntity = NULL;
   852 	START_CALL();
   853 	DECODE_VALVE_PARAM(1, vparams, 0);
   854 	DECODE_VALVE_PARAM(2, vparams, 1);
   855 	FINISH_CALL_SIMPLE(&pEntity);
   856 
   857 	return gamehelpers->EntityToBCompatRef(pEntity);
   858 }
   859 #endif
   860 
   861 #if SOURCE_ENGINE >= SE_ORANGEBOX
   862 static cell_t DispatchSpawn(IPluginContext *pContext, const cell_t *params)
   863 {
   864 	CBaseEntity *pEntity = gamehelpers->ReferenceToEntity(params[1]);
   865 	if (!pEntity)
   866 	{
   867 		return pContext->ThrowNativeError("Entity %d (%d) is invalid", gamehelpers->ReferenceToIndex(params[1]), params[1]);
   868 	}
   869 
   870 	servertools->DispatchSpawn(pEntity);
   871 
   872 	return 1;
   873 }
   874 #else
   875 static cell_t DispatchSpawn(IPluginContext *pContext, const cell_t *params)
   876 {
   877 	static ValveCall *pCall = NULL;
   878 	if (!pCall)
   879 	{
   880 		ValvePassInfo pass[2];
   881 		InitPass(pass[0], Valve_CBaseEntity, PassType_Basic, PASSFLAG_BYVAL);
   882 		InitPass(pass[1], Valve_POD, PassType_Basic, PASSFLAG_BYVAL);
   883 		if (!CreateBaseCall("DispatchSpawn", ValveCall_Static, &pass[1], pass, 1, &pCall))
   884 		{
   885 			return pContext->ThrowNativeError("\"DispatchSpawn\" not supported by this mod");
   886 		} else if (!pCall) {
   887 			return pContext->ThrowNativeError("\"DispatchSpawn\" wrapper failed to initialize");
   888 		}
   889 	}
   890 
   891 	int ret;
   892 	START_CALL();
   893 	DECODE_VALVE_PARAM(1, vparams, 0);
   894 	FINISH_CALL_SIMPLE(&ret);
   895 
   896 	return (ret == -1) ? 0 : 1;
   897 }
   898 #endif
   899 
   900 #if SOURCE_ENGINE >= SE_ORANGEBOX
   901 static cell_t DispatchKeyValue(IPluginContext *pContext, const cell_t *params)
   902 {
   903 	CBaseEntity *pEntity = gamehelpers->ReferenceToEntity(params[1]);
   904 	if (!pEntity)
   905 	{
   906 		return pContext->ThrowNativeError("Entity %d (%d) is invalid", gamehelpers->ReferenceToIndex(params[1]), params[1]);
   907 	}
   908 
   909 	char *key;
   910 	char *value;
   911 	pContext->LocalToString(params[2], &key);
   912 	pContext->LocalToString(params[3], &value);
   913 
   914 	return (servertools->SetKeyValue(pEntity, key, value) ? 1 : 0);
   915 }
   916 #else
   917 static cell_t DispatchKeyValue(IPluginContext *pContext, const cell_t *params)
   918 {
   919 	static ValveCall *pCall = NULL;
   920 	if (!pCall)
   921 	{
   922 		ValvePassInfo pass[3];
   923 		InitPass(pass[0], Valve_String, PassType_Basic, PASSFLAG_BYVAL);
   924 		InitPass(pass[1], Valve_String, PassType_Basic, PASSFLAG_BYVAL);
   925 		InitPass(pass[2], Valve_Bool, PassType_Basic, PASSFLAG_BYVAL);
   926 		if (!CreateBaseCall("DispatchKeyValue", ValveCall_Entity, &pass[2], pass, 2, &pCall))
   927 		{
   928 			return pContext->ThrowNativeError("\"DispatchKeyValue\" not supported by this mod");
   929 		} else if (!pCall) {
   930 			return pContext->ThrowNativeError("\"DispatchKeyValue\" wrapper failed to initialize");
   931 		}
   932 	}
   933 
   934 	bool ret;
   935 	START_CALL();
   936 	DECODE_VALVE_PARAM(1, thisinfo, 0);
   937 	DECODE_VALVE_PARAM(2, vparams, 0);
   938 	DECODE_VALVE_PARAM(3, vparams, 1);
   939 	FINISH_CALL_SIMPLE(&ret);
   940 
   941 	return ret ? 1 : 0;
   942 }
   943 #endif
   944 
   945 #if SOURCE_ENGINE >= SE_ORANGEBOX
   946 static cell_t DispatchKeyValueFloat(IPluginContext *pContext, const cell_t *params)
   947 {
   948 	CBaseEntity *pEntity = gamehelpers->ReferenceToEntity(params[1]);
   949 	if (!pEntity)
   950 	{
   951 		return pContext->ThrowNativeError("Entity %d (%d) is invalid", gamehelpers->ReferenceToIndex(params[1]), params[1]);
   952 	}
   953 
   954 	char *key;
   955 	pContext->LocalToString(params[2], &key);
   956 	
   957 	float value = sp_ctof(params[3]);
   958 
   959 	return (servertools->SetKeyValue(pEntity, key, value) ? 1 : 0);
   960 }
   961 #else
   962 static cell_t DispatchKeyValueFloat(IPluginContext *pContext, const cell_t *params)
   963 {
   964 	static ValveCall *pCall = NULL;
   965 	if (!pCall)
   966 	{
   967 		ValvePassInfo pass[3];
   968 		InitPass(pass[0], Valve_String, PassType_Basic, PASSFLAG_BYVAL);
   969 		InitPass(pass[1], Valve_Float, PassType_Float, PASSFLAG_BYVAL);
   970 		InitPass(pass[2], Valve_Bool, PassType_Basic, PASSFLAG_BYVAL);
   971 		if (!CreateBaseCall("DispatchKeyValueFloat", ValveCall_Entity, &pass[2], pass, 2, &pCall))
   972 		{
   973 			return pContext->ThrowNativeError("\"DispatchKeyValueFloat\" not supported by this mod");
   974 		} else if (!pCall) {
   975 			return pContext->ThrowNativeError("\"DispatchKeyValueFloat\" wrapper failed to initialize");
   976 		}
   977 	}
   978 
   979 	bool ret;
   980 	START_CALL();
   981 	DECODE_VALVE_PARAM(1, thisinfo, 0);
   982 	DECODE_VALVE_PARAM(2, vparams, 0);
   983 	DECODE_VALVE_PARAM(3, vparams, 1);
   984 	FINISH_CALL_SIMPLE(&ret);
   985 
   986 	return ret ? 1 : 0;
   987 }
   988 #endif
   989 
   990 #if SOURCE_ENGINE >= SE_ORANGEBOX
   991 static cell_t DispatchKeyValueVector(IPluginContext *pContext, const cell_t *params)
   992 {
   993 	CBaseEntity *pEntity = gamehelpers->ReferenceToEntity(params[1]);
   994 	if (!pEntity)
   995 	{
   996 		return pContext->ThrowNativeError("Entity %d (%d) is invalid", gamehelpers->ReferenceToIndex(params[1]), params[1]);
   997 	}
   998 
   999 	char *key;
  1000 	pContext->LocalToString(params[2], &key);
  1001 	
  1002 	cell_t *vec;
  1003 	pContext->LocalToPhysAddr(params[3], &vec);
  1004 
  1005 	const Vector *v = new Vector(
  1006 		sp_ctof(vec[0]),
  1007 		sp_ctof(vec[1]),
  1008 		sp_ctof(vec[2]));
  1009 
  1010 	return (servertools->SetKeyValue(pEntity, key, *v) ? 1 : 0);
  1011 }
  1012 #else
  1013 static cell_t DispatchKeyValueVector(IPluginContext *pContext, const cell_t *params)
  1014 {
  1015 	static ValveCall *pCall = NULL;
  1016 	if (!pCall)
  1017 	{
  1018 		ValvePassInfo pass[3];
  1019 		InitPass(pass[0], Valve_String, PassType_Basic, PASSFLAG_BYVAL);
  1020 #if SOURCE_ENGINE >= SE_ORANGEBOX
  1021 		InitPass(pass[1], Valve_Vector, PassType_Basic, PASSFLAG_BYVAL);
  1022 #else
  1023 		InitPass(pass[1], Valve_Vector, PassType_Object, PASSFLAG_BYVAL|PASSFLAG_OCTOR|PASSFLAG_OASSIGNOP);
  1024 #endif
  1025 		InitPass(pass[2], Valve_Bool, PassType_Basic, PASSFLAG_BYVAL);
  1026 		if (!CreateBaseCall("DispatchKeyValueVector", ValveCall_Entity, &pass[2], pass, 2, &pCall))
  1027 		{
  1028 			return pContext->ThrowNativeError("\"DispatchKeyValueVector\" not supported by this mod");
  1029 		} else if (!pCall) {
  1030 			return pContext->ThrowNativeError("\"DispatchKeyValueVector\" wrapper failed to initialize");
  1031 		}
  1032 	}
  1033 
  1034 	bool ret;
  1035 	START_CALL();
  1036 	DECODE_VALVE_PARAM(1, thisinfo, 0);
  1037 	DECODE_VALVE_PARAM(2, vparams, 0);
  1038 	DECODE_VALVE_PARAM(3, vparams, 1);
  1039 	FINISH_CALL_SIMPLE(&ret);
  1040 
  1041 	return ret ? 1 : 0;
  1042 }
  1043 #endif
  1044 
  1045 static cell_t sm_GetClientAimTarget(IPluginContext *pContext, const cell_t *params)
  1046 {
  1047 	IGamePlayer *pPlayer = playerhelpers->GetGamePlayer(params[1]);
  1048 
  1049 	if (!pPlayer)
  1050 	{
  1051 		return pContext->ThrowNativeError("Invalid client index %d", params[1]);
  1052 	}
  1053 	else if (!pPlayer->IsInGame())
  1054 	{
  1055 		return pContext->ThrowNativeError("Client %d is not in game", params[1]);
  1056 	}
  1057 
  1058 	return GetClientAimTarget(pPlayer->GetEdict(), params[2] ? true : false);
  1059 }
  1060 
  1061 static cell_t sm_SetEntityModel(IPluginContext *pContext, const cell_t *params)
  1062 {
  1063 	static ValveCall *pCall = NULL;
  1064 	if (!pCall)
  1065 	{
  1066 		ValvePassInfo pass[1];
  1067 		InitPass(pass[0], Valve_String, PassType_Basic, PASSFLAG_BYVAL);
  1068 		if (!CreateBaseCall("SetEntityModel", ValveCall_Entity, NULL, pass, 1, &pCall))
  1069 		{
  1070 			return pContext->ThrowNativeError("\"SetEntityModel\" not supported by this mod");
  1071 		} else if (!pCall) {
  1072 			return pContext->ThrowNativeError("\"SetEntityModel\" wrapper failed to initialize");
  1073 		}
  1074 	}
  1075 
  1076 	START_CALL();
  1077 	DECODE_VALVE_PARAM(1, thisinfo, 0);
  1078 	DECODE_VALVE_PARAM(2, vparams, 0);
  1079 	FINISH_CALL_SIMPLE(NULL);
  1080 
  1081 	return 1;
  1082 }
  1083 
  1084 static cell_t GetPlayerDecalFile(IPluginContext *pContext, const cell_t *params)
  1085 {
  1086 	IGamePlayer *player = playerhelpers->GetGamePlayer(params[1]);
  1087 	if (player == NULL)
  1088 	{
  1089 		return pContext->ThrowNativeError("Invalid client index %d", params[1]);
  1090 	}
  1091 	if (!player->IsInGame())
  1092 	{
  1093 		return pContext->ThrowNativeError("Client %d is not in game", params[1]);
  1094 	}
  1095 
  1096 	player_info_t info;
  1097 	char *buffer;
  1098 
  1099 	if (!GetPlayerInfo(params[1], &info) || !info.customFiles[0])
  1100 	{
  1101 		return 0;
  1102 	}
  1103 
  1104 	pContext->LocalToString(params[2], &buffer);
  1105 	Q_binarytohex((byte *)&info.customFiles[0], sizeof(info.customFiles[0]), buffer, params[3]);
  1106 
  1107 	return 1;
  1108 }
  1109 
  1110 static cell_t GetPlayerJingleFile(IPluginContext *pContext, const cell_t *params)
  1111 {
  1112 	IGamePlayer *player = playerhelpers->GetGamePlayer(params[1]);
  1113 	if (player == NULL)
  1114 	{
  1115 		return pContext->ThrowNativeError("Invalid client index %d", params[1]);
  1116 	}
  1117 	if (!player->IsInGame())
  1118 	{
  1119 		return pContext->ThrowNativeError("Client %d is not in game", params[1]);
  1120 	}
  1121 
  1122 	player_info_t info;
  1123 	char *buffer;
  1124 
  1125 	if (!GetPlayerInfo(params[1], &info) || !info.customFiles[1])
  1126 	{
  1127 		return 0;
  1128 	}
  1129 
  1130 	pContext->LocalToString(params[2], &buffer);
  1131 	Q_binarytohex((byte *)&info.customFiles[1], sizeof(info.customFiles[1]), buffer, params[3]);
  1132 
  1133 	return 1;
  1134 }
  1135 
  1136 static cell_t GetServerNetStats(IPluginContext *pContext, const cell_t *params)
  1137 {
  1138 	if (iserver == NULL)
  1139 	{
  1140 		return pContext->ThrowNativeError("IServer interface not supported, file a bug report.");
  1141 	}
  1142 
  1143 	float in, out;
  1144 	cell_t *pIn, *pOut;
  1145 
  1146 	pContext->LocalToPhysAddr(params[1], &pIn);
  1147 	pContext->LocalToPhysAddr(params[2], &pOut);
  1148 	iserver->GetNetStats(in, out);
  1149 
  1150 	*pIn = sp_ftoc(in);
  1151 	*pOut = sp_ftoc(out);
  1152 
  1153 	return 1;
  1154 }
  1155 
  1156 static cell_t WeaponEquip(IPluginContext *pContext, const cell_t *params)
  1157 {
  1158 	static ValveCall *pCall = NULL;
  1159 	if (!pCall)
  1160 	{
  1161 		ValvePassInfo pass[1];
  1162 		InitPass(pass[0], Valve_CBaseEntity, PassType_Basic, PASSFLAG_BYVAL);
  1163 		if (!CreateBaseCall("WeaponEquip", ValveCall_Player, NULL, pass, 1, &pCall))
  1164 		{
  1165 			return pContext->ThrowNativeError("\"WeaponEquip\" not supported by this mod");
  1166 		} else if (!pCall) {
  1167 			return pContext->ThrowNativeError("\"WeaponEquip\" wrapper failed to initialize");
  1168 		}
  1169 	}
  1170 
  1171 	START_CALL();
  1172 	DECODE_VALVE_PARAM(1, thisinfo, 0);
  1173 	DECODE_VALVE_PARAM(2, vparams, 0);
  1174 	FINISH_CALL_SIMPLE(NULL);
  1175 
  1176 	return 1;
  1177 }
  1178 
  1179 static cell_t ActivateEntity(IPluginContext *pContext, const cell_t *params)
  1180 {
  1181 	static ValveCall *pCall = NULL;
  1182 	if (!pCall)
  1183 	{
  1184 		if (!CreateBaseCall("Activate", ValveCall_Entity, NULL, NULL, 0, &pCall))
  1185 		{
  1186 			return pContext->ThrowNativeError("\"Activate\" not supported by this mod");
  1187 		}
  1188 		else if (!pCall)
  1189 		{
  1190 			return pContext->ThrowNativeError("\"Activate\" wrapper failed to initialize");
  1191 		}
  1192 	}
  1193 
  1194 	START_CALL();
  1195 	DECODE_VALVE_PARAM(1, thisinfo, 0);
  1196 	FINISH_CALL_SIMPLE(NULL);
  1197 
  1198 	return 1;
  1199 }
  1200 
  1201 static cell_t SetClientInfo(IPluginContext *pContext, const cell_t *params)
  1202 {
  1203 	if (iserver == NULL)
  1204 	{
  1205 		return pContext->ThrowNativeError("IServer interface not supported, file a bug report.");
  1206 	}
  1207 
  1208 	IGamePlayer *player = playerhelpers->GetGamePlayer(params[1]);
  1209 	IClient *pClient = iserver->GetClient(params[1] - 1);
  1210 
  1211 	if (player == NULL || pClient == NULL)
  1212 	{
  1213 		return pContext->ThrowNativeError("Invalid client index %d", params[1]);
  1214 	}
  1215 	if (!player->IsConnected())
  1216 	{
  1217 		return pContext->ThrowNativeError("Client %d is not connected", params[1]);
  1218 	}
  1219 
  1220 	static ValveCall *pCall = NULL;
  1221 	if (!pCall)
  1222 	{
  1223 		ValvePassInfo params[2];
  1224 		InitPass(params[0], Valve_String, PassType_Basic, PASSFLAG_BYVAL);
  1225 		InitPass(params[1], Valve_String, PassType_Basic, PASSFLAG_BYVAL);
  1226 
  1227 		if (!CreateBaseCall("SetUserCvar", ValveCall_Entity, NULL, params, 2, &pCall))
  1228 		{
  1229 			return pContext->ThrowNativeError("\"SetUserCvar\" not supported by this mod");
  1230 		}
  1231 		else if (!pCall)
  1232 		{
  1233 			return pContext->ThrowNativeError("\"SetUserCvar\" wrapper failed to initialize");
  1234 		}
  1235 	}
  1236 
  1237 /* TODO: Use UpdateUserSettings function for all engines */
  1238 #if SOURCE_ENGINE == SE_DARKMESSIAH
  1239 	static ValveCall *pUpdateSettings = NULL;
  1240 	if (!pUpdateSettings)
  1241 	{
  1242 		if (!CreateBaseCall("UpdateUserSettings", ValveCall_Entity, NULL, NULL, 0, &pUpdateSettings))
  1243 		{
  1244 			return pContext->ThrowNativeError("\"SetUserCvar\" not supported by this mod");
  1245 		}
  1246 		else if (!pUpdateSettings)
  1247 		{
  1248 			return pContext->ThrowNativeError("\"SetUserCvar\" wrapper failed to initialize");
  1249 		}
  1250 	}
  1251 #else
  1252 	static int changedOffset = -1;
  1253 
  1254 	if (changedOffset == -1)
  1255 	{	
  1256 		if (!g_pGameConf->GetOffset("InfoChanged", &changedOffset))
  1257 		{
  1258 			return pContext->ThrowNativeError("\"SetUserCvar\" not supported by this mod");
  1259 		}
  1260 	}
  1261 #endif
  1262 
  1263 	unsigned char *CGameClient = (unsigned char *)pClient - 4;
  1264 
  1265 	START_CALL();
  1266 	/* Not really a CBaseEntity* but this works */
  1267 	CBaseEntity **ebuf = (CBaseEntity **)vptr;
  1268 	*ebuf = (CBaseEntity *)CGameClient;
  1269 	DECODE_VALVE_PARAM(2, vparams, 0);
  1270 	DECODE_VALVE_PARAM(3, vparams, 1);
  1271 	FINISH_CALL_SIMPLE(NULL);
  1272 
  1273 #if SOURCE_ENGINE == SE_DARKMESSIAH
  1274 	unsigned char *args = pUpdateSettings->stk_get();
  1275 	*(void **)args = CGameClient;
  1276 	pUpdateSettings->call->Execute(args, NULL);
  1277 	pUpdateSettings->stk_put(args);
  1278 #else
  1279 	uint8_t* changed = (uint8_t *)(CGameClient + changedOffset);
  1280 	*changed = 1;
  1281 #endif
  1282 
  1283 	return 1;
  1284 }
  1285 
  1286 static cell_t GetPlayerResourceEntity(IPluginContext *pContext, const cell_t *params)
  1287 {
  1288 	if (gamehelpers->GetHandleEntity(g_ResourceEntity) != NULL)
  1289 	{
  1290 		return g_ResourceEntity.GetEntryIndex();
  1291 	}
  1292 
  1293 	return -1;
  1294 }
  1295 
  1296 sp_nativeinfo_t g_Natives[] = 
  1297 {
  1298 	{"ExtinguishEntity",		ExtinguishEntity},
  1299 	{"ForcePlayerSuicide",		ForcePlayerSuicide},
  1300 	{"GivePlayerItem",			GiveNamedItem},
  1301 	{"GetPlayerWeaponSlot",		GetPlayerWeaponSlot},
  1302 	{"IgniteEntity",			IgniteEntity},
  1303 	{"RemovePlayerItem",		RemovePlayerItem},
  1304 	{"TeleportEntity",			TeleportEntity},
  1305 	{"SetClientViewEntity",		SetClientViewEntity},
  1306 	{"SetLightStyle",			SetLightStyle},
  1307 	{"SlapPlayer",				SlapPlayer},
  1308 	{"GetClientEyePosition",	GetClientEyePosition},
  1309 	{"GetClientEyeAngles",		GetClientEyeAngles},
  1310 	{"FindEntityByClassname",	FindEntityByClassname},
  1311 	{"CreateEntityByName",		CreateEntityByName},
  1312 	{"DispatchSpawn",			DispatchSpawn},
  1313 	{"DispatchKeyValue",		DispatchKeyValue},
  1314 	{"DispatchKeyValueFloat",	DispatchKeyValueFloat},
  1315 	{"DispatchKeyValueVector",	DispatchKeyValueVector},
  1316 	{"GetClientAimTarget",		sm_GetClientAimTarget},
  1317 	{"SetEntityModel",			sm_SetEntityModel},
  1318 	{"GetPlayerDecalFile",		GetPlayerDecalFile},
  1319 	{"GetPlayerJingleFile",		GetPlayerJingleFile},
  1320 	{"GetServerNetStats",		GetServerNetStats},
  1321 	{"EquipPlayerWeapon",		WeaponEquip},
  1322 	{"ActivateEntity",			ActivateEntity},
  1323 	{"SetClientInfo",			SetClientInfo},
  1324 	{"GetPlayerResourceEntity", GetPlayerResourceEntity},
  1325 	{NULL,						NULL},
  1326 };