3 * =============================================================================
4 * SourceMod SDKTools Extension
5 * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
6 * =============================================================================
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.
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
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/>.
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>.
33 #include <sh_string.h>
34 #include "extension.h"
35 #include "vcallbuilder.h"
39 #include "CellRecipientFilter.h"
40 #include <inetchannel.h>
44 SourceHook::List<ValveCall *> g_RegCalls;
45 SourceHook::List<ICallWrapper *> g_CallWraps;
47 inline void InitPass(ValvePassInfo &info, ValveType vtype, PassType type, unsigned int flags, unsigned int decflags=0)
49 info.decflags = decflags;
56 #define START_CALL() \
57 unsigned char *vptr = pCall->stk_get();
59 #define FINISH_CALL_SIMPLE(vret) \
60 pCall->call->Execute(vptr, vret); \
63 #define ENCODE_VALVE_PARAM(num, which, vnum) \
64 if (EncodeValveParam(pContext, \
67 &pCall->which[vnum], \
74 #define DECODE_VALVE_PARAM(num, which, vnum) \
75 if (DecodeValveParam(pContext, \
78 &pCall->which[vnum], \
85 bool CreateBaseCall(const char *name,
86 ValveCallType vcalltype,
87 const ValvePassInfo *retinfo,
88 const ValvePassInfo params[],
89 unsigned int numParams,
94 if (g_pGameConf->GetOffset(name, &offset))
96 call = CreateValveVCall(offset, vcalltype, retinfo, params, numParams);
99 g_RegCalls.push_back(call);
105 if (g_pGameConf->GetMemSig(name, &addr) && addr != NULL)
107 call = CreateValveCall(addr, vcalltype, retinfo, params, numParams);
110 g_RegCalls.push_back(call);
119 static cell_t RemovePlayerItem(IPluginContext *pContext, const cell_t *params)
121 static ValveCall *pCall = NULL;
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))
129 return pContext->ThrowNativeError("\"RemovePlayerItem\" not supported by this mod");
131 return pContext->ThrowNativeError("\"RemovePlayerItem\" wrapper failed to initialize");
137 DECODE_VALVE_PARAM(1, thisinfo, 0);
138 DECODE_VALVE_PARAM(2, vparams, 0);
139 FINISH_CALL_SIMPLE(&ret);
143 static cell_t GiveNamedItem(IPluginContext *pContext, const cell_t *params)
145 static ValveCall *pCall = NULL;
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))
154 return pContext->ThrowNativeError("\"GiveNamedItem\" not supported by this mod");
156 return pContext->ThrowNativeError("\"GiveNamedItem\" wrapper failed to initialize");
160 CBaseEntity *pEntity = NULL;
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);
172 edict_t *pEdict = gameents->BaseEntityToEdict(pEntity);
178 return IndexOfEdict(pEdict);
181 static cell_t GetPlayerWeaponSlot(IPluginContext *pContext, const cell_t *params)
183 static ValveCall *pCall = NULL;
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))
191 return pContext->ThrowNativeError("\"Weapon_GetSlot\" not supported by this mod");
193 return pContext->ThrowNativeError("\"Weapon_GetSlot\" wrapper failed to initialize");
197 CBaseEntity *pEntity;
199 DECODE_VALVE_PARAM(1, thisinfo, 0);
200 DECODE_VALVE_PARAM(2, vparams, 0);
201 FINISH_CALL_SIMPLE(&pEntity);
208 edict_t *pEdict = gameents->BaseEntityToEdict(pEntity);
214 return IndexOfEdict(pEdict);
217 #if SOURCE_ENGINE != SE_DARKMESSIAH
218 static cell_t IgniteEntity(IPluginContext *pContext, const cell_t *params)
220 static ValveCall *pCall = NULL;
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))
230 return pContext->ThrowNativeError("\"Ignite\" not supported by this mod");
232 return pContext->ThrowNativeError("\"Ignite\" wrapper failed to initialize");
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);
247 /* Dark Messiah specific version */
248 static cell_t IgniteEntity(IPluginContext *pContext, const cell_t *params)
250 static ValveCall *pCall = NULL;
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))
262 return pContext->ThrowNativeError("\"Ignite\" not supported by this mod");
264 return pContext->ThrowNativeError("\"Ignite\" wrapper failed to initialize");
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);
283 static cell_t ExtinguishEntity(IPluginContext *pContext, const cell_t *params)
285 static ValveCall *pCall = NULL;
288 if (!CreateBaseCall("Extinguish", ValveCall_Entity, NULL, NULL, 0, &pCall))
290 return pContext->ThrowNativeError("\"Extinguish\" not supported by this mod");
292 return pContext->ThrowNativeError("\"Extinguish\" wrapper failed to initialize");
297 DECODE_VALVE_PARAM(1, thisinfo, 0);
298 FINISH_CALL_SIMPLE(NULL);
303 static cell_t TeleportEntity(IPluginContext *pContext, const cell_t *params)
305 static ValveCall *pCall = NULL;
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))
314 return pContext->ThrowNativeError("\"Teleport\" not supported by this mod");
316 return pContext->ThrowNativeError("\"Teleport\" wrapper failed to initialize");
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);
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)
334 static ValveCall *pCall = NULL;
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))
342 return pContext->ThrowNativeError("\"CommitSuicide\" not supported by this mod");
346 return pContext->ThrowNativeError("\"CommitSuicide\" wrapper failed to initialize");
351 DECODE_VALVE_PARAM(1, thisinfo, 0);
352 *(bool *)(vptr + 4) = false;
353 *(bool *)(vptr + 5) = false;
354 FINISH_CALL_SIMPLE(NULL);
359 static cell_t ForcePlayerSuicide(IPluginContext *pContext, const cell_t *params)
361 static ValveCall *pCall = NULL;
364 if (!CreateBaseCall("CommitSuicide", ValveCall_Player, NULL, NULL, 0, &pCall))
366 return pContext->ThrowNativeError("\"CommitSuicide\" not supported by this mod");
368 return pContext->ThrowNativeError("\"CommitSuicide\" wrapper failed to initialize");
373 DECODE_VALVE_PARAM(1, thisinfo, 0);
374 FINISH_CALL_SIMPLE(NULL);
380 static cell_t SetClientViewEntity(IPluginContext *pContext, const cell_t *params)
382 IGamePlayer *player = playerhelpers->GetGamePlayer(params[1]);
385 return pContext->ThrowNativeError("Invalid client index %d", params[1]);
387 if (!player->IsInGame())
389 return pContext->ThrowNativeError("Client %d is not in game", params[1]);
392 edict_t *pEdict = PEntityOfEntIndex(params[2]);
393 if (!pEdict || pEdict->IsFree())
395 return pContext->ThrowNativeError("Entity %d is not valid", params[2]);
398 engine->SetView(player->GetEdict(), pEdict);
403 static SourceHook::String *g_lightstyle[MAX_LIGHTSTYLES] = {NULL};
404 static cell_t SetLightStyle(IPluginContext *pContext, const cell_t *params)
406 int style = params[1];
407 if (style >= MAX_LIGHTSTYLES)
409 return pContext->ThrowNativeError("Light style %d is invalid (range: 0-%d)", style, MAX_LIGHTSTYLES - 1);
412 if (g_lightstyle[style] == NULL)
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.
419 g_lightstyle[style] = new SourceHook::String();
423 pContext->LocalToString(params[2], &str);
425 g_lightstyle[style]->assign(str);
427 engine->LightStyle(style, g_lightstyle[style]->c_str());
432 static cell_t SlapPlayer(IPluginContext *pContext, const cell_t *params)
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;
447 if (IsTeleportSupported())
451 if (IsGetVelocitySupported())
457 if (g_pGameConf->GetOffset("m_iHealth", &s_health_offs) && s_health_offs)
464 s_slap_supported = true;
467 if ((key = g_pGameConf->GetKeyValue("SlapSoundCount")) != NULL)
469 s_sound_count = atoi(key);
474 if (!s_slap_supported)
476 return pContext->ThrowNativeError("This function is not supported on this mod");
479 /* First check if the client is valid */
480 int client = params[1];
481 IGamePlayer *player = playerhelpers->GetGamePlayer(client);
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);
489 edict_t *pEdict = player->GetEdict();
490 CBaseEntity *pEntity = pEdict->GetUnknown()->GetBaseEntity();
492 /* See if we should be taking away health */
493 bool should_slay = false;
496 #if SOURCE_ENGINE != SE_DARKMESSIAH
497 int *health = (int *)((char *)pEntity + s_health_offs);
499 if (*health - params[2] <= 0)
504 *health -= params[2];
507 float *health = (float *)((char *)pEntity + s_health_offs);
509 if (*health - (float)params[2] <= 0)
514 *health -= (float)params[2];
519 /* Teleport in a random direction - thank you, Mani!*/
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);
527 /* Play a random sound */
528 if (params[3] && s_sound_count > 0)
531 const char *sound_name;
532 cell_t player_list[256], total_players = 0;
533 int maxClients = playerhelpers->GetMaxClients();
535 int r = (rand() % s_sound_count) + 1;
536 snprintf(name, sizeof(name), "SlapSound%d", r);
538 if ((sound_name = g_pGameConf->GetKeyValue(name)) != NULL)
541 for (int i=1; i<=maxClients; i++)
543 other = playerhelpers->GetGamePlayer(i);
544 if (other->IsInGame())
546 player_list[total_players++] = i;
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);
560 const char *frag_prop = g_pGameConf->GetKeyValue("m_iFrags");
563 datamap_t *pMap = gamehelpers->GetDataMap(pEntity);
564 typedescription_t *pType = gamehelpers->FindInDataMap(pMap, frag_prop);
567 s_frag_offs = pType->fieldOffset[TD_OFFSET_NORMAL];
579 old_frags = *(int *)((char *)pEntity + s_frag_offs);
585 pluginhelpers->ClientCommand(pEdict, "kill\n");
590 *(int *)((char *)pEntity + s_frag_offs) = old_frags;
596 static cell_t GetClientEyePosition(IPluginContext *pContext, const cell_t *params)
598 IGamePlayer *player = playerhelpers->GetGamePlayer(params[1]);
601 return pContext->ThrowNativeError("Invalid client index %d", params[1]);
603 if (!player->IsInGame())
605 return pContext->ThrowNativeError("Client %d is not in game", params[1]);
609 serverClients->ClientEarPosition(player->GetEdict(), &pos);
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);
620 static cell_t GetClientEyeAngles(IPluginContext *pContext, const cell_t *params)
622 int client = params[1];
623 IGamePlayer *pPlayer = playerhelpers->GetGamePlayer(client);
627 return pContext->ThrowNativeError("Invalid client index %d", client);
629 else if (!pPlayer->IsInGame())
631 return pContext->ThrowNativeError("Client %d is not in game", client);
634 edict_t *pEdict = pPlayer->GetEdict();
635 CBaseEntity *pEntity = pEdict->GetUnknown() ? pEdict->GetUnknown()->GetBaseEntity() : NULL;
637 /* We always set the angles for backwards compatibility --
638 * The original function had no return value.
641 bool got_angles = false;
645 got_angles = GetEyeAngles(pEntity, &angles);
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);
654 return got_angles ? 1 : 0;
657 static cell_t FindEntityByClassname(IPluginContext *pContext, const cell_t *params)
659 static ValveCall *pCall = NULL;
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))
668 return pContext->ThrowNativeError("\"FindEntityByClassname\" not supported by this mod");
670 return pContext->ThrowNativeError("\"FindEntityByClassname\" wrapper failed to initialize");
674 CBaseEntity *pEntity;
676 *(void **)vptr = g_EntList;
677 DECODE_VALVE_PARAM(1, vparams, 0);
678 DECODE_VALVE_PARAM(2, vparams, 1);
679 FINISH_CALL_SIMPLE(&pEntity);
686 edict_t *pEdict = gameents->BaseEntityToEdict(pEntity);
692 return IndexOfEdict(pEdict);
695 #if SOURCE_ENGINE == SE_LEFT4DEAD
696 static cell_t CreateEntityByName(IPluginContext *pContext, const cell_t *params)
698 static ValveCall *pCall = NULL;
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))
708 return pContext->ThrowNativeError("\"CreateEntityByName\" not supported by this mod");
710 return pContext->ThrowNativeError("\"CreateEntityByName\" wrapper failed to initialize");
714 CBaseEntity *pEntity = NULL;
716 DECODE_VALVE_PARAM(1, vparams, 0);
717 DECODE_VALVE_PARAM(2, vparams, 1);
718 *(bool *)(vptr + 8) = true;
719 FINISH_CALL_SIMPLE(&pEntity);
726 edict_t *pEdict = gameents->BaseEntityToEdict(pEntity);
732 return IndexOfEdict(pEdict);
735 static cell_t CreateEntityByName(IPluginContext *pContext, const cell_t *params)
737 static ValveCall *pCall = NULL;
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))
746 return pContext->ThrowNativeError("\"CreateEntityByName\" not supported by this mod");
748 return pContext->ThrowNativeError("\"CreateEntityByName\" wrapper failed to initialize");
752 CBaseEntity *pEntity = NULL;
754 DECODE_VALVE_PARAM(1, vparams, 0);
755 DECODE_VALVE_PARAM(2, vparams, 1);
756 FINISH_CALL_SIMPLE(&pEntity);
763 edict_t *pEdict = gameents->BaseEntityToEdict(pEntity);
769 return IndexOfEdict(pEdict);
773 static cell_t DispatchSpawn(IPluginContext *pContext, const cell_t *params)
775 static ValveCall *pCall = NULL;
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))
783 return pContext->ThrowNativeError("\"DispatchSpawn\" not supported by this mod");
785 return pContext->ThrowNativeError("\"DispatchSpawn\" wrapper failed to initialize");
791 DECODE_VALVE_PARAM(1, vparams, 0);
792 FINISH_CALL_SIMPLE(&ret);
794 return (ret == -1) ? 0 : 1;
797 static cell_t DispatchKeyValue(IPluginContext *pContext, const cell_t *params)
799 static ValveCall *pCall = NULL;
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))
808 return pContext->ThrowNativeError("\"DispatchKeyValue\" not supported by this mod");
810 return pContext->ThrowNativeError("\"DispatchKeyValue\" wrapper failed to initialize");
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);
824 static cell_t DispatchKeyValueFloat(IPluginContext *pContext, const cell_t *params)
826 static ValveCall *pCall = NULL;
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))
835 return pContext->ThrowNativeError("\"DispatchKeyValueFloat\" not supported by this mod");
837 return pContext->ThrowNativeError("\"DispatchKeyValueFloat\" wrapper failed to initialize");
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);
851 static cell_t DispatchKeyValueVector(IPluginContext *pContext, const cell_t *params)
853 static ValveCall *pCall = NULL;
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);
861 InitPass(pass[1], Valve_Vector, PassType_Object, PASSFLAG_BYVAL|PASSFLAG_OCTOR|PASSFLAG_OASSIGNOP);
863 InitPass(pass[2], Valve_Bool, PassType_Basic, PASSFLAG_BYVAL);
864 if (!CreateBaseCall("DispatchKeyValueVector", ValveCall_Entity, &pass[2], pass, 2, &pCall))
866 return pContext->ThrowNativeError("\"DispatchKeyValueVector\" not supported by this mod");
868 return pContext->ThrowNativeError("\"DispatchKeyValueVector\" wrapper failed to initialize");
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);
882 static cell_t sm_GetClientAimTarget(IPluginContext *pContext, const cell_t *params)
884 int client = params[1];
885 IGamePlayer *pPlayer = playerhelpers->GetGamePlayer(client);
889 return pContext->ThrowNativeError("Invalid client index %d", client);
891 else if (!pPlayer->IsInGame())
893 return pContext->ThrowNativeError("Client %d is not in game", client);
896 return GetClientAimTarget(pPlayer->GetEdict(), params[2] ? true : false);
899 static cell_t sm_SetEntityModel(IPluginContext *pContext, const cell_t *params)
901 static ValveCall *pCall = NULL;
904 ValvePassInfo pass[1];
905 InitPass(pass[0], Valve_String, PassType_Basic, PASSFLAG_BYVAL);
906 if (!CreateBaseCall("SetEntityModel", ValveCall_Entity, NULL, pass, 1, &pCall))
908 return pContext->ThrowNativeError("\"SetEntityModel\" not supported by this mod");
910 return pContext->ThrowNativeError("\"SetEntityModel\" wrapper failed to initialize");
915 DECODE_VALVE_PARAM(1, thisinfo, 0);
916 DECODE_VALVE_PARAM(2, vparams, 0);
917 FINISH_CALL_SIMPLE(NULL);
922 static cell_t GetPlayerDecalFile(IPluginContext *pContext, const cell_t *params)
924 IGamePlayer *player = playerhelpers->GetGamePlayer(params[1]);
927 return pContext->ThrowNativeError("Invalid client index %d", params[1]);
929 if (!player->IsInGame())
931 return pContext->ThrowNativeError("Client %d is not in game", params[1]);
937 if (!GetPlayerInfo(params[1], &info) || !info.customFiles[0])
942 pContext->LocalToString(params[2], &buffer);
943 Q_binarytohex((byte *)&info.customFiles[0], sizeof(info.customFiles[0]), buffer, params[3]);
948 static cell_t GetServerNetStats(IPluginContext *pContext, const cell_t *params)
952 return pContext->ThrowNativeError("IServer interface not supported, file a bug report.");
958 pContext->LocalToPhysAddr(params[1], &pIn);
959 pContext->LocalToPhysAddr(params[2], &pOut);
960 iserver->GetNetStats(in, out);
963 *pOut = sp_ftoc(out);
968 static cell_t WeaponEquip(IPluginContext *pContext, const cell_t *params)
970 static ValveCall *pCall = NULL;
973 ValvePassInfo pass[1];
974 InitPass(pass[0], Valve_CBaseEntity, PassType_Basic, PASSFLAG_BYVAL);
975 if (!CreateBaseCall("WeaponEquip", ValveCall_Player, NULL, pass, 1, &pCall))
977 return pContext->ThrowNativeError("\"WeaponEquip\" not supported by this mod");
979 return pContext->ThrowNativeError("\"WeaponEquip\" wrapper failed to initialize");
984 DECODE_VALVE_PARAM(1, thisinfo, 0);
985 DECODE_VALVE_PARAM(2, vparams, 0);
986 FINISH_CALL_SIMPLE(NULL);
991 static cell_t ActivateEntity(IPluginContext *pContext, const cell_t *params)
993 static ValveCall *pCall = NULL;
996 if (!CreateBaseCall("Activate", ValveCall_Entity, NULL, NULL, 0, &pCall))
998 return pContext->ThrowNativeError("\"Activate\" not supported by this mod");
1002 return pContext->ThrowNativeError("\"Activate\" wrapper failed to initialize");
1007 DECODE_VALVE_PARAM(1, thisinfo, 0);
1008 FINISH_CALL_SIMPLE(NULL);
1013 static cell_t SetClientInfo(IPluginContext *pContext, const cell_t *params)
1015 if (iserver == NULL)
1017 return pContext->ThrowNativeError("IServer interface not supported, file a bug report.");
1020 IGamePlayer *player = playerhelpers->GetGamePlayer(params[1]);
1021 IClient *pClient = iserver->GetClient(params[1] - 1);
1023 if (player == NULL || pClient == NULL)
1025 return pContext->ThrowNativeError("Invalid client index %d", params[1]);
1027 if (!player->IsConnected())
1029 return pContext->ThrowNativeError("Client %d is not connected", params[1]);
1032 static ValveCall *pCall = NULL;
1035 ValvePassInfo params[2];
1036 InitPass(params[0], Valve_String, PassType_Basic, PASSFLAG_BYVAL);
1037 InitPass(params[1], Valve_String, PassType_Basic, PASSFLAG_BYVAL);
1039 if (!CreateBaseCall("SetUserCvar", ValveCall_Entity, NULL, params, 2, &pCall))
1041 return pContext->ThrowNativeError("\"SetUserCvar\" not supported by this mod");
1045 return pContext->ThrowNativeError("\"SetUserCvar\" wrapper failed to initialize");
1049 /* TODO: Use UpdateUserSettings function for all engines */
1050 #if SOURCE_ENGINE == SE_DARKMESSIAH
1051 static ValveCall *pUpdateSettings = NULL;
1052 if (!pUpdateSettings)
1054 if (!CreateBaseCall("UpdateUserSettings", ValveCall_Entity, NULL, NULL, 0, &pUpdateSettings))
1056 return pContext->ThrowNativeError("\"SetUserCvar\" not supported by this mod");
1058 else if (!pUpdateSettings)
1060 return pContext->ThrowNativeError("\"SetUserCvar\" wrapper failed to initialize");
1064 static int changedOffset = -1;
1066 if (changedOffset == -1)
1068 if (!g_pGameConf->GetOffset("InfoChanged", &changedOffset))
1070 return pContext->ThrowNativeError("\"SetUserCvar\" not supported by this mod");
1075 unsigned char *CGameClient = (unsigned char *)pClient - 4;
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);
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);
1091 uint8_t* changed = (uint8_t *)(CGameClient + changedOffset);
1098 sp_nativeinfo_t g_Natives[] =
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},