diff --git a/include/core/netvars.hpp b/include/core/netvars.hpp index dfcf1ac0..f5b8e37e 100644 --- a/include/core/netvars.hpp +++ b/include/core/netvars.hpp @@ -177,6 +177,7 @@ public: offset_t m_angEyeAnglesLocal; offset_t m_nSequence; offset_t m_flSimulationTime; + offset_t m_flAnimTime; offset_t m_angRotation; offset_t m_hOwnerEntity; diff --git a/src/core/netvars.cpp b/src/core/netvars.cpp index 0b8caf47..db81ebfc 100644 --- a/src/core/netvars.cpp +++ b/src/core/netvars.cpp @@ -29,6 +29,7 @@ void NetVars::Init() this->m_iClip2 = gNetvars.get_offset("DT_BaseCombatWeapon", "LocalWeaponData", "m_iClip2"); this->m_Collision = gNetvars.get_offset("DT_BaseEntity", "m_Collision"); this->m_flSimulationTime = gNetvars.get_offset("DT_BaseEntity", "m_flSimulationTime"); + this->m_flAnimTime = gNetvars.get_offset("DT_BaseEntity", "AnimTimeMustBeFirst", "m_flAnimTime"); this->m_angRotation = gNetvars.get_offset("DT_BaseEntity", "m_angRotation"); IF_GAME(IsTF2()) diff --git a/src/hooks/CMakeLists.txt b/src/hooks/CMakeLists.txt index b860f238..4f441b3d 100755 --- a/src/hooks/CMakeLists.txt +++ b/src/hooks/CMakeLists.txt @@ -10,6 +10,7 @@ set(files "${CMAKE_CURRENT_LIST_DIR}/CanPacket.cpp" "${CMAKE_CURRENT_LIST_DIR}/nographics.cpp" "${CMAKE_CURRENT_LIST_DIR}/others.cpp" "${CMAKE_CURRENT_LIST_DIR}/Paint.cpp" + "${CMAKE_CURRENT_LIST_DIR}/PlayerAnimFix.cpp" "${CMAKE_CURRENT_LIST_DIR}/PreDataUpdate.cpp" "${CMAKE_CURRENT_LIST_DIR}/RandomInt.cpp" "${CMAKE_CURRENT_LIST_DIR}/RunCommand.cpp" diff --git a/src/hooks/PlayerAnimFix.cpp b/src/hooks/PlayerAnimFix.cpp new file mode 100644 index 00000000..1d7c8b7e --- /dev/null +++ b/src/hooks/PlayerAnimFix.cpp @@ -0,0 +1,57 @@ +#include "common.hpp" +#include "DetourHook.hpp" + +namespace hacks::tf2::animfix +{ +DetourHook frameadvance_detour{}; +typedef float (*FrameAdvance_t)(IClientEntity *, float); + +std::vector previous_simtimes; + +// Credits to Blackfire62 for telling me how this could be realized +float FrameAdvance_hook(IClientEntity *self, float flInterval) +{ + float newInterval = flInterval; + + // Check if the entity is valid + if (self && IDX_GOOD(self->entindex()) && self->entindex() > 0 && self->entindex() <= (int) previous_simtimes.size()) + { + // Check if they are an alive player + CachedEntity *ent = ENTITY(self->entindex()); + // Set new interval based on their simtime + if (CE_GOOD(ent) && ent->m_Type() == ENTITY_PLAYER && ent->m_bAlivePlayer()) + { + float simtime = CE_FLOAT(ent, netvar.m_flSimulationTime); + // Calculate the time we need to animate by + float time_difference = simtime - previous_simtimes.at(ent->m_IDX - 1); + if (time_difference > 0.0f) + newInterval = time_difference; + previous_simtimes.at(ent->m_IDX - 1) = simtime; + // If the simtime didn't update we need to make sure that the original function also does not update + if (newInterval == 0.0f) + CE_FLOAT(ent, netvar.m_flAnimTime) = g_GlobalVars->curtime; + } + } + + FrameAdvance_t original = (FrameAdvance_t) frameadvance_detour.GetOriginalFunc(); + float return_value = original(self, newInterval); + frameadvance_detour.RestorePatch(); + return return_value; +} + +void LevelInit() +{ + previous_simtimes.clear(); + previous_simtimes.resize(g_IEngine->GetMaxClients()); +} + +static InitRoutine init([]() { + static auto FrameAdvance_signature = gSignatures.GetClientSignature("55 89 E5 57 56 53 83 EC 4C 8B 5D ? 80 BB ? ? ? ? 00 0F 85 ? ? ? ? 8B B3"); + frameadvance_detour.Init(FrameAdvance_signature, (void *) FrameAdvance_hook); + EC::Register(EC::LevelInit, LevelInit, "levelinit_animfix"); + LevelInit(); + + EC::Register( + EC::Shutdown, []() { frameadvance_detour.Shutdown(); }, "shutdown_animfix"); +}); +} // namespace hacks::tf2::animfix