diff --git a/extensions/sdktools/extension.cpp b/extensions/sdktools/extension.cpp
index d9150cc8..3511d682 100644
--- a/extensions/sdktools/extension.cpp
+++ b/extensions/sdktools/extension.cpp
@@ -24,6 +24,7 @@
#include "extension.h"
#include "vcallbuilder.h"
#include "vnatives.h"
+#include "vhelpers.h"
#include "tempents.h"
#include "gamerules.h"
@@ -37,6 +38,7 @@ IServerGameEnts *gameents = NULL;
IEngineTrace *enginetrace = NULL;
IEngineSound *engsound = NULL;
INetworkStringTableContainer *netstringtables = NULL;
+IServerPluginHelpers *pluginhelpers = NULL;
IBinTools *g_pBinTools = NULL;
IGameConfig *g_pGameConf = NULL;
IGameHelpers *g_pGameHelpers = NULL;
@@ -99,6 +101,7 @@ void SDKTools::SDK_OnUnload()
delete (*iter);
}
g_RegCalls.clear();
+ ShutdownHelpers();
g_TEManager.Shutdown();
@@ -111,6 +114,7 @@ bool SDKTools::SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlen, bool
GET_V_IFACE_ANY(engineFactory, engsound, IEngineSound, IENGINESOUND_SERVER_INTERFACE_VERSION);
GET_V_IFACE_ANY(engineFactory, enginetrace, IEngineTrace, INTERFACEVERSION_ENGINETRACE_SERVER);
GET_V_IFACE_ANY(engineFactory, netstringtables, INetworkStringTableContainer, INTERFACENAME_NETWORKSTRINGTABLESERVER);
+ GET_V_IFACE_ANY(engineFactory, pluginhelpers, IServerPluginHelpers, INTERFACEVERSION_ISERVERPLUGINHELPERS);
return true;
}
@@ -150,6 +154,7 @@ void SDKTools::NotifyInterfaceDrop(SMInterface *pInterface)
delete (*iter);
}
g_RegCalls.clear();
+ ShutdownHelpers();
g_TEManager.Shutdown();
}
diff --git a/extensions/sdktools/extension.h b/extensions/sdktools/extension.h
index 3dc7b6ee..d705af6c 100644
--- a/extensions/sdktools/extension.h
+++ b/extensions/sdktools/extension.h
@@ -71,6 +71,7 @@ extern IServerGameEnts *gameents;
extern IEngineTrace *enginetrace;
extern IEngineSound *engsound;
extern INetworkStringTableContainer *netstringtables;
+extern IServerPluginHelpers *pluginhelpers;
/* Interfaces from SourceMod */
extern IBinTools *g_pBinTools;
extern IGameConfig *g_pGameConf;
diff --git a/extensions/sdktools/msvc8/sdktools.vcproj b/extensions/sdktools/msvc8/sdktools.vcproj
index 216d46ee..37f469e7 100644
--- a/extensions/sdktools/msvc8/sdktools.vcproj
+++ b/extensions/sdktools/msvc8/sdktools.vcproj
@@ -215,6 +215,10 @@
RelativePath="..\vdecoder.cpp"
>
+
+
@@ -257,6 +261,10 @@
RelativePath="..\vdecoder.h"
>
+
+
diff --git a/extensions/sdktools/sdk/smsdk_config.h b/extensions/sdktools/sdk/smsdk_config.h
index 22e73f51..e75bfd1f 100644
--- a/extensions/sdktools/sdk/smsdk_config.h
+++ b/extensions/sdktools/sdk/smsdk_config.h
@@ -54,7 +54,7 @@
//#define SMEXT_ENABLE_DBMANAGER
#define SMEXT_ENABLE_GAMECONF
#define SMEXT_ENABLE_MEMUTILS
-//#define SMEXT_ENABLE_GAMEHELPERS
+#define SMEXT_ENABLE_GAMEHELPERS
//#define SMEXT_ENABLE_TIMERSYS
#define SMEXT_ENABLE_ADTFACTORY
diff --git a/extensions/sdktools/vhelpers.cpp b/extensions/sdktools/vhelpers.cpp
new file mode 100644
index 00000000..9ef7b3b7
--- /dev/null
+++ b/extensions/sdktools/vhelpers.cpp
@@ -0,0 +1,128 @@
+/**
+ * vim: set ts=4 :
+ * ===============================================================
+ * SourceMod SDK Tools Extension
+ * Copyright (C) 2004-2007 AlliedModders LLC. All rights reserved.
+ * ===============================================================
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Version: $Id$
+ */
+#include "extension.h"
+#include "vhelpers.h"
+
+CallHelper s_Teleport;
+CallHelper s_GetVelocity;
+
+bool SetupTeleport()
+{
+ if (s_Teleport.setup)
+ {
+ return s_Teleport.supported;
+ }
+
+ /* Setup Teleport */
+ int offset;
+ if (g_pGameConf->GetOffset("Teleport", &offset))
+ {
+ PassInfo info[3];
+ info[0].flags = info[1].flags = info[2].flags = PASSFLAG_BYVAL;
+ info[0].size = info[1].size = info[2].size = sizeof(void *);
+ info[0].type = info[1].type = info[2].type = PassType_Basic;
+
+ s_Teleport.call = g_pBinTools->CreateVCall(offset, 0, 0, NULL, info, 3);
+
+ if (s_Teleport.call)
+ {
+ s_Teleport.supported = true;
+ }
+ }
+
+ s_Teleport.setup = true;
+
+ return s_Teleport.supported;
+}
+
+void Teleport(CBaseEntity *pEntity, Vector *origin, QAngle *ang, Vector *velocity)
+{
+ unsigned char params[sizeof(void *) * 4];
+ unsigned char *vptr = params;
+ *(CBaseEntity **)vptr = pEntity;
+ vptr += sizeof(CBaseEntity *);
+ *(Vector **)vptr = origin;
+ vptr += sizeof(Vector *);
+ *(QAngle **)vptr = ang;
+ vptr += sizeof(QAngle *);
+ *(Vector **)vptr = velocity;
+
+ s_Teleport.call->Execute(params, NULL);
+}
+
+bool IsTeleportSupported()
+{
+ return SetupTeleport();
+}
+
+bool SetupGetVelocity()
+{
+ if (s_GetVelocity.setup)
+ {
+ return s_GetVelocity.supported;
+ }
+
+ int offset;
+ if (g_pGameConf->GetOffset("GetVelocity", &offset))
+ {
+ PassInfo info[2];
+ info[0].flags = info[1].flags = PASSFLAG_BYVAL;
+ info[0].size = info[1].size = sizeof(void *);
+ info[0].type = info[1].type = PassType_Basic;
+
+ s_GetVelocity.call = g_pBinTools->CreateVCall(offset, 0, 0, NULL, info, 2);
+
+ if (s_GetVelocity.call)
+ {
+ s_GetVelocity.supported = true;
+ }
+ }
+
+ s_GetVelocity.setup = true;
+
+ return s_GetVelocity.supported;
+}
+
+void GetVelocity(CBaseEntity *pEntity, Vector *velocity, AngularImpulse *angvelocity)
+{
+ unsigned char params[sizeof(void *) * 3];
+ unsigned char *vptr = params;
+ *(CBaseEntity **)vptr = pEntity;
+ vptr += sizeof(CBaseEntity *);
+ *(Vector **)vptr = velocity;
+ vptr += sizeof(Vector *);
+ *(AngularImpulse **)vptr = angvelocity;
+
+ s_GetVelocity.call->Execute(params, NULL);
+}
+
+bool IsGetVelocitySupported()
+{
+ return SetupGetVelocity();
+}
+
+void ShutdownHelpers()
+{
+ s_Teleport.Shutdown();
+ s_GetVelocity.Shutdown();
+}
diff --git a/extensions/sdktools/vhelpers.h b/extensions/sdktools/vhelpers.h
new file mode 100644
index 00000000..7a160993
--- /dev/null
+++ b/extensions/sdktools/vhelpers.h
@@ -0,0 +1,60 @@
+/**
+ * vim: set ts=4 :
+ * ===============================================================
+ * SourceMod SDK Tools Extension
+ * Copyright (C) 2004-2007 AlliedModders LLC. All rights reserved.
+ * ===============================================================
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Version: $Id$
+ */
+
+#ifndef _INCLUDE_SDKTOOLS_VHELPERS_H_
+#define _INCLUDE_SDKTOOLS_VHELPERS_H_
+
+#include
+#include
+#include
+
+using namespace SourceMod;
+
+struct CallHelper
+{
+ CallHelper() : call(NULL), supported(false), setup(false)
+ {
+ }
+ void Shutdown()
+ {
+ if (call)
+ {
+ call->Destroy();
+ call = NULL;
+ supported = false;
+ }
+ }
+ ICallWrapper *call;
+ bool supported;
+ bool setup;
+};
+
+void Teleport(CBaseEntity *pEntity, Vector *origin, QAngle *ang, Vector *velocity);
+bool IsTeleportSupported();
+
+void GetVelocity(CBaseEntity *pEntity, Vector *velocity, AngularImpulse *angvelocity);
+bool IsGetVelocitySupported();
+
+void ShutdownHelpers();
+
+#endif //_INCLUDE_SDKTOOLS_VHELPERS_H_
diff --git a/extensions/sdktools/vnatives.cpp b/extensions/sdktools/vnatives.cpp
index 826d95c6..d2fc1d58 100644
--- a/extensions/sdktools/vnatives.cpp
+++ b/extensions/sdktools/vnatives.cpp
@@ -21,12 +21,16 @@
* Version: $Id$
*/
+#include
#include
#include "extension.h"
#include "vcallbuilder.h"
#include "vnatives.h"
+#include "vhelpers.h"
+#include "CellRecipientFilter.h"
List g_RegCalls;
+List g_CallWraps;
inline void InitPass(ValvePassInfo &info, ValveType vtype, PassType type, unsigned int flags, unsigned int decflags=0)
{
@@ -347,6 +351,157 @@ static cell_t SetLightStyle(IPluginContext *pContext, const cell_t *params)
return 1;
}
+static cell_t SlapPlayer(IPluginContext *pContext, const cell_t *params)
+{
+ static bool s_slap_supported = false;
+ static bool s_slap_setup = false;
+ static ICallWrapper *s_teleport = NULL;
+ static int s_health_offs = NULL;
+ static int s_sound_count = 0;
+ static int s_frag_offs = 0;
+
+ if (!s_slap_setup)
+ {
+ int tries = 0;
+
+ s_slap_setup = true;
+
+ if (IsTeleportSupported())
+ {
+ tries++;
+ }
+ if (IsGetVelocitySupported())
+ {
+ tries++;
+ }
+
+ /* Setup health */
+ if (g_pGameConf->GetOffset("m_iHealth", &s_health_offs) && s_health_offs)
+ {
+ tries++;
+ }
+
+ if (tries == 3)
+ {
+ s_slap_supported = true;
+
+ const char *key;
+ if ((key = g_pGameConf->GetKeyValue("SlapSoundCount")) != NULL)
+ {
+ s_sound_count = atoi(key);
+ }
+ }
+ }
+
+ if (!s_slap_supported)
+ {
+ return pContext->ThrowNativeError("This function is not supported on this mod");
+ }
+
+ /* First check if the client is valid */
+ int client = params[1];
+ IGamePlayer *player = playerhelpers->GetGamePlayer(client);
+ if (!player)
+ {
+ return pContext->ThrowNativeError("Client %d is not valid", client);
+ } else if (!player->IsInGame()) {
+ return pContext->ThrowNativeError("Client %d is not in game", client);
+ }
+
+ edict_t *pEdict = player->GetEdict();
+ CBaseEntity *pEntity = pEdict->GetUnknown()->GetBaseEntity();
+
+ /* See if we should be taking away health */
+ bool should_slay = false;
+ if (params[2])
+ {
+ int *health = (int *)((char *)pEntity + s_health_offs);
+ if (*health - params[2] <= 0)
+ {
+ *health = 1;
+ should_slay = true;
+ } else {
+ *health -= params[2];
+ }
+ }
+
+ /* Teleport in a random direction - thank you, Mani!*/
+ Vector velocity;
+ GetVelocity(pEntity, &velocity, NULL);
+ velocity.x += ((rand() % 180) + 50) * (((rand() % 2) == 1) ? -1 : 1);
+ velocity.y += ((rand() % 180) + 50) * (((rand() % 2) == 1) ? -1 : 1);
+ velocity.z += rand() % 200 + 100;
+ Teleport(pEntity, NULL, NULL, &velocity);
+
+ /* Play a random sound */
+ if (params[3] && s_sound_count > 0)
+ {
+ char name[48];
+ const char *sound_name;
+ cell_t player_list[256], total_players = 0;
+ int maxClients = playerhelpers->GetMaxClients();
+
+ int r = (rand() % s_sound_count) + 1;
+ snprintf(name, sizeof(name), "SlapSound%d", r);
+
+ if ((sound_name = g_pGameConf->GetKeyValue(name)) != NULL)
+ {
+ IGamePlayer *other;
+ for (int i=1; i<=maxClients; i++)
+ {
+ other = playerhelpers->GetGamePlayer(i);
+ if (other->IsInGame())
+ {
+ player_list[total_players++] = i;
+ }
+ }
+
+ const Vector & pos = pEdict->GetCollideable()->GetCollisionOrigin();
+ CellRecipientFilter rf;
+ rf.SetToReliable(true);
+ rf.Initialize(player_list, total_players);
+ engsound->EmitSound(rf, client, CHAN_AUTO, sound_name, VOL_NORM, ATTN_NORM, 0, PITCH_NORM, &pos);
+ }
+ }
+
+ if (!s_frag_offs)
+ {
+ const char *frag_prop = g_pGameConf->GetKeyValue("m_iFrags");
+ if (frag_prop)
+ {
+ datamap_t *pMap = gamehelpers->GetDataMap(pEntity);
+ typedescription_t *pType = gamehelpers->FindInDataMap(pMap, frag_prop);
+ if (pType != NULL)
+ {
+ s_frag_offs = pType->fieldOffset[TD_OFFSET_NORMAL];
+ }
+ }
+ if (!s_frag_offs)
+ {
+ s_frag_offs = -1;
+ }
+ }
+
+ int old_frags = 0;
+ if (s_frag_offs > 0)
+ {
+ old_frags = *(int *)((char *)pEntity + s_frag_offs);
+ }
+
+ /* Force suicide */
+ if (should_slay)
+ {
+ pluginhelpers->ClientCommand(pEdict, "kill\n");
+ }
+
+ if (s_frag_offs > 0)
+ {
+ *(int *)((char *)pEntity + s_frag_offs) = old_frags;
+ }
+
+ return 1;
+}
+
sp_nativeinfo_t g_Natives[] =
{
{"ExtinguishPlayer", ExtinguishPlayer},
@@ -361,6 +516,7 @@ sp_nativeinfo_t g_Natives[] =
{"TeleportEntity", TeleportPlayer},
{"SetClientViewEntity", SetClientViewEntity},
{"SetLightStyle", SetLightStyle},
+ {"SlapPlayer", SlapPlayer},
{NULL, NULL},
};
diff --git a/gamedata/sdktools.games.txt b/gamedata/sdktools.games.txt
index 7cc51a04..91ae9c89 100644
--- a/gamedata/sdktools.games.txt
+++ b/gamedata/sdktools.games.txt
@@ -1,5 +1,26 @@
"Games"
{
+ /* Sounds */
+ "#default"
+ {
+ "Keys"
+ {
+ "SlapSoundCount" "3"
+ "SlapSound1" "player/pl_fallpain1.wav"
+ "SlapSound2" "player/pl_fallpain3.wav"
+ "SlapSound3" "player/pl_pain5.wav"
+ "m_iFrags" "m_iFrags"
+ }
+ "Offsets"
+ {
+ "m_iHealth"
+ {
+ "class" "CBasePlayer"
+ "prop" "m_iHealth"
+ }
+ }
+ }
+
/* General Temp Entities */
"#default"
{
@@ -102,6 +123,14 @@
/* Counter-Strike: Source */
"cstrike"
{
+ "Keys"
+ {
+ "SlapSoundCount" "3"
+ "SlapSound1" "player/damage1.wav"
+ "SlapSound2" "player/damage2.wav"
+ "SlapSound3" "player/damage3.wav"
+ }
+
"Offsets"
{
"GiveNamedItem"
@@ -139,6 +168,11 @@
"windows" "357"
"linux" "358"
}
+ "GetVelocity"
+ {
+ "windows" "126"
+ "linux" "127"
+ }
}
}
@@ -182,6 +216,11 @@
"windows" "356"
"linux" "357"
}
+ "GetVelocity"
+ {
+ "windows" "126"
+ "linux" "127"
+ }
}
}
@@ -225,6 +264,11 @@
"windows" "356"
"linux" "357"
}
+ "GetVelocity"
+ {
+ "windows" "126"
+ "linux" "127"
+ }
}
}
@@ -269,6 +313,11 @@
"windows" "343"
"linux" "344"
}
+ "GetVelocity"
+ {
+ "windows" "124"
+ "linux" "125"
+ }
/* Temp Entities */
"TE_GetServerClass"
@@ -321,6 +370,11 @@
"windows" "360"
"linux" "361"
}
+ "GetVelocity"
+ {
+ "windows" "128"
+ "linux" "129"
+ }
/* Offset into CBaseTempEntity constructor.
* On Windows Dsytopia is heavily inlined; we use the function
@@ -379,6 +433,11 @@
"windows" "90"
"linux" "91"
}
+ "GetVelocity"
+ {
+ "windows" "115"
+ "linux" "116"
+ }
/* Temp Entities */
"s_pTempEntities"
@@ -429,6 +488,11 @@
"windows" "356"
"linux" "357"
}
+ "GetVelocity"
+ {
+ "windows" "126"
+ "linux" "127"
+ }
/* Temp Entities */
"s_pTempEntities"
@@ -474,6 +538,11 @@
"windows" "90"
"linux" "91"
}
+ "GetVelocity"
+ {
+ "windows" "114"
+ "linux" "115"
+ }
/* Temp Entities */
"s_pTempEntities"
diff --git a/plugins/include/sdktools_functions.inc b/plugins/include/sdktools_functions.inc
index 1f61c262..45b54aa7 100644
--- a/plugins/include/sdktools_functions.inc
+++ b/plugins/include/sdktools_functions.inc
@@ -89,10 +89,21 @@ native TeleportEntity(entity, const Float:origin[3], const Float:angles[3], cons
*
* @param client Client index.
* @noreturn
- * @error Invalid entity or client not in game, or lack of mod support.
+ * @error Invalid client or client not in game, or lack of mod support.
*/
native ForcePlayerSuicide(client);
+/**
+ * Slaps a player in a random direction.
+ *
+ * @param client Client index.
+ * @param health Health to subtract.
+ * @param sound False to disable the sound effects.
+ * @noreturn
+ * @error Invalid client or client not in game, or lack of mod support.
+ */
+native SlapPlayer(client, health=5, bool:sound=true);
+
/**
* @deprecated
*/