pred@1979: /** pred@1979: * vim: set ts=4 : pred@1979: * ============================================================================= ds@1990: * SourceMod Team Fortress 2 Extension pred@1979: * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. pred@1979: * ============================================================================= pred@1979: * pred@1979: * This program is free software; you can redistribute it and/or modify it under pred@1979: * the terms of the GNU General Public License, version 3.0, as published by the pred@1979: * Free Software Foundation. pred@1979: * pred@1979: * This program is distributed in the hope that it will be useful, but WITHOUT pred@1979: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS pred@1979: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more pred@1979: * details. pred@1979: * pred@1979: * You should have received a copy of the GNU General Public License along with pred@1979: * this program. If not, see . pred@1979: * pred@1979: * As a special exception, AlliedModders LLC gives you permission to link the pred@1979: * code of this program (as well as its derivative works) to "Half-Life 2," the pred@1979: * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software pred@1979: * by the Valve Corporation. You must obey the GNU General Public License in pred@1979: * all respects for all other code used. Additionally, AlliedModders LLC grants pred@1979: * this exception to all derivative works. AlliedModders LLC defines further pred@1979: * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), pred@1979: * or . pred@1979: * pred@1979: * Version: $Id$ pred@1979: */ pred@1979: pred@1979: #include "criticals.h" flaminsarge@4001: #include "util.h" pred@1979: skamonkey@4092: CritManager g_CritManager; skamonkey@4092: skamonkey@4092: IForward *g_critForward = nullptr; skamonkey@4092: skamonkey@4092: SH_DECL_MANUALHOOK0(CalcIsAttackCriticalHelper, 0, 0, 0, bool); skamonkey@4092: SH_DECL_MANUALHOOK0(CalcIsAttackCriticalHelperNoCrits, 0, 0, 0, bool); skamonkey@4092: skamonkey@4092: const char TF_WEAPON_DATATABLE[] = "DT_TFWeaponBase"; pred@1979: skamonkey@4092: CritManager::CritManager() : skamonkey@4092: m_enabled(false), skamonkey@4092: m_hooksSetup(false) skamonkey@4092: { skamonkey@4092: m_entsHooked.Init(); skamonkey@4092: } flaminsarge@4001: skamonkey@4092: bool CritManager::TryEnable() skamonkey@4092: { skamonkey@4092: if (!m_hooksSetup) skamonkey@4092: { skamonkey@4092: int offset; pred@1979: skamonkey@4092: if (!g_pGameConf->GetOffset("CalcIsAttackCriticalHelper", &offset)) skamonkey@4092: { skamonkey@4092: g_pSM->LogError(myself, "Failed to find CalcIsAttackCriticalHelper offset"); skamonkey@4092: return false; skamonkey@4092: } skamonkey@4092: skamonkey@4092: SH_MANUALHOOK_RECONFIGURE(CalcIsAttackCriticalHelper, offset, 0, 0); pred@1979: skamonkey@4092: if (!g_pGameConf->GetOffset("CalcIsAttackCriticalHelperNoCrits", &offset)) skamonkey@4092: { skamonkey@4092: g_pSM->LogError(myself, "Failed to find CalcIsAttackCriticalHelperNoCrits offset"); skamonkey@4092: return false; skamonkey@4092: } skamonkey@4092: skamonkey@4092: SH_MANUALHOOK_RECONFIGURE(CalcIsAttackCriticalHelperNoCrits, offset, 0, 0); skamonkey@4092: skamonkey@4092: m_hooksSetup = true; skamonkey@4092: } pred@2619: skamonkey@4092: for (size_t i = playerhelpers->GetMaxClients() + 1; i < MAX_EDICTS; ++i) pred@1979: { skamonkey@4092: CBaseEntity *pEntity = gamehelpers->ReferenceToEntity(i); skamonkey@4092: if (pEntity == nullptr) skamonkey@4092: continue; skamonkey@4092: skamonkey@4092: IServerUnknown *pUnknown = (IServerUnknown *)pEntity; skamonkey@4092: IServerNetworkable *pNetworkable = pUnknown->GetNetworkable(); skamonkey@4092: if (!pNetworkable) skamonkey@4092: continue; skamonkey@4092: skamonkey@4092: if (!UTIL_ContainsDataTable(pNetworkable->GetServerClass()->m_pTable, TF_WEAPON_DATATABLE)) skamonkey@4092: continue; skamonkey@4092: skamonkey@4092: SH_ADD_MANUALHOOK(CalcIsAttackCriticalHelper, pEntity, SH_MEMBER(&g_CritManager, &CritManager::Hook_CalcIsAttackCriticalHelpers), false); skamonkey@4092: SH_ADD_MANUALHOOK(CalcIsAttackCriticalHelperNoCrits, pEntity, SH_MEMBER(&g_CritManager, &CritManager::Hook_CalcIsAttackCriticalHelpers), false); skamonkey@4092: skamonkey@4092: m_entsHooked.Set(i); pred@1979: } pred@1979: skamonkey@4092: m_enabled = true; pred@1979: skamonkey@4092: return true; skamonkey@4092: } pred@1979: skamonkey@4092: void CritManager::Disable() skamonkey@4092: { skamonkey@4092: int i = m_entsHooked.FindNextSetBit(playerhelpers->GetMaxClients() + 1); skamonkey@4092: for (i; i != -1; i = m_entsHooked.FindNextSetBit(i)) skamonkey@4092: { skamonkey@4092: CBaseEntity *pEntity = gamehelpers->ReferenceToEntity(i); skamonkey@4092: SH_REMOVE_MANUALHOOK(CalcIsAttackCriticalHelper, pEntity, SH_MEMBER(&g_CritManager, &CritManager::Hook_CalcIsAttackCriticalHelpers), false); skamonkey@4092: SH_REMOVE_MANUALHOOK(CalcIsAttackCriticalHelperNoCrits, pEntity, SH_MEMBER(&g_CritManager, &CritManager::Hook_CalcIsAttackCriticalHelpers), false); pred@1979: skamonkey@4092: m_entsHooked.Set(i, false); pred@1979: } pred@1979: skamonkey@4092: m_enabled = false; skamonkey@4092: } pred@1979: skamonkey@4092: void CritManager::OnEntityCreated(CBaseEntity *pEntity, const char *classname) skamonkey@4092: { skamonkey@4092: if (!m_enabled) skamonkey@4092: return; pred@1979: skamonkey@4092: IServerUnknown *pUnknown = (IServerUnknown *)pEntity; skamonkey@4092: IServerNetworkable *pNetworkable = pUnknown->GetNetworkable(); skamonkey@4092: if (!pNetworkable) skamonkey@4092: return; pred@1979: skamonkey@4092: if (!UTIL_ContainsDataTable(pNetworkable->GetServerClass()->m_pTable, TF_WEAPON_DATATABLE)) skamonkey@4092: return; skamonkey@4092: skamonkey@4092: SH_ADD_MANUALHOOK(CalcIsAttackCriticalHelper, pEntity, SH_MEMBER(&g_CritManager, &CritManager::Hook_CalcIsAttackCriticalHelpers), false); skamonkey@4092: SH_ADD_MANUALHOOK(CalcIsAttackCriticalHelperNoCrits, pEntity, SH_MEMBER(&g_CritManager, &CritManager::Hook_CalcIsAttackCriticalHelpers), false); skamonkey@4092: skamonkey@4092: m_entsHooked.Set(gamehelpers->EntityToBCompatRef(pEntity)); pred@1979: } pred@1979: skamonkey@4092: void CritManager::OnEntityDestroyed(CBaseEntity *pEntity) pred@1979: { skamonkey@4092: if (!m_enabled) skamonkey@4092: return; skamonkey@4092: skamonkey@4092: int index = gamehelpers->EntityToBCompatRef(pEntity); skamonkey@4092: if (index < 0 || index >= MAX_EDICTS) skamonkey@4092: return; skamonkey@4092: skamonkey@4092: if (!m_entsHooked.IsBitSet(index)) skamonkey@4092: return; skamonkey@4092: skamonkey@4092: SH_REMOVE_MANUALHOOK(CalcIsAttackCriticalHelper, pEntity, SH_MEMBER(&g_CritManager, &CritManager::Hook_CalcIsAttackCriticalHelpers), false); skamonkey@4092: SH_REMOVE_MANUALHOOK(CalcIsAttackCriticalHelperNoCrits, pEntity, SH_MEMBER(&g_CritManager, &CritManager::Hook_CalcIsAttackCriticalHelpers), false); pred@2619: skamonkey@4092: m_entsHooked.Set(index, false); skamonkey@4092: } skamonkey@4092: skamonkey@4092: bool CritManager::Hook_CalcIsAttackCriticalHelpers() skamonkey@4092: { skamonkey@4092: CBaseEntity *pWeapon = META_IFACEPTR(CBaseEntity); skamonkey@4092: skamonkey@4092: // If there's an invalid ent or invalid networkable here, we've got issues elsewhere. skamonkey@4092: skamonkey@4092: IServerNetworkable *pNetWeapon = ((IServerUnknown *)pWeapon)->GetNetworkable(); skamonkey@4092: ServerClass *pServerClass = pNetWeapon->GetServerClass(); skamonkey@4092: if (!pServerClass) pred@1979: { skamonkey@4092: g_pSM->LogError(myself, "Invalid server class on weapon."); skamonkey@4092: RETURN_META_VALUE(MRES_IGNORED, false); pred@1979: } pred@1979: pred@1979: sm_sendprop_info_t info; skamonkey@4092: if (!gamehelpers->FindSendPropInfo(pServerClass->GetName(), "m_hOwnerEntity", &info)) pred@1979: { skamonkey@4092: g_pSM->LogError(myself, "Could not find m_hOwnerEntity on %s", pServerClass->GetName()); skamonkey@4092: RETURN_META_VALUE(MRES_IGNORED, false); pred@1979: } pred@2136: skamonkey@4092: int returnValue = 0; pred@1979: skamonkey@4092: int ownerIndex = -1; skamonkey@4092: CBaseHandle &hndl = *(CBaseHandle *) ((intptr_t)pWeapon + info.actual_offset); skamonkey@4092: CBaseEntity *pHandleEntity = gamehelpers->ReferenceToEntity(hndl.GetEntryIndex()); skamonkey@4092: skamonkey@4092: if (pHandleEntity != nullptr && hndl == reinterpret_cast(pHandleEntity)->GetRefEHandle()) flaminsarge@4001: { skamonkey@4092: ownerIndex = hndl.GetEntryIndex(); flaminsarge@4001: } flaminsarge@4001: skamonkey@4092: g_critForward->PushCell(ownerIndex); //Client index skamonkey@4092: g_critForward->PushCell(gamehelpers->EntityToBCompatRef(pWeapon)); // Weapon index skamonkey@4092: g_critForward->PushString(gamehelpers->GetEntityClassname(pWeapon)); //Weapon classname pred@2136: g_critForward->PushCellByRef(&returnValue); //return value pred@1979: pred@1979: cell_t result = 0; pred@1979: pred@2136: g_critForward->Execute(&result); pred@1979: skamonkey@4092: if (result && returnValue) flaminsarge@4001: { skamonkey@4092: RETURN_META_VALUE(MRES_SUPERCEDE, true); skamonkey@3159: } flaminsarge@4001: skamonkey@4092: RETURN_META_VALUE(MRES_IGNORED, false); pred@2136: }