1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/extensions/tf2/extension.cpp Sun Jul 06 04:49:55 2008 +0000
1.3 @@ -0,0 +1,364 @@
1.4 +/**
1.5 + * vim: set ts=4 :
1.6 + * =============================================================================
1.7 + * SourceMod Team Fortress 2 Extension
1.8 + * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
1.9 + * =============================================================================
1.10 + *
1.11 + * This program is free software; you can redistribute it and/or modify it under
1.12 + * the terms of the GNU General Public License, version 3.0, as published by the
1.13 + * Free Software Foundation.
1.14 + *
1.15 + * This program is distributed in the hope that it will be useful, but WITHOUT
1.16 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
1.17 + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
1.18 + * details.
1.19 + *
1.20 + * You should have received a copy of the GNU General Public License along with
1.21 + * this program. If not, see <http://www.gnu.org/licenses/>.
1.22 + *
1.23 + * As a special exception, AlliedModders LLC gives you permission to link the
1.24 + * code of this program (as well as its derivative works) to "Half-Life 2," the
1.25 + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
1.26 + * by the Valve Corporation. You must obey the GNU General Public License in
1.27 + * all respects for all other code used. Additionally, AlliedModders LLC grants
1.28 + * this exception to all derivative works. AlliedModders LLC defines further
1.29 + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
1.30 + * or <http://www.sourcemod.net/license.php>.
1.31 + *
1.32 + * Version: $Id$
1.33 + */
1.34 +
1.35 +#include "extension.h"
1.36 +#include "util.h"
1.37 +#include "RegNatives.h"
1.38 +#include "iplayerinfo.h"
1.39 +#include "sm_trie_tpl.h"
1.40 +#include "criticals.h"
1.41 +#include "CDetour/detours.h"
1.42 +
1.43 +/**
1.44 + * @file extension.cpp
1.45 + * @brief Implement extension code here.
1.46 + */
1.47 +
1.48 +
1.49 +TF2Tools g_TF2Tools;
1.50 +IGameConfig *g_pGameConf = NULL;
1.51 +
1.52 +IBinTools *g_pBinTools = NULL;
1.53 +
1.54 +SMEXT_LINK(&g_TF2Tools);
1.55 +
1.56 +ICvar *icvar = NULL;
1.57 +
1.58 +CGlobalVars *gpGlobals = NULL;
1.59 +
1.60 +sm_sendprop_info_t *playerSharedOffset;
1.61 +
1.62 +extern sp_nativeinfo_t g_TFNatives[];
1.63 +
1.64 +int g_resourceEntity;
1.65 +
1.66 +SH_DECL_HOOK3_void(IServerGameDLL, ServerActivate, SH_NOATTRIB, 0, edict_t *, int , int);
1.67 +
1.68 +bool TF2Tools::SDK_OnLoad(char *error, size_t maxlength, bool late)
1.69 +{
1.70 + if (strcmp(g_pSM->GetGameFolderName(), "tf") != 0)
1.71 + {
1.72 + UTIL_Format(error, maxlength, "Cannot Load TF2 Extension on mods other than TF2");
1.73 + return false;
1.74 + }
1.75 +
1.76 + ServerClass *sc = UTIL_FindServerClass("CTFPlayer");
1.77 + if (sc == NULL)
1.78 + {
1.79 + UTIL_Format(error, maxlength, "Could not find CTFPlayer server class");
1.80 + return false;
1.81 + }
1.82 +
1.83 + playerSharedOffset = new sm_sendprop_info_t;
1.84 +
1.85 + if (!UTIL_FindDataTable(sc->m_pTable, "DT_TFPlayerShared", playerSharedOffset, 0))
1.86 + {
1.87 + UTIL_Format(error, maxlength, "Could not find DT_TFPlayerShared data table");
1.88 + return false;
1.89 + }
1.90 +
1.91 + sharesys->AddDependency(myself, "bintools.ext", true, true);
1.92 +
1.93 + char conf_error[255] = "";
1.94 + if (!gameconfs->LoadGameConfigFile("sm-tf2.games", &g_pGameConf, conf_error, sizeof(conf_error)))
1.95 + {
1.96 + if (conf_error)
1.97 + {
1.98 + UTIL_Format(error, maxlength, "Could not read sm-tf2.games.txt: %s", conf_error);
1.99 + }
1.100 + return false;
1.101 + }
1.102 +
1.103 + CDetourManager::Init(g_pSM->GetScriptingEngine(), g_pGameConf);
1.104 +
1.105 + sharesys->AddNatives(myself, g_TFNatives);
1.106 + sharesys->RegisterLibrary(myself, "tf2");
1.107 +
1.108 + playerhelpers->RegisterCommandTargetProcessor(this);
1.109 +
1.110 + g_critForward = forwards->CreateForward("TF2_CalcIsAttackCritical", ET_Hook, 4, NULL, Param_Cell, Param_Cell, Param_String, Param_CellByRef);
1.111 +
1.112 + g_pCVar = icvar;
1.113 +
1.114 + return true;
1.115 +}
1.116 +
1.117 +bool TF2Tools::SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlen, bool late)
1.118 +{
1.119 + GET_V_IFACE_CURRENT(GetEngineFactory, engine, IVEngineServer, INTERFACEVERSION_VENGINESERVER);
1.120 +
1.121 + gpGlobals = ismm->GetCGlobals();
1.122 +
1.123 + SH_ADD_HOOK(IServerGameDLL, ServerActivate, gamedll, SH_STATIC(OnServerActivate), true);
1.124 +
1.125 + GET_V_IFACE_CURRENT(GetEngineFactory, icvar, ICvar, CVAR_INTERFACE_VERSION);
1.126 +
1.127 + GET_V_IFACE_CURRENT(GetServerFactory, gameents, IServerGameEnts, INTERFACEVERSION_SERVERGAMEENTS);
1.128 +
1.129 + GET_V_IFACE_CURRENT(GetEngineFactory, m_GameEventManager, IGameEventManager2, INTERFACEVERSION_GAMEEVENTSMANAGER2);
1.130 + m_GameEventManager->AddListener(this, "teamplay_restart_round", true);
1.131 +
1.132 + return true;
1.133 +}
1.134 +
1.135 +void TF2Tools::SDK_OnUnload()
1.136 +{
1.137 + SH_REMOVE_HOOK(IServerGameDLL, ServerActivate, gamedll, SH_STATIC(OnServerActivate), true);
1.138 +
1.139 + g_RegNatives.UnregisterAll();
1.140 + gameconfs->CloseGameConfigFile(g_pGameConf);
1.141 + playerhelpers->UnregisterCommandTargetProcessor(this);
1.142 +
1.143 + forwards->ReleaseForward(g_critForward);
1.144 +
1.145 + RemoveDetours();
1.146 +}
1.147 +
1.148 +void TF2Tools::SDK_OnAllLoaded()
1.149 +{
1.150 + SM_GET_LATE_IFACE(BINTOOLS, g_pBinTools);
1.151 +
1.152 + InitialiseDetours();
1.153 +}
1.154 +
1.155 +bool TF2Tools::RegisterConCommandBase(ConCommandBase *pVar)
1.156 +{
1.157 + return g_SMAPI->RegisterConCommandBase(g_PLAPI, pVar);
1.158 +}
1.159 +
1.160 +void TF2Tools::FireGameEvent( IGameEvent *event )
1.161 +{
1.162 + timersys->NotifyOfGameStart();
1.163 + timersys->MapTimeLeftChanged();
1.164 +}
1.165 +
1.166 +bool TF2Tools::QueryRunning(char *error, size_t maxlength)
1.167 +{
1.168 + SM_CHECK_IFACE(BINTOOLS, g_pBinTools);
1.169 +
1.170 + return true;
1.171 +}
1.172 +
1.173 +bool TF2Tools::QueryInterfaceDrop(SMInterface *pInterface)
1.174 +{
1.175 + if (pInterface == g_pBinTools)
1.176 + {
1.177 + return false;
1.178 + }
1.179 +
1.180 + return IExtensionInterface::QueryInterfaceDrop(pInterface);
1.181 +}
1.182 +
1.183 +void TF2Tools::NotifyInterfaceDrop(SMInterface *pInterface)
1.184 +{
1.185 + g_RegNatives.UnregisterAll();
1.186 +}
1.187 +
1.188 +void OnServerActivate(edict_t *pEdictList, int edictCount, int clientMax)
1.189 +{
1.190 + g_resourceEntity = FindResourceEntity();
1.191 +}
1.192 +
1.193 +
1.194 +bool TF2Tools::ProcessCommandTarget(cmd_target_info_t *info)
1.195 +{
1.196 + int max_clients;
1.197 + IPlayerInfo *pInfo;
1.198 + unsigned int team_index = 0;
1.199 + IGamePlayer *pPlayer, *pAdmin;
1.200 +
1.201 + if ((info->flags & COMMAND_FILTER_NO_MULTI) == COMMAND_FILTER_NO_MULTI)
1.202 + {
1.203 + return false;
1.204 + }
1.205 +
1.206 + if (info->admin)
1.207 + {
1.208 + if ((pAdmin = playerhelpers->GetGamePlayer(info->admin)) == NULL)
1.209 + {
1.210 + return false;
1.211 + }
1.212 + if (!pAdmin->IsInGame())
1.213 + {
1.214 + return false;
1.215 + }
1.216 + }
1.217 + else
1.218 + {
1.219 + pAdmin = NULL;
1.220 + }
1.221 +
1.222 + if (strcmp(info->pattern, "@red") == 0 )
1.223 + {
1.224 + team_index = 2;
1.225 + }
1.226 + else if (strcmp(info->pattern, "@blue") == 0)
1.227 + {
1.228 + team_index = 3;
1.229 + }
1.230 + else
1.231 + {
1.232 + return false;
1.233 + }
1.234 +
1.235 + info->num_targets = 0;
1.236 +
1.237 + max_clients = playerhelpers->GetMaxClients();
1.238 + for (int i = 1;
1.239 + i <= max_clients && (cell_t)info->num_targets < info->max_targets;
1.240 + i++)
1.241 + {
1.242 + if ((pPlayer = playerhelpers->GetGamePlayer(i)) == NULL)
1.243 + {
1.244 + continue;
1.245 + }
1.246 + if (!pPlayer->IsInGame())
1.247 + {
1.248 + continue;
1.249 + }
1.250 + if ((pInfo = pPlayer->GetPlayerInfo()) == NULL)
1.251 + {
1.252 + continue;
1.253 + }
1.254 + if (pInfo->GetTeamIndex() != (int)team_index)
1.255 + {
1.256 + continue;
1.257 + }
1.258 + if (playerhelpers->FilterCommandTarget(pAdmin, pPlayer, info->flags)
1.259 + != COMMAND_TARGET_VALID)
1.260 + {
1.261 + continue;
1.262 + }
1.263 + info->targets[info->num_targets] = i;
1.264 + info->num_targets++;
1.265 + }
1.266 +
1.267 + if (info->num_targets == 0)
1.268 + {
1.269 + info->reason = COMMAND_TARGET_EMPTY_FILTER;
1.270 + }
1.271 + else
1.272 + {
1.273 + info->reason = COMMAND_TARGET_VALID;
1.274 + }
1.275 +
1.276 + info->target_name_style = COMMAND_TARGETNAME_RAW;
1.277 + if (team_index == 2)
1.278 + {
1.279 + UTIL_Format(info->target_name, info->target_name_maxlength, "Red Team");
1.280 + }
1.281 + else if (team_index == 3)
1.282 + {
1.283 + UTIL_Format(info->target_name, info->target_name_maxlength, "Blue Team");
1.284 + }
1.285 +
1.286 + return true;
1.287 +}
1.288 +
1.289 +int FindResourceEntity()
1.290 +{
1.291 + return FindEntityByNetClass(-1, "CTFPlayerResource");
1.292 +}
1.293 +
1.294 +
1.295 +int FindEntityByNetClass(int start, const char *classname)
1.296 +{
1.297 + edict_t *current;
1.298 +
1.299 + for (int i = ((start != -1) ? start : 0); i < gpGlobals->maxEntities; i++)
1.300 + {
1.301 + current = engine->PEntityOfEntIndex(i);
1.302 + if (current == NULL)
1.303 + {
1.304 + continue;
1.305 + }
1.306 +
1.307 + IServerNetworkable *network = current->GetNetworkable();
1.308 +
1.309 + if (network == NULL)
1.310 + {
1.311 + continue;
1.312 + }
1.313 +
1.314 + ServerClass *sClass = network->GetServerClass();
1.315 + const char *name = sClass->GetName();
1.316 +
1.317 +
1.318 + if (strcmp(name, classname) == 0)
1.319 + {
1.320 + return i;
1.321 + }
1.322 + }
1.323 +
1.324 + return -1;
1.325 +}
1.326 +
1.327 +TFClassType ClassnameToType(const char *classname)
1.328 +{
1.329 + static KTrie<TFClassType> trie;
1.330 + static bool filled = false;
1.331 +
1.332 + if (!filled)
1.333 + {
1.334 + trie.insert("scout", TFClass_Scout);
1.335 + trie.insert("sniper", TFClass_Sniper);
1.336 + trie.insert("soldier", TFClass_Soldier);
1.337 + trie.insert("demoman", TFClass_DemoMan);
1.338 + trie.insert("demo", TFClass_DemoMan);
1.339 + trie.insert("medic", TFClass_Medic);
1.340 + trie.insert("heavy", TFClass_Heavy);
1.341 + trie.insert("hwg", TFClass_Heavy);
1.342 + trie.insert("pyro", TFClass_Pyro);
1.343 + trie.insert("spy", TFClass_Spy);
1.344 + trie.insert("engineer", TFClass_Engineer);
1.345 + trie.insert("engy", TFClass_Engineer);
1.346 +
1.347 + filled = true;
1.348 + }
1.349 +
1.350 + TFClassType *value;
1.351 +
1.352 + if (!(value = trie.retrieve(classname)))
1.353 + {
1.354 + return TFClass_Unknown;
1.355 + }
1.356 +
1.357 + return *value;
1.358 +}
1.359 +
1.360 +
1.361 +
1.362 +/**
1.363 + * A picture of a blue crab given to me as a gift and stored here for safe keeping
1.364 + *
1.365 + * http://www.democracycellproject.net/blog/archives/Clown%20car.jpg
1.366 + */
1.367 +