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