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