diff --git a/Client/game_sa/CTasksSA.cpp b/Client/game_sa/CTasksSA.cpp index 9c080e58357..3da63537a61 100644 --- a/Client/game_sa/CTasksSA.cpp +++ b/Client/game_sa/CTasksSA.cpp @@ -124,7 +124,7 @@ CTaskSimpleChoking* CTasksSA::CreateTaskSimpleChoking(CPed* pAttacker, bool bIsT return pTask; } -CTaskSimpleClimb* CTasksSA::CreateTaskSimpleClimb(CEntity* pClimbEnt, const CVector& vecTarget, float fHeading, unsigned char nSurfaceType, char nHeight, +CTaskSimpleClimb* CTasksSA::CreateTaskSimpleClimb(CEntitySAInterface* pClimbEnt, const CVector& vecTarget, float fHeading, unsigned char nSurfaceType, eClimbHeights nHeight, const bool bForceClimb) { CTaskSimpleClimbSA* pTask = NewTask(pClimbEnt, vecTarget, fHeading, nSurfaceType, nHeight, bForceClimb); diff --git a/Client/game_sa/CTasksSA.h b/Client/game_sa/CTasksSA.h index 56a614f8c6b..fe1808d0419 100644 --- a/Client/game_sa/CTasksSA.h +++ b/Client/game_sa/CTasksSA.h @@ -70,7 +70,7 @@ class CTasksSA : public CTasks CTaskSimpleDuck* CreateTaskSimpleDuck(eDuckControlTypes nDuckControl, unsigned short nLengthOfDuck = 0, unsigned short nUseShotsWhizzingEvents = -1); CTaskSimpleChoking* CreateTaskSimpleChoking(CPed* pAttacker, bool bIsTearGas); - CTaskSimpleClimb* CreateTaskSimpleClimb(CEntity* pClimbEnt, const CVector& vecTarget, float fHeading, unsigned char nSurfaceType, char nHeight = CLIMB_GRAB, + CTaskSimpleClimb* CreateTaskSimpleClimb(CEntitySAInterface* pClimbEnt, const CVector& vecTarget, float fHeading, unsigned char nSurfaceType, eClimbHeights nHeight = CLIMB_GRAB, const bool bForceClimb = false); CTaskSimpleJetPack* CreateTaskSimpleJetpack(const CVector* pVecTargetPos = NULL, float fCruiseHeight = 10.0f, int nHoverTime = 0); diff --git a/Client/game_sa/TaskJumpFallSA.cpp b/Client/game_sa/TaskJumpFallSA.cpp index d6f73eb529d..be09abd82b9 100644 --- a/Client/game_sa/TaskJumpFallSA.cpp +++ b/Client/game_sa/TaskJumpFallSA.cpp @@ -12,7 +12,7 @@ #include "StdInc.h" #include "TaskJumpFallSA.h" -CTaskSimpleClimbSA::CTaskSimpleClimbSA(CEntity* pClimbEnt, const CVector& vecTarget, float fHeading, unsigned char nSurfaceType, char nHeight, +CTaskSimpleClimbSA::CTaskSimpleClimbSA(CEntitySAInterface* pClimbEnt, const CVector& vecTarget, float fHeading, unsigned char nSurfaceType, eClimbHeights nHeight, const bool bForceClimb) { CreateTaskInterface(sizeof(CTaskSimpleClimbSAInterface)); diff --git a/Client/game_sa/TaskJumpFallSA.h b/Client/game_sa/TaskJumpFallSA.h index a107dc7f577..61f211ec61a 100644 --- a/Client/game_sa/TaskJumpFallSA.h +++ b/Client/game_sa/TaskJumpFallSA.h @@ -37,7 +37,7 @@ class CTaskSimpleClimbSAInterface : public CTaskSimpleSAInterface char m_nFallAfterVault; float m_fHandholdHeading; CVector m_vecHandholdPos; - CEntity* m_pClimbEnt; + CEntitySAInterface* m_pClimbEnt; short m_nGetToPosCounter; CAnimBlendAssociation* m_pAnim; @@ -47,7 +47,7 @@ class CTaskSimpleClimbSA : public virtual CTaskSimpleSA, public virtual CTaskSim { public: CTaskSimpleClimbSA(){}; - CTaskSimpleClimbSA(CEntity* pClimbEnt, const CVector& vecTarget, float fHeading, unsigned char nSurfaceType, char nHeight = CLIMB_GRAB, + CTaskSimpleClimbSA(CEntitySAInterface* pClimbEnt, const CVector& vecTarget, float fHeading, unsigned char nSurfaceType, eClimbHeights nHeight = CLIMB_GRAB, const bool bForceClimb = false); eClimbHeights GetHeightForPos() const override { return static_cast(GetInterface())->m_nHeightForPos; } diff --git a/Client/mods/deathmatch/logic/CClientPed.cpp b/Client/mods/deathmatch/logic/CClientPed.cpp index 0528f1e4b35..6728837887b 100644 --- a/Client/mods/deathmatch/logic/CClientPed.cpp +++ b/Client/mods/deathmatch/logic/CClientPed.cpp @@ -7216,3 +7216,36 @@ void CClientPed::SetSyncing(bool bIsSyncing) ResetVehicleInOut(); } } + +void CClientPed::RunClimbingTask() +{ + if (!m_pPlayerPed) + return; + + CVector climbPos; + float climbAngle; + int surfaceType; + + CEntitySAInterface* climbEntity = CTaskSimpleClimb::TestForClimb(m_pPlayerPed, climbPos, climbAngle, surfaceType, true); + + // If a ped is in the air, its rotation is inverted (see GetRotationDegressNew, GetRotationRadiansNew) + if (!IsOnGround() && !climbEntity) + { + CVector rot; + GetRotationDegrees(rot); + + rot.fZ += 180.0f; + SetRotationDegrees(rot); + + climbEntity = CTaskSimpleClimb::TestForClimb(m_pPlayerPed, climbPos, climbAngle, surfaceType, true); + } + + if (!climbEntity) + return; + + CTaskSimpleClimb* climbTask = g_pGame->GetTasks()->CreateTaskSimpleClimb(climbEntity, climbPos, climbAngle, surfaceType, eClimbHeights::CLIMB_GRAB, false); + if (!climbTask) + return; + + climbTask->SetAsPedTask(m_pPlayerPed, TASK_PRIORITY_PRIMARY, true); +} diff --git a/Client/mods/deathmatch/logic/CClientPed.h b/Client/mods/deathmatch/logic/CClientPed.h index 489b1a0a501..13efcaefa4e 100644 --- a/Client/mods/deathmatch/logic/CClientPed.h +++ b/Client/mods/deathmatch/logic/CClientPed.h @@ -563,6 +563,8 @@ class CClientPed : public CClientStreamElement, public CAntiCheatModule void SetHasSyncedAnim(bool synced) noexcept { m_hasSyncedAnim = synced; } bool HasSyncedAnim() const noexcept { return m_hasSyncedAnim; } + void RunClimbingTask(); + protected: // This constructor is for peds managed by a player. These are unknown to the ped manager. CClientPed(CClientManager* pManager, unsigned long ulModelID, ElementID ID, bool bIsLocalPlayer); diff --git a/Client/mods/deathmatch/logic/CClientTask.cpp b/Client/mods/deathmatch/logic/CClientTask.cpp index 58193f2e130..4aadf7a76c1 100644 --- a/Client/mods/deathmatch/logic/CClientTask.cpp +++ b/Client/mods/deathmatch/logic/CClientTask.cpp @@ -617,7 +617,7 @@ CTask* CClientTask::CreateTask(bool& bTaskPrimary, int& iTaskPriority) iTaskPriority = TASK_PRIORITY_PRIMARY; // Create the task - return g_pGame->GetTasks()->CreateTaskSimpleClimb(NULL, vecTarget, fHeading, static_cast(fSurfaceType), static_cast(fClimbStage), + return g_pGame->GetTasks()->CreateTaskSimpleClimb(NULL, vecTarget, fHeading, static_cast(fSurfaceType), static_cast(fClimbStage), bForceClimb); } diff --git a/Client/mods/deathmatch/logic/CNetAPI.cpp b/Client/mods/deathmatch/logic/CNetAPI.cpp index dcad1020468..e6e7d9efd47 100644 --- a/Client/mods/deathmatch/logic/CNetAPI.cpp +++ b/Client/mods/deathmatch/logic/CNetAPI.cpp @@ -981,6 +981,9 @@ void CNetAPI::ReadPlayerPuresync(CClientPlayer* pPlayer, NetBitStreamInterface& pPlayer->SetOnFire(flags.data.bIsOnFire); pPlayer->SetStealthAiming(flags.data.bStealthAiming); + if (flags.data.hangingDuringClimb && pPlayer->GetMovementState() != eMovementState::MOVEMENTSTATE_HANGING && pPlayer->GetMovementState() != eMovementState::MOVEMENTSTATE_CLIMB) + pPlayer->RunClimbingTask(); + // Remember now as the last puresync time pPlayer->SetLastPuresyncTime(CClientTime::GetTime()); pPlayer->SetLastPuresyncPosition(position.data.vecPosition); @@ -1112,6 +1115,7 @@ void CNetAPI::WritePlayerPuresync(CClientPlayer* pPlayerModel, NetBitStreamInter flags.data.bStealthAiming = (pPlayerModel->IsStealthAiming() == true); flags.data.isReloadingWeapon = (pPlayerModel->IsReloadingWeapon() == true); flags.data.animInterrupted = pPlayerModel->HasSyncedAnim() && (!pPlayerModel->IsRunningAnimation() || pPlayerModel->m_animationOverridedByClient); + flags.data.hangingDuringClimb = pPlayerModel->GetMovementState() == eMovementState::MOVEMENTSTATE_HANGING; // The animation has been overwritten or interrupted by the client if (flags.data.animInterrupted) diff --git a/Client/sdk/game/CTasks.h b/Client/sdk/game/CTasks.h index d7499b92d9b..f5b90e4f556 100644 --- a/Client/sdk/game/CTasks.h +++ b/Client/sdk/game/CTasks.h @@ -86,8 +86,8 @@ class CTasks unsigned short nUseShotsWhizzingEvents = -1) = 0; virtual CTaskSimpleChoking* CreateTaskSimpleChoking(CPed* pAttacker, bool bIsTearGas) = 0; - virtual CTaskSimpleClimb* CreateTaskSimpleClimb(CEntity* pClimbEnt, const CVector& vecTarget, float fHeading, unsigned char nSurfaceType, - char nHeight = CLIMB_GRAB, const bool bForceClimb = false) = 0; + virtual CTaskSimpleClimb* CreateTaskSimpleClimb(CEntitySAInterface* pClimbEnt, const CVector& vecTarget, float fHeading, unsigned char nSurfaceType, + eClimbHeights nHeight = CLIMB_GRAB, const bool bForceClimb = false) = 0; virtual CTaskSimpleJetPack* CreateTaskSimpleJetpack(const CVector* pVecTargetPos = NULL, float fCruiseHeight = 10.0f, int nHoverTime = 0) = 0; virtual CTaskSimpleRunAnim* CreateTaskSimpleRunAnim(const AssocGroupId animGroup, const AnimationId animID, const float fBlendDelta, const int iTaskType, diff --git a/Client/sdk/game/TaskJumpFall.h b/Client/sdk/game/TaskJumpFall.h index 08fb0516a3d..b5399e4f79c 100644 --- a/Client/sdk/game/TaskJumpFall.h +++ b/Client/sdk/game/TaskJumpFall.h @@ -12,6 +12,7 @@ #pragma once #include "Task.h" +#include "CPed.h" enum eClimbHeights : std::int8_t; @@ -21,6 +22,15 @@ class CTaskSimpleClimb : public virtual CTaskSimple virtual ~CTaskSimpleClimb(){}; virtual eClimbHeights GetHeightForPos() const = 0; + + static class CEntitySAInterface* TestForClimb(CPed* ped, CVector& climbPos, float& climbAngle, int& surfaceType, bool launch) + { + if (!ped) + return nullptr; + + // CTaskSimpleClimb::TestForClimb + return ((class CEntitySAInterface*(__cdecl*)(class CPedSAInterface*, CVector*, float*, int*, bool))0x6803A0)(ped->GetPedInterface(), &climbPos, &climbAngle, &surfaceType, launch); + } }; class CTaskSimpleJetPack : public virtual CTaskSimple diff --git a/Server/mods/deathmatch/logic/CPed.h b/Server/mods/deathmatch/logic/CPed.h index 67ec9057429..22adb2a5c33 100644 --- a/Server/mods/deathmatch/logic/CPed.h +++ b/Server/mods/deathmatch/logic/CPed.h @@ -309,6 +309,9 @@ class CPed : public CElement void SetAnimationProgress(float progress) { m_animData.progress = progress; }; void SetAnimationSpeed(float speed) { m_animData.speed = speed; }; + void SetHanging(bool hanging) noexcept { m_hanging = hanging; } + bool IsHanging() const noexcept { return m_hanging; } + protected: bool ReadSpecialData(const int iLine) override; @@ -350,6 +353,7 @@ class CPed : public CElement CVehicle* m_pJackingVehicle; SPlayerAnimData m_animData{}; float m_cameraRotation{}; + bool m_hanging{false}; // Is the player hanging during a climb task? CVehicle* m_pVehicle; unsigned int m_uiVehicleSeat; diff --git a/Server/mods/deathmatch/logic/packets/CPlayerPuresyncPacket.cpp b/Server/mods/deathmatch/logic/packets/CPlayerPuresyncPacket.cpp index f4c398c1618..5525b73b0aa 100644 --- a/Server/mods/deathmatch/logic/packets/CPlayerPuresyncPacket.cpp +++ b/Server/mods/deathmatch/logic/packets/CPlayerPuresyncPacket.cpp @@ -66,6 +66,8 @@ bool CPlayerPuresyncPacket::Read(NetBitStreamInterface& BitStream) if (flags.data.animInterrupted) pSourcePlayer->SetAnimationData({}); + pSourcePlayer->SetHanging(flags.data.hangingDuringClimb); + // Contact element CElement* pContactElement = NULL; if (flags.data.bHasContact) @@ -374,6 +376,7 @@ bool CPlayerPuresyncPacket::Write(NetBitStreamInterface& BitStream) const flags.data.bSyncingVelocity = (!flags.data.bIsOnGround || pSourcePlayer->IsSyncingVelocity()); flags.data.bStealthAiming = (pSourcePlayer->IsStealthAiming() == true); flags.data.isReloadingWeapon = pSourcePlayer->IsReloadingWeapon(); + flags.data.hangingDuringClimb = pSourcePlayer->IsHanging(); CVector vecPosition = pSourcePlayer->GetPosition(); if (pContactElement) diff --git a/Shared/sdk/net/SyncStructures.h b/Shared/sdk/net/SyncStructures.h index c4112603bbc..c30cf4ce504 100644 --- a/Shared/sdk/net/SyncStructures.h +++ b/Shared/sdk/net/SyncStructures.h @@ -544,7 +544,7 @@ struct SPlayerPuresyncFlags : public ISyncStructure { enum { - BITCOUNT = 14 + BITCOUNT = 15 }; bool Read(NetBitStreamInterface& stream) @@ -573,6 +573,7 @@ struct SPlayerPuresyncFlags : public ISyncStructure bool bStealthAiming : 1; bool isReloadingWeapon : 1; bool animInterrupted : 1; + bool hangingDuringClimb : 1; } data; };