From 2c9d9c2cadf38df56702f644dd046ef279ea1caf Mon Sep 17 00:00:00 2001 From: whitehai11 Date: Wed, 5 Aug 2026 17:40:16 +0200 Subject: [PATCH] Add null strafe and subtick strafe (32-step analog subtick air accel) --- .../AndromedaClient/Features/CMisc/CMisc.cpp | 246 +++++++++++++++++- .../AndromedaClient/Features/CMisc/CMisc.hpp | 8 + .../AndromedaClient/GUI/CAndromedaMenu.cpp | 2 + .../AndromedaClient/Settings/Settings.hpp | 2 + .../GameClient/CL_Bypass.cpp | 13 +- .../GameClient/CL_Bypass.hpp | 5 +- 6 files changed, 272 insertions(+), 4 deletions(-) diff --git a/Andromeda-CS2-Base/Andromeda-CS2-Base/AndromedaClient/Features/CMisc/CMisc.cpp b/Andromeda-CS2-Base/Andromeda-CS2-Base/AndromedaClient/Features/CMisc/CMisc.cpp index 0a9b31d..9da0b68 100644 --- a/Andromeda-CS2-Base/Andromeda-CS2-Base/AndromedaClient/Features/CMisc/CMisc.cpp +++ b/Andromeda-CS2-Base/Andromeda-CS2-Base/AndromedaClient/Features/CMisc/CMisc.cpp @@ -507,7 +507,11 @@ auto CMisc::RunMovement( CCSGOInput* pInput , CUserCmd* pUserCmd ) -> void const bool bOnGround = ( flFlags & FL_ONGROUND ) != 0; - if ( Settings::Misc::AutoStrafe ) + if ( Settings::Misc::SubtickStrafe ) + RunSubtickStrafe( pInput , pUserCmd , pLocalPawn , bOnGround ); + else if ( Settings::Misc::NullStrafe ) + RunNullStrafe( pInput , pUserCmd , pLocalPawn , bOnGround ); + else if ( Settings::Misc::AutoStrafe ) RunAutoStrafe( pInput , pUserCmd , pLocalPawn , bOnGround ); if ( Settings::Misc::JumpBug ) @@ -642,6 +646,246 @@ auto CMisc::RunAutoStrafe( CCSGOInput* pInput , CUserCmd* pUserCmd , C_CSPlayerP GetCL_Bypass()->SetViewAngles( &NewAngles , pInput , pUserCmd ); } +auto CMisc::RunNullStrafe( CCSGOInput* pInput , CUserCmd* pUserCmd , C_CSPlayerPawn* pLocalPawn , bool bOnGround ) -> void +{ + if ( bOnGround ) + return; + + if ( !pUserCmd || !pLocalPawn ) + return; + + auto* pBaseCmd = pUserCmd->cmd.mutable_base(); + + if ( !pBaseCmd ) + return; + + auto* pViewAngles = CCSGOInput_GetViewAngles( pInput , 0 ); + + if ( !pViewAngles ) + return; + + const Vector3 Velocity = pLocalPawn->m_vecVelocity(); + + const float flSpeed = Velocity.Length2D(); + + if ( flSpeed <= 10.f ) + return; + + constexpr float k_Pi = 3.14159265358979f; + + const float flVelYaw = std::atan2f( Velocity.m_y , Velocity.m_x ); + const float flViewYaw = pViewAngles->m_y * ( k_Pi / 180.f ); + const float flRelativeYaw = flVelYaw - flViewYaw; + + const float flForward = std::clamp( -std::cosf( flRelativeYaw ) , -1.f , 1.f ); + const float flLeft = std::clamp( -std::sinf( flRelativeYaw ) , -1.f , 1.f ); + + pBaseCmd->set_forwardmove( flForward ); + pBaseCmd->set_leftmove( flLeft ); + + GetCL_Bypass()->AddProcessSubTick( 0 , false , 0.f , pBaseCmd->forwardmove() - m_flLastForwardMove , pBaseCmd->leftmove() - m_flLastLeftMove ); + + m_flLastForwardMove = pBaseCmd->forwardmove(); + m_flLastLeftMove = pBaseCmd->leftmove(); +} + +auto CMisc::RunSubtickStrafe( CCSGOInput* pInput , CUserCmd* pUserCmd , C_CSPlayerPawn* pLocalPawn , bool bOnGround ) -> void +{ + if ( bOnGround ) + return; + + if ( !pUserCmd || !pLocalPawn ) + return; + + auto* pBaseCmd = pUserCmd->cmd.mutable_base(); + + if ( !pBaseCmd ) + return; + + auto* pViewAngles = CCSGOInput_GetViewAngles( pInput , 0 ); + + if ( !pViewAngles ) + return; + + constexpr float k_Pi = 3.14159265358979f; + constexpr int k_SubtickCount = 32; + constexpr float k_TickInterval = 1.f / 64.f; + constexpr float k_FrameTime = k_TickInterval / static_cast( k_SubtickCount ); + constexpr float k_SvAiraccelerate = 12.f; + constexpr float k_SvAirMaxWishspeed = 30.f; + constexpr float k_SvGravity = 800.f; + constexpr float k_MaxSpeed = 250.f; + + const auto uButtons = pUserCmd->button_states.buttonstate1; + const bool bLeft = ( uButtons & IN_MOVELEFT ) != 0; + const bool bRight = ( uButtons & IN_MOVERIGHT ) != 0; + + float flYawOffset = 0.f; + + if ( bLeft ) + flYawOffset += 90.f; + + if ( bRight ) + flYawOffset -= 90.f; + + if ( uButtons & IN_FORWARD ) + flYawOffset *= 0.5f; + else if ( uButtons & IN_BACK ) + flYawOffset = -flYawOffset * 0.5f + 180.f; + + const float flCmdForward = pBaseCmd->forwardmove(); + const float flCmdLeft = pBaseCmd->leftmove(); + + Vector3 VelocitySim = pLocalPawn->m_vecVelocity(); + + float flLastImpulseForward = m_flLastForwardMove; + float flLastImpulseLeft = m_flLastLeftMove; + + for ( auto i = 0; i < k_SubtickCount; ++i ) + { + pBaseCmd->set_forwardmove( flCmdForward ); + pBaseCmd->set_leftmove( flCmdLeft ); + + float flSpeed = VelocitySim.Length2D(); + + if ( flSpeed > 0.0001f ) + { + const float flYawRad = pViewAngles->m_y * ( k_Pi / 180.f ); + + const Vector3 vForward( std::cosf( flYawRad ) , std::sinf( flYawRad ) , 0.f ); + const Vector3 vRight( -std::sinf( flYawRad ) , std::cosf( flYawRad ) , 0.f ); + + Vector3 vWishDir + { + ( vForward.m_x * flCmdForward * k_MaxSpeed ) + ( vRight.m_x * flCmdLeft * k_MaxSpeed ), + ( vForward.m_y * flCmdForward * k_MaxSpeed ) + ( vRight.m_y * flCmdLeft * k_MaxSpeed ), + 0.f + }; + + float flWishSpeed = vWishDir.Length2D(); + + if ( flWishSpeed > 0.0001f ) + { + vWishDir.m_x /= flWishSpeed; + vWishDir.m_y /= flWishSpeed; + } + + flWishSpeed = std::fminf( flWishSpeed , k_MaxSpeed ); + + const float flCappedWish = std::fminf( flWishSpeed , k_SvAirMaxWishspeed ); + const float flCurrentSpeed = ( VelocitySim.m_x * vWishDir.m_x ) + ( VelocitySim.m_y * vWishDir.m_y ); + const float flAddSpeed = flCappedWish - flCurrentSpeed; + + if ( flAddSpeed > 0.f ) + { + const float flAccelSpeed = k_SvAiraccelerate * k_MaxSpeed * k_FrameTime; + const float flGain = std::fminf( flAccelSpeed * 0.5f , flAddSpeed ); + + VelocitySim.m_x += vWishDir.m_x * flGain; + VelocitySim.m_y += vWishDir.m_y * flGain; + } + } + + VelocitySim.m_z -= k_SvGravity * k_FrameTime; + flSpeed = VelocitySim.Length2D(); + + if ( flSpeed >= 10.f ) + { + pBaseCmd->set_forwardmove( 0.f ); + pBaseCmd->set_leftmove( 0.f ); + + const float flVelocityAngle = std::atan2f( VelocitySim.m_y , VelocitySim.m_x ) * ( 180.f / k_Pi ); + + const float flAccelSpeed = k_SvAiraccelerate * k_MaxSpeed * k_FrameTime; + const float flOptimalFloor = std::fmaxf( flAccelSpeed * 0.5f , k_SvAirMaxWishspeed - flAccelSpeed * 0.5f ); + const float flIdealAngle = std::clamp( std::atanf( flOptimalFloor / flSpeed ) * ( 180.f / k_Pi ) , 0.f , 45.f ); + + float flTargetYaw = pViewAngles->m_y + flYawOffset; + + while ( flTargetYaw > 180.f ) + flTargetYaw -= 360.f; + + while ( flTargetYaw < -180.f ) + flTargetYaw += 360.f; + + float flVelocityDelta = flTargetYaw - flVelocityAngle; + + while ( flVelocityDelta > 180.f ) + flVelocityDelta -= 360.f; + + while ( flVelocityDelta < -180.f ) + flVelocityDelta += 360.f; + + if ( ( std::fabsf( flVelocityDelta ) > 170.f && flSpeed > 80.f ) || ( flVelocityDelta > flIdealAngle && flSpeed > 80.f ) ) + { + flTargetYaw = flVelocityAngle + flIdealAngle; + pBaseCmd->set_leftmove( -1.f ); + } + else if ( -flIdealAngle <= flVelocityDelta || flSpeed <= 80.f ) + { + if ( m_bSideSwitch ) + { + flTargetYaw = flTargetYaw - flIdealAngle; + pBaseCmd->set_leftmove( -1.f ); + } + else + { + flTargetYaw = flTargetYaw + flIdealAngle; + pBaseCmd->set_leftmove( 1.f ); + } + } + else + { + flTargetYaw = flVelocityAngle - flIdealAngle; + pBaseCmd->set_leftmove( 1.f ); + } + + while ( flTargetYaw > 180.f ) + flTargetYaw -= 360.f; + + while ( flTargetYaw < -180.f ) + flTargetYaw += 360.f; + + RotateMoveToYaw( pBaseCmd , flTargetYaw , pViewAngles->m_y ); + } + + GetCL_Bypass()->AddProcessSubTick( 0 , false , static_cast( i ) / static_cast( k_SubtickCount ) , pBaseCmd->forwardmove() - flLastImpulseForward , pBaseCmd->leftmove() - flLastImpulseLeft ); + + flLastImpulseForward = pBaseCmd->forwardmove(); + flLastImpulseLeft = pBaseCmd->leftmove(); + + m_bSideSwitch = !m_bSideSwitch; + } + + m_flLastForwardMove = flLastImpulseForward; + m_flLastLeftMove = flLastImpulseLeft; +} + +auto CMisc::RotateMoveToYaw( CBaseUserCmdPB* pBaseCmd , const float flTargetYaw , const float flViewYaw ) -> void +{ + const float k_Pi = 3.14159265358979f; + + const float flTargetRad = flTargetYaw * ( k_Pi / 180.f ); + const float flViewRad = flViewYaw * ( k_Pi / 180.f ); + + const float flForwardMove = pBaseCmd->forwardmove(); + const float flSideMove = pBaseCmd->leftmove(); + + const Vector3 vTargetForward( std::cosf( flTargetRad ) , std::sinf( flTargetRad ) , 0.f ); + const Vector3 vTargetRight( -std::sinf( flTargetRad ) , std::cosf( flTargetRad ) , 0.f ); + + const Vector3 vViewForward( std::cosf( flViewRad ) , std::sinf( flViewRad ) , 0.f ); + const Vector3 vViewRight( -std::sinf( flViewRad ) , std::cosf( flViewRad ) , 0.f ); + + const Vector3 vMove = vTargetForward * flForwardMove + vTargetRight * flSideMove; + + const float flCorrectedForward = ( vViewForward.m_x * vMove.m_x ) + ( vViewForward.m_y * vMove.m_y ); + const float flCorrectedSide = ( vViewRight.m_x * vMove.m_x ) + ( vViewRight.m_y * vMove.m_y ); + + pBaseCmd->set_forwardmove( std::clamp( -flCorrectedForward , -1.f , 1.f ) ); + pBaseCmd->set_leftmove( std::clamp( -flCorrectedSide , -1.f , 1.f ) ); +} + auto CMisc::RunTriggerbot( CCSGOInput* pInput , CUserCmd* pUserCmd ) -> void { auto Reset = [ & ]() diff --git a/Andromeda-CS2-Base/Andromeda-CS2-Base/AndromedaClient/Features/CMisc/CMisc.hpp b/Andromeda-CS2-Base/Andromeda-CS2-Base/AndromedaClient/Features/CMisc/CMisc.hpp index 6b6074e..08774f8 100644 --- a/Andromeda-CS2-Base/Andromeda-CS2-Base/AndromedaClient/Features/CMisc/CMisc.hpp +++ b/Andromeda-CS2-Base/Andromeda-CS2-Base/AndromedaClient/Features/CMisc/CMisc.hpp @@ -12,6 +12,7 @@ class CCSGOInput; class CUserCmd; class IGameEvent; class C_CSPlayerPawn; +class CBaseUserCmdPB; class IMisc { @@ -39,6 +40,9 @@ private: auto RunMovement( CCSGOInput* pInput , CUserCmd* pUserCmd ) -> void; auto RunAutoStrafe( CCSGOInput* pInput , CUserCmd* pUserCmd , C_CSPlayerPawn* pLocalPawn , bool bOnGround ) -> void; + auto RunNullStrafe( CCSGOInput* pInput , CUserCmd* pUserCmd , C_CSPlayerPawn* pLocalPawn , bool bOnGround ) -> void; + auto RunSubtickStrafe( CCSGOInput* pInput , CUserCmd* pUserCmd , C_CSPlayerPawn* pLocalPawn , bool bOnGround ) -> void; + auto RotateMoveToYaw( CBaseUserCmdPB* pBaseCmd , const float flTargetYaw , const float flViewYaw ) -> void; private: std::chrono::steady_clock::time_point m_HitTime{}; @@ -55,6 +59,10 @@ private: std::atomic_bool m_bTriggerFiring = false; bool m_bWasOnGround = false; + + bool m_bSideSwitch = false; + float m_flLastForwardMove = 0.f; + float m_flLastLeftMove = 0.f; }; auto GetMisc() -> CMisc*; diff --git a/Andromeda-CS2-Base/Andromeda-CS2-Base/AndromedaClient/GUI/CAndromedaMenu.cpp b/Andromeda-CS2-Base/Andromeda-CS2-Base/AndromedaClient/GUI/CAndromedaMenu.cpp index d3dd4a1..f43abce 100644 --- a/Andromeda-CS2-Base/Andromeda-CS2-Base/AndromedaClient/GUI/CAndromedaMenu.cpp +++ b/Andromeda-CS2-Base/Andromeda-CS2-Base/AndromedaClient/GUI/CAndromedaMenu.cpp @@ -333,6 +333,8 @@ auto CAndromedaMenu::RenderMiscPanel() -> void Checkbox( "Bunnyhop" , Settings::Misc::Bunnyhop ); Checkbox( "Auto-Strafe" , Settings::Misc::AutoStrafe ); + Checkbox( "Null-Strafe" , Settings::Misc::NullStrafe ); + Checkbox( "SubTick-Strafe" , Settings::Misc::SubtickStrafe ); Checkbox( "JumpBug" , Settings::Misc::JumpBug ); Checkbox( "EdgeJump" , Settings::Misc::EdgeJump ); Checkbox( "Fast-Stop" , Settings::Misc::FastStop ); diff --git a/Andromeda-CS2-Base/Andromeda-CS2-Base/AndromedaClient/Settings/Settings.hpp b/Andromeda-CS2-Base/Andromeda-CS2-Base/AndromedaClient/Settings/Settings.hpp index 05f02eb..8cb2e6a 100644 --- a/Andromeda-CS2-Base/Andromeda-CS2-Base/AndromedaClient/Settings/Settings.hpp +++ b/Andromeda-CS2-Base/Andromeda-CS2-Base/AndromedaClient/Settings/Settings.hpp @@ -45,6 +45,8 @@ namespace Settings { inline auto Bunnyhop = false; inline auto AutoStrafe = false; + inline auto NullStrafe = false; + inline auto SubtickStrafe = false; inline auto JumpBug = false; inline auto EdgeJump = false; inline auto FastStop = false; diff --git a/Andromeda-CS2-Base/Andromeda-CS2-Base/GameClient/CL_Bypass.cpp b/Andromeda-CS2-Base/Andromeda-CS2-Base/GameClient/CL_Bypass.cpp index 155a204..6d833ec 100644 --- a/Andromeda-CS2-Base/Andromeda-CS2-Base/GameClient/CL_Bypass.cpp +++ b/Andromeda-CS2-Base/Andromeda-CS2-Base/GameClient/CL_Bypass.cpp @@ -21,7 +21,7 @@ auto CL_Bypass::PreClientCreateMove( CUserCmd* pUserCmd ) -> void auto CL_Bypass::PostClientCreateMove( CCSGOInput* pCCSGOInput , CUserCmd* pUserCmd ) -> void { for ( auto& SubTick : m_InternalSubTickList ) - AddSubtickMoveStep( pUserCmd , SubTick.m_Button , SubTick.m_Pressed , SubTick.m_flWhen ); + AddSubtickMoveStep( pUserCmd , SubTick.m_Button , SubTick.m_Pressed , SubTick.m_flWhen , SubTick.m_flForwardDelta , SubTick.m_flLeftDelta ); int EncodeLen = 0; @@ -155,6 +155,11 @@ auto CL_Bypass::AddProcessSubTick( const uint64_t Button , const bool Pressed , m_InternalSubTickList.emplace_back( Button , Pressed , When ); } +auto CL_Bypass::AddProcessSubTick( const uint64_t Button , const bool Pressed , const float When , const float ForwardDelta , const float LeftDelta ) -> void +{ + m_InternalSubTickList.emplace_back( Button , Pressed , When , ForwardDelta , LeftDelta ); +} + auto CL_Bypass::OnCBaseUserCmdPB( CBaseUserCmdPB* pBaseUserCmdPB ) -> void { m_Backup = *pBaseUserCmdPB; @@ -165,7 +170,7 @@ auto CL_Bypass::CalculateWhenValue() -> float return 0.99f; } -auto CL_Bypass::AddSubtickMoveStep( CUserCmd* pUserCmd , const uint64_t button , const bool pressed , const float when ) -> void +auto CL_Bypass::AddSubtickMoveStep( CUserCmd* pUserCmd , const uint64_t button , const bool pressed , const float when , const float forward_delta , const float left_delta ) -> void { auto pRepeatedMoveSteps = reinterpret_cast*>( (PBYTE)pUserCmd->cmd.mutable_base() + 0x18 ); @@ -180,6 +185,8 @@ auto CL_Bypass::AddSubtickMoveStep( CUserCmd* pUserCmd , const uint64_t button , pSubtickMoveStep->set_button( button ); pSubtickMoveStep->set_pressed( pressed ); pSubtickMoveStep->set_when( when ); + pSubtickMoveStep->set_analog_forward_delta( forward_delta ); + pSubtickMoveStep->set_analog_left_delta( left_delta ); } } else @@ -189,6 +196,8 @@ auto CL_Bypass::AddSubtickMoveStep( CUserCmd* pUserCmd , const uint64_t button , pSubtickMoveStep->set_button( button ); pSubtickMoveStep->set_pressed( pressed ); pSubtickMoveStep->set_when( when ); + pSubtickMoveStep->set_analog_forward_delta( forward_delta ); + pSubtickMoveStep->set_analog_left_delta( left_delta ); ProtobufAddToRepeatedPtrElement( pRepeatedMoveSteps , pSubtickMoveStep ); } diff --git a/Andromeda-CS2-Base/Andromeda-CS2-Base/GameClient/CL_Bypass.hpp b/Andromeda-CS2-Base/Andromeda-CS2-Base/GameClient/CL_Bypass.hpp index 7572152..70d4724 100644 --- a/Andromeda-CS2-Base/Andromeda-CS2-Base/GameClient/CL_Bypass.hpp +++ b/Andromeda-CS2-Base/Andromeda-CS2-Base/GameClient/CL_Bypass.hpp @@ -27,13 +27,14 @@ public: public: auto AddProcessSubTick( const uint64_t Button , const bool Pressed ) -> void; auto AddProcessSubTick( const uint64_t Button , const bool Pressed , const float When ) -> void; + auto AddProcessSubTick( const uint64_t Button , const bool Pressed , const float When , const float ForwardDelta , const float LeftDelta ) -> void; public: auto OnCBaseUserCmdPB( CBaseUserCmdPB* pBaseUserCmdPB ) -> void; private: auto CalculateWhenValue() -> float; - auto AddSubtickMoveStep( CUserCmd* pUserCmd , const uint64_t button , const bool pressed , const float when ) -> void; + auto AddSubtickMoveStep( CUserCmd* pUserCmd , const uint64_t button , const bool pressed , const float when , const float forward_delta = 0.f , const float left_delta = 0.f ) -> void; auto SpoofCrc() -> std::string; private: @@ -45,6 +46,8 @@ private: uint64_t m_Button = 0; bool m_Pressed = false; float m_flWhen = 0.f; + float m_flForwardDelta = 0.f; + float m_flLeftDelta = 0.f; }; private: