From 742bb9269d6f1a8ece6de786c03bf13bacc366d8 Mon Sep 17 00:00:00 2001 From: Kyle Date: Sat, 19 Oct 2024 11:47:02 +0800 Subject: [PATCH] feat: ``point_viewcontrol`` (#306) * update pb * point_viewcontrol * modelentity only * wip remove at control add fov changer fix compatibility with gamerules * finish feat disarm fix disable handle * fix: linux build * fix shutdown * fix view entity * skip bot * safe check * fix: crash with bot * think every tick * feat: ``EnableCameraAll`` / ``DisableCameraAll`` * update gamedata * Revert "update pb" This reverts commit 5c47f5f4c0cab3678667b1a085f8347f5bcc4707. --------- Co-authored-by: Alex --- CS2Fixes.vcxproj | 2 + CS2Fixes.vcxproj.filters | 6 + gamedata/cs2fixes.games.txt | 12 + src/cs2_sdk/entity/cbaseentity.h | 14 + src/cs2_sdk/entity/cbasemodelentity.h | 9 +- src/cs2_sdk/entity/cbaseplayercontroller.h | 1 + src/cs2_sdk/entity/cbaseplayerpawn.h | 2 + src/cs2_sdk/entity/ccsplayerpawn.h | 5 + src/cs2_sdk/entity/ccsweaponbase.h | 10 + src/cs2_sdk/entity/cgamerules.h | 1 + src/cs2_sdk/entity/cpointviewcontrol.h | 69 +++++ src/cs2_sdk/entity/services.h | 22 +- src/cs2fixes.h | 7 + src/detours.cpp | 90 +++++- src/detours.h | 11 +- src/entities.cpp | 334 ++++++++++++++++++++- src/entities.h | 15 +- src/entitylistener.cpp | 7 +- src/events.cpp | 3 + 19 files changed, 597 insertions(+), 23 deletions(-) create mode 100644 src/cs2_sdk/entity/cpointviewcontrol.h diff --git a/CS2Fixes.vcxproj b/CS2Fixes.vcxproj index dc5095c2..b0e5926f 100644 --- a/CS2Fixes.vcxproj +++ b/CS2Fixes.vcxproj @@ -237,9 +237,11 @@ + + diff --git a/CS2Fixes.vcxproj.filters b/CS2Fixes.vcxproj.filters index 0b7637ab..89e84892 100644 --- a/CS2Fixes.vcxproj.filters +++ b/CS2Fixes.vcxproj.filters @@ -349,5 +349,11 @@ Header Files\cs2_sdk\entity + + Header Files + + + Header Files + \ No newline at end of file diff --git a/gamedata/cs2fixes.games.txt b/gamedata/cs2fixes.games.txt index b45964a7..9db4904d 100644 --- a/gamedata/cs2fixes.games.txt +++ b/gamedata/cs2fixes.games.txt @@ -345,6 +345,18 @@ "windows" "\x48\x89\x5C\x24\x20\x48\x89\x4C\x24\x08\x55\x56\x41\x55" "linux" "\x55\x48\x89\xE5\x41\x57\x41\x56\x49\x89\xCE\x41\x55\x4D\x89\xC5\x41\x54\x49\x89\xD4\x53\x4C\x89\xCB" } + "CBasePlayerPawn_GetEyePosition" + { + "library" "server" + "windows" "\x48\x89\x5C\x24\x2A\x57\x48\x83\xEC\x2A\x48\x8B\xF9\x48\x8B\xDA\x48\x8B\x89\x2A\x2A\x2A\x2A\x48\x85\xC9\x74\x2A\x48\x8B\x01" + "linux" "\x55\x48\x89\xE5\x41\x54\x49\x89\xFC\x48\x83\xEC\x2A\x48\x8B\xBF\x2A\x2A\x2A\x2A\x48\x85\xFF\x74\x2A\x48\x8B\x07\x48\x8D\x15" + } + "CBasePlayerPawn_GetEyeAngles" + { + "library" "server" + "windows" "\x48\x89\x5C\x24\x2A\x57\x48\x81\xEC\x2A\x2A\x2A\x2A\x48\x8B\xF9\x48\x8B\xDA\x48\x8B\x89" + "linux" "\x55\x48\x89\xE5\x41\x55\x41\x54\x49\x89\xFC\x48\x83\xEC\x2A\x48\x8B\xBF\x2A\x2A\x2A\x2A\x48\x85\xFF\x0F\x84\x2A\x2A\x2A\x2A\x48\x8B\x07\x48\x8D\x15" + } } "Offsets" { diff --git a/src/cs2_sdk/entity/cbaseentity.h b/src/cs2_sdk/entity/cbaseentity.h index a3e10947..e51b5f92 100644 --- a/src/cs2_sdk/entity/cbaseentity.h +++ b/src/cs2_sdk/entity/cbaseentity.h @@ -34,6 +34,7 @@ extern CGameConfig *g_GameConfig; class CGameUI; class CEnvHudHint; +class CPointViewControl; class CGameSceneNode { @@ -291,6 +292,19 @@ class CBaseEntity : public CEntityInstance return nullptr; } + [[nodiscard]] CPointViewControl *AsPointViewControl() + { + if (V_strcasecmp(GetClassname(), "logic_relay") != 0) + return nullptr; + + const auto tag = m_iszPrivateVScripts.IsValid() ? m_iszPrivateVScripts.String() : nullptr; + + if (tag && V_strcasecmp(tag, "point_viewcontrol") == 0) + return reinterpret_cast(this); + + return nullptr; + } + /* End Custom Entities Cast */ }; diff --git a/src/cs2_sdk/entity/cbasemodelentity.h b/src/cs2_sdk/entity/cbasemodelentity.h index 266dced3..74ab7635 100644 --- a/src/cs2_sdk/entity/cbasemodelentity.h +++ b/src/cs2_sdk/entity/cbasemodelentity.h @@ -32,7 +32,8 @@ class CBaseModelEntity : public CBaseEntity SCHEMA_FIELD(Color, m_clrRender) SCHEMA_FIELD(RenderMode_t, m_nRenderMode) SCHEMA_FIELD(float, m_flDissolveStartTime) - + SCHEMA_FIELD(Vector, m_vecViewOffset) + void SetModel(const char *szModel) { addresses::CBaseModelEntity_SetModel(this, szModel); @@ -48,4 +49,10 @@ class CBaseModelEntity : public CBaseEntity { return ((CSkeletonInstance*)m_CBodyComponent->m_pSceneNode.Get())->m_modelState().m_ModelName.Get().String(); } + + Vector GetEyePosition() + { + const auto x = m_vecViewOffset(); + return x + GetAbsOrigin(); + } }; \ No newline at end of file diff --git a/src/cs2_sdk/entity/cbaseplayercontroller.h b/src/cs2_sdk/entity/cbaseplayercontroller.h index 8d2296ef..f8610cfa 100644 --- a/src/cs2_sdk/entity/cbaseplayercontroller.h +++ b/src/cs2_sdk/entity/cbaseplayercontroller.h @@ -44,6 +44,7 @@ class CBasePlayerController : public CBaseEntity SCHEMA_FIELD_POINTER(char, m_iszPlayerName) SCHEMA_FIELD(PlayerConnectedState, m_iConnected) SCHEMA_FIELD(bool, m_bIsHLTV) + SCHEMA_FIELD(uint, m_iDesiredFOV) // Returns the current pawn, which could be one of those: // - The player's actual pawn diff --git a/src/cs2_sdk/entity/cbaseplayerpawn.h b/src/cs2_sdk/entity/cbaseplayerpawn.h index dfb403b0..662af781 100644 --- a/src/cs2_sdk/entity/cbaseplayerpawn.h +++ b/src/cs2_sdk/entity/cbaseplayerpawn.h @@ -34,7 +34,9 @@ class CBasePlayerPawn : public CBaseModelEntity SCHEMA_FIELD(CCSPlayer_WeaponServices*, m_pWeaponServices) SCHEMA_FIELD(CCSPlayer_ItemServices*, m_pItemServices) SCHEMA_FIELD(CPlayer_ObserverServices*, m_pObserverServices) + SCHEMA_FIELD(CPlayer_CameraServices*, m_pCameraServices) SCHEMA_FIELD(CHandle, m_hController) + SCHEMA_FIELD(QAngle, v_angle) // Drops any map-spawned weapons the pawn is holding // NOTE: Currently very broken with map items (entities parented to weapons?) due to a game bug..? Needs further investigation/work diff --git a/src/cs2_sdk/entity/ccsplayerpawn.h b/src/cs2_sdk/entity/ccsplayerpawn.h index 7cf50481..6b6b6a0f 100644 --- a/src/cs2_sdk/entity/ccsplayerpawn.h +++ b/src/cs2_sdk/entity/ccsplayerpawn.h @@ -62,4 +62,9 @@ class CCSPlayerPawn : public CCSPlayerPawnBase SCHEMA_FIELD(float, m_flVelocityModifier) SCHEMA_FIELD(CCSPlayer_ActionTrackingServices*, m_pActionTrackingServices) + + [[nodiscard]] CCSPlayer_CameraServices* GetCameraService() + { + return reinterpret_cast(m_pCameraServices()); + } }; \ No newline at end of file diff --git a/src/cs2_sdk/entity/ccsweaponbase.h b/src/cs2_sdk/entity/ccsweaponbase.h index 0d828ad3..c31a59c5 100644 --- a/src/cs2_sdk/entity/ccsweaponbase.h +++ b/src/cs2_sdk/entity/ccsweaponbase.h @@ -21,6 +21,8 @@ #include "cbaseentity.h" +extern CGlobalVars* gpGlobals; + enum gear_slot_t : uint32_t { GEAR_SLOT_INVALID = 0xffffffff, @@ -88,8 +90,16 @@ class CBasePlayerWeapon : public CEconEntity { public: DECLARE_SCHEMA_CLASS(CBasePlayerWeapon) + SCHEMA_FIELD(int, m_nNextPrimaryAttackTick) + SCHEMA_FIELD(int, m_nNextSecondaryAttackTick) CCSWeaponBaseVData* GetWeaponVData() { return (CCSWeaponBaseVData*)GetVData(); } + + void Disarm() + { + m_nNextPrimaryAttackTick(MAX(m_nNextPrimaryAttackTick(), gpGlobals->tickcount + 24)); + m_nNextSecondaryAttackTick(MAX(m_nNextSecondaryAttackTick(), gpGlobals->tickcount + 24)); + } }; class CCSWeaponBase : public CBasePlayerWeapon diff --git a/src/cs2_sdk/entity/cgamerules.h b/src/cs2_sdk/entity/cgamerules.h index c193384b..61c25a63 100644 --- a/src/cs2_sdk/entity/cgamerules.h +++ b/src/cs2_sdk/entity/cgamerules.h @@ -67,6 +67,7 @@ class CCSGameRules : public CGameRules SCHEMA_FIELD_POINTER(int, m_nEndMatchMapGroupVoteOptions) SCHEMA_FIELD(int, m_nEndMatchMapVoteWinner) SCHEMA_FIELD(int, m_iRoundTime) + SCHEMA_FIELD(bool, m_bFreezePeriod) SCHEMA_FIELD_POINTER(CUtlVector, m_CTSpawnPoints) SCHEMA_FIELD_POINTER(CUtlVector, m_TerroristSpawnPoints) diff --git a/src/cs2_sdk/entity/cpointviewcontrol.h b/src/cs2_sdk/entity/cpointviewcontrol.h new file mode 100644 index 00000000..069d45a8 --- /dev/null +++ b/src/cs2_sdk/entity/cpointviewcontrol.h @@ -0,0 +1,69 @@ +/** + * ============================================================================= + * 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" +#include "entity.h" + +/* logic_relay */ + +class CPointViewControl : public CBaseEntity +{ + DECLARE_SCHEMA_CLASS(CPointViewControl) + + // hello, don't port my garbage code to cssharp :) + + static constexpr int SF_POINT_VIEWCONTROL_FROZEN = 1 << 5; + static constexpr int SF_POINT_VIEWCONTROL_FOV = 1 << 6; + static constexpr int SF_POINT_VIEWCONTROL_DISARM = 1 << 7; + +public: + [[nodiscard]] CBaseEntity* GetTargetCameraEntity() + { + const auto pTarget = UTIL_FindEntityByName(nullptr, m_target().String()); + return pTarget && pTarget->m_pCollision() ? pTarget : nullptr; + } + + [[nodiscard]] bool HasTargetCameraEntity() + { + return m_target().IsValid() && strlen(m_target().String()) >= 2; + } + + [[nodiscard]] bool HasFrozen() + { + return !!(m_spawnflags() & SF_POINT_VIEWCONTROL_FROZEN); + } + + [[nodiscard]] bool HasFOV() + { + return !!(m_spawnflags() & SF_POINT_VIEWCONTROL_FOV); + } + + [[nodiscard]] bool HasDisarm() + { + return !!(m_spawnflags() & SF_POINT_VIEWCONTROL_DISARM); + } + + [[nodiscard]] uint GetFOV() + { + return clamp(static_cast(m_iHealth()), 16, 179); + } +}; diff --git a/src/cs2_sdk/entity/services.h b/src/cs2_sdk/entity/services.h index 658fbf7a..6c0ea436 100644 --- a/src/cs2_sdk/entity/services.h +++ b/src/cs2_sdk/entity/services.h @@ -219,4 +219,24 @@ class CPlayer_ObserverServices SCHEMA_FIELD(CHandle, m_hObserverTarget) SCHEMA_FIELD(ObserverMode_t, m_iObserverLastMode) SCHEMA_FIELD(bool, m_bForcedObserverMode) -}; \ No newline at end of file +}; + +class CPlayer_CameraServices +{ +public: + DECLARE_SCHEMA_CLASS(CPlayer_CameraServices) + + SCHEMA_FIELD(CHandle, m_hViewEntity) +}; + +class CCSPlayerBase_CameraServices : public CPlayer_CameraServices +{ +public: + DECLARE_SCHEMA_CLASS(CCSPlayerBase_CameraServices) + + SCHEMA_FIELD(CHandle, m_hZoomOwner) + SCHEMA_FIELD(uint, m_iFOV) +}; + +class CCSPlayer_CameraServices : public CCSPlayerBase_CameraServices +{}; \ No newline at end of file diff --git a/src/cs2fixes.h b/src/cs2fixes.h index edd4b924..c053b7f3 100644 --- a/src/cs2fixes.h +++ b/src/cs2fixes.h @@ -67,6 +67,13 @@ class CS2Fixes : public ISmmPlugin, public IMetamodListener void Hook_CreateWorkshopMapGroup(const char* name, const CUtlStringList& mapList); void Hook_GoToIntermission(bool bAbortedMatch); bool Hook_OnTakeDamage_Alive(CTakeDamageInfoContainer *pInfoContainer); +#ifdef PLATFORM_WINDOWS + Vector* Hook_GetEyePosition(Vector*); + QAngle* Hook_GetEyeAngles(QAngle*); +#else + Vector Hook_GetEyePosition(); + QAngle Hook_GetEyeAngles(); +#endif void Hook_CheckMovingGround(double frametime); int Hook_LoadEventsFromFile(const char *filename, bool bSearchAll); diff --git a/src/detours.cpp b/src/detours.cpp index 13bb3e4f..4bb60869 100644 --- a/src/detours.cpp +++ b/src/detours.cpp @@ -17,37 +17,38 @@ * this program. If not, see . */ +#include "cs_usercmd.pb.h" #include "networkbasetypes.pb.h" #include "usercmd.pb.h" -#include "cs_usercmd.pb.h" -#include "cdetour.h" -#include "common.h" -#include "module.h" #include "addresses.h" +#include "cdetour.h" #include "commands.h" -#include "detours.h" +#include "common.h" #include "ctimer.h" -#include "irecipientfilter.h" +#include "customio.h" +#include "detours.h" +#include "entities.h" +#include "entity/cbasemodelentity.h" #include "entity/ccsplayercontroller.h" #include "entity/ccsplayerpawn.h" -#include "entity/cbasemodelentity.h" #include "entity/ccsweaponbase.h" #include "entity/cenvhudhint.h" -#include "entity/ctriggerpush.h" #include "entity/cgamerules.h" +#include "entity/cpointviewcontrol.h" #include "entity/ctakedamageinfo.h" +#include "entity/ctriggerpush.h" #include "entity/services.h" -#include "playermanager.h" -#include "igameevents.h" #include "gameconfig.h" -#include "zombiereborn.h" -#include "customio.h" -#include "entities.h" -#include "serversideclient.h" -#include "networksystem/inetworkserializer.h" +#include "igameevents.h" +#include "irecipientfilter.h" #include "map_votes.h" +#include "module.h" +#include "networksystem/inetworkserializer.h" +#include "playermanager.h" +#include "serversideclient.h" #include "tier0/vprof.h" +#include "zombiereborn.h" #include "tier0/memdbgon.h" @@ -77,6 +78,8 @@ DECLARE_DETOUR(CCSPlayerPawn_GetMaxSpeed, Detour_CCSPlayerPawn_GetMaxSpeed); DECLARE_DETOUR(FindUseEntity, Detour_FindUseEntity); DECLARE_DETOUR(TraceFunc, Detour_TraceFunc); DECLARE_DETOUR(TraceShape, Detour_TraceShape); +DECLARE_DETOUR(CBasePlayerPawn_GetEyePosition, Detour_CBasePlayerPawn_GetEyePosition); +DECLARE_DETOUR(CBasePlayerPawn_GetEyeAngles, Detour_CBasePlayerPawn_GetEyeAngles); static bool g_bBlockMolotovSelfDmg = false; static bool g_bBlockAllDamage = false; @@ -443,6 +446,17 @@ bool FASTCALL Detour_CEntityIdentity_AcceptInput(CEntityIdentity* pThis, CUtlSym if (!V_strcasecmp(pInputName->String(), "Deactivate")) return CGameUIHandler::OnDeactivate(pGameUI, reinterpret_cast(pActivator)); } + else if (const auto pViewControl = reinterpret_cast(pThis->m_pInstance)->AsPointViewControl()) + { + if (!V_strcasecmp(pInputName->String(), "EnableCamera")) + return CPointViewControlHandler::OnEnable(pViewControl, reinterpret_cast(pActivator)); + if (!V_strcasecmp(pInputName->String(), "DisableCamera")) + return CPointViewControlHandler::OnDisable(pViewControl, reinterpret_cast(pActivator)); + if (!V_strcasecmp(pInputName->String(), "EnableCameraAll")) + return CPointViewControlHandler::OnEnableAll(pViewControl); + if (!V_strcasecmp(pInputName->String(), "DisableCameraAll")) + return CPointViewControlHandler::OnDisableAll(pViewControl); + } VPROF_SCOPE_END(); @@ -597,6 +611,52 @@ bool FASTCALL Detour_TraceShape(int64* a1, int64 a2, int64 a3, int64 a4, CTraceF return TraceShape(a1, a2, a3, a4, filter, a6); } +#ifdef PLATFORM_WINDOWS +Vector* FASTCALL Detour_CBasePlayerPawn_GetEyePosition(CBasePlayerPawn* pPawn, Vector* pRet) +{ + if (pPawn->IsAlive() && CPointViewControlHandler::IsViewControl(reinterpret_cast(pPawn))) + { + const auto& origin = pPawn->GetEyePosition(); + pRet->Init(origin.x, origin.y, origin.z); + return pRet; + } + + return CBasePlayerPawn_GetEyePosition(pPawn, pRet); +} +QAngle* FASTCALL Detour_CBasePlayerPawn_GetEyeAngles(CBasePlayerPawn* pPawn, QAngle* pRet) +{ + if (pPawn->IsAlive() && CPointViewControlHandler::IsViewControl(reinterpret_cast(pPawn))) + { + const auto& angles = pPawn->v_angle(); + pRet->Init(angles.x, angles.y, angles.z); + return pRet; + } + + return CBasePlayerPawn_GetEyeAngles(pPawn, pRet); +} +#else +Vector FASTCALL Detour_CBasePlayerPawn_GetEyePosition(CBasePlayerPawn* pPawn) +{ + if (pPawn->IsAlive() && CPointViewControlHandler::IsViewControl(reinterpret_cast(pPawn))) + { + const auto& origin = pPawn->GetEyePosition(); + return origin; + } + + return CBasePlayerPawn_GetEyePosition(pPawn); +} +QAngle FASTCALL Detour_CBasePlayerPawn_GetEyeAngles(CBasePlayerPawn* pPawn) +{ + if (pPawn->IsAlive() && CPointViewControlHandler::IsViewControl(reinterpret_cast(pPawn))) + { + const auto& angles = pPawn->v_angle(); + return angles; + } + + return CBasePlayerPawn_GetEyeAngles(pPawn); +} +#endif + bool InitDetours(CGameConfig *gameConfig) { bool success = true; diff --git a/src/detours.h b/src/detours.h index 727484fa..d85983db 100644 --- a/src/detours.h +++ b/src/detours.h @@ -48,6 +48,8 @@ class InputData_t; class CCSPlayerPawn; class CCSPlayer_UseServices; class CTraceFilter; +class Vector; +class QAngle; bool InitDetours(CGameConfig *gameConfig); void FlushAllDetours(); @@ -68,4 +70,11 @@ CServerSideClient* FASTCALL Detour_GetFreeClient(int64_t unk1, const __m128i* un float FASTCALL Detour_CCSPlayerPawn_GetMaxSpeed(CCSPlayerPawn*); int64 FASTCALL Detour_FindUseEntity(CCSPlayer_UseServices* pThis, float); bool FASTCALL Detour_TraceFunc(int64*, int*, float*, uint64); -bool FASTCALL Detour_TraceShape(int64*, int64, int64, int64, CTraceFilter*, int64); \ No newline at end of file +bool FASTCALL Detour_TraceShape(int64*, int64, int64, int64, CTraceFilter*, int64); +#ifdef PLATFORM_WINDOWS +Vector* FASTCALL Detour_CBasePlayerPawn_GetEyePosition(CBasePlayerPawn*, Vector*); +QAngle* FASTCALL Detour_CBasePlayerPawn_GetEyeAngles(CBasePlayerPawn*, QAngle*); +#else +Vector FASTCALL Detour_CBasePlayerPawn_GetEyePosition(CBasePlayerPawn*); +QAngle FASTCALL Detour_CBasePlayerPawn_GetEyeAngles(CBasePlayerPawn*); +#endif \ No newline at end of file diff --git a/src/entities.cpp b/src/entities.cpp index c57080f4..d48f4f2f 100644 --- a/src/entities.cpp +++ b/src/entities.cpp @@ -22,19 +22,24 @@ #include "ctimer.h" #include "entity.h" #include "entity/cbaseplayercontroller.h" +#include "entity/ccsplayercontroller.h" #include "entity/ccsplayerpawn.h" #include "entity/cgameplayerequip.h" +#include "entity/cgamerules.h" #include "entity/clogiccase.h" +#include "entity/cpointviewcontrol.h" // #define ENTITY_HANDLER_ASSERTION +extern CCSGameRules* g_pGameRules; + class InputData_t { public: CBaseEntity* pActivator; CBaseEntity* pCaller; - variant_t value; - int nOutputID; + variant_t value; + int nOutputID; }; inline bool StripPlayer(CCSPlayerPawn* pPawn) @@ -379,12 +384,327 @@ bool OnDeactivate(CGameUI* pEntity, CBaseEntity* pActivator) } // namespace CGameUIHandler +namespace CPointViewControlHandler +{ +struct ViewControl +{ + CUtlVector> m_players; + std::string m_viewTarget; + std::string m_name; +}; + +static std::unordered_map s_repository; +static constexpr uint INVALID_FOV = 0xFFFFFFFF; +static constexpr uint RESET_FOV = 0xFFFFFFFE; +static CHandle INVALID_HANDLE(0xFFFFFFFF); + +inline void UpdatePlayerState(CCSPlayerPawn* pPawn, const CHandle& target, bool frozen, uint fov = INVALID_FOV, bool disarm = false) +{ + if (!pPawn) + return; + + if (const auto pCameraService = pPawn->GetCameraService()) + { + pCameraService->m_hViewEntity(target); + pCameraService->m_hZoomOwner(INVALID_HANDLE); + + if (fov != INVALID_FOV) + { + if (const auto pController = pPawn->GetController()) + { + if (fov == RESET_FOV) + { + pCameraService->m_iFOV(pController->m_iDesiredFOV()); + } + else + { + pCameraService->m_iFOV(fov); + } + } + } + } + + if (disarm) + { + if (const auto pWeaponService = pPawn->m_pWeaponServices()) + { + if (const auto pActiveWeapon = pWeaponService->m_hActiveWeapon().Get()) + { + pActiveWeapon->Disarm(); + } + } + } + + auto flags = pPawn->m_fFlags(); + + if (g_pGameRules && g_pGameRules->m_bFreezePeriod()) + frozen = true; + + if (frozen) + { + flags |= FL_FROZEN; + } + else + { + flags &= ~FL_FROZEN; + } + + pPawn->m_fFlags(flags); +} + +void OnCreated(CBaseEntity* pEntity) +{ + const auto pViewControl = pEntity->AsPointViewControl(); + if (!pViewControl) + return; + + if (!pViewControl->HasTargetCameraEntity()) + { + Warning("PointViewControl %s has no target camera entity\n", pViewControl->GetName()); + return; + } + + ViewControl vc{}; + vc.m_viewTarget = pViewControl->m_target().String(); + vc.m_name = pViewControl->GetName(); + s_repository[pEntity->GetHandle().ToInt()] = vc; +} +bool OnEnable(CPointViewControl* pEntity, CBaseEntity* pActivator) +{ + if (!pActivator || !pActivator->IsPawn() || !pActivator->IsAlive()) + return false; + + const auto key = pEntity->GetHandle().ToInt(); + const auto it = s_repository.find(key); + if (it == s_repository.end()) + return false; + + const auto pPawn = reinterpret_cast(pActivator); + const auto pController = reinterpret_cast(pPawn->GetController()); + if (!pController) + return false; + + if (pController->IsBot() || pController->m_bIsHLTV()) + { + Warning("PointViewControl %s try enable for bot or HLTV: %s\n", it->second.m_name.c_str(), pController->GetPlayerName()); + return false; + } + + const auto handle = CHandle(pPawn->GetHandle()); + + for (auto& [vk, vc] : s_repository) + { + if (const auto index = vc.m_players.Find(handle); index > -1) + { + if (vk == static_cast(key)) + { + Warning("PointViewControl %s was enabled twice in a row! player: %s\n", vc.m_name.c_str(), pController->GetPlayerName()); + return false; + } + + vc.m_players.Remove(index); + UpdatePlayerState(pPawn, INVALID_HANDLE, false, RESET_FOV); + Warning("PointViewControl %s already enabled for %s\n", vc.m_name.c_str(), pController->GetPlayerName()); + break; + } + } + + return it->second.m_players.AddToTail(handle) >= 0; +} +bool OnDisable(CPointViewControl* pEntity, CBaseEntity* pActivator) +{ + if (!pActivator || !pActivator->IsPawn() || !pActivator->IsAlive()) + return false; + + const auto key = pEntity->GetHandle().ToInt(); + const auto it = s_repository.find(key); + if (it == s_repository.end()) + return false; + + const auto pPawn = reinterpret_cast(pActivator); + const auto pController = reinterpret_cast(pPawn->GetController()); + if (!pController) + return false; + + if (pController->IsBot() || pController->m_bIsHLTV()) + { + Warning("PointViewControl %s try disable for bot or HLTV: %s\n", it->second.m_name.c_str(), pController->GetPlayerName()); + return false; + } + + const auto handle = CHandle(pPawn->GetHandle()); + + UpdatePlayerState(pPawn, INVALID_HANDLE, false, RESET_FOV); + + return it->second.m_players.FindAndRemove(handle); +} +bool OnEnableAll(CPointViewControl* pEntity) +{ + const auto key = pEntity->GetHandle().ToInt(); + const auto it = s_repository.find(key); + if (it == s_repository.end()) + return false; + + for (auto i = 0; i < gpGlobals->maxClients; i++) + { + const auto pController = CCSPlayerController::FromSlot(i); + if (!pController || !pController->IsConnected() || pController->IsBot() || pController->m_bIsHLTV()) + continue; + + const auto pPawn = pController->GetPlayerPawn(); + if (!pPawn || !pPawn->IsAlive()) + continue; + + const auto handle = CHandle(pPawn->GetHandle()); + + for (auto& [vk, vc] : s_repository) + { + if (vk == static_cast(key)) + { + continue; + } + if (const auto index = vc.m_players.Find(handle); index > -1) + { + vc.m_players.Remove(index); + UpdatePlayerState(pPawn, INVALID_HANDLE, false, RESET_FOV); + Warning("PointViewControl %s already enabled for %s\n", vc.m_name.c_str(), pController->GetPlayerName()); + } + } + + it->second.m_players.AddToTail(handle); + } + + return true; +} +bool OnDisableAll(CPointViewControl* pEntity) +{ + const auto key = pEntity->GetHandle().ToInt(); + const auto it = s_repository.find(key); + if (it == s_repository.end()) + return false; + + FOR_EACH_VEC(it->second.m_players, i) + { + const auto& handle = it->second.m_players.Element(i); + + if (const auto player = handle.Get()) + UpdatePlayerState(player, INVALID_HANDLE, false, RESET_FOV); + } + + it->second.m_players.Purge(); + + return true; +} + +void RunThink(int tick) +{ + // validate + for (auto it = s_repository.begin(); it != s_repository.end();) + { + const auto entity = CHandle(it->first).Get(); + if (!entity) + { + FOR_EACH_VEC(it->second.m_players, i) + { + const auto& handle = it->second.m_players.Element(i); + + if (const auto player = handle.Get()) + UpdatePlayerState(player, INVALID_HANDLE, false, RESET_FOV); + } + + it = s_repository.erase(it); + } + else + { + ++it; + } + } + + // think every tick + + for (auto& [vk, vc] : s_repository) + { + const auto entity = CHandle(vk).Get(); + if (!entity) + { + Error("Why invalid entity here?"); + continue; + } + + if (vc.m_players.Count() == 0) + continue; + + const auto pTarget = entity->GetTargetCameraEntity(); + if (!pTarget) + { + FOR_EACH_VEC(vc.m_players, i) + { + const auto& handle = vc.m_players.Element(i); + + if (const auto player = handle.Get()) + UpdatePlayerState(player, INVALID_HANDLE, false, RESET_FOV); + } + vc.m_players.Purge(); + continue; + } + + FOR_EACH_VEC(vc.m_players, i) + { + const auto& handle = vc.m_players.Element(i); + const auto player = handle.Get(); + if (!player) + { + vc.m_players.Remove(i--); + continue; + } + if (!player->IsAlive()) + { + UpdatePlayerState(player, INVALID_HANDLE, false, RESET_FOV); + vc.m_players.Remove(i--); + continue; + } + + UpdatePlayerState(player, pTarget->GetHandle(), entity->HasFrozen(), entity->HasFOV() ? entity->GetFOV() : INVALID_FOV, entity->HasDisarm()); + } + } +} +bool IsViewControl(CCSPlayerPawn* pPawn) +{ + const auto handle = pPawn->GetHandle().ToInt(); + for (const auto& [vk, vc] : s_repository) + { + FOR_EACH_VEC(vc.m_players, i) + { + if (vc.m_players.Element(i).ToInt() == handle) + return true; + } + } + return false; +} +void Shutdown() +{ + for (auto& [vk, vc] : s_repository) + { + FOR_EACH_VEC(vc.m_players, i) + { + const auto& handle = vc.m_players.Element(i); + + if (const auto player = handle.Get()) + UpdatePlayerState(player, INVALID_HANDLE, false, RESET_FOV); + } + vc.m_players.Purge(); + } + s_repository.clear(); +} +} // namespace CPointViewControlHandler + void EntityHandler_OnGameFramePre(bool simulate, int tick) { if (!simulate) return; CGameUIHandler::RunThink(tick); + CPointViewControlHandler::RunThink(tick); } void EntityHandler_OnGameFramePost(bool simulate, int tick) @@ -392,3 +712,13 @@ void EntityHandler_OnGameFramePost(bool simulate, int tick) if (!simulate) return; } + +void EntityHandler_OnRoundRestart() +{ + CPointViewControlHandler::Shutdown(); +} + +void EntityHandler_OnEntitySpawned(CBaseEntity* pEntity) +{ + CPointViewControlHandler::OnCreated(pEntity); +} diff --git a/src/entities.h b/src/entities.h index 828e4fd2..07fad8f9 100644 --- a/src/entities.h +++ b/src/entities.h @@ -23,6 +23,8 @@ class InputData_t; class CGamePlayerEquip; class CBaseEntity; class CGameUI; +class CPointViewControl; +class CCSPlayerPawn; namespace CGamePlayerEquipHandler { @@ -38,5 +40,16 @@ bool OnDeactivate(CGameUI* pEntity, CBaseEntity* pActivator); void RunThink(int tick); } // namespace CGameUIHandler +namespace CPointViewControlHandler +{ +bool OnEnable(CPointViewControl* pEntity, CBaseEntity* pActivator); +bool OnDisable(CPointViewControl* pEntity, CBaseEntity* pActivator); +bool OnEnableAll(CPointViewControl* pEntity); +bool OnDisableAll(CPointViewControl* pEntity); +bool IsViewControl(CCSPlayerPawn*); +} // namespace CPointViewControlHandler + void EntityHandler_OnGameFramePre(bool simulate, int tick); -void EntityHandler_OnGameFramePost(bool simulate, int tick); \ No newline at end of file +void EntityHandler_OnGameFramePost(bool simulate, int tick); +void EntityHandler_OnRoundRestart(); +void EntityHandler_OnEntitySpawned(CBaseEntity* pEntity); \ No newline at end of file diff --git a/src/entitylistener.cpp b/src/entitylistener.cpp index 75f05c0d..4cc4df6f 100644 --- a/src/entitylistener.cpp +++ b/src/entitylistener.cpp @@ -19,11 +19,12 @@ #include "entitylistener.h" #include "common.h" +#include "cs2_sdk/entity/cbaseentity.h" #include "cs2fixes.h" +#include "entities.h" +#include "entity/cgamerules.h" #include "gameconfig.h" -#include "cs2_sdk/entity/cbaseentity.h" #include "plat.h" -#include "entity/cgamerules.h" extern CGameConfig *g_GameConfig; extern CCSGameRules* g_pGameRules; @@ -52,6 +53,8 @@ void CEntityListener::OnEntitySpawned(CEntityInstance* pEntity) { reinterpret_cast(pEntity)->SetCollisionGroup(COLLISION_GROUP_DEBRIS); } + + EntityHandler_OnEntitySpawned(reinterpret_cast(pEntity)); } void CEntityListener::OnEntityCreated(CEntityInstance* pEntity) diff --git a/src/events.cpp b/src/events.cpp index fce359fd..b0a04588 100644 --- a/src/events.cpp +++ b/src/events.cpp @@ -21,6 +21,7 @@ #include "KeyValues.h" #include "commands.h" #include "ctimer.h" +#include "entities.h" #include "eventlistener.h" #include "networkstringtabledefs.h" #include "entity/cbaseplayercontroller.h" @@ -100,6 +101,8 @@ GAME_EVENT_F(round_prestart) } } + EntityHandler_OnRoundRestart(); + CBaseEntity* pShake = nullptr; // Prevent shakes carrying over from previous rounds