Skip to main content
The HotWheels SDK movement system provides automated and assisted movement features for CS:GO, including bunny hopping, jump bugs, and edge detection.

Overview

The movement module (hacks/features/movement/) implements:
  • Bunny hop (auto jump)
  • Edge jump detection
  • Jump bug exploit
  • Mini jump
  • Auto-align for pixel-perfect movement
  • Movement vector correction

Architecture

The movement system operates in two phases:
struct impl {
    struct pre_prediction {
        void think( );
    } pre_prediction;
    
    struct post_prediction {
        void think( );
    } post_prediction;
};
Source: hacks/features/movement/movement.h:17-23
Pre-prediction handles features that modify input before game physics, while post-prediction handles features that require physics results.

Bunny Hop

Automatic bunny hopping removes jump input when the player is airborne:
void movement::impl::bhop( )
{
    // its a bhop hack
    [[unlikely]] if ( !g_config.find< bool >( HASH( "m_bh" ) ) ) return;

    if ( g_config.find< bool >( HASH( "m_jb" ) ) && 
         g_input.key_state( input::key_state_t::KEY_DOWN, g_config.find< int >( HASH( "m_jb_key" ) ) ) )
        return;

    // will return if player is noclip/ladder/fly mode
    [[unlikely]] if ( g_ctx.local->move_type( ).has_any_of(
        { sdk::move_type::MOVE_NOCLIP, sdk::move_type::MOVE_LADDER, sdk::move_type::MOVE_FLY } ) ) return;

    // remove jump flag if player in air
    if ( !g_ctx.local->flags( ).has( sdk::flags::ONGROUND ) )
        g_ctx.cmd->buttons.remove( sdk::buttons::IN_JUMP );
}
Source: hacks/features/movement/movement.cpp:131-146
Bunny hop is disabled when jump bug is active to prevent conflicts.

Edge Jump

Detects when the player leaves the ground and automatically jumps:
1

Detect Edge

Check if player was on ground last tick but is airborne now
2

Auto Jump

Add the jump button to maintain velocity
3

Long Jump (Optional)

Duck for 2 ticks after edge jump for extended distance

Implementation

void movement::impl::edge_jump( )
{
    if ( !g_config.find< bool >( HASH( "m_ej" ) ) ||
         !g_input.key_state( input::key_state_t::KEY_DOWN, g_config.find< int >( HASH( "m_ej_key" ) ) ) ) {
        g_movement.longjump.start_timer = false;
        g_movement.longjump.time_stamp  = 0;
        return;
    }

    if ( g_prediction.backup_vars.flags.has( sdk::flags::ONGROUND ) && 
         !g_ctx.local->flags( ).has( sdk::flags::ONGROUND ) ) {
        g_ctx.cmd->buttons.add( sdk::buttons::IN_JUMP );

        g_movement.longjump.start_timer = true;
        g_movement.longjump.time_stamp  = g_interfaces.globals->tick_count;
    }

    // Long jump extension
    [[likely]] if ( g_config.find< bool >( HASH( "m_lj" ) ) ) {
        if ( g_movement.longjump.start_timer ) {
            if ( g_movement.longjump.time_stamp + 2 > g_interfaces.globals->tick_count ) {
                g_ctx.cmd->buttons.add( sdk::buttons::IN_DUCK );
                g_ctx.cmd->forward_move = 0;
            } else {
                g_movement.longjump.start_timer = false;
            }
        }
    }
}
Source: hacks/features/movement/movement.cpp:36-71

Jump Bug

Exploits CS:GO physics to negate fall damage:
void movement::impl::jump_bug( )
{
    if ( !g_config.find< bool >( HASH( "m_jb" ) ) )
        return;

    if ( !g_input.key_state( input::key_state_t::KEY_DOWN, g_config.find< int >( HASH( "m_jb_key" ) ) ) )
        return;

    [[unlikely]] if ( g_ctx.local->move_type( ).has_any_of(
        { sdk::move_type::MOVE_NOCLIP, sdk::move_type::MOVE_LADDER, sdk::move_type::MOVE_FLY } ) ) return;

    if ( g_ctx.local->flags( ).has( sdk::flags::ONGROUND ) ) {
        if ( !g_prediction.backup_vars.flags.has( sdk::flags::ONGROUND ) )
            g_ctx.cmd->buttons.add( sdk::buttons::IN_DUCK );

        g_ctx.cmd->buttons.remove( sdk::buttons::IN_JUMP );
    }

    // secret sauce
    if ( !g_ctx.local->flags( ).has( sdk::flags::ONGROUND ) && 
         g_prediction.backup_vars.flags.has( sdk::flags::ONGROUND ) )
        g_ctx.cmd->buttons.remove( sdk::buttons::IN_DUCK );
}
Source: hacks/features/movement/movement.cpp:73-113
Jump bug activates by ducking on the exact tick the player lands, canceling fall damage.

Mini Jump

