extensions/sdktools/vnatives.cpp
author Scott Ehlert <ds@alliedmods.net>
Fri Nov 14 09:18:30 2008 -0600 (2008-11-14)
changeset 2400 76b7b4575e21
parent 2289 44045ead61cd
child 2488 f7f1bbcc85e4
permissions -rw-r--r--
Added preliminary support for Left 4 Dead; some things may not yet work.
     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;
   105 		if (g_pGameConf->GetMemSig(name, &addr))
   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 initialized");
   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 initialized");
   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 initialized");
   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 static cell_t IgniteEntity(IPluginContext *pContext, const cell_t *params)
   218 {
   219 	static ValveCall *pCall = NULL;
   220 	if (!pCall)
   221 	{
   222 		ValvePassInfo pass[4];
   223 		InitPass(pass[0], Valve_Float, PassType_Float, PASSFLAG_BYVAL);
   224 		InitPass(pass[1], Valve_Bool, PassType_Basic, PASSFLAG_BYVAL);
   225 		InitPass(pass[2], Valve_Float, PassType_Float, PASSFLAG_BYVAL);
   226 		InitPass(pass[3], Valve_Bool, PassType_Basic, PASSFLAG_BYVAL);
   227 		if (!CreateBaseCall("Ignite", ValveCall_Entity, NULL, pass, 4, &pCall))
   228 		{
   229 			return pContext->ThrowNativeError("\"Ignite\" not supported by this mod");
   230 		} else if (!pCall) {
   231 			return pContext->ThrowNativeError("\"Ignite\" wrapper failed to initialized");
   232 		}
   233 	}
   234 
   235 	START_CALL();
   236 	DECODE_VALVE_PARAM(1, thisinfo, 0);
   237 	DECODE_VALVE_PARAM(2, vparams, 0);
   238 	DECODE_VALVE_PARAM(3, vparams, 1);
   239 	DECODE_VALVE_PARAM(4, vparams, 2);
   240 	DECODE_VALVE_PARAM(5, vparams, 3);
   241 	FINISH_CALL_SIMPLE(NULL);
   242 
   243 	return 1;
   244 }
   245 
   246 static cell_t ExtinguishEntity(IPluginContext *pContext, const cell_t *params)
   247 {
   248 	static ValveCall *pCall = NULL;
   249 	if (!pCall)
   250 	{
   251 		if (!CreateBaseCall("Extinguish", ValveCall_Entity, NULL, NULL, 0, &pCall))
   252 		{
   253 			return pContext->ThrowNativeError("\"Extinguish\" not supported by this mod");
   254 		} else if (!pCall) {
   255 			return pContext->ThrowNativeError("\"Extinguish\" wrapper failed to initialized");
   256 		}
   257 	}
   258 
   259 	START_CALL();
   260 	DECODE_VALVE_PARAM(1, thisinfo, 0);
   261 	FINISH_CALL_SIMPLE(NULL);
   262 
   263 	return 1;
   264 }
   265 
   266 static cell_t TeleportEntity(IPluginContext *pContext, const cell_t *params)
   267 {
   268 	static ValveCall *pCall = NULL;
   269 	if (!pCall)
   270 	{
   271 		ValvePassInfo pass[3];
   272 		InitPass(pass[0], Valve_Vector, PassType_Basic, PASSFLAG_BYVAL, VDECODE_FLAG_ALLOWNULL);
   273 		InitPass(pass[1], Valve_QAngle, PassType_Basic, PASSFLAG_BYVAL, VDECODE_FLAG_ALLOWNULL);
   274 		InitPass(pass[2], Valve_Vector, PassType_Basic, PASSFLAG_BYVAL, VDECODE_FLAG_ALLOWNULL);
   275 		if (!CreateBaseCall("Teleport", ValveCall_Entity, NULL, pass, 3, &pCall))
   276 		{
   277 			return pContext->ThrowNativeError("\"Teleport\" not supported by this mod");
   278 		} else if (!pCall) {
   279 			return pContext->ThrowNativeError("\"Teleport\" wrapper failed to initialized");
   280 		}
   281 	}
   282 
   283 	START_CALL();
   284 	DECODE_VALVE_PARAM(1, thisinfo, 0);
   285 	DECODE_VALVE_PARAM(2, vparams, 0);
   286 	DECODE_VALVE_PARAM(3, vparams, 1);
   287 	DECODE_VALVE_PARAM(4, vparams, 2);
   288 	FINISH_CALL_SIMPLE(NULL);
   289 
   290 	return 1;
   291 }
   292 
   293 #if SOURCE_ENGINE >= SE_ORANGEBOX
   294 /* :TODO: This is Team Fortress 2 specific */
   295 static cell_t ForcePlayerSuicide(IPluginContext *pContext, const cell_t *params)
   296 {
   297 	static ValveCall *pCall = NULL;
   298 	if (!pCall)
   299 	{
   300 		ValvePassInfo pass[2];
   301 		InitPass(pass[0], Valve_Bool, PassType_Basic, PASSFLAG_BYVAL);
   302 		InitPass(pass[1], Valve_Bool, PassType_Basic, PASSFLAG_BYVAL);
   303 		if (!CreateBaseCall("CommitSuicide", ValveCall_Player, NULL, pass, 2, &pCall))
   304 		{
   305 			return pContext->ThrowNativeError("\"CommitSuicide\" not supported by this mod");
   306 		}
   307 		else if (!pCall)
   308 		{
   309 			return pContext->ThrowNativeError("\"CommitSuicide\" wrapper failed to initialized");
   310 		}
   311 	}
   312 
   313 	START_CALL();
   314 	DECODE_VALVE_PARAM(1, thisinfo, 0);
   315 	*(bool *)(vptr + 4) = false;
   316 	*(bool *)(vptr + 5) = false;
   317 	FINISH_CALL_SIMPLE(NULL);
   318 
   319 	return 1;
   320 }
   321 #else
   322 static cell_t ForcePlayerSuicide(IPluginContext *pContext, const cell_t *params)
   323 {
   324 	static ValveCall *pCall = NULL;
   325 	if (!pCall)
   326 	{
   327 		if (!CreateBaseCall("CommitSuicide", ValveCall_Player, NULL, NULL, 0, &pCall))
   328 		{
   329 			return pContext->ThrowNativeError("\"CommitSuicide\" not supported by this mod");
   330 		} else if (!pCall) {
   331 			return pContext->ThrowNativeError("\"CommitSuicide\" wrapper failed to initialized");
   332 		}
   333 	}
   334 
   335 	START_CALL();
   336 	DECODE_VALVE_PARAM(1, thisinfo, 0);
   337 	FINISH_CALL_SIMPLE(NULL);
   338 
   339 	return 1;
   340 }
   341 #endif
   342 
   343 static cell_t SetClientViewEntity(IPluginContext *pContext, const cell_t *params)
   344 {
   345 	IGamePlayer *player = playerhelpers->GetGamePlayer(params[1]);
   346 	if (player == NULL)
   347 	{
   348 		return pContext->ThrowNativeError("Invalid client index %d", params[1]);
   349 	}
   350 	if (!player->IsInGame())
   351 	{
   352 		return pContext->ThrowNativeError("Client %d is not in game", params[1]);
   353 	}
   354 
   355 	edict_t *pEdict = PEntityOfEntIndex(params[2]);
   356 	if (!pEdict || pEdict->IsFree())
   357 	{
   358 		return pContext->ThrowNativeError("Entity %d is not valid", params[2]);
   359 	}
   360 
   361 	engine->SetView(player->GetEdict(), pEdict);
   362 
   363 	return 1;
   364 }
   365 
   366 static SourceHook::String *g_lightstyle[MAX_LIGHTSTYLES] = {NULL};
   367 static cell_t SetLightStyle(IPluginContext *pContext, const cell_t *params)
   368 {
   369 	int style = params[1];
   370 	if (style >= MAX_LIGHTSTYLES)
   371 	{
   372 		return pContext->ThrowNativeError("Light style %d is invalid (range: 0-%d)", style, MAX_LIGHTSTYLES - 1);
   373 	}
   374 
   375 	if (g_lightstyle[style] == NULL)
   376 	{
   377 		/* We allocate and never free this because the Engine wants to hold onto it :\
   378 		 * in theory we could hook light style and know whether we're supposed to free 
   379 		 * this or not on shutdown, but for ~4K of memory MAX, it doesn't seem worth it yet.
   380 		 * So, it's a :TODO:!
   381 		 */
   382 		g_lightstyle[style] = new SourceHook::String();
   383 	}
   384 
   385 	char *str;
   386 	pContext->LocalToString(params[2], &str);
   387 
   388 	g_lightstyle[style]->assign(str);
   389 
   390 	engine->LightStyle(style, g_lightstyle[style]->c_str());
   391 
   392 	return 1;
   393 }
   394 
   395 static cell_t SlapPlayer(IPluginContext *pContext, const cell_t *params)
   396 {
   397 	static bool s_slap_supported = false;
   398 	static bool s_slap_setup = false;
   399 	static ICallWrapper *s_teleport = NULL;
   400 	static int s_health_offs = 0;
   401 	static int s_sound_count = 0;
   402 	static int s_frag_offs = 0;
   403 
   404 	if (!s_slap_setup)
   405 	{
   406 		int tries = 0;
   407 
   408 		s_slap_setup = true;
   409 
   410 		if (IsTeleportSupported())
   411 		{
   412 			tries++;
   413 		}
   414 		if (IsGetVelocitySupported())
   415 		{
   416 			tries++;
   417 		}
   418 
   419 		/* Setup health */
   420 		if (g_pGameConf->GetOffset("m_iHealth", &s_health_offs) && s_health_offs)
   421 		{
   422 			tries++;
   423 		}
   424 
   425 		if (tries == 3)
   426 		{
   427 			s_slap_supported = true;
   428 
   429 			const char *key;
   430 			if ((key = g_pGameConf->GetKeyValue("SlapSoundCount")) != NULL)
   431 			{
   432 				s_sound_count = atoi(key);
   433 			}
   434 		}
   435 	}
   436 
   437 	if (!s_slap_supported)
   438 	{
   439 		return pContext->ThrowNativeError("This function is not supported on this mod");
   440 	}
   441 
   442 	/* First check if the client is valid */
   443 	int client = params[1];
   444 	IGamePlayer *player = playerhelpers->GetGamePlayer(client);
   445 	if (!player)
   446 	{
   447 		return pContext->ThrowNativeError("Client %d is not valid", client);
   448 	} else if (!player->IsInGame()) {
   449 		return pContext->ThrowNativeError("Client %d is not in game", client);
   450 	}
   451 
   452 	edict_t *pEdict = player->GetEdict();
   453 	CBaseEntity *pEntity = pEdict->GetUnknown()->GetBaseEntity();
   454 
   455 	/* See if we should be taking away health */
   456 	bool should_slay = false;
   457 	if (params[2])
   458 	{
   459 		int *health = (int *)((char *)pEntity + s_health_offs);
   460 		if (*health - params[2] <= 0)
   461 		{
   462 			*health = 1;
   463 			should_slay = true;
   464 		} else {
   465 			*health -= params[2];
   466 		}
   467 	}
   468 
   469 	/* Teleport in a random direction - thank you, Mani!*/
   470 	Vector velocity;
   471 	GetVelocity(pEntity, &velocity, NULL);
   472 	velocity.x += ((rand() % 180) + 50) * (((rand() % 2) == 1) ?  -1 : 1);
   473 	velocity.y += ((rand() % 180) + 50) * (((rand() % 2) == 1) ?  -1 : 1);
   474 	velocity.z += rand() % 200 + 100;
   475 	Teleport(pEntity, NULL, NULL, &velocity);
   476 
   477 	/* Play a random sound */
   478 	if (params[3] && s_sound_count > 0)
   479 	{
   480 		char name[48];
   481 		const char *sound_name;
   482 		cell_t player_list[256], total_players = 0;
   483 		int maxClients = playerhelpers->GetMaxClients();
   484 
   485 		int r = (rand() % s_sound_count) + 1;
   486 		snprintf(name, sizeof(name), "SlapSound%d", r);
   487 
   488 		if ((sound_name = g_pGameConf->GetKeyValue(name)) != NULL)
   489 		{
   490 			IGamePlayer *other;
   491 			for (int i=1; i<=maxClients; i++)
   492 			{
   493 				other = playerhelpers->GetGamePlayer(i);
   494 				if (other->IsInGame())
   495 				{
   496 					player_list[total_players++] = i;
   497 				}
   498 			}
   499 
   500 			const Vector & pos = pEdict->GetCollideable()->GetCollisionOrigin();
   501 			CellRecipientFilter rf;
   502 			rf.SetToReliable(true);
   503 			rf.Initialize(player_list, total_players);
   504 			engsound->EmitSound(rf, client, CHAN_AUTO, sound_name, VOL_NORM, ATTN_NORM, 0, PITCH_NORM, &pos);
   505 		}
   506 	}
   507 
   508 	if (!s_frag_offs)
   509 	{
   510 		const char *frag_prop = g_pGameConf->GetKeyValue("m_iFrags");
   511 		if (frag_prop)
   512 		{
   513 			datamap_t *pMap = gamehelpers->GetDataMap(pEntity);
   514 			typedescription_t *pType = gamehelpers->FindInDataMap(pMap, frag_prop);
   515 			if (pType != NULL)
   516 			{
   517 				s_frag_offs = pType->fieldOffset[TD_OFFSET_NORMAL];
   518 			}
   519 		}
   520 		if (!s_frag_offs)
   521 		{
   522 			s_frag_offs = -1;
   523 		}
   524 	}
   525 
   526 	int old_frags = 0;
   527 	if (s_frag_offs > 0)
   528 	{
   529 		old_frags = *(int *)((char *)pEntity + s_frag_offs);
   530 	}
   531 	
   532 	/* Force suicide */
   533 	if (should_slay)
   534 	{
   535 		pluginhelpers->ClientCommand(pEdict, "kill\n");
   536 	}
   537 
   538 	if (s_frag_offs > 0)
   539 	{
   540 		*(int *)((char *)pEntity + s_frag_offs) = old_frags;
   541 	}
   542 
   543 	return 1;
   544 }
   545 
   546 static cell_t GetClientEyePosition(IPluginContext *pContext, const cell_t *params)
   547 {
   548 	IGamePlayer *player = playerhelpers->GetGamePlayer(params[1]);
   549 	if (player == NULL)
   550 	{
   551 		return pContext->ThrowNativeError("Invalid client index %d", params[1]);
   552 	}
   553 	if (!player->IsInGame())
   554 	{
   555 		return pContext->ThrowNativeError("Client %d is not in game", params[1]);
   556 	}
   557 
   558 	Vector pos;
   559 	serverClients->ClientEarPosition(player->GetEdict(), &pos);
   560 
   561 	cell_t *addr;
   562 	pContext->LocalToPhysAddr(params[2], &addr);
   563 	addr[0] = sp_ftoc(pos.x);
   564 	addr[1] = sp_ftoc(pos.y);
   565 	addr[2] = sp_ftoc(pos.z);
   566 
   567 	return 1;
   568 }
   569 
   570 static cell_t GetClientEyeAngles(IPluginContext *pContext, const cell_t *params)
   571 {
   572 	int client = params[1];
   573 	IGamePlayer *pPlayer = playerhelpers->GetGamePlayer(client);
   574 
   575 	if (!pPlayer)
   576 	{
   577 		return pContext->ThrowNativeError("Invalid client index %d", client);
   578 	}
   579 	else if (!pPlayer->IsInGame())
   580 	{
   581 		return pContext->ThrowNativeError("Client %d is not in game", client);
   582 	}
   583 
   584 	edict_t *pEdict = pPlayer->GetEdict();
   585 	CBaseEntity *pEntity = pEdict->GetUnknown() ? pEdict->GetUnknown()->GetBaseEntity() : NULL;
   586 
   587 	/* We always set the angles for backwards compatibility -- 
   588 	 * The original function had no return value.
   589 	 */
   590 	QAngle angles;
   591 	bool got_angles = false;
   592 
   593 	if (pEntity != NULL)
   594 	{
   595 		got_angles = GetEyeAngles(pEntity, &angles);
   596 	}
   597 	
   598 	cell_t *addr;
   599 	pContext->LocalToPhysAddr(params[2], &addr);
   600 	addr[0] = sp_ftoc(angles.x);
   601 	addr[1] = sp_ftoc(angles.y);
   602 	addr[2] = sp_ftoc(angles.z);
   603 
   604 	return got_angles ? 1 : 0;
   605 }
   606 
   607 static cell_t FindEntityByClassname(IPluginContext *pContext, const cell_t *params)
   608 {
   609 	static ValveCall *pCall = NULL;
   610 	if (!pCall)
   611 	{
   612 		ValvePassInfo pass[3];
   613 		InitPass(pass[0], Valve_CBaseEntity, PassType_Basic, PASSFLAG_BYVAL, VDECODE_FLAG_ALLOWNULL|VDECODE_FLAG_ALLOWWORLD);
   614 		InitPass(pass[1], Valve_String, PassType_Basic, PASSFLAG_BYVAL);
   615 		InitPass(pass[2], Valve_CBaseEntity, PassType_Basic, PASSFLAG_BYVAL);
   616 		if (!CreateBaseCall("FindEntityByClassname", ValveCall_EntityList, &pass[2], pass, 2, &pCall))
   617 		{
   618 			return pContext->ThrowNativeError("\"FindEntityByClassname\" not supported by this mod");
   619 		} else if (!pCall) {
   620 			return pContext->ThrowNativeError("\"FindEntityByClassname\" wrapper failed to initialized");
   621 		}
   622 	}
   623 
   624 	CBaseEntity *pEntity;
   625 	START_CALL();
   626 	*(void **)vptr = g_EntList; 
   627 	DECODE_VALVE_PARAM(1, vparams, 0);
   628 	DECODE_VALVE_PARAM(2, vparams, 1);
   629 	FINISH_CALL_SIMPLE(&pEntity);
   630 
   631 	if (pEntity == NULL)
   632 	{
   633 		return -1;
   634 	}
   635 
   636 	edict_t *pEdict = gameents->BaseEntityToEdict(pEntity);
   637 	if (!pEdict)
   638 	{
   639 		return -1;
   640 	}
   641 
   642 	return IndexOfEdict(pEdict);
   643 }
   644 
   645 #if SOURCE_ENGINE == SE_LEFT4DEAD
   646 static cell_t CreateEntityByName(IPluginContext *pContext, const cell_t *params)
   647 {
   648 	static ValveCall *pCall = NULL;
   649 	if (!pCall)
   650 	{
   651 		ValvePassInfo pass[4];
   652 		InitPass(pass[0], Valve_String, PassType_Basic, PASSFLAG_BYVAL);
   653 		InitPass(pass[1], Valve_POD, PassType_Basic, PASSFLAG_BYVAL);
   654 		InitPass(pass[2], Valve_Bool, PassType_Basic, PASSFLAG_BYVAL);
   655 		InitPass(pass[3], Valve_CBaseEntity, PassType_Basic, PASSFLAG_BYVAL);
   656 		if (!CreateBaseCall("CreateEntityByName", ValveCall_Static, &pass[3], pass, 3, &pCall))
   657 		{
   658 			return pContext->ThrowNativeError("\"CreateEntityByName\" not supported by this mod");
   659 		} else if (!pCall) {
   660 			return pContext->ThrowNativeError("\"CreateEntityByName\" wrapper failed to initialized");
   661 		}
   662 	}
   663 
   664 	CBaseEntity *pEntity = NULL;
   665 	START_CALL();
   666 	DECODE_VALVE_PARAM(1, vparams, 0);
   667 	DECODE_VALVE_PARAM(2, vparams, 1);
   668 	*(bool *)(vptr + 8) = true;
   669 	FINISH_CALL_SIMPLE(&pEntity);
   670 
   671 	if (pEntity == NULL)
   672 	{
   673 		return -1;
   674 	}
   675 
   676 	edict_t *pEdict = gameents->BaseEntityToEdict(pEntity);
   677 	if (!pEdict)
   678 	{
   679 		return -1;
   680 	}
   681 
   682 	return IndexOfEdict(pEdict);
   683 }
   684 #else
   685 static cell_t CreateEntityByName(IPluginContext *pContext, const cell_t *params)
   686 {
   687 	static ValveCall *pCall = NULL;
   688 	if (!pCall)
   689 	{
   690 		ValvePassInfo pass[3];
   691 		InitPass(pass[0], Valve_String, PassType_Basic, PASSFLAG_BYVAL);
   692 		InitPass(pass[1], Valve_POD, PassType_Basic, PASSFLAG_BYVAL);
   693 		InitPass(pass[2], Valve_CBaseEntity, PassType_Basic, PASSFLAG_BYVAL);
   694 		if (!CreateBaseCall("CreateEntityByName", ValveCall_Static, &pass[2], pass, 2, &pCall))
   695 		{
   696 			return pContext->ThrowNativeError("\"CreateEntityByName\" not supported by this mod");
   697 		} else if (!pCall) {
   698 			return pContext->ThrowNativeError("\"CreateEntityByName\" wrapper failed to initialized");
   699 		}
   700 	}
   701 
   702 	CBaseEntity *pEntity = NULL;
   703 	START_CALL();
   704 	DECODE_VALVE_PARAM(1, vparams, 0);
   705 	DECODE_VALVE_PARAM(2, vparams, 1);
   706 	FINISH_CALL_SIMPLE(&pEntity);
   707 
   708 	if (pEntity == NULL)
   709 	{
   710 		return -1;
   711 	}
   712 
   713 	edict_t *pEdict = gameents->BaseEntityToEdict(pEntity);
   714 	if (!pEdict)
   715 	{
   716 		return -1;
   717 	}
   718 
   719 	return IndexOfEdict(pEdict);
   720 }
   721 #endif
   722 
   723 static cell_t DispatchSpawn(IPluginContext *pContext, const cell_t *params)
   724 {
   725 	static ValveCall *pCall = NULL;
   726 	if (!pCall)
   727 	{
   728 		ValvePassInfo pass[2];
   729 		InitPass(pass[0], Valve_CBaseEntity, PassType_Basic, PASSFLAG_BYVAL);
   730 		InitPass(pass[1], Valve_POD, PassType_Basic, PASSFLAG_BYVAL);
   731 		if (!CreateBaseCall("DispatchSpawn", ValveCall_Static, &pass[1], pass, 1, &pCall))
   732 		{
   733 			return pContext->ThrowNativeError("\"DispatchSpawn\" not supported by this mod");
   734 		} else if (!pCall) {
   735 			return pContext->ThrowNativeError("\"DispatchSpawn\" wrapper failed to initialized");
   736 		}
   737 	}
   738 
   739 	int ret;
   740 	START_CALL();
   741 	DECODE_VALVE_PARAM(1, vparams, 0);
   742 	FINISH_CALL_SIMPLE(&ret);
   743 
   744 	return (ret == -1) ? 0 : 1;
   745 }
   746 
   747 static cell_t DispatchKeyValue(IPluginContext *pContext, const cell_t *params)
   748 {
   749 	static ValveCall *pCall = NULL;
   750 	if (!pCall)
   751 	{
   752 		ValvePassInfo pass[3];
   753 		InitPass(pass[0], Valve_String, PassType_Basic, PASSFLAG_BYVAL);
   754 		InitPass(pass[1], Valve_String, PassType_Basic, PASSFLAG_BYVAL);
   755 		InitPass(pass[2], Valve_Bool, PassType_Basic, PASSFLAG_BYVAL);
   756 		if (!CreateBaseCall("DispatchKeyValue", ValveCall_Entity, &pass[2], pass, 2, &pCall))
   757 		{
   758 			return pContext->ThrowNativeError("\"DispatchKeyValue\" not supported by this mod");
   759 		} else if (!pCall) {
   760 			return pContext->ThrowNativeError("\"DispatchKeyValue\" wrapper failed to initialized");
   761 		}
   762 	}
   763 
   764 	bool ret;
   765 	START_CALL();
   766 	DECODE_VALVE_PARAM(1, thisinfo, 0);
   767 	DECODE_VALVE_PARAM(2, vparams, 0);
   768 	DECODE_VALVE_PARAM(3, vparams, 1);
   769 	FINISH_CALL_SIMPLE(&ret);
   770 
   771 	return (ret) ? 1 : 0;
   772 }
   773 
   774 static cell_t DispatchKeyValueFloat(IPluginContext *pContext, const cell_t *params)
   775 {
   776 	static ValveCall *pCall = NULL;
   777 	if (!pCall)
   778 	{
   779 		ValvePassInfo pass[3];
   780 		InitPass(pass[0], Valve_String, PassType_Basic, PASSFLAG_BYVAL);
   781 		InitPass(pass[1], Valve_Float, PassType_Float, PASSFLAG_BYVAL);
   782 		InitPass(pass[2], Valve_Bool, PassType_Basic, PASSFLAG_BYVAL);
   783 		if (!CreateBaseCall("DispatchKeyValueFloat", ValveCall_Entity, &pass[2], pass, 2, &pCall))
   784 		{
   785 			return pContext->ThrowNativeError("\"DispatchKeyValueFloat\" not supported by this mod");
   786 		} else if (!pCall) {
   787 			return pContext->ThrowNativeError("\"DispatchKeyValueFloat\" wrapper failed to initialized");
   788 		}
   789 	}
   790 
   791 	bool ret;
   792 	START_CALL();
   793 	DECODE_VALVE_PARAM(1, thisinfo, 0);
   794 	DECODE_VALVE_PARAM(2, vparams, 0);
   795 	DECODE_VALVE_PARAM(3, vparams, 1);
   796 	FINISH_CALL_SIMPLE(&ret);
   797 
   798 	return (ret) ? 1 : 0;
   799 }
   800 
   801 static cell_t DispatchKeyValueVector(IPluginContext *pContext, const cell_t *params)
   802 {
   803 	static ValveCall *pCall = NULL;
   804 	if (!pCall)
   805 	{
   806 		ValvePassInfo pass[3];
   807 		InitPass(pass[0], Valve_String, PassType_Basic, PASSFLAG_BYVAL);
   808 #if SOURCE_ENGINE >= SE_ORANGEBOX
   809 		InitPass(pass[1], Valve_Vector, PassType_Basic, PASSFLAG_BYVAL);
   810 #else
   811 		InitPass(pass[1], Valve_Vector, PassType_Object, PASSFLAG_BYVAL|PASSFLAG_OCTOR|PASSFLAG_OASSIGNOP);
   812 #endif
   813 		InitPass(pass[2], Valve_Bool, PassType_Basic, PASSFLAG_BYVAL);
   814 		if (!CreateBaseCall("DispatchKeyValueVector", ValveCall_Entity, &pass[2], pass, 2, &pCall))
   815 		{
   816 			return pContext->ThrowNativeError("\"DispatchKeyValueVector\" not supported by this mod");
   817 		} else if (!pCall) {
   818 			return pContext->ThrowNativeError("\"DispatchKeyValueVector\" wrapper failed to initialized");
   819 		}
   820 	}
   821 
   822 	bool ret;
   823 	START_CALL();
   824 	DECODE_VALVE_PARAM(1, thisinfo, 0);
   825 	DECODE_VALVE_PARAM(2, vparams, 0);
   826 	DECODE_VALVE_PARAM(3, vparams, 1);
   827 	FINISH_CALL_SIMPLE(&ret);
   828 
   829 	return (ret) ? 1 : 0;
   830 }
   831 
   832 static cell_t sm_GetClientAimTarget(IPluginContext *pContext, const cell_t *params)
   833 {
   834 	int client = params[1];
   835 	IGamePlayer *pPlayer = playerhelpers->GetGamePlayer(client);
   836 
   837 	if (!pPlayer)
   838 	{
   839 		return pContext->ThrowNativeError("Invalid client index %d", client);
   840 	}
   841 	else if (!pPlayer->IsInGame())
   842 	{
   843 		return pContext->ThrowNativeError("Client %d is not in game", client);
   844 	}
   845 
   846 	return GetClientAimTarget(pPlayer->GetEdict(), params[2] ? true : false);
   847 }
   848 
   849 static cell_t sm_SetEntityModel(IPluginContext *pContext, const cell_t *params)
   850 {
   851 	static ValveCall *pCall = NULL;
   852 	if (!pCall)
   853 	{
   854 		ValvePassInfo pass[1];
   855 		InitPass(pass[0], Valve_String, PassType_Basic, PASSFLAG_BYVAL);
   856 		if (!CreateBaseCall("SetEntityModel", ValveCall_Entity, NULL, pass, 1, &pCall))
   857 		{
   858 			return pContext->ThrowNativeError("\"SetEntityModel\" not supported by this mod");
   859 		} else if (!pCall) {
   860 			return pContext->ThrowNativeError("\"SetEntityModel\" wrapper failed to initialized");
   861 		}
   862 	}
   863 
   864 	START_CALL();
   865 	DECODE_VALVE_PARAM(1, thisinfo, 0);
   866 	DECODE_VALVE_PARAM(2, vparams, 0);
   867 	FINISH_CALL_SIMPLE(NULL);
   868 
   869 	return 1;
   870 }
   871 
   872 static cell_t GetPlayerDecalFile(IPluginContext *pContext, const cell_t *params)
   873 {
   874 	IGamePlayer *player = playerhelpers->GetGamePlayer(params[1]);
   875 	if (player == NULL)
   876 	{
   877 		return pContext->ThrowNativeError("Invalid client index %d", params[1]);
   878 	}
   879 	if (!player->IsInGame())
   880 	{
   881 		return pContext->ThrowNativeError("Client %d is not in game", params[1]);
   882 	}
   883 
   884 	player_info_t info;
   885 	char *buffer;
   886 
   887 	if (!GetPlayerInfo(params[1], &info) || !info.customFiles[0])
   888 	{
   889 		return 0;
   890 	}
   891 
   892 	pContext->LocalToString(params[2], &buffer);
   893 	Q_binarytohex((byte *)&info.customFiles[0], sizeof(info.customFiles[0]), buffer, params[3]);
   894 
   895 	return 1;
   896 }
   897 
   898 static cell_t GetServerNetStats(IPluginContext *pContext, const cell_t *params)
   899 {
   900 	if (iserver == NULL)
   901 	{
   902 		return pContext->ThrowNativeError("IServer interface not supported, file a bug report.");
   903 	}
   904 
   905 	float in, out;
   906 	cell_t *pIn, *pOut;
   907 
   908 	pContext->LocalToPhysAddr(params[1], &pIn);
   909 	pContext->LocalToPhysAddr(params[2], &pOut);
   910 	iserver->GetNetStats(in, out);
   911 
   912 	*pIn = sp_ftoc(in);
   913 	*pOut = sp_ftoc(out);
   914 
   915 	return 1;
   916 }
   917 
   918 static cell_t WeaponEquip(IPluginContext *pContext, const cell_t *params)
   919 {
   920 	static ValveCall *pCall = NULL;
   921 	if (!pCall)
   922 	{
   923 		ValvePassInfo pass[1];
   924 		InitPass(pass[0], Valve_CBaseEntity, PassType_Basic, PASSFLAG_BYVAL);
   925 		if (!CreateBaseCall("WeaponEquip", ValveCall_Player, NULL, pass, 1, &pCall))
   926 		{
   927 			return pContext->ThrowNativeError("\"WeaponEquip\" not supported by this mod");
   928 		} else if (!pCall) {
   929 			return pContext->ThrowNativeError("\"WeaponEquip\" wrapper failed to initialized");
   930 		}
   931 	}
   932 
   933 	START_CALL();
   934 	DECODE_VALVE_PARAM(1, thisinfo, 0);
   935 	DECODE_VALVE_PARAM(2, vparams, 0);
   936 	FINISH_CALL_SIMPLE(NULL);
   937 
   938 	return 1;
   939 }
   940 
   941 static cell_t ActivateEntity(IPluginContext *pContext, const cell_t *params)
   942 {
   943 	static ValveCall *pCall = NULL;
   944 	if (!pCall)
   945 	{
   946 		if (!CreateBaseCall("Activate", ValveCall_Entity, NULL, NULL, 0, &pCall))
   947 		{
   948 			return pContext->ThrowNativeError("\"Activate\" not supported by this mod");
   949 		}
   950 		else if (!pCall)
   951 		{
   952 			return pContext->ThrowNativeError("\"Activate\" wrapper failed to initialized");
   953 		}
   954 	}
   955 
   956 	START_CALL();
   957 	DECODE_VALVE_PARAM(1, thisinfo, 0);
   958 	FINISH_CALL_SIMPLE(NULL);
   959 
   960 	return 1;
   961 }
   962 
   963 static cell_t SetClientInfo(IPluginContext *pContext, const cell_t *params)
   964 {
   965 	IGamePlayer *player = playerhelpers->GetGamePlayer(params[1]);
   966 	IClient *pClient = iserver->GetClient(params[1]-1);
   967 	if (player == NULL || pClient == NULL)
   968 	{
   969 		return pContext->ThrowNativeError("Invalid client index %d", params[1]);
   970 	}
   971 	if (!player->IsConnected())
   972 	{
   973 		return pContext->ThrowNativeError("Client %d is not connected", params[1]);
   974 	}
   975 
   976 	static ValveCall *pCall = NULL;
   977 	if (!pCall)
   978 	{
   979 		ValvePassInfo params[2];
   980 		InitPass(params[0], Valve_String, PassType_Basic, PASSFLAG_BYVAL);
   981 		InitPass(params[1], Valve_String, PassType_Basic, PASSFLAG_BYVAL);
   982 
   983 		if (!CreateBaseCall("SetUserCvar", ValveCall_Entity, NULL, params, 2, &pCall))
   984 		{
   985 			return pContext->ThrowNativeError("\"SetUserCvar\" not supported by this mod");
   986 		}
   987 		else if (!pCall)
   988 		{
   989 			return pContext->ThrowNativeError("\"SetUserCvar\" wrapper failed to initialized");
   990 		}
   991 	}
   992 
   993 	static int changedOffset = -1;
   994 
   995 	if (changedOffset == -1)
   996 	{	
   997 		if (!g_pGameConf->GetOffset("InfoChanged", &changedOffset))
   998 		{
   999 			return pContext->ThrowNativeError("\"SetUserCvar\" not supported by this mod");
  1000 		}
  1001 	}
  1002 
  1003 	unsigned char *CGameClient = (unsigned char *)pClient - 4;
  1004 
  1005 	START_CALL();
  1006 	/* Not really a CBaseEntity* but this works */
  1007 	CBaseEntity **ebuf = (CBaseEntity **)vptr;
  1008 	*ebuf = (CBaseEntity *)CGameClient;
  1009 	DECODE_VALVE_PARAM(2, vparams, 0);
  1010 	DECODE_VALVE_PARAM(3, vparams, 1);
  1011 	FINISH_CALL_SIMPLE(NULL);
  1012 
  1013 	uint8_t* changed = (uint8_t *)(CGameClient + changedOffset);
  1014 	*changed = 1;
  1015 
  1016 	return 1;
  1017 }
  1018 
  1019 sp_nativeinfo_t g_Natives[] = 
  1020 {
  1021 	{"ExtinguishEntity",		ExtinguishEntity},
  1022 	{"ForcePlayerSuicide",		ForcePlayerSuicide},
  1023 	{"GivePlayerItem",			GiveNamedItem},
  1024 	{"GetPlayerWeaponSlot",		GetPlayerWeaponSlot},
  1025 	{"IgniteEntity",			IgniteEntity},
  1026 	{"RemovePlayerItem",		RemovePlayerItem},
  1027 	{"TeleportEntity",			TeleportEntity},
  1028 	{"SetClientViewEntity",		SetClientViewEntity},
  1029 	{"SetLightStyle",			SetLightStyle},
  1030 	{"SlapPlayer",				SlapPlayer},
  1031 	{"GetClientEyePosition",	GetClientEyePosition},
  1032 	{"GetClientEyeAngles",		GetClientEyeAngles},
  1033 	{"FindEntityByClassname",	FindEntityByClassname},
  1034 	{"CreateEntityByName",		CreateEntityByName},
  1035 	{"DispatchSpawn",			DispatchSpawn},
  1036 	{"DispatchKeyValue",		DispatchKeyValue},
  1037 	{"DispatchKeyValueFloat",	DispatchKeyValueFloat},
  1038 	{"DispatchKeyValueVector",	DispatchKeyValueVector},
  1039 	{"GetClientAimTarget",		sm_GetClientAimTarget},
  1040 	{"SetEntityModel",			sm_SetEntityModel},
  1041 	{"GetPlayerDecalFile",		GetPlayerDecalFile},
  1042 	{"GetServerNetStats",		GetServerNetStats},
  1043 	{"EquipPlayerWeapon",		WeaponEquip},
  1044 	{"ActivateEntity",			ActivateEntity},
  1045 	{"SetClientInfo",			SetClientInfo},
  1046 	{NULL,						NULL},
  1047 };