extensions/sdktools/vnatives.cpp
author Fyren <fyrenmoo@gmail.com>
Mon May 11 18:46:19 2009 -0700 (2009-05-11)
changeset 2710 3bf47c5fa143
parent 2583 d3b2571c378b
child 2743 a42b24399f60
permissions -rw-r--r--
Some sdktools string and style changes (r=pred)
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod SDKTools Extension
     5  * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
     6  * =============================================================================
     7  *
     8  * This program is free software; you can redistribute it and/or modify it under
     9  * the terms of the GNU General Public License, version 3.0, as published by the
    10  * Free Software Foundation.
    11  * 
    12  * This program is distributed in the hope that it will be useful, but WITHOUT
    13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    14  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    15  * details.
    16  *
    17  * You should have received a copy of the GNU General Public License along with
    18  * this program.  If not, see <http://www.gnu.org/licenses/>.
    19  *
    20  * As a special exception, AlliedModders LLC gives you permission to link the
    21  * code of this program (as well as its derivative works) to "Half-Life 2," the
    22  * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    23  * by the Valve Corporation.  You must obey the GNU General Public License in
    24  * all respects for all other code used.  Additionally, AlliedModders LLC grants
    25  * this exception to all derivative works.  AlliedModders LLC defines further
    26  * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    27  * or <http://www.sourcemod.net/license.php>.
    28  *
    29  * Version: $Id$
    30  */
    31 
    32 #include <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 	if (pEntity == NULL)
   168 	{
   169 		return -1;
   170 	}
   171 
   172 	edict_t *pEdict = gameents->BaseEntityToEdict(pEntity);
   173 	if (!pEdict)
   174 	{
   175 		return -1;
   176 	}
   177 
   178 	return IndexOfEdict(pEdict);
   179 }
   180 
   181 static cell_t GetPlayerWeaponSlot(IPluginContext *pContext, const cell_t *params)
   182 {
   183 	static ValveCall *pCall = NULL;
   184 	if (!pCall)
   185 	{
   186 		ValvePassInfo pass[2];
   187 		InitPass(pass[0], Valve_POD, PassType_Basic, PASSFLAG_BYVAL);
   188 		InitPass(pass[1], Valve_CBaseEntity, PassType_Basic, PASSFLAG_BYVAL);
   189 		if (!CreateBaseCall("Weapon_GetSlot", ValveCall_Player, &pass[1], pass, 1, &pCall))
   190 		{
   191 			return pContext->ThrowNativeError("\"Weapon_GetSlot\" not supported by this mod");
   192 		} else if (!pCall) {
   193 			return pContext->ThrowNativeError("\"Weapon_GetSlot\" wrapper failed to initialize");
   194 		}
   195 	}
   196 
   197 	CBaseEntity *pEntity;
   198 	START_CALL();
   199 	DECODE_VALVE_PARAM(1, thisinfo, 0);
   200 	DECODE_VALVE_PARAM(2, vparams, 0);
   201 	FINISH_CALL_SIMPLE(&pEntity);
   202 
   203 	if (pEntity == NULL)
   204 	{
   205 		return -1;
   206 	}
   207 
   208 	edict_t *pEdict = gameents->BaseEntityToEdict(pEntity);
   209 	if (!pEdict)
   210 	{
   211 		return -1;
   212 	}
   213 
   214 	return IndexOfEdict(pEdict);
   215 }
   216 
   217 #if SOURCE_ENGINE != SE_DARKMESSIAH
   218 static cell_t IgniteEntity(IPluginContext *pContext, const cell_t *params)
   219 {
   220 	static ValveCall *pCall = NULL;
   221 	if (!pCall)
   222 	{
   223 		ValvePassInfo pass[4];
   224 		InitPass(pass[0], Valve_Float, PassType_Float, PASSFLAG_BYVAL);
   225 		InitPass(pass[1], Valve_Bool, PassType_Basic, PASSFLAG_BYVAL);
   226 		InitPass(pass[2], Valve_Float, PassType_Float, PASSFLAG_BYVAL);
   227 		InitPass(pass[3], Valve_Bool, PassType_Basic, PASSFLAG_BYVAL);
   228 		if (!CreateBaseCall("Ignite", ValveCall_Entity, NULL, pass, 4, &pCall))
   229 		{
   230 			return pContext->ThrowNativeError("\"Ignite\" not supported by this mod");
   231 		} else if (!pCall) {
   232 			return pContext->ThrowNativeError("\"Ignite\" wrapper failed to initialize");
   233 		}
   234 	}
   235 
   236 	START_CALL();
   237 	DECODE_VALVE_PARAM(1, thisinfo, 0);
   238 	DECODE_VALVE_PARAM(2, vparams, 0);
   239 	DECODE_VALVE_PARAM(3, vparams, 1);
   240 	DECODE_VALVE_PARAM(4, vparams, 2);
   241 	DECODE_VALVE_PARAM(5, vparams, 3);
   242 	FINISH_CALL_SIMPLE(NULL);
   243 
   244 	return 1;
   245 }
   246 #else
   247 /* Dark Messiah specific version */
   248 static cell_t IgniteEntity(IPluginContext *pContext, const cell_t *params)
   249 {
   250 	static ValveCall *pCall = NULL;
   251 	if (!pCall)
   252 	{
   253 		ValvePassInfo pass[6];
   254 		InitPass(pass[0], Valve_Float, PassType_Float, PASSFLAG_BYVAL);
   255 		InitPass(pass[1], Valve_Bool, PassType_Basic, PASSFLAG_BYVAL);
   256 		InitPass(pass[2], Valve_Float, PassType_Float, PASSFLAG_BYVAL);
   257 		InitPass(pass[3], Valve_Bool, PassType_Basic, PASSFLAG_BYVAL);
   258 		InitPass(pass[4], Valve_POD, PassType_Basic, PASSFLAG_BYVAL);
   259 		InitPass(pass[5], Valve_POD, PassType_Basic, PASSFLAG_BYVAL);
   260 		if (!CreateBaseCall("Ignite", ValveCall_Entity, NULL, pass, 6, &pCall))
   261 		{
   262 			return pContext->ThrowNativeError("\"Ignite\" not supported by this mod");
   263 		} else if (!pCall) {
   264 			return pContext->ThrowNativeError("\"Ignite\" wrapper failed to initialize");
   265 		}
   266 	}
   267 
   268 	START_CALL();
   269 	DECODE_VALVE_PARAM(1, thisinfo, 0);
   270 	DECODE_VALVE_PARAM(2, vparams, 0);
   271 	DECODE_VALVE_PARAM(3, vparams, 1);
   272 	DECODE_VALVE_PARAM(4, vparams, 2);
   273 	DECODE_VALVE_PARAM(5, vparams, 3);
   274 	/* Not sure what these params do, but they appear to be the default values */
   275 	*(int *)(vptr + 14) = 3;
   276 	*(int *)(vptr + 18) = 0;
   277 	FINISH_CALL_SIMPLE(NULL);
   278 
   279 	return 1;
   280 }
   281 #endif
   282 
   283 static cell_t ExtinguishEntity(IPluginContext *pContext, const cell_t *params)
   284 {
   285 	static ValveCall *pCall = NULL;
   286 	if (!pCall)
   287 	{
   288 		if (!CreateBaseCall("Extinguish", ValveCall_Entity, NULL, NULL, 0, &pCall))
   289 		{
   290 			return pContext->ThrowNativeError("\"Extinguish\" not supported by this mod");
   291 		} else if (!pCall) {
   292 			return pContext->ThrowNativeError("\"Extinguish\" wrapper failed to initialize");
   293 		}
   294 	}
   295 
   296 	START_CALL();
   297 	DECODE_VALVE_PARAM(1, thisinfo, 0);
   298 	FINISH_CALL_SIMPLE(NULL);
   299 
   300 	return 1;
   301 }
   302 
   303 static cell_t TeleportEntity(IPluginContext *pContext, const cell_t *params)
   304 {
   305 	static ValveCall *pCall = NULL;
   306 	if (!pCall)
   307 	{
   308 		ValvePassInfo pass[3];
   309 		InitPass(pass[0], Valve_Vector, PassType_Basic, PASSFLAG_BYVAL, VDECODE_FLAG_ALLOWNULL);
   310 		InitPass(pass[1], Valve_QAngle, PassType_Basic, PASSFLAG_BYVAL, VDECODE_FLAG_ALLOWNULL);
   311 		InitPass(pass[2], Valve_Vector, PassType_Basic, PASSFLAG_BYVAL, VDECODE_FLAG_ALLOWNULL);
   312 		if (!CreateBaseCall("Teleport", ValveCall_Entity, NULL, pass, 3, &pCall))
   313 		{
   314 			return pContext->ThrowNativeError("\"Teleport\" not supported by this mod");
   315 		} else if (!pCall) {
   316 			return pContext->ThrowNativeError("\"Teleport\" wrapper failed to initialize");
   317 		}
   318 	}
   319 
   320 	START_CALL();
   321 	DECODE_VALVE_PARAM(1, thisinfo, 0);
   322 	DECODE_VALVE_PARAM(2, vparams, 0);
   323 	DECODE_VALVE_PARAM(3, vparams, 1);
   324 	DECODE_VALVE_PARAM(4, vparams, 2);
   325 	FINISH_CALL_SIMPLE(NULL);
   326 
   327 	return 1;
   328 }
   329 
   330 #if SOURCE_ENGINE >= SE_ORANGEBOX
   331 /* :TODO: This is Team Fortress 2 specific */
   332 static cell_t ForcePlayerSuicide(IPluginContext *pContext, const cell_t *params)
   333 {
   334 	static ValveCall *pCall = NULL;
   335 	if (!pCall)
   336 	{
   337 		ValvePassInfo pass[2];
   338 		InitPass(pass[0], Valve_Bool, PassType_Basic, PASSFLAG_BYVAL);
   339 		InitPass(pass[1], Valve_Bool, PassType_Basic, PASSFLAG_BYVAL);
   340 		if (!CreateBaseCall("CommitSuicide", ValveCall_Player, NULL, pass, 2, &pCall))
   341 		{
   342 			return pContext->ThrowNativeError("\"CommitSuicide\" not supported by this mod");
   343 		}
   344 		else if (!pCall)
   345 		{
   346 			return pContext->ThrowNativeError("\"CommitSuicide\" wrapper failed to initialize");
   347 		}
   348 	}
   349 
   350 	START_CALL();
   351 	DECODE_VALVE_PARAM(1, thisinfo, 0);
   352 	*(bool *)(vptr + 4) = false;
   353 	*(bool *)(vptr + 5) = false;
   354 	FINISH_CALL_SIMPLE(NULL);
   355 
   356 	return 1;
   357 }
   358 #else
   359 static cell_t ForcePlayerSuicide(IPluginContext *pContext, const cell_t *params)
   360 {
   361 	static ValveCall *pCall = NULL;
   362 	if (!pCall)
   363 	{
   364 		if (!CreateBaseCall("CommitSuicide", ValveCall_Player, NULL, NULL, 0, &pCall))
   365 		{
   366 			return pContext->ThrowNativeError("\"CommitSuicide\" not supported by this mod");
   367 		} else if (!pCall) {
   368 			return pContext->ThrowNativeError("\"CommitSuicide\" wrapper failed to initialize");
   369 		}
   370 	}
   371 
   372 	START_CALL();
   373 	DECODE_VALVE_PARAM(1, thisinfo, 0);
   374 	FINISH_CALL_SIMPLE(NULL);
   375 
   376 	return 1;
   377 }
   378 #endif
   379 
   380 static cell_t SetClientViewEntity(IPluginContext *pContext, const cell_t *params)
   381 {
   382 	IGamePlayer *player = playerhelpers->GetGamePlayer(params[1]);
   383 	if (player == NULL)
   384 	{
   385 		return pContext->ThrowNativeError("Invalid client index %d", params[1]);
   386 	}
   387 	if (!player->IsInGame())
   388 	{
   389 		return pContext->ThrowNativeError("Client %d is not in game", params[1]);
   390 	}
   391 
   392 	edict_t *pEdict = PEntityOfEntIndex(params[2]);
   393 	if (!pEdict || pEdict->IsFree())
   394 	{
   395 		return pContext->ThrowNativeError("Entity %d is not valid", params[2]);
   396 	}
   397 
   398 	engine->SetView(player->GetEdict(), pEdict);
   399 
   400 	return 1;
   401 }
   402 
   403 static SourceHook::String *g_lightstyle[MAX_LIGHTSTYLES] = {NULL};
   404 static cell_t SetLightStyle(IPluginContext *pContext, const cell_t *params)
   405 {
   406 	int style = params[1];
   407 	if (style >= MAX_LIGHTSTYLES)
   408 	{
   409 		return pContext->ThrowNativeError("Light style %d is invalid (range: 0-%d)", style, MAX_LIGHTSTYLES - 1);
   410 	}
   411 
   412 	if (g_lightstyle[style] == NULL)
   413 	{
   414 		/* We allocate and never free this because the Engine wants to hold onto it :\
   415 		 * in theory we could hook light style and know whether we're supposed to free 
   416 		 * this or not on shutdown, but for ~4K of memory MAX, it doesn't seem worth it yet.
   417 		 * So, it's a :TODO:!
   418 		 */
   419 		g_lightstyle[style] = new SourceHook::String();
   420 	}
   421 
   422 	char *str;
   423 	pContext->LocalToString(params[2], &str);
   424 
   425 	g_lightstyle[style]->assign(str);
   426 
   427 	engine->LightStyle(style, g_lightstyle[style]->c_str());
   428 
   429 	return 1;
   430 }
   431 
   432 static cell_t SlapPlayer(IPluginContext *pContext, const cell_t *params)
   433 {
   434 	static bool s_slap_supported = false;
   435 	static bool s_slap_setup = false;
   436 	static ICallWrapper *s_teleport = NULL;
   437 	static int s_health_offs = 0;
   438 	static int s_sound_count = 0;
   439 	static int s_frag_offs = 0;
   440 
   441 	if (!s_slap_setup)
   442 	{
   443 		int tries = 0;
   444 
   445 		s_slap_setup = true;
   446 
   447 		if (IsTeleportSupported())
   448 		{
   449 			tries++;
   450 		}
   451 		if (IsGetVelocitySupported())
   452 		{
   453 			tries++;
   454 		}
   455 
   456 		/* Setup health */
   457 		if (g_pGameConf->GetOffset("m_iHealth", &s_health_offs) && s_health_offs)
   458 		{
   459 			tries++;
   460 		}
   461 
   462 		if (tries == 3)
   463 		{
   464 			s_slap_supported = true;
   465 
   466 			const char *key;
   467 			if ((key = g_pGameConf->GetKeyValue("SlapSoundCount")) != NULL)
   468 			{
   469 				s_sound_count = atoi(key);
   470 			}
   471 		}
   472 	}
   473 
   474 	if (!s_slap_supported)
   475 	{
   476 		return pContext->ThrowNativeError("This function is not supported on this mod");
   477 	}
   478 
   479 	/* First check if the client is valid */
   480 	int client = params[1];
   481 	IGamePlayer *player = playerhelpers->GetGamePlayer(client);
   482 	if (!player)
   483 	{
   484 		return pContext->ThrowNativeError("Client %d is not valid", client);
   485 	} else if (!player->IsInGame()) {
   486 		return pContext->ThrowNativeError("Client %d is not in game", client);
   487 	}
   488 
   489 	edict_t *pEdict = player->GetEdict();
   490 	CBaseEntity *pEntity = pEdict->GetUnknown()->GetBaseEntity();
   491 
   492 	/* See if we should be taking away health */
   493 	bool should_slay = false;
   494 	if (params[2])
   495 	{
   496 #if SOURCE_ENGINE != SE_DARKMESSIAH
   497 		int *health = (int *)((char *)pEntity + s_health_offs);
   498 
   499 		if (*health - params[2] <= 0)
   500 		{
   501 			*health = 1;
   502 			should_slay = true;
   503 		} else {
   504 			*health -= params[2];
   505 		}
   506 #else
   507 		float *health = (float *)((char *)pEntity + s_health_offs);
   508 
   509 		if (*health - (float)params[2] <= 0)
   510 		{
   511 			*health = 1.0f;
   512 			should_slay = true;
   513 		} else {
   514 			*health -= (float)params[2];
   515 		}
   516 #endif
   517 	}
   518 
   519 	/* Teleport in a random direction - thank you, Mani!*/
   520 	Vector velocity;
   521 	GetVelocity(pEntity, &velocity, NULL);
   522 	velocity.x += ((rand() % 180) + 50) * (((rand() % 2) == 1) ?  -1 : 1);
   523 	velocity.y += ((rand() % 180) + 50) * (((rand() % 2) == 1) ?  -1 : 1);
   524 	velocity.z += rand() % 200 + 100;
   525 	Teleport(pEntity, NULL, NULL, &velocity);
   526 
   527 	/* Play a random sound */
   528 	if (params[3] && s_sound_count > 0)
   529 	{
   530 		char name[48];
   531 		const char *sound_name;
   532 		cell_t player_list[256], total_players = 0;
   533 		int maxClients = playerhelpers->GetMaxClients();
   534 
   535 		int r = (rand() % s_sound_count) + 1;
   536 		snprintf(name, sizeof(name), "SlapSound%d", r);
   537 
   538 		if ((sound_name = g_pGameConf->GetKeyValue(name)) != NULL)
   539 		{
   540 			IGamePlayer *other;
   541 			for (int i=1; i<=maxClients; i++)
   542 			{
   543 				other = playerhelpers->GetGamePlayer(i);
   544 				if (other->IsInGame())
   545 				{
   546 					player_list[total_players++] = i;
   547 				}
   548 			}
   549 
   550 			const Vector & pos = pEdict->GetCollideable()->GetCollisionOrigin();
   551 			CellRecipientFilter rf;
   552 			rf.SetToReliable(true);
   553 			rf.Initialize(player_list, total_players);
   554 			engsound->EmitSound(rf, client, CHAN_AUTO, sound_name, VOL_NORM, ATTN_NORM, 0, PITCH_NORM, &pos);
   555 		}
   556 	}
   557 
   558 	if (!s_frag_offs)
   559 	{
   560 		const char *frag_prop = g_pGameConf->GetKeyValue("m_iFrags");
   561 		if (frag_prop)
   562 		{
   563 			datamap_t *pMap = gamehelpers->GetDataMap(pEntity);
   564 			typedescription_t *pType = gamehelpers->FindInDataMap(pMap, frag_prop);
   565 			if (pType != NULL)
   566 			{
   567 				s_frag_offs = pType->fieldOffset[TD_OFFSET_NORMAL];
   568 			}
   569 		}
   570 		if (!s_frag_offs)
   571 		{
   572 			s_frag_offs = -1;
   573 		}
   574 	}
   575 
   576 	int old_frags = 0;
   577 	if (s_frag_offs > 0)
   578 	{
   579 		old_frags = *(int *)((char *)pEntity + s_frag_offs);
   580 	}
   581 	
   582 	/* Force suicide */
   583 	if (should_slay)
   584 	{
   585 		pluginhelpers->ClientCommand(pEdict, "kill\n");
   586 	}
   587 
   588 	if (s_frag_offs > 0)
   589 	{
   590 		*(int *)((char *)pEntity + s_frag_offs) = old_frags;
   591 	}
   592 
   593 	return 1;
   594 }
   595 
   596 static cell_t GetClientEyePosition(IPluginContext *pContext, const cell_t *params)
   597 {
   598 	IGamePlayer *player = playerhelpers->GetGamePlayer(params[1]);
   599 	if (player == NULL)
   600 	{
   601 		return pContext->ThrowNativeError("Invalid client index %d", params[1]);
   602 	}
   603 	if (!player->IsInGame())
   604 	{
   605 		return pContext->ThrowNativeError("Client %d is not in game", params[1]);
   606 	}
   607 
   608 	Vector pos;
   609 	serverClients->ClientEarPosition(player->GetEdict(), &pos);
   610 
   611 	cell_t *addr;
   612 	pContext->LocalToPhysAddr(params[2], &addr);
   613 	addr[0] = sp_ftoc(pos.x);
   614 	addr[1] = sp_ftoc(pos.y);
   615 	addr[2] = sp_ftoc(pos.z);
   616 
   617 	return 1;
   618 }
   619 
   620 static cell_t GetClientEyeAngles(IPluginContext *pContext, const cell_t *params)
   621 {
   622 	int client = params[1];
   623 	IGamePlayer *pPlayer = playerhelpers->GetGamePlayer(client);
   624 
   625 	if (!pPlayer)
   626 	{
   627 		return pContext->ThrowNativeError("Invalid client index %d", client);
   628 	}
   629 	else if (!pPlayer->IsInGame())
   630 	{
   631 		return pContext->ThrowNativeError("Client %d is not in game", client);
   632 	}
   633 
   634 	edict_t *pEdict = pPlayer->GetEdict();
   635 	CBaseEntity *pEntity = pEdict->GetUnknown() ? pEdict->GetUnknown()->GetBaseEntity() : NULL;
   636 
   637 	/* We always set the angles for backwards compatibility -- 
   638 	 * The original function had no return value.
   639 	 */
   640 	QAngle angles;
   641 	bool got_angles = false;
   642 
   643 	if (pEntity != NULL)
   644 	{
   645 		got_angles = GetEyeAngles(pEntity, &angles);
   646 	}
   647 	
   648 	cell_t *addr;
   649 	pContext->LocalToPhysAddr(params[2], &addr);
   650 	addr[0] = sp_ftoc(angles.x);
   651 	addr[1] = sp_ftoc(angles.y);
   652 	addr[2] = sp_ftoc(angles.z);
   653 
   654 	return got_angles ? 1 : 0;
   655 }
   656 
   657 static cell_t FindEntityByClassname(IPluginContext *pContext, const cell_t *params)
   658 {
   659 	static ValveCall *pCall = NULL;
   660 	if (!pCall)
   661 	{
   662 		ValvePassInfo pass[3];
   663 		InitPass(pass[0], Valve_CBaseEntity, PassType_Basic, PASSFLAG_BYVAL, VDECODE_FLAG_ALLOWNULL|VDECODE_FLAG_ALLOWWORLD);
   664 		InitPass(pass[1], Valve_String, PassType_Basic, PASSFLAG_BYVAL);
   665 		InitPass(pass[2], Valve_CBaseEntity, PassType_Basic, PASSFLAG_BYVAL);
   666 		if (!CreateBaseCall("FindEntityByClassname", ValveCall_EntityList, &pass[2], pass, 2, &pCall))
   667 		{
   668 			return pContext->ThrowNativeError("\"FindEntityByClassname\" not supported by this mod");
   669 		} else if (!pCall) {
   670 			return pContext->ThrowNativeError("\"FindEntityByClassname\" wrapper failed to initialize");
   671 		}
   672 	}
   673 
   674 	CBaseEntity *pEntity;
   675 	START_CALL();
   676 	*(void **)vptr = g_EntList; 
   677 	DECODE_VALVE_PARAM(1, vparams, 0);
   678 	DECODE_VALVE_PARAM(2, vparams, 1);
   679 	FINISH_CALL_SIMPLE(&pEntity);
   680 
   681 	if (pEntity == NULL)
   682 	{
   683 		return -1;
   684 	}
   685 
   686 	edict_t *pEdict = gameents->BaseEntityToEdict(pEntity);
   687 	if (!pEdict)
   688 	{
   689 		return -1;
   690 	}
   691 
   692 	return IndexOfEdict(pEdict);
   693 }
   694 
   695 #if SOURCE_ENGINE == SE_LEFT4DEAD
   696 static cell_t CreateEntityByName(IPluginContext *pContext, const cell_t *params)
   697 {
   698 	static ValveCall *pCall = NULL;
   699 	if (!pCall)
   700 	{
   701 		ValvePassInfo pass[4];
   702 		InitPass(pass[0], Valve_String, PassType_Basic, PASSFLAG_BYVAL);
   703 		InitPass(pass[1], Valve_POD, PassType_Basic, PASSFLAG_BYVAL);
   704 		InitPass(pass[2], Valve_Bool, PassType_Basic, PASSFLAG_BYVAL);
   705 		InitPass(pass[3], Valve_CBaseEntity, PassType_Basic, PASSFLAG_BYVAL);
   706 		if (!CreateBaseCall("CreateEntityByName", ValveCall_Static, &pass[3], pass, 3, &pCall))
   707 		{
   708 			return pContext->ThrowNativeError("\"CreateEntityByName\" not supported by this mod");
   709 		} else if (!pCall) {
   710 			return pContext->ThrowNativeError("\"CreateEntityByName\" wrapper failed to initialize");
   711 		}
   712 	}
   713 
   714 	CBaseEntity *pEntity = NULL;
   715 	START_CALL();
   716 	DECODE_VALVE_PARAM(1, vparams, 0);
   717 	DECODE_VALVE_PARAM(2, vparams, 1);
   718 	*(bool *)(vptr + 8) = true;
   719 	FINISH_CALL_SIMPLE(&pEntity);
   720 
   721 	if (pEntity == NULL)
   722 	{
   723 		return -1;
   724 	}
   725 
   726 	edict_t *pEdict = gameents->BaseEntityToEdict(pEntity);
   727 	if (!pEdict)
   728 	{
   729 		return -1;
   730 	}
   731 
   732 	return IndexOfEdict(pEdict);
   733 }
   734 #else
   735 static cell_t CreateEntityByName(IPluginContext *pContext, const cell_t *params)
   736 {
   737 	static ValveCall *pCall = NULL;
   738 	if (!pCall)
   739 	{
   740 		ValvePassInfo pass[3];
   741 		InitPass(pass[0], Valve_String, PassType_Basic, PASSFLAG_BYVAL);
   742 		InitPass(pass[1], Valve_POD, PassType_Basic, PASSFLAG_BYVAL);
   743 		InitPass(pass[2], Valve_CBaseEntity, PassType_Basic, PASSFLAG_BYVAL);
   744 		if (!CreateBaseCall("CreateEntityByName", ValveCall_Static, &pass[2], pass, 2, &pCall))
   745 		{
   746 			return pContext->ThrowNativeError("\"CreateEntityByName\" not supported by this mod");
   747 		} else if (!pCall) {
   748 			return pContext->ThrowNativeError("\"CreateEntityByName\" wrapper failed to initialize");
   749 		}
   750 	}
   751 
   752 	CBaseEntity *pEntity = NULL;
   753 	START_CALL();
   754 	DECODE_VALVE_PARAM(1, vparams, 0);
   755 	DECODE_VALVE_PARAM(2, vparams, 1);
   756 	FINISH_CALL_SIMPLE(&pEntity);
   757 
   758 	if (pEntity == NULL)
   759 	{
   760 		return -1;
   761 	}
   762 
   763 	edict_t *pEdict = gameents->BaseEntityToEdict(pEntity);
   764 	if (!pEdict)
   765 	{
   766 		return -1;
   767 	}
   768 
   769 	return IndexOfEdict(pEdict);
   770 }
   771 #endif
   772 
   773 static cell_t DispatchSpawn(IPluginContext *pContext, const cell_t *params)
   774 {
   775 	static ValveCall *pCall = NULL;
   776 	if (!pCall)
   777 	{
   778 		ValvePassInfo pass[2];
   779 		InitPass(pass[0], Valve_CBaseEntity, PassType_Basic, PASSFLAG_BYVAL);
   780 		InitPass(pass[1], Valve_POD, PassType_Basic, PASSFLAG_BYVAL);
   781 		if (!CreateBaseCall("DispatchSpawn", ValveCall_Static, &pass[1], pass, 1, &pCall))
   782 		{
   783 			return pContext->ThrowNativeError("\"DispatchSpawn\" not supported by this mod");
   784 		} else if (!pCall) {
   785 			return pContext->ThrowNativeError("\"DispatchSpawn\" wrapper failed to initialize");
   786 		}
   787 	}
   788 
   789 	int ret;
   790 	START_CALL();
   791 	DECODE_VALVE_PARAM(1, vparams, 0);
   792 	FINISH_CALL_SIMPLE(&ret);
   793 
   794 	return (ret == -1) ? 0 : 1;
   795 }
   796 
   797 static cell_t DispatchKeyValue(IPluginContext *pContext, const cell_t *params)
   798 {
   799 	static ValveCall *pCall = NULL;
   800 	if (!pCall)
   801 	{
   802 		ValvePassInfo pass[3];
   803 		InitPass(pass[0], Valve_String, PassType_Basic, PASSFLAG_BYVAL);
   804 		InitPass(pass[1], Valve_String, PassType_Basic, PASSFLAG_BYVAL);
   805 		InitPass(pass[2], Valve_Bool, PassType_Basic, PASSFLAG_BYVAL);
   806 		if (!CreateBaseCall("DispatchKeyValue", ValveCall_Entity, &pass[2], pass, 2, &pCall))
   807 		{
   808 			return pContext->ThrowNativeError("\"DispatchKeyValue\" not supported by this mod");
   809 		} else if (!pCall) {
   810 			return pContext->ThrowNativeError("\"DispatchKeyValue\" wrapper failed to initialize");
   811 		}
   812 	}
   813 
   814 	bool ret;
   815 	START_CALL();
   816 	DECODE_VALVE_PARAM(1, thisinfo, 0);
   817 	DECODE_VALVE_PARAM(2, vparams, 0);
   818 	DECODE_VALVE_PARAM(3, vparams, 1);
   819 	FINISH_CALL_SIMPLE(&ret);
   820 
   821 	return ret ? 1 : 0;
   822 }
   823 
   824 static cell_t DispatchKeyValueFloat(IPluginContext *pContext, const cell_t *params)
   825 {
   826 	static ValveCall *pCall = NULL;
   827 	if (!pCall)
   828 	{
   829 		ValvePassInfo pass[3];
   830 		InitPass(pass[0], Valve_String, PassType_Basic, PASSFLAG_BYVAL);
   831 		InitPass(pass[1], Valve_Float, PassType_Float, PASSFLAG_BYVAL);
   832 		InitPass(pass[2], Valve_Bool, PassType_Basic, PASSFLAG_BYVAL);
   833 		if (!CreateBaseCall("DispatchKeyValueFloat", ValveCall_Entity, &pass[2], pass, 2, &pCall))
   834 		{
   835 			return pContext->ThrowNativeError("\"DispatchKeyValueFloat\" not supported by this mod");
   836 		} else if (!pCall) {
   837 			return pContext->ThrowNativeError("\"DispatchKeyValueFloat\" wrapper failed to initialize");
   838 		}
   839 	}
   840 
   841 	bool ret;
   842 	START_CALL();
   843 	DECODE_VALVE_PARAM(1, thisinfo, 0);
   844 	DECODE_VALVE_PARAM(2, vparams, 0);
   845 	DECODE_VALVE_PARAM(3, vparams, 1);
   846 	FINISH_CALL_SIMPLE(&ret);
   847 
   848 	return ret ? 1 : 0;
   849 }
   850 
   851 static cell_t DispatchKeyValueVector(IPluginContext *pContext, const cell_t *params)
   852 {
   853 	static ValveCall *pCall = NULL;
   854 	if (!pCall)
   855 	{
   856 		ValvePassInfo pass[3];
   857 		InitPass(pass[0], Valve_String, PassType_Basic, PASSFLAG_BYVAL);
   858 #if SOURCE_ENGINE >= SE_ORANGEBOX
   859 		InitPass(pass[1], Valve_Vector, PassType_Basic, PASSFLAG_BYVAL);
   860 #else
   861 		InitPass(pass[1], Valve_Vector, PassType_Object, PASSFLAG_BYVAL|PASSFLAG_OCTOR|PASSFLAG_OASSIGNOP);
   862 #endif
   863 		InitPass(pass[2], Valve_Bool, PassType_Basic, PASSFLAG_BYVAL);
   864 		if (!CreateBaseCall("DispatchKeyValueVector", ValveCall_Entity, &pass[2], pass, 2, &pCall))
   865 		{
   866 			return pContext->ThrowNativeError("\"DispatchKeyValueVector\" not supported by this mod");
   867 		} else if (!pCall) {
   868 			return pContext->ThrowNativeError("\"DispatchKeyValueVector\" wrapper failed to initialize");
   869 		}
   870 	}
   871 
   872 	bool ret;
   873 	START_CALL();
   874 	DECODE_VALVE_PARAM(1, thisinfo, 0);
   875 	DECODE_VALVE_PARAM(2, vparams, 0);
   876 	DECODE_VALVE_PARAM(3, vparams, 1);
   877 	FINISH_CALL_SIMPLE(&ret);
   878 
   879 	return ret ? 1 : 0;
   880 }
   881 
   882 static cell_t sm_GetClientAimTarget(IPluginContext *pContext, const cell_t *params)
   883 {
   884 	int client = params[1];
   885 	IGamePlayer *pPlayer = playerhelpers->GetGamePlayer(client);
   886 
   887 	if (!pPlayer)
   888 	{
   889 		return pContext->ThrowNativeError("Invalid client index %d", client);
   890 	}
   891 	else if (!pPlayer->IsInGame())
   892 	{
   893 		return pContext->ThrowNativeError("Client %d is not in game", client);
   894 	}
   895 
   896 	return GetClientAimTarget(pPlayer->GetEdict(), params[2] ? true : false);
   897 }
   898 
   899 static cell_t sm_SetEntityModel(IPluginContext *pContext, const cell_t *params)
   900 {
   901 	static ValveCall *pCall = NULL;
   902 	if (!pCall)
   903 	{
   904 		ValvePassInfo pass[1];
   905 		InitPass(pass[0], Valve_String, PassType_Basic, PASSFLAG_BYVAL);
   906 		if (!CreateBaseCall("SetEntityModel", ValveCall_Entity, NULL, pass, 1, &pCall))
   907 		{
   908 			return pContext->ThrowNativeError("\"SetEntityModel\" not supported by this mod");
   909 		} else if (!pCall) {
   910 			return pContext->ThrowNativeError("\"SetEntityModel\" wrapper failed to initialize");
   911 		}
   912 	}
   913 
   914 	START_CALL();
   915 	DECODE_VALVE_PARAM(1, thisinfo, 0);
   916 	DECODE_VALVE_PARAM(2, vparams, 0);
   917 	FINISH_CALL_SIMPLE(NULL);
   918 
   919 	return 1;
   920 }
   921 
   922 static cell_t GetPlayerDecalFile(IPluginContext *pContext, const cell_t *params)
   923 {
   924 	IGamePlayer *player = playerhelpers->GetGamePlayer(params[1]);
   925 	if (player == NULL)
   926 	{
   927 		return pContext->ThrowNativeError("Invalid client index %d", params[1]);
   928 	}
   929 	if (!player->IsInGame())
   930 	{
   931 		return pContext->ThrowNativeError("Client %d is not in game", params[1]);
   932 	}
   933 
   934 	player_info_t info;
   935 	char *buffer;
   936 
   937 	if (!GetPlayerInfo(params[1], &info) || !info.customFiles[0])
   938 	{
   939 		return 0;
   940 	}
   941 
   942 	pContext->LocalToString(params[2], &buffer);
   943 	Q_binarytohex((byte *)&info.customFiles[0], sizeof(info.customFiles[0]), buffer, params[3]);
   944 
   945 	return 1;
   946 }
   947 
   948 static cell_t GetServerNetStats(IPluginContext *pContext, const cell_t *params)
   949 {
   950 	if (iserver == NULL)
   951 	{
   952 		return pContext->ThrowNativeError("IServer interface not supported, file a bug report.");
   953 	}
   954 
   955 	float in, out;
   956 	cell_t *pIn, *pOut;
   957 
   958 	pContext->LocalToPhysAddr(params[1], &pIn);
   959 	pContext->LocalToPhysAddr(params[2], &pOut);
   960 	iserver->GetNetStats(in, out);
   961 
   962 	*pIn = sp_ftoc(in);
   963 	*pOut = sp_ftoc(out);
   964 
   965 	return 1;
   966 }
   967 
   968 static cell_t WeaponEquip(IPluginContext *pContext, const cell_t *params)
   969 {
   970 	static ValveCall *pCall = NULL;
   971 	if (!pCall)
   972 	{
   973 		ValvePassInfo pass[1];
   974 		InitPass(pass[0], Valve_CBaseEntity, PassType_Basic, PASSFLAG_BYVAL);
   975 		if (!CreateBaseCall("WeaponEquip", ValveCall_Player, NULL, pass, 1, &pCall))
   976 		{
   977 			return pContext->ThrowNativeError("\"WeaponEquip\" not supported by this mod");
   978 		} else if (!pCall) {
   979 			return pContext->ThrowNativeError("\"WeaponEquip\" wrapper failed to initialize");
   980 		}
   981 	}
   982 
   983 	START_CALL();
   984 	DECODE_VALVE_PARAM(1, thisinfo, 0);
   985 	DECODE_VALVE_PARAM(2, vparams, 0);
   986 	FINISH_CALL_SIMPLE(NULL);
   987 
   988 	return 1;
   989 }
   990 
   991 static cell_t ActivateEntity(IPluginContext *pContext, const cell_t *params)
   992 {
   993 	static ValveCall *pCall = NULL;
   994 	if (!pCall)
   995 	{
   996 		if (!CreateBaseCall("Activate", ValveCall_Entity, NULL, NULL, 0, &pCall))
   997 		{
   998 			return pContext->ThrowNativeError("\"Activate\" not supported by this mod");
   999 		}
  1000 		else if (!pCall)
  1001 		{
  1002 			return pContext->ThrowNativeError("\"Activate\" wrapper failed to initialize");
  1003 		}
  1004 	}
  1005 
  1006 	START_CALL();
  1007 	DECODE_VALVE_PARAM(1, thisinfo, 0);
  1008 	FINISH_CALL_SIMPLE(NULL);
  1009 
  1010 	return 1;
  1011 }
  1012 
  1013 static cell_t SetClientInfo(IPluginContext *pContext, const cell_t *params)
  1014 {
  1015 	if (iserver == NULL)
  1016 	{
  1017 		return pContext->ThrowNativeError("IServer interface not supported, file a bug report.");
  1018 	}
  1019 
  1020 	IGamePlayer *player = playerhelpers->GetGamePlayer(params[1]);
  1021 	IClient *pClient = iserver->GetClient(params[1] - 1);
  1022 
  1023 	if (player == NULL || pClient == NULL)
  1024 	{
  1025 		return pContext->ThrowNativeError("Invalid client index %d", params[1]);
  1026 	}
  1027 	if (!player->IsConnected())
  1028 	{
  1029 		return pContext->ThrowNativeError("Client %d is not connected", params[1]);
  1030 	}
  1031 
  1032 	static ValveCall *pCall = NULL;
  1033 	if (!pCall)
  1034 	{
  1035 		ValvePassInfo params[2];
  1036 		InitPass(params[0], Valve_String, PassType_Basic, PASSFLAG_BYVAL);
  1037 		InitPass(params[1], Valve_String, PassType_Basic, PASSFLAG_BYVAL);
  1038 
  1039 		if (!CreateBaseCall("SetUserCvar", ValveCall_Entity, NULL, params, 2, &pCall))
  1040 		{
  1041 			return pContext->ThrowNativeError("\"SetUserCvar\" not supported by this mod");
  1042 		}
  1043 		else if (!pCall)
  1044 		{
  1045 			return pContext->ThrowNativeError("\"SetUserCvar\" wrapper failed to initialize");
  1046 		}
  1047 	}
  1048 
  1049 /* TODO: Use UpdateUserSettings function for all engines */
  1050 #if SOURCE_ENGINE == SE_DARKMESSIAH
  1051 	static ValveCall *pUpdateSettings = NULL;
  1052 	if (!pUpdateSettings)
  1053 	{
  1054 		if (!CreateBaseCall("UpdateUserSettings", ValveCall_Entity, NULL, NULL, 0, &pUpdateSettings))
  1055 		{
  1056 			return pContext->ThrowNativeError("\"SetUserCvar\" not supported by this mod");
  1057 		}
  1058 		else if (!pUpdateSettings)
  1059 		{
  1060 			return pContext->ThrowNativeError("\"SetUserCvar\" wrapper failed to initialize");
  1061 		}
  1062 	}
  1063 #else
  1064 	static int changedOffset = -1;
  1065 
  1066 	if (changedOffset == -1)
  1067 	{	
  1068 		if (!g_pGameConf->GetOffset("InfoChanged", &changedOffset))
  1069 		{
  1070 			return pContext->ThrowNativeError("\"SetUserCvar\" not supported by this mod");
  1071 		}
  1072 	}
  1073 #endif
  1074 
  1075 	unsigned char *CGameClient = (unsigned char *)pClient - 4;
  1076 
  1077 	START_CALL();
  1078 	/* Not really a CBaseEntity* but this works */
  1079 	CBaseEntity **ebuf = (CBaseEntity **)vptr;
  1080 	*ebuf = (CBaseEntity *)CGameClient;
  1081 	DECODE_VALVE_PARAM(2, vparams, 0);
  1082 	DECODE_VALVE_PARAM(3, vparams, 1);
  1083 	FINISH_CALL_SIMPLE(NULL);
  1084 
  1085 #if SOURCE_ENGINE == SE_DARKMESSIAH
  1086 	unsigned char *args = pUpdateSettings->stk_get();
  1087 	*(void **)args = CGameClient;
  1088 	pUpdateSettings->call->Execute(args, NULL);
  1089 	pUpdateSettings->stk_put(args);
  1090 #else
  1091 	uint8_t* changed = (uint8_t *)(CGameClient + changedOffset);
  1092 	*changed = 1;
  1093 #endif
  1094 
  1095 	return 1;
  1096 }
  1097 
  1098 sp_nativeinfo_t g_Natives[] = 
  1099 {
  1100 	{"ExtinguishEntity",		ExtinguishEntity},
  1101 	{"ForcePlayerSuicide",		ForcePlayerSuicide},
  1102 	{"GivePlayerItem",			GiveNamedItem},
  1103 	{"GetPlayerWeaponSlot",		GetPlayerWeaponSlot},
  1104 	{"IgniteEntity",			IgniteEntity},
  1105 	{"RemovePlayerItem",		RemovePlayerItem},
  1106 	{"TeleportEntity",			TeleportEntity},
  1107 	{"SetClientViewEntity",		SetClientViewEntity},
  1108 	{"SetLightStyle",			SetLightStyle},
  1109 	{"SlapPlayer",				SlapPlayer},
  1110 	{"GetClientEyePosition",	GetClientEyePosition},
  1111 	{"GetClientEyeAngles",		GetClientEyeAngles},
  1112 	{"FindEntityByClassname",	FindEntityByClassname},
  1113 	{"CreateEntityByName",		CreateEntityByName},
  1114 	{"DispatchSpawn",			DispatchSpawn},
  1115 	{"DispatchKeyValue",		DispatchKeyValue},
  1116 	{"DispatchKeyValueFloat",	DispatchKeyValueFloat},
  1117 	{"DispatchKeyValueVector",	DispatchKeyValueVector},
  1118 	{"GetClientAimTarget",		sm_GetClientAimTarget},
  1119 	{"SetEntityModel",			sm_SetEntityModel},
  1120 	{"GetPlayerDecalFile",		GetPlayerDecalFile},
  1121 	{"GetServerNetStats",		GetServerNetStats},
  1122 	{"EquipPlayerWeapon",		WeaponEquip},
  1123 	{"ActivateEntity",			ActivateEntity},
  1124 	{"SetClientInfo",			SetClientInfo},
  1125 	{NULL,						NULL},
  1126 };