Combines jump and duck for lower jump height:
void movement::impl::mini_jump( )
{
    if ( !g_config.find< bool >( HASH( "m_mj" ) ) )
        return;

    if ( !g_input.key_state( input::key_state_t::KEY_DOWN, g_config.find< int >( HASH( "m_mj_key" ) ) ) )
        return;

    [[unlikely]] if ( g_ctx.local->move_type( ).has_any_of(
        { sdk::move_type::MOVE_NOCLIP, sdk::move_type::MOVE_LADDER, sdk::move_type::MOVE_FLY } ) ) return;

    if ( g_prediction.backup_vars.flags.has( sdk::flags::ONGROUND ) && 
         !g_ctx.local->flags( ).has( sdk::flags::ONGROUND ) ) {
        g_ctx.cmd->buttons.add( sdk::buttons::IN_DUCK | sdk::buttons::IN_JUMP );
    }
}
Source: hacks/features/movement/movement.cpp:115-129

Auto-Align

Automatically aligns player position to unit grid for pixel-perfect movement:
1

Check Alignment Need

Determine if player position is within alignment threshold (0.03125 units)
2

Detect Wall Collision

Ray trace in 4 directions to find nearby walls
3

Calculate Strafe Angle

Compute optimal movement angle based on velocity and wall direction
4

Apply Movement Correction

Rotate movement input to align with calculated angle

Implementation

void movement::impl::auto_align( )
{
    if ( !g_config.find< bool >( HASH( "m_auto_align" ) ) )
        return;

    [[unlikely]] if ( g_ctx.local->move_type( ).has_any_of(
        { sdk::move_type::MOVE_NOCLIP, sdk::move_type::MOVE_LADDER, sdk::move_type::MOVE_FLY } ) ) return;

    if ( g_prediction.backup_vars.flags.has( sdk::flags::ONGROUND ) || 
         g_prediction.backup_vars.velocity.length_2d( ) < 10.f )
        return;

    const math::vec3 origin = g_ctx.local->get_abs_origin( );
    const math::vec3 velocity = g_ctx.local->velocity( );

    // Check if alignment is needed
    const auto has_to_align = []( ) -> bool {
        const auto origin = g_ctx.local->get_abs_origin( );

        const math::vec2 remainder1 = math::vec2( 1.f - ( origin.x - floor( origin.x ) ), 
                                                   1.f - ( origin.y - floor( origin.y ) ) );
        const math::vec2 remainder2 = math::vec2( ( origin.x - floor( origin.x ) ), 
                                                   ( origin.y - floor( origin.y ) ) );

        return ( ( remainder1.x >= movement::distance_to_stop && remainder1.x <= movement::distance_till_adjust ) ||
                 ( remainder1.y >= movement::distance_to_stop && remainder1.y <= movement::distance_till_adjust ) ) ||
               ( ( remainder2.x >= movement::distance_to_stop && remainder2.x <= movement::distance_till_adjust ) ||
                 ( remainder2.y >= movement::distance_to_stop && remainder2.y <= movement::distance_till_adjust ) );
    };

    if ( !has_to_align( ) )
        return;

    // Calculate strafe angle
    float angle_diff = math::rad2deg( atan( ( g_ctx.cmd->buttons.has( sdk::buttons::IN_DUCK ) ? 4.6775f : 4.5500f ) / 
                                             velocity.length_2d( ) ) ) * ( 2.f * math::util::pi_f );

    math::vec3 angle = { g_ctx.cmd->view_angles.x, 
                         math::normalize_yaw( base_yaw + direction * angle_diff ), 
                         g_ctx.cmd->view_angles.z };

    g_movement.rotate_movement( angle );
}
Source: hacks/features/movement/movement.cpp:166-240

Alignment Constants

constexpr float distance_to_stop = 0.00100f;
constexpr float distance_till_adjust = 0.03125f;
Source: hacks/features/movement/movement.h:8-9
Auto-align only works while airborne with velocity > 10 units/sec. Ground movement is not affected.

Movement Correction

Corrects movement vectors after angle changes:
void movement::impl::rotate_movement( math::vec3& angle )
{
    if ( angle.is_zero( ) )
        angle = g_interfaces.engine->get_view_angles( );

    math::vec3 vec_move( g_ctx.cmd->forward_move, g_ctx.cmd->side_move, g_ctx.cmd->up_move );

    math::vec3 ang_move{ };
    math::vector_angles( vec_move, ang_move );

    const float speed = vec_move.length_2d( );

    const float rotation = math::deg2rad( g_ctx.cmd->view_angles.y - angle.y + ang_move.y );

    g_ctx.cmd->forward_move = std::cosf( rotation ) * speed;
    g_ctx.cmd->side_move = std::sinf( rotation ) * speed;
}
Source: hacks/features/movement/movement.cpp:148-164

Execution Pipeline

void movement::impl::pre_prediction::think( )
{
    if ( !g_ctx.local->is_alive( ) || !g_interfaces.engine->is_fully_connected( ) )
        return;

    if ( !g_ctx.cmd->buttons.has( sdk::IN_BULLRUSH ) && g_config.find< bool >( HASH( "m_fast_duck" ) ) )
        g_ctx.cmd->buttons.add( sdk::IN_BULLRUSH );

    g_movement.bhop( );
}
Source: hacks/features/movement/movement.cpp:6-29

API Reference

bhop()

Automatic bunny hop implementation.

edge_jump()

Detects and executes edge jumps with optional long jump.

jump_bug()

Performs jump bug to negate fall damage.

mini_jump()

Executes lower-height jumps.

auto_align()

Aligns player to unit grid for pixel-perfect movement.

rotate_movement(angle)

Corrects movement vectors after view angle changes. Parameters:
  • angle - Target view angle for movement correction

Build docs developers (and LLMs) love