extensions/sdktools/vnatives.cpp
author Scott Ehlert <ds@alliedmods.net>
Sun Mar 30 07:00:22 2008 +0000 (2008-03-30)
changeset 1952 b7aa2b626807
child 1956 8ae29b9df92c
permissions -rw-r--r--
Spring Cleaning, Part Ichi (1)
Various minor things done to project files
Updated sample extension project file and updated makefile to the new unified version (more changes likely on the way)
Updated regex project file and makefile
     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 
    41 SourceHook::List<ValveCall *> g_RegCalls;
    42 SourceHook::List<ICallWrapper *> g_CallWraps;
    43 
    44 inline void InitPass(ValvePassInfo &info, ValveType vtype, PassType type, unsigned int flags, unsigned int decflags=0)
    45 {
    46 	info.decflags = decflags;
    47 	info.encflags = 0;
    48 	info.flags = flags;
    49 	info.type = type;
    50 	info.vtype = vtype;
    51 }
    52 
    53 #define START_CALL() \
    54 	unsigned char *vptr = pCall->stk_get();
    55 
    56 #define FINISH_CALL_SIMPLE(vret) \
    57 	pCall->call->Execute(vptr, vret); \
    58 	pCall->stk_put(vptr);
    59 
    60 #define ENCODE_VALVE_PARAM(num, which, vnum) \
    61 	if (EncodeValveParam(pContext, \
    62 			params[num], \
    63 			pCall, \
    64 			&pCall->which[vnum], \
    65 			vptr) \
    66 		== Data_Fail) \
    67 	{ \
    68 		return 0; \
    69 	}
    70 
    71 #define DECODE_VALVE_PARAM(num, which, vnum) \
    72 	if (DecodeValveParam(pContext, \
    73 			params[num], \
    74 			pCall, \
    75 			&pCall->which[vnum], \
    76 			vptr) \
    77 		== Data_Fail) \
    78 	{ \
    79 		return 0; \
    80 	}
    81 
    82 bool CreateBaseCall(const char *name,
    83 						  ValveCallType vcalltype,
    84 						  const ValvePassInfo *retinfo,
    85 						  const ValvePassInfo params[],
    86 						  unsigned int numParams,
    87 						  ValveCall **vaddr)
    88 {
    89 	int offset;
    90 	ValveCall *call;
    91 	if (g_pGameConf->GetOffset(name, &offset))
    92 	{
    93 		call = CreateValveVCall(offset, vcalltype, retinfo, params, numParams);
    94 		if (call)
    95 		{
    96 			g_RegCalls.push_back(call);
    97 		}
    98 		*vaddr = call;
    99 		return true;
   100 	} else {
   101 		void *addr;
   102 		if (g_pGameConf->GetMemSig(name, &addr))
   103 		{
   104 			call = CreateValveCall(addr, vcalltype, retinfo, params, numParams);
   105 			if (call)
   106 			{
   107 				g_RegCalls.push_back(call);
   108 			}
   109 			*vaddr = call;
   110 			return true;
   111 		}
   112 	}
   113 	return false;
   114 }
   115 
   116 static cell_t RemovePlayerItem(IPluginContext *pContext, const cell_t *params)
   117 {
   118 	static ValveCall *pCall = NULL;
   119 	if (!pCall)
   120 	{
   121 		ValvePassInfo pass[2];
   122 		InitPass(pass[0], Valve_CBaseEntity, PassType_Basic, PASSFLAG_BYVAL);
   123 		InitPass(pass[1], Valve_Bool, PassType_Basic, PASSFLAG_BYVAL);
   124 		if (!CreateBaseCall("RemovePlayerItem", ValveCall_Player, &pass[1], pass, 1, &pCall))
   125 		{
   126 			return pContext->ThrowNativeError("\"RemovePlayerItem\" not supported by this mod");
   127 		} else if (!pCall) {
   128 			return pContext->ThrowNativeError("\"RemovePlayerItem\" wrapper failed to initialized");
   129 		}
   130 	}
   131 
   132 	bool ret;
   133 	START_CALL();
   134 	DECODE_VALVE_PARAM(1, thisinfo, 0);
   135 	DECODE_VALVE_PARAM(2, vparams, 0);
   136 	FINISH_CALL_SIMPLE(&ret);
   137 	return ret ? 1 : 0;
   138 }
   139 
   140 static cell_t GiveNamedItem(IPluginContext *pContext, const cell_t *params)
   141 {
   142 	static ValveCall *pCall = NULL;
   143 	if (!pCall)
   144 	{
   145 		ValvePassInfo pass[3];
   146 		InitPass(pass[0], Valve_String, PassType_Basic, PASSFLAG_BYVAL);
   147 		InitPass(pass[1], Valve_POD, PassType_Basic, PASSFLAG_BYVAL);
   148 		InitPass(pass[2], Valve_CBaseEntity, PassType_Basic, PASSFLAG_BYVAL);
   149 		if (!CreateBaseCall("GiveNamedItem", ValveCall_Player, &pass[2], pass, 2, &pCall))
   150 		{
   151 			return pContext->ThrowNativeError("\"GiveNamedItem\" not supported by this mod");
   152 		} else if (!pCall) {
   153 			return pContext->ThrowNativeError("\"GiveNamedItem\" wrapper failed to initialized");
   154 		}
   155 	}
   156 
   157 	CBaseEntity *pEntity = NULL;
   158 	START_CALL();
   159 	DECODE_VALVE_PARAM(1, thisinfo, 0);
   160 	DECODE_VALVE_PARAM(2, vparams, 0);
   161 	DECODE_VALVE_PARAM(3, vparams, 1);
   162 	FINISH_CALL_SIMPLE(&pEntity);
   163 
   164 	if (pEntity == NULL)
   165 	{
   166 		return -1;
   167 	}
   168 
   169 	edict_t *pEdict = gameents->BaseEntityToEdict(pEntity);
   170 	if (!pEdict)
   171 	{
   172 		return -1;
   173 	}
   174 
   175 	return engine->IndexOfEdict(pEdict);
   176 }
   177 
   178 static cell_t GetPlayerWeaponSlot(IPluginContext *pContext, const cell_t *params)
   179 {
   180 	static ValveCall *pCall = NULL;
   181 	if (!pCall)
   182 	{
   183 		ValvePassInfo pass[2];
   184 		InitPass(pass[0], Valve_POD, PassType_Basic, PASSFLAG_BYVAL);
   185 		InitPass(pass[1], Valve_CBaseEntity, PassType_Basic, PASSFLAG_BYVAL);
   186 		if (!CreateBaseCall("Weapon_GetSlot", ValveCall_Player, &pass[1], pass, 1, &pCall))
   187 		{
   188 			return pContext->ThrowNativeError("\"Weapon_GetSlot\" not supported by this mod");
   189 		} else if (!pCall) {
   190 			return pContext->ThrowNativeError("\"Weapon_GetSlot\" wrapper failed to initialized");
   191 		}
   192 	}
   193 
   194 	CBaseEntity *pEntity;
   195 	START_CALL();
   196 	DECODE_VALVE_PARAM(1, thisinfo, 0);
   197 	DECODE_VALVE_PARAM(2, vparams, 0);
   198 	FINISH_CALL_SIMPLE(&pEntity);
   199 
   200 	if (pEntity == NULL)
   201 	{
   202 		return -1;
   203 	}
   204 
   205 	edict_t *pEdict = gameents->BaseEntityToEdict(pEntity);
   206 	if (!pEdict)
   207 	{
   208 		return -1;
   209 	}
   210 
   211 	return engine->IndexOfEdict(pEdict);
   212 }
   213 
   214 static cell_t IgniteEntity(IPluginContext *pContext, const cell_t *params)
   215 {
   216 	static ValveCall *pCall = NULL;
   217 	if (!pCall)
   218 	{
   219 		ValvePassInfo pass[4];
   220 		InitPass(pass[0], Valve_Float, PassType_Float, PASSFLAG_BYVAL);
   221 		InitPass(pass[1], Valve_Bool, PassType_Basic, PASSFLAG_BYVAL);
   222 		InitPass(pass[2], Valve_Float, PassType_Float, PASSFLAG_BYVAL);
   223 		InitPass(pass[3], Valve_Bool, PassType_Basic, PASSFLAG_BYVAL);
   224 		if (!CreateBaseCall("Ignite", ValveCall_Entity, NULL, pass, 4, &pCall))
   225 		{
   226 			return pContext->ThrowNativeError("\"Ignite\" not supported by this mod");
   227 		} else if (!pCall) {
   228 			return pContext->ThrowNativeError("\"Ignite\" wrapper failed to initialized");
   229 		}
   230 	}
   231 
   232 	START_CALL();
   233 	DECODE_VALVE_PARAM(1, thisinfo, 0);
   234 	DECODE_VALVE_PARAM(2, vparams, 0);
   235 	DECODE_VALVE_PARAM(3, vparams, 1);
   236 	DECODE_VALVE_PARAM(4, vparams, 2);
   237 	DECODE_VALVE_PARAM(5, vparams, 3);
   238 	FINISH_CALL_SIMPLE(NULL);
   239 
   240 	return 1;
   241 }
   242 
   243 static cell_t ExtinguishEntity(IPluginContext *pContext, const cell_t *params)
   244 {
   245 	static ValveCall *pCall = NULL;
   246 	if (!pCall)
   247 	{
   248 		if (!CreateBaseCall("Extinguish", ValveCall_Entity, NULL, NULL, 0, &pCall))
   249 		{
   250 			return pContext->ThrowNativeError("\"Extinguish\" not supported by this mod");
   251 		} else if (!pCall) {
   252 			return pContext->ThrowNativeError("\"Extinguish\" wrapper failed to initialized");
   253 		}
   254 	}
   255 
   256 	START_CALL();
   257 	DECODE_VALVE_PARAM(1, thisinfo, 0);
   258 	FINISH_CALL_SIMPLE(NULL);
   259 
   260 	return 1;
   261 }
   262 
   263 static cell_t TeleportEntity(IPluginContext *pContext, const cell_t *params)
   264 {
   265 	static ValveCall *pCall = NULL;
   266 	if (!pCall)
   267 	{
   268 		ValvePassInfo pass[3];
   269 		InitPass(pass[0], Valve_Vector, PassType_Basic, PASSFLAG_BYVAL, VDECODE_FLAG_ALLOWNULL);
   270 		InitPass(pass[1], Valve_QAngle, PassType_Basic, PASSFLAG_BYVAL, VDECODE_FLAG_ALLOWNULL);
   271 		InitPass(pass[2], Valve_Vector, PassType_Basic, PASSFLAG_BYVAL, VDECODE_FLAG_ALLOWNULL);
   272 		if (!CreateBaseCall("Teleport", ValveCall_Entity, NULL, pass, 3, &pCall))
   273 		{
   274 			return pContext->ThrowNativeError("\"Teleport\" not supported by this mod");
   275 		} else if (!pCall) {
   276 			return pContext->ThrowNativeError("\"Teleport\" wrapper failed to initialized");
   277 		}
   278 	}
   279 
   280 	START_CALL();
   281 	DECODE_VALVE_PARAM(1, thisinfo, 0);
   282 	DECODE_VALVE_PARAM(2, vparams, 0);
   283 	DECODE_VALVE_PARAM(3, vparams, 1);
   284 	DECODE_VALVE_PARAM(4, vparams, 2);
   285 	FINISH_CALL_SIMPLE(NULL);
   286 
   287 	return 1;
   288 }
   289 
   290 #if defined ORANGEBOX_BUILD
   291 /* :TODO: This is Team Fortress 2 specific */
   292 static cell_t ForcePlayerSuicide(IPluginContext *pContext, const cell_t *params)
   293 {
   294 	static ValveCall *pCall = NULL;
   295 	if (!pCall)
   296 	{
   297 		ValvePassInfo pass[2];
   298 		InitPass(pass[0], Valve_Bool, PassType_Basic, PASSFLAG_BYVAL);
   299 		InitPass(pass[1], Valve_Bool, PassType_Basic, PASSFLAG_BYVAL);
   300 		if (!CreateBaseCall("CommitSuicide", ValveCall_Player, NULL, pass, 2, &pCall))
   301 		{
   302 			return pContext->ThrowNativeError("\"CommitSuicide\" not supported by this mod");
   303 		}
   304 		else if (!pCall)
   305 		{
   306 			return pContext->ThrowNativeError("\"CommitSuicide\" wrapper failed to initialized");
   307 		}
   308 	}
   309 
   310 	START_CALL();
   311 	DECODE_VALVE_PARAM(1, thisinfo, 0);
   312 	*(bool *)(vptr + 4) = false;
   313 	*(bool *)(vptr + 5) = false;
   314 	FINISH_CALL_SIMPLE(NULL);
   315 
   316 	return 1;
   317 }
   318 #else
   319 static cell_t ForcePlayerSuicide(IPluginContext *pContext, const cell_t *params)
   320 {
   321 	static ValveCall *pCall = NULL;
   322 	if (!pCall)
   323 	{
   324 		if (!CreateBaseCall("CommitSuicide", ValveCall_Player, NULL, NULL, 0, &pCall))
   325 		{
   326 			return pContext->ThrowNativeError("\"CommitSuicide\" not supported by this mod");
   327 		} else if (!pCall) {
   328 			return pContext->ThrowNativeError("\"CommitSuicide\" wrapper failed to initialized");
   329 		}
   330 	}
   331 
   332 	START_CALL();
   333 	DECODE_VALVE_PARAM(1, thisinfo, 0);
   334 	FINISH_CALL_SIMPLE(NULL);
   335 
   336 	return 1;
   337 }
   338 #endif
   339 
   340 static cell_t SetClientViewEntity(IPluginContext *pContext, const cell_t *params)
   341 {
   342 	IGamePlayer *player = playerhelpers->GetGamePlayer(params[1]);
   343 	if (player == NULL)
   344 	{
   345 		return pContext->ThrowNativeError("Invalid client index %d", params[1]);
   346 	}
   347 	if (!player->IsInGame())
   348 	{
   349 		return pContext->ThrowNativeError("Client %d is not in game", params[1]);
   350 	}
   351 
   352 	edict_t *pEdict = engine->PEntityOfEntIndex(params[2]);
   353 	if (!pEdict || pEdict->IsFree())
   354 	{
   355 		return pContext->ThrowNativeError("Entity %d is not valid", params[2]);
   356 	}
   357 
   358 	engine->SetView(player->GetEdict(), pEdict);
   359 
   360 	return 1;
   361 }
   362 
   363 static SourceHook::String *g_lightstyle[MAX_LIGHTSTYLES] = {NULL};
   364 static cell_t SetLightStyle(IPluginContext *pContext, const cell_t *params)
   365 {
   366 	int style = params[1];
   367 	if (style >= MAX_LIGHTSTYLES)
   368 	{
   369 		return pContext->ThrowNativeError("Light style %d is invalid (range: 0-%d)", style, MAX_LIGHTSTYLES - 1);
   370 	}
   371 
   372 	if (g_lightstyle[style] == NULL)
   373 	{
   374 		/* We allocate and never free this because the Engine wants to hold onto it :\
   375 		 * in theory we could hook light style and know whether we're supposed to free 
   376 		 * this or not on shutdown, but for ~4K of memory MAX, it doesn't seem worth it yet.
   377 		 * So, it's a :TODO:!
   378 		 */
   379 		g_lightstyle[style] = new SourceHook::String();
   380 	}
   381 
   382 	char *str;
   383 	pContext->LocalToString(params[2], &str);
   384 
   385 	g_lightstyle[style]->assign(str);
   386 
   387 	engine->LightStyle(style, g_lightstyle[style]->c_str());
   388 
   389 	return 1;
   390 }
   391 
   392 static cell_t SlapPlayer(IPluginContext *pContext, const cell_t *params)
   393 {
   394 	static bool s_slap_supported = false;
   395 	static bool s_slap_setup = false;
   396 	static ICallWrapper *s_teleport = NULL;
   397 	static int s_health_offs = 0;
   398 	static int s_sound_count = 0;
   399 	static int s_frag_offs = 0;
   400 
   401 	if (!s_slap_setup)
   402 	{
   403 		int tries = 0;
   404 
   405 		s_slap_setup = true;
   406 
   407 		if (IsTeleportSupported())
   408 		{
   409 			tries++;
   410 		}
   411 		if (IsGetVelocitySupported())
   412 		{
   413 			tries++;
   414 		}
   415 
   416 		/* Setup health */
   417 		if (g_pGameConf->GetOffset("m_iHealth", &s_health_offs) && s_health_offs)
   418 		{
   419 			tries++;
   420 		}
   421 
   422 		if (tries == 3)
   423 		{
   424 			s_slap_supported = true;
   425 
   426 			const char *key;
   427 			if ((key = g_pGameConf->GetKeyValue("SlapSoundCount")) != NULL)
   428 			{
   429 				s_sound_count = atoi(key);
   430 			}
   431 		}
   432 	}
   433 
   434 	if (!s_slap_supported)
   435 	{
   436 		return pContext->ThrowNativeError("This function is not supported on this mod");
   437 	}
   438 
   439 	/* First check if the client is valid */
   440 	int client = params[1];
   441 	IGamePlayer *player = playerhelpers->GetGamePlayer(client);
   442 	if (!player)
   443 	{
   444 		return pContext->ThrowNativeError("Client %d is not valid", client);
   445 	} else if (!player->IsInGame()) {
   446 		return pContext->ThrowNativeError("Client %d is not in game", client);
   447 	}
   448 
   449 	edict_t *pEdict = player->GetEdict();
   450 	CBaseEntity *pEntity = pEdict->GetUnknown()->GetBaseEntity();
   451 
   452 	/* See if we should be taking away health */
   453 	bool should_slay = false;
   454 	if (params[2])
   455 	{
   456 		int *health = (int *)((char *)pEntity + s_health_offs);
   457 		if (*health - params[2] <= 0)
   458 		{
   459 			*health = 1;
   460 			should_slay = true;
   461 		} else {
   462 			*health -= params[2];
   463 		}
   464 	}
   465 
   466 	/* Teleport in a random direction - thank you, Mani!*/
   467 	Vector velocity;
   468 	GetVelocity(pEntity, &velocity, NULL);
   469 	velocity.x += ((rand() % 180) + 50) * (((rand() % 2) == 1) ?  -1 : 1);
   470 	velocity.y += ((rand() % 180) + 50) * (((rand() % 2) == 1) ?  -1 : 1);
   471 	velocity.z += rand() % 200 + 100;
   472 	Teleport(pEntity, NULL, NULL, &velocity);
   473 
   474 	/* Play a random sound */
   475 	if (params[3] && s_sound_count > 0)
   476 	{
   477 		char name[48];
   478 		const char *sound_name;
   479 		cell_t player_list[256], total_players = 0;
   480 		int maxClients = playerhelpers->GetMaxClients();
   481 
   482 		int r = (rand() % s_sound_count) + 1;
   483 		snprintf(name, sizeof(name), "SlapSound%d", r);
   484 
   485 		if ((sound_name = g_pGameConf->GetKeyValue(name)) != NULL)
   486 		{
   487 			IGamePlayer *other;
   488 			for (int i=1; i<=maxClients; i++)
   489 			{
   490 				other = playerhelpers->GetGamePlayer(i);
   491 				if (other->IsInGame())
   492 				{
   493 					player_list[total_players++] = i;
   494 				}
   495 			}
   496 
   497 			const Vector & pos = pEdict->GetCollideable()->GetCollisionOrigin();
   498 			CellRecipientFilter rf;
   499 			rf.SetToReliable(true);
   500 			rf.Initialize(player_list, total_players);
   501 			engsound->EmitSound(rf, client, CHAN_AUTO, sound_name, VOL_NORM, ATTN_NORM, 0, PITCH_NORM, &pos);
   502 		}
   503 	}
   504 
   505 	if (!s_frag_offs)
   506 	{
   507 		const char *frag_prop = g_pGameConf->GetKeyValue("m_iFrags");
   508 		if (frag_prop)
   509 		{
   510 			datamap_t *pMap = gamehelpers->GetDataMap(pEntity);
   511 			typedescription_t *pType = gamehelpers->FindInDataMap(pMap, frag_prop);
   512 			if (pType != NULL)
   513 			{
   514 				s_frag_offs = pType->fieldOffset[TD_OFFSET_NORMAL];
   515 			}
   516 		}
   517 		if (!s_frag_offs)
   518 		{
   519 			s_frag_offs = -1;
   520 		}
   521 	}
   522 
   523 	int old_frags = 0;
   524 	if (s_frag_offs > 0)
   525 	{
   526 		old_frags = *(int *)((char *)pEntity + s_frag_offs);
   527 	}
   528 	
   529 	/* Force suicide */
   530 	if (should_slay)
   531 	{
   532 		pluginhelpers->ClientCommand(pEdict, "kill\n");
   533 	}
   534 
   535 	if (s_frag_offs > 0)
   536 	{
   537 		*(int *)((char *)pEntity + s_frag_offs) = old_frags;
   538 	}
   539 
   540 	return 1;
   541 }
   542 
   543 static cell_t GetClientEyePosition(IPluginContext *pContext, const cell_t *params)
   544 {
   545 	IGamePlayer *player = playerhelpers->GetGamePlayer(params[1]);
   546 	if (player == NULL)
   547 	{
   548 		return pContext->ThrowNativeError("Invalid client index %d", params[1]);
   549 	}
   550 	if (!player->IsInGame())
   551 	{
   552 		return pContext->ThrowNativeError("Client %d is not in game", params[1]);
   553 	}
   554 
   555 	Vector pos;
   556 	serverClients->ClientEarPosition(player->GetEdict(), &pos);
   557 
   558 	cell_t *addr;
   559 	pContext->LocalToPhysAddr(params[2], &addr);
   560 	addr[0] = sp_ftoc(pos.x);
   561 	addr[1] = sp_ftoc(pos.y);
   562 	addr[2] = sp_ftoc(pos.z);
   563 
   564 	return 1;
   565 }
   566 
   567 static cell_t GetClientEyeAngles(IPluginContext *pContext, const cell_t *params)
   568 {
   569 	int client = params[1];
   570 	IGamePlayer *pPlayer = playerhelpers->GetGamePlayer(client);
   571 
   572 	if (!pPlayer)
   573 	{
   574 		return pContext->ThrowNativeError("Invalid client index %d", client);
   575 	}
   576 	else if (!pPlayer->IsInGame())
   577 	{
   578 		return pContext->ThrowNativeError("Client %d is not in game", client);
   579 	}
   580 
   581 	edict_t *pEdict = pPlayer->GetEdict();
   582 	CBaseEntity *pEntity = pEdict->GetUnknown() ? pEdict->GetUnknown()->GetBaseEntity() : NULL;
   583 
   584 	/* We always set the angles for backwards compatibility -- 
   585 	 * The original function had no return value.
   586 	 */
   587 	QAngle angles;
   588 	bool got_angles = false;
   589 
   590 	if (pEntity != NULL)
   591 	{
   592 		got_angles = GetEyeAngles(pEntity, &angles);
   593 	}
   594 	
   595 	cell_t *addr;
   596 	pContext->LocalToPhysAddr(params[2], &addr);
   597 	addr[0] = sp_ftoc(angles.x);
   598 	addr[1] = sp_ftoc(angles.y);
   599 	addr[2] = sp_ftoc(angles.z);
   600 
   601 	return got_angles ? 1 : 0;
   602 }
   603 
   604 static cell_t FindEntityByClassname(IPluginContext *pContext, const cell_t *params)
   605 {
   606 	static ValveCall *pCall = NULL;
   607 	if (!pCall)
   608 	{
   609 		ValvePassInfo pass[3];
   610 		InitPass(pass[0], Valve_CBaseEntity, PassType_Basic, PASSFLAG_BYVAL, VDECODE_FLAG_ALLOWNULL|VDECODE_FLAG_ALLOWWORLD);
   611 		InitPass(pass[1], Valve_String, PassType_Basic, PASSFLAG_BYVAL);
   612 		InitPass(pass[2], Valve_CBaseEntity, PassType_Basic, PASSFLAG_BYVAL);
   613 		if (!CreateBaseCall("FindEntityByClassname", ValveCall_EntityList, &pass[2], pass, 2, &pCall))
   614 		{
   615 			return pContext->ThrowNativeError("\"FindEntityByClassname\" not supported by this mod");
   616 		} else if (!pCall) {
   617 			return pContext->ThrowNativeError("\"FindEntityByClassname\" wrapper failed to initialized");
   618 		}
   619 	}
   620 
   621 	CBaseEntity *pEntity;
   622 	START_CALL();
   623 	*(void **)vptr = g_EntList; 
   624 	DECODE_VALVE_PARAM(1, vparams, 0);
   625 	DECODE_VALVE_PARAM(2, vparams, 1);
   626 	FINISH_CALL_SIMPLE(&pEntity);
   627 
   628 	if (pEntity == NULL)
   629 	{
   630 		return -1;
   631 	}
   632 
   633 	edict_t *pEdict = gameents->BaseEntityToEdict(pEntity);
   634 	if (!pEdict)
   635 	{
   636 		return -1;
   637 	}
   638 
   639 	return engine->IndexOfEdict(pEdict);
   640 }
   641 
   642 static cell_t CreateEntityByName(IPluginContext *pContext, const cell_t *params)
   643 {
   644 	static ValveCall *pCall = NULL;
   645 	if (!pCall)
   646 	{
   647 		ValvePassInfo pass[3];
   648 		InitPass(pass[0], Valve_String, PassType_Basic, PASSFLAG_BYVAL);
   649 		InitPass(pass[1], Valve_POD, PassType_Basic, PASSFLAG_BYVAL);
   650 		InitPass(pass[2], Valve_CBaseEntity, PassType_Basic, PASSFLAG_BYVAL);
   651 		if (!CreateBaseCall("CreateEntityByName", ValveCall_Static, &pass[2], pass, 2, &pCall))
   652 		{
   653 			return pContext->ThrowNativeError("\"CreateEntityByName\" not supported by this mod");
   654 		} else if (!pCall) {
   655 			return pContext->ThrowNativeError("\"CreateEntityByName\" wrapper failed to initialized");
   656 		}
   657 	}
   658 
   659 	CBaseEntity *pEntity = NULL;
   660 	START_CALL();
   661 	DECODE_VALVE_PARAM(1, vparams, 0);
   662 	DECODE_VALVE_PARAM(2, vparams, 1);
   663 	FINISH_CALL_SIMPLE(&pEntity);
   664 
   665 	if (pEntity == NULL)
   666 	{
   667 		return -1;
   668 	}
   669 
   670 	edict_t *pEdict = gameents->BaseEntityToEdict(pEntity);
   671 	if (!pEdict)
   672 	{
   673 		return -1;
   674 	}
   675 
   676 	return engine->IndexOfEdict(pEdict);
   677 }
   678 
   679 static cell_t DispatchSpawn(IPluginContext *pContext, const cell_t *params)
   680 {
   681 	static ValveCall *pCall = NULL;
   682 	if (!pCall)
   683 	{
   684 		ValvePassInfo pass[2];
   685 		InitPass(pass[0], Valve_CBaseEntity, PassType_Basic, PASSFLAG_BYVAL);
   686 		InitPass(pass[1], Valve_POD, PassType_Basic, PASSFLAG_BYVAL);
   687 		if (!CreateBaseCall("DispatchSpawn", ValveCall_Static, &pass[1], pass, 1, &pCall))
   688 		{
   689 			return pContext->ThrowNativeError("\"DispatchSpawn\" not supported by this mod");
   690 		} else if (!pCall) {
   691 			return pContext->ThrowNativeError("\"DispatchSpawn\" wrapper failed to initialized");
   692 		}
   693 	}
   694 
   695 	int ret;
   696 	START_CALL();
   697 	DECODE_VALVE_PARAM(1, vparams, 0);
   698 	FINISH_CALL_SIMPLE(&ret);
   699 
   700 	return (ret == -1) ? 0 : 1;
   701 }
   702 
   703 static cell_t DispatchKeyValue(IPluginContext *pContext, const cell_t *params)
   704 {
   705 	static ValveCall *pCall = NULL;
   706 	if (!pCall)
   707 	{
   708 		ValvePassInfo pass[3];
   709 		InitPass(pass[0], Valve_String, PassType_Basic, PASSFLAG_BYVAL);
   710 		InitPass(pass[1], Valve_String, PassType_Basic, PASSFLAG_BYVAL);
   711 		InitPass(pass[2], Valve_Bool, PassType_Basic, PASSFLAG_BYVAL);
   712 		if (!CreateBaseCall("DispatchKeyValue", ValveCall_Entity, &pass[2], pass, 2, &pCall))
   713 		{
   714 			return pContext->ThrowNativeError("\"DispatchKeyValue\" not supported by this mod");
   715 		} else if (!pCall) {
   716 			return pContext->ThrowNativeError("\"DispatchKeyValue\" wrapper failed to initialized");
   717 		}
   718 	}
   719 
   720 	bool ret;
   721 	START_CALL();
   722 	DECODE_VALVE_PARAM(1, thisinfo, 0);
   723 	DECODE_VALVE_PARAM(2, vparams, 0);
   724 	DECODE_VALVE_PARAM(3, vparams, 1);
   725 	FINISH_CALL_SIMPLE(&ret);
   726 
   727 	return (ret) ? 1 : 0;
   728 }
   729 
   730 static cell_t DispatchKeyValueFloat(IPluginContext *pContext, const cell_t *params)
   731 {
   732 	static ValveCall *pCall = NULL;
   733 	if (!pCall)
   734 	{
   735 		ValvePassInfo pass[3];
   736 		InitPass(pass[0], Valve_String, PassType_Basic, PASSFLAG_BYVAL);
   737 		InitPass(pass[1], Valve_Float, PassType_Float, PASSFLAG_BYVAL);
   738 		InitPass(pass[2], Valve_Bool, PassType_Basic, PASSFLAG_BYVAL);
   739 		if (!CreateBaseCall("DispatchKeyValueFloat", ValveCall_Entity, &pass[2], pass, 2, &pCall))
   740 		{
   741 			return pContext->ThrowNativeError("\"DispatchKeyValueFloat\" not supported by this mod");
   742 		} else if (!pCall) {
   743 			return pContext->ThrowNativeError("\"DispatchKeyValueFloat\" wrapper failed to initialized");
   744 		}
   745 	}
   746 
   747 	bool ret;
   748 	START_CALL();
   749 	DECODE_VALVE_PARAM(1, thisinfo, 0);
   750 	DECODE_VALVE_PARAM(2, vparams, 0);
   751 	DECODE_VALVE_PARAM(3, vparams, 1);
   752 	FINISH_CALL_SIMPLE(&ret);
   753 
   754 	return (ret) ? 1 : 0;
   755 }
   756 
   757 static cell_t DispatchKeyValueVector(IPluginContext *pContext, const cell_t *params)
   758 {
   759 	static ValveCall *pCall = NULL;
   760 	if (!pCall)
   761 	{
   762 		ValvePassInfo pass[3];
   763 		InitPass(pass[0], Valve_String, PassType_Basic, PASSFLAG_BYVAL);
   764 #if defined ORANGEBOX_BUILD
   765 		InitPass(pass[1], Valve_Vector, PassType_Basic, PASSFLAG_BYVAL);
   766 #else
   767 		InitPass(pass[1], Valve_Vector, PassType_Object, PASSFLAG_BYVAL|PASSFLAG_OCTOR|PASSFLAG_OASSIGNOP);
   768 #endif
   769 		InitPass(pass[2], Valve_Bool, PassType_Basic, PASSFLAG_BYVAL);
   770 		if (!CreateBaseCall("DispatchKeyValueVector", ValveCall_Entity, &pass[2], pass, 2, &pCall))
   771 		{
   772 			return pContext->ThrowNativeError("\"DispatchKeyValueVector\" not supported by this mod");
   773 		} else if (!pCall) {
   774 			return pContext->ThrowNativeError("\"DispatchKeyValueVector\" wrapper failed to initialized");
   775 		}
   776 	}
   777 
   778 	bool ret;
   779 	START_CALL();
   780 	DECODE_VALVE_PARAM(1, thisinfo, 0);
   781 	DECODE_VALVE_PARAM(2, vparams, 0);
   782 	DECODE_VALVE_PARAM(3, vparams, 1);
   783 	FINISH_CALL_SIMPLE(&ret);
   784 
   785 	return (ret) ? 1 : 0;
   786 }
   787 
   788 static cell_t sm_GetClientAimTarget(IPluginContext *pContext, const cell_t *params)
   789 {
   790 	int client = params[1];
   791 	IGamePlayer *pPlayer = playerhelpers->GetGamePlayer(client);
   792 
   793 	if (!pPlayer)
   794 	{
   795 		return pContext->ThrowNativeError("Invalid client index %d", client);
   796 	}
   797 	else if (!pPlayer->IsInGame())
   798 	{
   799 		return pContext->ThrowNativeError("Client %d is not in game", client);
   800 	}
   801 
   802 	return GetClientAimTarget(pPlayer->GetEdict(), params[2] ? true : false);
   803 }
   804 
   805 static cell_t sm_SetEntityModel(IPluginContext *pContext, const cell_t *params)
   806 {
   807 	static ValveCall *pCall = NULL;
   808 	if (!pCall)
   809 	{
   810 		ValvePassInfo pass[1];
   811 		InitPass(pass[0], Valve_String, PassType_Basic, PASSFLAG_BYVAL);
   812 		if (!CreateBaseCall("SetEntityModel", ValveCall_Entity, NULL, pass, 1, &pCall))
   813 		{
   814 			return pContext->ThrowNativeError("\"SetEntityModel\" not supported by this mod");
   815 		} else if (!pCall) {
   816 			return pContext->ThrowNativeError("\"SetEntityModel\" wrapper failed to initialized");
   817 		}
   818 	}
   819 
   820 	START_CALL();
   821 	DECODE_VALVE_PARAM(1, thisinfo, 0);
   822 	DECODE_VALVE_PARAM(2, vparams, 0);
   823 	FINISH_CALL_SIMPLE(NULL);
   824 
   825 	return 1;
   826 }
   827 
   828 static cell_t GetPlayerDecalFile(IPluginContext *pContext, const cell_t *params)
   829 {
   830 	IGamePlayer *player = playerhelpers->GetGamePlayer(params[1]);
   831 	if (player == NULL)
   832 	{
   833 		return pContext->ThrowNativeError("Invalid client index %d", params[1]);
   834 	}
   835 	if (!player->IsInGame())
   836 	{
   837 		return pContext->ThrowNativeError("Client %d is not in game", params[1]);
   838 	}
   839 
   840 	player_info_t info;
   841 	char *buffer;
   842 
   843 	if (!GetPlayerInfo(params[1], &info) || !info.customFiles[0])
   844 	{
   845 		return 0;
   846 	}
   847 
   848 	pContext->LocalToString(params[2], &buffer);
   849 	Q_binarytohex((byte *)&info.customFiles[0], sizeof(info.customFiles[0]), buffer, params[3]);
   850 
   851 	return 1;
   852 }
   853 
   854 static cell_t GetServerNetStats(IPluginContext *pContext, const cell_t *params)
   855 {
   856 	if (iserver == NULL)
   857 	{
   858 		return pContext->ThrowNativeError("IServer interface not supported, file a bug report.");
   859 	}
   860 
   861 	float in, out;
   862 	cell_t *pIn, *pOut;
   863 
   864 	pContext->LocalToPhysAddr(params[1], &pIn);
   865 	pContext->LocalToPhysAddr(params[2], &pOut);
   866 	iserver->GetNetStats(in, out);
   867 
   868 	*pIn = sp_ftoc(in);
   869 	*pOut = sp_ftoc(out);
   870 
   871 	return 1;
   872 }
   873 
   874 sp_nativeinfo_t g_Natives[] = 
   875 {
   876 	{"ExtinguishEntity",		ExtinguishEntity},
   877 	{"ForcePlayerSuicide",		ForcePlayerSuicide},
   878 	{"GivePlayerItem",			GiveNamedItem},
   879 	{"GetPlayerWeaponSlot",		GetPlayerWeaponSlot},
   880 	{"IgniteEntity",			IgniteEntity},
   881 	{"RemovePlayerItem",		RemovePlayerItem},
   882 	{"TeleportEntity",			TeleportEntity},
   883 	{"SetClientViewEntity",		SetClientViewEntity},
   884 	{"SetLightStyle",			SetLightStyle},
   885 	{"SlapPlayer",				SlapPlayer},
   886 	{"GetClientEyePosition",	GetClientEyePosition},
   887 	{"GetClientEyeAngles",		GetClientEyeAngles},
   888 	{"FindEntityByClassname",	FindEntityByClassname},
   889 	{"CreateEntityByName",		CreateEntityByName},
   890 	{"DispatchSpawn",			DispatchSpawn},
   891 	{"DispatchKeyValue",		DispatchKeyValue},
   892 	{"DispatchKeyValueFloat",	DispatchKeyValueFloat},
   893 	{"DispatchKeyValueVector",	DispatchKeyValueVector},
   894 	{"GetClientAimTarget",		sm_GetClientAimTarget},
   895 	{"SetEntityModel",			sm_SetEntityModel},
   896 	{"GetPlayerDecalFile",		GetPlayerDecalFile},
   897 	{"GetServerNetStats",		GetServerNetStats},
   898 	{NULL,						NULL},
   899 };