diff --git a/CS2Fixes.vcxproj b/CS2Fixes.vcxproj index 58b9d288a..7aafa0709 100644 --- a/CS2Fixes.vcxproj +++ b/CS2Fixes.vcxproj @@ -223,6 +223,7 @@ + diff --git a/CS2Fixes.vcxproj.filters b/CS2Fixes.vcxproj.filters index 689bf91f3..71e88e173 100644 --- a/CS2Fixes.vcxproj.filters +++ b/CS2Fixes.vcxproj.filters @@ -298,5 +298,8 @@ Header Files\cs2_sdk\entity + + Header Files\cs2_sdk\entity + \ No newline at end of file diff --git a/src/cs2_sdk/entity/cbaseentity.h b/src/cs2_sdk/entity/cbaseentity.h index 8f77ee09d..70125fd23 100644 --- a/src/cs2_sdk/entity/cbaseentity.h +++ b/src/cs2_sdk/entity/cbaseentity.h @@ -31,6 +31,8 @@ extern CGameConfig *g_GameConfig; +class CGameUI; + class CGameSceneNode { public: @@ -134,6 +136,7 @@ class Z_CBaseEntity : public CBaseEntity SCHEMA_FIELD(float, m_flSpeed) SCHEMA_FIELD(CUtlString, m_sUniqueHammerID); SCHEMA_FIELD(CUtlSymbolLarge, m_target); + SCHEMA_FIELD(CUtlSymbolLarge, m_iGlobalname); int entindex() { return m_pEntity->m_EHandle.GetEntryIndex(); } @@ -239,6 +242,23 @@ class Z_CBaseEntity : public CBaseEntity } const char* GetName() const { return m_pEntity->m_name.String(); } + + /* Begin Custom Entities Cast */ + + [[nodiscard]] CGameUI *AsGameUI() + { + if (V_strcasecmp(GetClassname(), "logic_case") != 0) + return nullptr; + + const auto tag = m_iszPrivateVScripts.IsValid() ? m_iszPrivateVScripts.String() : nullptr; + + if (tag && V_strcasecmp(tag, "game_ui") == 0) + return reinterpret_cast(this); + + return nullptr; + } + + /* End Custom Entities Cast */ }; class SpawnPoint : public Z_CBaseEntity diff --git a/src/cs2_sdk/entity/clogiccase.h b/src/cs2_sdk/entity/clogiccase.h new file mode 100644 index 000000000..8ac85a0d0 --- /dev/null +++ b/src/cs2_sdk/entity/clogiccase.h @@ -0,0 +1,42 @@ +/** + * ============================================================================= + * CS2Fixes + * Copyright (C) 2023-2024 Source2ZE + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * 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, see . + */ + +#pragma once + +#include "../schema.h" +#include "cbaseentity.h" + +class CLogicCase : public Z_CBaseEntity +{ +public: + DECLARE_SCHEMA_CLASS(CLogicCase) +}; + +class CGameUI : public CLogicCase +{ +public: + static constexpr int SF_GAMEUI_FREEZE_PLAYER = 32; + static constexpr int SF_GAMEUI_JUMP_DEACTIVATE = 256; + + // TODO Hide Weapon requires more RE + static constexpr int SF_GAMEUI_HIDE_WEAPON = 64; + + // TODO subtick problem + static constexpr int SF_GAMEUI_USE_DEACTIVATE = 128; +}; \ No newline at end of file diff --git a/src/cs2fixes.cpp b/src/cs2fixes.cpp index d2b448267..5694e88f0 100644 --- a/src/cs2fixes.cpp +++ b/src/cs2fixes.cpp @@ -153,7 +153,7 @@ bool CS2Fixes::Load(PluginId id, ISmmAPI *ismm, char *error, size_t maxlen, bool Message( "Starting plugin.\n" ); - SH_ADD_HOOK(IServerGameDLL, GameFrame, g_pSource2Server, SH_MEMBER(this, &CS2Fixes::Hook_GameFrame), true); + SH_ADD_HOOK(IServerGameDLL, GameFrame, g_pSource2Server, SH_MEMBER(this, &CS2Fixes::Hook_GameFramePost), true); SH_ADD_HOOK(IServerGameDLL, GameServerSteamAPIActivated, g_pSource2Server, SH_MEMBER(this, &CS2Fixes::Hook_GameServerSteamAPIActivated), false); SH_ADD_HOOK(IServerGameDLL, GameServerSteamAPIDeactivated, g_pSource2Server, SH_MEMBER(this, &CS2Fixes::Hook_GameServerSteamAPIDeactivated), false); SH_ADD_HOOK(IServerGameClients, ClientActive, g_pSource2GameClients, SH_MEMBER(this, &CS2Fixes::Hook_ClientActive), true); @@ -288,7 +288,7 @@ bool CS2Fixes::Load(PluginId id, ISmmAPI *ismm, char *error, size_t maxlen, bool bool CS2Fixes::Unload(char *error, size_t maxlen) { - SH_REMOVE_HOOK(IServerGameDLL, GameFrame, g_pSource2Server, SH_MEMBER(this, &CS2Fixes::Hook_GameFrame), true); + SH_REMOVE_HOOK(IServerGameDLL, GameFrame, g_pSource2Server, SH_MEMBER(this, &CS2Fixes::Hook_GameFramePost), true); SH_REMOVE_HOOK(IServerGameDLL, GameServerSteamAPIActivated, g_pSource2Server, SH_MEMBER(this, &CS2Fixes::Hook_GameServerSteamAPIActivated), false); SH_REMOVE_HOOK(IServerGameDLL, GameServerSteamAPIDeactivated, g_pSource2Server, SH_MEMBER(this, &CS2Fixes::Hook_GameServerSteamAPIDeactivated), false); SH_REMOVE_HOOK(IServerGameClients, ClientActive, g_pSource2GameClients, SH_MEMBER(this, &CS2Fixes::Hook_ClientActive), true); @@ -620,7 +620,7 @@ void CS2Fixes::Hook_ClientDisconnect( CPlayerSlot slot, ENetworkDisconnectionRea g_playerManager->OnClientDisconnect(slot); } -void CS2Fixes::Hook_GameFrame( bool simulating, bool bFirstTick, bool bLastTick ) +void CS2Fixes::Hook_GameFramePost(bool simulating, bool bFirstTick, bool bLastTick) { VPROF_ENTER_SCOPE(__FUNCTION__); /** @@ -666,6 +666,8 @@ void CS2Fixes::Hook_GameFrame( bool simulating, bool bFirstTick, bool bLastTick if (g_bEnableZR) CZRRegenTimer::Tick(); + EntityHandler_OnGameFramePost(simulating, gpGlobals->tickcount); + VPROF_EXIT_SCOPE(); } diff --git a/src/cs2fixes.h b/src/cs2fixes.h index 8a2b2ce05..c542e4474 100644 --- a/src/cs2fixes.h +++ b/src/cs2fixes.h @@ -46,7 +46,7 @@ class CS2Fixes : public ISmmPlugin, public IMetamodListener bool loadGame, bool background ); void OnLevelShutdown(); - void Hook_GameFrame( bool simulating, bool bFirstTick, bool bLastTick ); + void Hook_GameFramePost(bool simulating, bool bFirstTick, bool bLastTick); void Hook_ClientActive( CPlayerSlot slot, bool bLoadGame, const char *pszName, uint64 xuid ); void Hook_ClientDisconnect( CPlayerSlot slot, ENetworkDisconnectionReason reason, const char *pszName, uint64 xuid, const char *pszNetworkID ); void Hook_ClientPutInServer( CPlayerSlot slot, char const *pszName, int type, uint64 xuid ); diff --git a/src/customio.cpp b/src/customio.cpp index fde9392ee..33f61663c 100644 --- a/src/customio.cpp +++ b/src/customio.cpp @@ -1,7 +1,7 @@ /** * ============================================================================= * CS2Fixes - * Copyright (C) 2023 Source2ZE + * Copyright (C) 2023-2024 Source2ZE * ============================================================================= * * This program is free software; you can redistribute it and/or modify it under @@ -31,7 +31,7 @@ #include #include -extern CGlobalVars *gpGlobals; +extern CGlobalVars* gpGlobals; struct AddOutputKey_t { @@ -140,8 +140,8 @@ static void AddOutputCustom_MoveType(Z_CBaseEntity* pInstance, const std::vector& vecArgs) { static Vector stopVelocity(0, 0, 0); - const auto value = clamp(Q_atoi(vecArgs[1].c_str()), MOVETYPE_NONE, MOVETYPE_LAST); - const auto type = static_cast(value); + const auto value = clamp(Q_atoi(vecArgs[1].c_str()), MOVETYPE_NONE, MOVETYPE_LAST); + const auto type = static_cast(value); pInstance->SetMoveType(type); @@ -185,14 +185,14 @@ static void AddOutputCustom_BaseVelocity(Z_CBaseEntity* pInstan #endif } -static void AddOutputCustom_AbsVelocity(Z_CBaseEntity *pInstance, - CEntityInstance *pActivator, - CEntityInstance *pCaller, - const std::vector &vecArgs) +static void AddOutputCustom_AbsVelocity(Z_CBaseEntity* pInstance, + CEntityInstance* pActivator, + CEntityInstance* pCaller, + const std::vector& vecArgs) { Vector velocity(clamp(Q_atof(vecArgs[1].c_str()), -4096.f, 4096.f), - clamp(Q_atof(vecArgs[2].c_str()), -4096.f, 4096.f), - clamp(Q_atof(vecArgs[3].c_str()), -4096.f, 4096.f)); + clamp(Q_atof(vecArgs[2].c_str()), -4096.f, 4096.f), + clamp(Q_atof(vecArgs[3].c_str()), -4096.f, 4096.f)); pInstance->Teleport(nullptr, nullptr, &velocity); @@ -201,7 +201,7 @@ static void AddOutputCustom_AbsVelocity(Z_CBaseEntity *pInstance, #endif } -static void AddOutputCustom_Target(Z_CBaseEntity* pInstance, +static void AddOutputCustom_Target(Z_CBaseEntity* pInstance, CEntityInstance* pActivator, CEntityInstance* pCaller, const std::vector& vecArgs) @@ -242,7 +242,7 @@ static void AddOutputCustom_Force(Z_CBaseEntity* pInstance, CEntityInstance* pCaller, const std::vector& vecArgs) { - const auto value = Q_atof(vecArgs[1].c_str()); + const auto value = Q_atof(vecArgs[1].c_str()); const auto pEntity = reinterpret_cast(pInstance); if (V_strcasecmp(pEntity->GetClassname(), "phys_thruster") == 0) { @@ -254,24 +254,24 @@ static void AddOutputCustom_Force(Z_CBaseEntity* pInstance, } } -static void AddOutputCustom_Gravity(Z_CBaseEntity *pInstance, - CEntityInstance *pActivator, - CEntityInstance *pCaller, - const std::vector &vecArgs) +static void AddOutputCustom_Gravity(Z_CBaseEntity* pInstance, + CEntityInstance* pActivator, + CEntityInstance* pCaller, + const std::vector& vecArgs) { const auto value = Q_atof(vecArgs[1].c_str()); pInstance->m_flGravityScale = value; #ifdef _DEBUG - Message("Set gravity to %f for %s\n", value, pInstance->GetName()); + Message("Set gravity to %f for %s\n", value, pInstance->GetName()); #endif } -static void AddOutputCustom_Timescale(Z_CBaseEntity *pInstance, - CEntityInstance *pActivator, - CEntityInstance *pCaller, - const std::vector &vecArgs) +static void AddOutputCustom_Timescale(Z_CBaseEntity* pInstance, + CEntityInstance* pActivator, + CEntityInstance* pCaller, + const std::vector& vecArgs) { const auto value = Q_atof(vecArgs[1].c_str()); @@ -282,10 +282,10 @@ static void AddOutputCustom_Timescale(Z_CBaseEntity *pInstance, #endif } -static void AddOutputCustom_Friction(Z_CBaseEntity *pInstance, - CEntityInstance *pActivator, - CEntityInstance *pCaller, - const std::vector &vecArgs) +static void AddOutputCustom_Friction(Z_CBaseEntity* pInstance, + CEntityInstance* pActivator, + CEntityInstance* pCaller, + const std::vector& vecArgs) { const auto value = Q_atof(vecArgs[1].c_str()); @@ -296,16 +296,16 @@ static void AddOutputCustom_Friction(Z_CBaseEntity *pInstance, #endif } -static void AddOutputCustom_Speed(Z_CBaseEntity *pInstance, - CEntityInstance *pActivator, - CEntityInstance *pCaller, - const std::vector &vecArgs) +static void AddOutputCustom_Speed(Z_CBaseEntity* pInstance, + CEntityInstance* pActivator, + CEntityInstance* pCaller, + const std::vector& vecArgs) { if (!pInstance->IsPawn()) return; - CCSPlayerPawn *pPawn = (CCSPlayerPawn*)pInstance; - CCSPlayerController *pController = (CCSPlayerController*)pPawn->GetOriginalController(); + const auto pPawn = reinterpret_cast(pInstance); + const auto pController = pPawn->GetOriginalController(); if (!pController || !pController->IsConnected()) return; @@ -319,15 +319,15 @@ static void AddOutputCustom_Speed(Z_CBaseEntity *pInstance, #endif } -static void AddOutputCustom_RunSpeed(Z_CBaseEntity *pInstance, - CEntityInstance *pActivator, - CEntityInstance *pCaller, - const std::vector &vecArgs) +static void AddOutputCustom_RunSpeed(Z_CBaseEntity* pInstance, + CEntityInstance* pActivator, + CEntityInstance* pCaller, + const std::vector& vecArgs) { if (!pInstance->IsPawn()) return; - CCSPlayerPawn *pPawn = reinterpret_cast(pInstance); + const auto pPawn = reinterpret_cast(pInstance); const auto value = Q_atof(vecArgs[1].c_str()); @@ -397,7 +397,6 @@ bool CustomIO_HandleInput(CEntityInstance* pInstance, return false; } - std::string g_sBurnParticle = "particles/burning_fx/burning_character_b.vpcf"; FAKE_STRING_CVAR(cs2f_burn_particle, "The particle to use for burning entities", g_sBurnParticle, false); @@ -410,26 +409,26 @@ FAKE_FLOAT_CVAR(cs2f_burn_slowdown, "The slowdown of each burn damage tick as a float g_flBurnInterval = 0.3f; FAKE_FLOAT_CVAR(cs2f_burn_interval, "The interval between burn damage ticks", g_flBurnInterval, 0.3f, false); -bool IgnitePawn(CCSPlayerPawn *pPawn, float flDuration, Z_CBaseEntity *pInflictor, Z_CBaseEntity *pAttacker, Z_CBaseEntity *pAbility, DamageTypes_t nDamageType) +bool IgnitePawn(CCSPlayerPawn* pPawn, float flDuration, Z_CBaseEntity* pInflictor, Z_CBaseEntity* pAttacker, Z_CBaseEntity* pAbility, DamageTypes_t nDamageType) { - CParticleSystem *pParticleEnt = (CParticleSystem*)pPawn->m_hEffectEntity().Get(); + auto pParticleEnt = reinterpret_cast(pPawn->m_hEffectEntity().Get()); // This guy is already burning, don't ignite again if (pParticleEnt) - { + { // Override the end time instead of just adding to it so players who get a ton of ignite inputs don't burn forever pParticleEnt->m_flDissolveStartTime = gpGlobals->curtime + flDuration; return true; } - Vector vecOrigin = pPawn->GetAbsOrigin(); + const auto vecOrigin = pPawn->GetAbsOrigin(); - pParticleEnt = (CParticleSystem *)CreateEntityByName("info_particle_system"); + pParticleEnt = reinterpret_cast(CreateEntityByName("info_particle_system")); pParticleEnt->m_bStartActive(true); pParticleEnt->m_iszEffectName(g_sBurnParticle.c_str()); pParticleEnt->m_hControlPointEnts[0] = pPawn; - pParticleEnt->m_flDissolveStartTime = gpGlobals->curtime + flDuration; // Store the end time in the particle itself so we can increment if needed + pParticleEnt->m_flDissolveStartTime = gpGlobals->curtime + flDuration; // Store the end time in the particle itself so we can increment if needed pParticleEnt->Teleport(&vecOrigin, nullptr, nullptr); pParticleEnt->DispatchSpawn(); @@ -443,14 +442,13 @@ bool IgnitePawn(CCSPlayerPawn *pPawn, float flDuration, Z_CBaseEntity *pInflicto CHandle hAttacker(pAttacker); CHandle hAbility(pAbility); - new CTimer(0.f, false, [hPawn, hInflictor, hAttacker, hAbility, nDamageType]() - { - CCSPlayerPawn *pPawn = hPawn.Get(); + new CTimer(0.f, false, [hPawn, hInflictor, hAttacker, hAbility, nDamageType]() { + CCSPlayerPawn* pPawn = hPawn.Get(); if (!pPawn) return -1.f; - CParticleSystem *pParticleEnt = (CParticleSystem*)pPawn->m_hEffectEntity().Get(); + const auto pParticleEnt = reinterpret_cast(pPawn->m_hEffectEntity().Get()); if (!pParticleEnt) return -1.f; @@ -475,7 +473,7 @@ bool IgnitePawn(CCSPlayerPawn *pPawn, float flDuration, Z_CBaseEntity *pInflicto // Damage doesn't apply if the inflictor is null if (!hInflictor.Get()) info.m_hInflictor.Set(hAttacker); - + pPawn->TakeDamage(info); pPawn->m_flVelocityModifier = g_flBurnSlowdown; diff --git a/src/detours.cpp b/src/detours.cpp index 3b70cb6ce..3e054fdd3 100644 --- a/src/detours.cpp +++ b/src/detours.cpp @@ -369,17 +369,17 @@ bool FASTCALL Detour_CEntityIdentity_AcceptInput(CEntityIdentity* pThis, CUtlSym ZR_Detour_CEntityIdentity_AcceptInput(pThis, pInputName, pActivator, pCaller, value, nOutputID); // Handle KeyValue(s) - if (!V_strnicmp(pInputName->String(), "KeyValue", 8)) - { - if ((value->m_type == FIELD_CSTRING || value->m_type == FIELD_STRING) && value->m_pszString) - { - // always const char*, even if it's FIELD_STRING (that is bug string from lua 'EntFire') - return CustomIO_HandleInput(pThis->m_pInstance, value->m_pszString, pActivator, pCaller); - } - Message("Invalid value type for input %s\n", pInputName->String()); - return false; - } - else if (!V_strnicmp(pInputName->String(), "IgniteL", 7)) // Override IgniteLifetime + if (!V_strnicmp(pInputName->String(), "KeyValue", 8)) + { + if ((value->m_type == FIELD_CSTRING || value->m_type == FIELD_STRING) && value->m_pszString) + { + // always const char*, even if it's FIELD_STRING (that is bug string from lua 'EntFire') + return CustomIO_HandleInput(pThis->m_pInstance, value->m_pszString, pActivator, pCaller); + } + Message("Invalid value type for input %s\n", pInputName->String()); + return false; + } + if (!V_strnicmp(pInputName->String(), "IgniteL", 7)) // Override IgniteLifetime { float flDuration = 0.f; @@ -388,13 +388,20 @@ bool FASTCALL Detour_CEntityIdentity_AcceptInput(CEntityIdentity* pThis, CUtlSym else flDuration = value->m_float; - CCSPlayerPawn *pPawn = (CCSPlayerPawn*)pThis->m_pInstance; + CCSPlayerPawn *pPawn = reinterpret_cast(pThis->m_pInstance); if (pPawn->IsPawn() && IgnitePawn(pPawn, flDuration, pPawn, pPawn)) return true; } + else if (const auto pGameUI = reinterpret_cast(pThis->m_pInstance)->AsGameUI()) + { + if (!V_strcasecmp(pInputName->String(), "Activate")) + return CGameUIHandler::OnActivate(pGameUI, reinterpret_cast(pActivator)); + if (!V_strcasecmp(pInputName->String(), "Deactivate")) + return CGameUIHandler::OnDeactivate(pGameUI, reinterpret_cast(pActivator)); + } - return CEntityIdentity_AcceptInput(pThis, pInputName, pActivator, pCaller, value, nOutputID); + return CEntityIdentity_AcceptInput(pThis, pInputName, pActivator, pCaller, value, nOutputID); } bool g_bBlockNavLookup = false; diff --git a/src/entities.cpp b/src/entities.cpp index 967ca9b4f..426ba0fc2 100644 --- a/src/entities.cpp +++ b/src/entities.cpp @@ -19,9 +19,14 @@ #include "entities.h" +#include "ctimer.h" #include "entity.h" +#include "entity/cbaseplayercontroller.h" #include "entity/ccsplayerpawn.h" #include "entity/cgameplayerequip.h" +#include "entity/clogiccase.h" + +// #define ENTITY_HANDLER_ASSERTION class InputData_t { @@ -32,9 +37,9 @@ class InputData_t int nOutputID; }; -bool StripPlayer(CCSPlayerPawn* pPawn) +inline bool StripPlayer(CCSPlayerPawn* pPawn) { - const auto pItemServices = pPawn->m_pItemServices(); + const auto pItemServices = pPawn->m_pItemServices(); if (!pItemServices) return false; @@ -44,6 +49,34 @@ bool StripPlayer(CCSPlayerPawn* pPawn) return true; } +// Must be called in GameFramePre +inline void DelayInput(Z_CBaseEntity* pCaller, const char* input, const char* param = "") +{ + const auto eh = pCaller->GetHandle(); + + new CTimer(0.f, false, [eh, input, param]() { + if (const auto entity = reinterpret_cast(eh.Get())) + entity->AcceptInput(input, param, nullptr, entity); + + return -1.f; + }); +} + +// Must be called in GameFramePre +inline void DelayInput(Z_CBaseEntity* pCaller, Z_CBaseEntity* pActivator, const char* input, const char* param = "") +{ + const auto eh = pCaller->GetHandle(); + const auto ph = pActivator->GetHandle(); + + new CTimer(0.f, false, [eh, ph, input, param]() { + const auto player = reinterpret_cast(ph.Get()); + if (const auto entity = reinterpret_cast(eh.Get())) + entity->AcceptInput(input, param, player, entity); + + return -1.f; + }); +} + namespace CGamePlayerEquipHandler { @@ -116,4 +149,239 @@ void TriggerForActivatedPlayer(CGamePlayerEquip* pEntity, InputData_t* pInput) pItemServices->GiveNamedItem(pszWeapon); } -} // namespace CGamePlayerEquipHandler \ No newline at end of file +} // namespace CGamePlayerEquipHandler + +namespace CGameUIHandler +{ +constexpr uint64 BAD_BUTTONS = ~0; + +struct CGameUIState +{ + CHandle m_pPlayer; + uint64 m_nButtonState; + + CGameUIState() : + m_pPlayer(CBaseHandle()), m_nButtonState(BAD_BUTTONS) {} + CGameUIState(const CGameUIState& other) = delete; + + CGameUIState(CCSPlayerPawn* pawn, uint64 buttons) : + m_pPlayer(pawn), m_nButtonState(buttons) {} + + [[nodiscard]] CCSPlayerPawn* GetPlayer() const { return m_pPlayer.Get(); } + + void UpdateButtons(uint64 buttons) { m_nButtonState = buttons; } +}; + +static std::unordered_map s_repository; + +inline uint64 GetButtons(CPlayer_MovementServices* pMovement) +{ + const auto buttonStates = pMovement->m_nButtons().m_pButtonStates(); + const auto buttons = buttonStates[0]; + return buttons; +} + +inline uint64 GameUIThink(CGameUI* pEntity, CCSPlayerPawn* pPlayer, uint32 lastButtons) +{ + const auto pMovement = pPlayer->m_pMovementServices(); + if (!pMovement) + return BAD_BUTTONS; + + const auto spawnFlags = pEntity->m_spawnflags(); + const auto buttons = GetButtons(pMovement); + + if ((spawnFlags & CGameUI::SF_GAMEUI_JUMP_DEACTIVATE) != 0 && (buttons & IN_JUMP) != 0) + { + DelayInput(pEntity, pPlayer, "Deactivate"); + return BAD_BUTTONS; + } + + const auto nButtonsChanged = buttons ^ lastButtons; + + // W + if ((nButtonsChanged & IN_FORWARD) != 0) + { + pEntity->AcceptInput("InValue", (lastButtons & IN_FORWARD) != 0 ? "UnpressedForward" : "PressedForward", pPlayer, pEntity); + } + + // A + if ((nButtonsChanged & IN_MOVELEFT) != 0) + { + pEntity->AcceptInput("InValue", (lastButtons & IN_MOVELEFT) != 0 ? "UnpressedMoveLeft" : "PressedMoveLeft", pPlayer, pEntity); + } + + // S + if ((nButtonsChanged & IN_BACK) != 0) + { + pEntity->AcceptInput("InValue", (lastButtons & IN_BACK) != 0 ? "UnpressedBack" : "PressedBack", pPlayer, pEntity); + } + + // D + if ((nButtonsChanged & IN_MOVERIGHT) != 0) + { + pEntity->AcceptInput("InValue", (lastButtons & IN_MOVERIGHT) != 0 ? "UnpressedMoveRight" : "PressedMoveRight", pPlayer, pEntity); + } + + // Attack + if ((nButtonsChanged & IN_ATTACK) != 0) + { + pEntity->AcceptInput("InValue", (lastButtons & IN_ATTACK) != 0 ? "UnpressedAttack" : "PressedAttack", pPlayer, pEntity); + } + + // Attack2 + if ((nButtonsChanged & IN_ATTACK2) != 0) + { + pEntity->AcceptInput("InValue", (lastButtons & IN_ATTACK2) != 0 ? "UnpressedAttack2" : "PressedAttack2", pPlayer, pEntity); + } + + // Speed + if ((nButtonsChanged & IN_SPEED) != 0) + { + pEntity->AcceptInput("InValue", (lastButtons & IN_SPEED) != 0 ? "UnpressedSpeed" : "PressedSpeed", pPlayer, pEntity); + } + + // Duck + if ((nButtonsChanged & IN_DUCK) != 0) + { + pEntity->AcceptInput("InValue", (lastButtons & IN_DUCK) != 0 ? "UnpressedDuck" : "PressedDuck", pPlayer, pEntity); + } + + return buttons; +} + +// [Kxnrl]: Must be called on game frame pre, and timer done in post! +void RunThink(int tick) +{ + // validate + for (auto it = s_repository.begin(); it != s_repository.end();) + { + const auto entity = CHandle(it->first).Get(); + if (!entity) + { + it = s_repository.erase(it); +#ifdef ENTITY_HANDLER_ASSERTION + Message("Remove Entity %d due to invalid.\n", CBaseHandle(it->first).GetEntryIndex()); +#endif + } + else + { + ++it; + } + } + + // think every 4 tick + if ((tick & 4) != 0) + return; + + for (auto& [key, state] : s_repository) + { + const auto entity = CHandle(key).Get(); + const auto player = state.GetPlayer(); + + if (!player || !player->IsPawn()) + { + DelayInput(entity, "Deactivate"); +#ifdef ENTITY_HANDLER_ASSERTION + Message("Deactivate Entity %d due to invalid player.\n", entity->entindex()); +#endif + continue; + } + + if (!player->IsAlive()) + { + DelayInput(entity, player, "Deactivate"); +#ifdef ENTITY_HANDLER_ASSERTION + Message("Deactivate Entity %d due to player dead.\n", entity->entindex()); +#endif + continue; + } + + const auto newButtons = GameUIThink(entity, player, state.m_nButtonState); + + if (newButtons != BAD_BUTTONS) + { + state.UpdateButtons(newButtons); + } + } +} + +bool OnActivate(CGameUI* pEntity, Z_CBaseEntity* pActivator) +{ + if (!pActivator || !pActivator->IsPawn()) + return false; + + const auto pPlayer = reinterpret_cast(pActivator); + + const auto pMovement = pPlayer->m_pMovementServices(); + if (!pMovement) + return false; + + if ((pEntity->m_spawnflags() & CGameUI::SF_GAMEUI_FREEZE_PLAYER) != 0) + pPlayer->m_fFlags(pPlayer->m_fFlags() | FL_ATCONTROLS); + + const CBaseHandle handle = pEntity->GetHandle(); + const auto key = static_cast(handle.ToInt()); + + DelayInput(pEntity, pPlayer, "InValue", "PlayerOn"); + + s_repository[key] = CGameUIState(pPlayer, GetButtons(pMovement) & ~IN_USE); + +#ifdef ENTITY_HANDLER_ASSERTION + Message("Activate Entity %d<%u> -> %s\n", pEntity->entindex(), key, pPlayer->GetController()->GetPlayerName()); +#endif + + return true; +} + +bool OnDeactivate(CGameUI* pEntity, Z_CBaseEntity* pActivator) +{ + const CBaseHandle handle = CHandle(pEntity); + const auto key = static_cast(handle.ToInt()); + const auto it = s_repository.find(key); + + if (it == s_repository.end()) + { +#ifdef ENTITY_HANDLER_ASSERTION + Message("Deactivate Entity %d -> but does not exists <%u>\n", pEntity->entindex(), key); +#endif + return false; + } + + if (const auto pPlayer = it->second.GetPlayer()) + { + if ((pEntity->m_spawnflags() & CGameUI::SF_GAMEUI_FREEZE_PLAYER) != 0) + pPlayer->m_fFlags(pPlayer->m_fFlags() & ~FL_ATCONTROLS); + + DelayInput(pEntity, pPlayer, "InValue", "PlayerOff"); + +#ifdef ENTITY_HANDLER_ASSERTION + Message("Deactivate Entity %d -> %s\n", pEntity->entindex(), pPlayer->GetController()->GetPlayerName()); +#endif + } + else + { +#ifdef ENTITY_HANDLER_ASSERTION + Message("Deactivate Entity %d -> nullptr\n", pEntity->entindex()); +#endif + } + + s_repository.erase(it); + + return true; +} + +} // namespace CGameUIHandler + +void EntityHandler_OnGameFramePre(bool simulate, int tick) +{ + if (!simulate) + return; + + CGameUIHandler::RunThink(tick); +} + +void EntityHandler_OnGameFramePost(bool simulate, int tick) +{ + if (!simulate) + return; +} diff --git a/src/entities.h b/src/entities.h index 6b1e1f4ea..2a22424fe 100644 --- a/src/entities.h +++ b/src/entities.h @@ -21,6 +21,8 @@ class InputData_t; class CGamePlayerEquip; +class Z_CBaseEntity; +class CGameUI; namespace CGamePlayerEquipHandler { @@ -28,3 +30,13 @@ void Use(CGamePlayerEquip* pEntity, InputData_t* pInput); void TriggerForAllPlayers(CGamePlayerEquip* pEntity, InputData_t* pInput); void TriggerForActivatedPlayer(CGamePlayerEquip* pEntity, InputData_t* pInput); } // namespace CGamePlayerEquipHandler + +namespace CGameUIHandler +{ +bool OnActivate(CGameUI* pEntity, Z_CBaseEntity* pActivator); +bool OnDeactivate(CGameUI* pEntity, Z_CBaseEntity* pActivator); +void RunThink(int tick); +} // namespace CGameUIHandler + +void EntityHandler_OnGameFramePre(bool simulate, int tick); +void EntityHandler_OnGameFramePost(bool simulate, int tick); \ No newline at end of file diff --git a/src/gamesystem.cpp b/src/gamesystem.cpp index 065d5f403..9a284a6b7 100644 --- a/src/gamesystem.cpp +++ b/src/gamesystem.cpp @@ -23,9 +23,11 @@ #include "gamesystem.h" #include "zombiereborn.h" #include "adminsystem.h" +#include "entities.h" #include "tier0/memdbgon.h" +extern CGlobalVars* gpGlobals; extern CGameConfig *g_GameConfig; CBaseGameSystemFactory **CBaseGameSystemFactory::sm_pFirst = nullptr; @@ -83,6 +85,7 @@ GS_EVENT_MEMBER(CGameSystem, ServerPreEntityThink) { // This could've gone into CS2Fixes::Hook_GameFrame but I've kept it here as an example g_playerManager->FlashLightThink(); + EntityHandler_OnGameFramePre(gpGlobals->m_bInSimulation, gpGlobals->tickcount); } // Called every frame after entities think