diff --git a/BunnymodXT/cvars.hpp b/BunnymodXT/cvars.hpp index e7d92843..6c42f8bf 100644 --- a/BunnymodXT/cvars.hpp +++ b/BunnymodXT/cvars.hpp @@ -234,7 +234,11 @@ X(bxt_splits_end_on_last_split, "0") \ X(bxt_ch_hook_speed, "869") \ X(bxt_ch_checkpoint_with_vel, "1") \ - X(bxt_ch_checkpoint_onground_only, "0") + X(bxt_ch_checkpoint_onground_only, "0") \ + X(bxt_ch_trigger_tp_keeps_momentum, "0") \ + X(bxt_ch_trigger_tp_keeps_momentum_velocity, "1") \ + X(bxt_ch_trigger_tp_keeps_momentum_velocity_redirect, "0") \ + X(bxt_ch_trigger_tp_keeps_momentum_viewangles, "1") class CVarWrapper { diff --git a/BunnymodXT/modules/ServerDLL.cpp b/BunnymodXT/modules/ServerDLL.cpp index 598d4643..383da2b6 100644 --- a/BunnymodXT/modules/ServerDLL.cpp +++ b/BunnymodXT/modules/ServerDLL.cpp @@ -98,6 +98,11 @@ extern "C" int __cdecl _ZN11CBaseEntity9IsInWorldEv(void *thisptr) { return ServerDLL::HOOKED_CBaseEntity__IsInWorld_Linux(thisptr); } + +extern "C" void __cdecl _ZN12CBaseTrigger13TeleportTouchEP11CBaseEntity(void* thisptr, void* pOther) +{ + return ServerDLL::HOOKED_CBaseTrigger__TeleportTouch_Linux(thisptr, pOther); +} #endif void ServerDLL::Hook(const std::wstring& moduleName, void* moduleHandle, void* moduleBase, size_t moduleLength, bool needToIntercept) @@ -160,7 +165,9 @@ void ServerDLL::Hook(const std::wstring& moduleName, void* moduleHandle, void* m ORIG_PM_Duck, HOOKED_PM_Duck, ORIG_PM_UnDuck, HOOKED_PM_UnDuck, ORIG_CBaseEntity__IsInWorld, HOOKED_CBaseEntity__IsInWorld, - ORIG_CBaseEntity__IsInWorld_Linux, HOOKED_CBaseEntity__IsInWorld_Linux); + ORIG_CBaseEntity__IsInWorld_Linux, HOOKED_CBaseEntity__IsInWorld_Linux, + ORIG_CBaseTrigger__TeleportTouch, HOOKED_CBaseTrigger__TeleportTouch, + ORIG_CBaseTrigger__TeleportTouch_Linux, HOOKED_CBaseTrigger__TeleportTouch_Linux); } } @@ -213,7 +220,9 @@ void ServerDLL::Unhook() ORIG_PM_Duck, ORIG_PM_UnDuck, ORIG_CBaseEntity__IsInWorld, - ORIG_CBaseEntity__IsInWorld_Linux); + ORIG_CBaseEntity__IsInWorld_Linux, + ORIG_CBaseTrigger__TeleportTouch, + ORIG_CBaseTrigger__TeleportTouch_Linux); } Clear(); @@ -285,6 +294,8 @@ void ServerDLL::Clear() ORIG_PM_UnDuck = nullptr; ORIG_CBaseEntity__IsInWorld = nullptr; ORIG_CBaseEntity__IsInWorld_Linux = nullptr; + ORIG_CBaseTrigger__TeleportTouch = nullptr; + ORIG_CBaseTrigger__TeleportTouch_Linux = nullptr; ppmove = nullptr; offPlayerIndex = 0; offOldbuttons = 0; @@ -1576,6 +1587,19 @@ void ServerDLL::FindStuff() } } + { + ORIG_CBaseTrigger__TeleportTouch = reinterpret_cast<_CBaseTrigger__TeleportTouch>(MemUtils::GetSymbolAddress(m_Handle, "?TeleportTouch@CBaseTrigger@@QAEXPAVCBaseEntity@@@Z")); + if (ORIG_CBaseTrigger__TeleportTouch) + EngineDevMsg("[server dll] Found CBaseTrigger::TriggerTouch at %p.\n", ORIG_CBaseTrigger__TeleportTouch); + else { + ORIG_CBaseTrigger__TeleportTouch_Linux = reinterpret_cast<_CBaseTrigger__TeleportTouch_Linux>(MemUtils::GetSymbolAddress(m_Handle, "_ZN12CBaseTrigger13TeleportTouchEP11CBaseEntity")); + if (ORIG_CBaseTrigger__TeleportTouch_Linux) + EngineDevMsg("[server dll] Found CBaseTrigger::TriggerTouch [Linux] at %p.\n", ORIG_CBaseTrigger__TeleportTouch_Linux); + else + EngineDevWarning("[server dll] Could not find CBaseTrigger::TriggerTouch.\n"); + } + } + if (!pEngfuncs) { pEngfuncs = reinterpret_cast(MemUtils::GetSymbolAddress(m_Handle, "g_engfuncs")); @@ -1637,6 +1661,12 @@ void ServerDLL::RegisterCVarsAndCommands() REG(bxt_cof_disable_monsters_teleport_to_spawn_after_load); if (ORIG_CTriggerCamera__FollowTarget && is_cof) REG(bxt_cof_allow_skipping_all_cutscenes); + if (ORIG_CBaseTrigger__TeleportTouch || ORIG_CBaseTrigger__TeleportTouch_Linux) { + REG(bxt_ch_trigger_tp_keeps_momentum); + REG(bxt_ch_trigger_tp_keeps_momentum_velocity); + REG(bxt_ch_trigger_tp_keeps_momentum_velocity_redirect); + REG(bxt_ch_trigger_tp_keeps_momentum_viewangles); + } REG(bxt_splits_print); REG(bxt_splits_print_times_at_end); @@ -3310,4 +3340,111 @@ HOOK_DEF_1(ServerDLL, int, __cdecl, CBaseEntity__IsInWorld_Linux, void*, thisptr return ORIG_CBaseEntity__IsInWorld_Linux(thisptr); } -#undef ALERT +bool ServerDLL::IsPlayer(edict_t *ent) +{ + // https://github.com/ValveSoftware/halflife/blob/c7240b965743a53a29491dd49320c88eecf6257b/dlls/player.cpp#L2850 + + auto &hw = HwDLL::GetInstance(); + + if (strcmp(hw.GetString(ent->v.classname), "player") != 0) + return false; + + if (!(ent->v.flags & FL_CLIENT)) + return false; + + if (pEngfuncs && hw.ppGlobals) + { + int index = pEngfuncs->pfnIndexOfEdict(ent); + + if ((index < 1) || (index > hw.ppGlobals->maxClients)) // gGlobalVariables.maxClients = svs.maxclients + return false; + } + + return true; +} + +void TriggerTpKeepsMomentumRestore(Vector prev_vel, Vector prev_view, Vector prev_angles, Vector prev_basevelocity, entvars_t *pev, enginefuncs_t *pEngfuncs) +{ + // Set velocity before viewangles because viewangles will mess with the velocity angle for redirection + if (CVars::bxt_ch_trigger_tp_keeps_momentum_velocity.GetBool()) { + if (CVars::bxt_ch_trigger_tp_keeps_momentum_velocity_redirect.GetBool()) { + // https://github.com/fireblizzard/agmod/blob/bf06e4ffd31c1427784685118820e15552803bcb/dlls/triggers.cpp#L1935 + // After teleportation, pevToucher has the same viewangles as pentTarget. + Vector vecAngles = Vector(0, pev->v_angle.y, 0); + Vector vecForward; + + pEngfuncs->pfnAngleVectors(vecAngles, vecForward, nullptr, nullptr); + + // For velocity + float xy_vel = prev_vel.Length2D(); + + pev->velocity.x = vecForward.x * xy_vel; + pev->velocity.y = vecForward.y * xy_vel; + + // For base velocity + float xy_basevel = prev_basevelocity.Length2D(); + + pev->basevelocity.x = vecForward.x * xy_basevel; + pev->basevelocity.y = vecForward.y * xy_basevel; + } else { + pev->velocity = prev_vel; + pev->basevelocity = prev_basevelocity; + } + } + + if (CVars::bxt_ch_trigger_tp_keeps_momentum_viewangles.GetBool()) { + pev->fixangle = 0; // cannot change angle if it is 1 + pev->v_angle = prev_view; + pev->angles = prev_angles; + } +} + +HOOK_DEF_3(ServerDLL, void, __fastcall, CBaseTrigger__TeleportTouch, void*, thisptr, int, edx, void*, pOther) +{ + auto is_bxt_ch_trigger_tp_keeps_momentum_enabled = CVars::sv_cheats.GetBool() && CVars::bxt_ch_trigger_tp_keeps_momentum.GetBool(); + + entvars_t *pev = *reinterpret_cast(reinterpret_cast(pOther) + 4); + Vector prev_vel; + Vector prev_view; + Vector prev_angles; + Vector prev_basevelocity; + + if (pev) { + prev_vel = pev->velocity; + prev_view = pev->v_angle; + prev_angles = pev->angles; + prev_basevelocity = pev->basevelocity; + } + + ORIG_CBaseTrigger__TeleportTouch(thisptr, edx, pOther); + + if (is_bxt_ch_trigger_tp_keeps_momentum_enabled && pev && pEngfuncs && IsPlayer(pev->pContainingEntity)) { + TriggerTpKeepsMomentumRestore(prev_vel, prev_vel, prev_angles, prev_basevelocity, pev, pEngfuncs); + } +} + +HOOK_DEF_2(ServerDLL, void, __cdecl, CBaseTrigger__TeleportTouch_Linux, void*, thisptr, void*, pOther) +{ + auto is_bxt_ch_trigger_tp_keeps_momentum_enabled = CVars::sv_cheats.GetBool() && CVars::bxt_ch_trigger_tp_keeps_momentum.GetBool(); + + entvars_t *pev = *reinterpret_cast(reinterpret_cast(pOther) + 4); + Vector prev_vel; + Vector prev_view; + Vector prev_angles; + Vector prev_basevelocity; + + if (pev) { + prev_vel = pev->velocity; + prev_view = pev->v_angle; + prev_angles = pev->angles; + prev_basevelocity = pev->basevelocity; + } + + ORIG_CBaseTrigger__TeleportTouch_Linux(thisptr, pOther); + + if (is_bxt_ch_trigger_tp_keeps_momentum_enabled && pev && pEngfuncs && IsPlayer(pev->pContainingEntity)) { + TriggerTpKeepsMomentumRestore(prev_vel, prev_vel, prev_angles, prev_basevelocity, pev, pEngfuncs); + } +} + +#undef ALERT \ No newline at end of file diff --git a/BunnymodXT/modules/ServerDLL.hpp b/BunnymodXT/modules/ServerDLL.hpp index 0b584416..a45de856 100644 --- a/BunnymodXT/modules/ServerDLL.hpp +++ b/BunnymodXT/modules/ServerDLL.hpp @@ -62,6 +62,8 @@ class ServerDLL : public IHookableDirFilter HOOK_DECL(void, __fastcall, CTriggerCamera__FollowTarget, void* thisptr) HOOK_DECL(int, __fastcall, CBaseEntity__IsInWorld, void* thisptr) HOOK_DECL(int, __cdecl, CBaseEntity__IsInWorld_Linux, void* thisptr) + HOOK_DECL(void, __fastcall, CBaseTrigger__TeleportTouch, void* thisptr, int edx, void* pOther) + HOOK_DECL(void, __cdecl, CBaseTrigger__TeleportTouch_Linux, void* thisptr, void* pOther) public: static ServerDLL& GetInstance() @@ -115,6 +117,8 @@ class ServerDLL : public IHookableDirFilter void GiveNamedItem(entvars_t *pev, int istr); + bool IsPlayer(edict_t *ent); + private: ServerDLL() : IHookableDirFilter({ L"dlls", L"cl_dlls"}) {}; ServerDLL(const ServerDLL&);