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 */