void c_movement::simulate_movement_for_jumpbug(c_user_cmd* cmd)
{
    auto pawn = g_ctx->m_local_pawn;
    if (!pawn)
        return;
    auto movement_services = pawn->m_movement_services();
    if (!movement_services)
        return;
    c_user_cmd temp_cmd = *cmd;
    CMoveData move_data{};
    movement_services->Setup(&temp_cmd, &move_data);
    pawn->PhysicsRunThink(0);
    movement_services->ProcessMovement(&temp_cmd);
    movement_services->Finish(&temp_cmd, &move_data);
}
void c_movement::jump_bug()
{
    if (!(g_ctx->m_user_cmd->m_button_state.m_button_state & IN_JUMP))
        return;
    if (!g_configs->misc.m_jumpbug)
        return;
    if (!g_interfaces->m_var->get_by_name("sv_autobunnyhopping")->get_bool())
        return;
    c_user_cmd* cmd = g_ctx->m_user_cmd;
    auto* base_cmd = cmd->pb.mutable_base();
    auto* pawn = g_ctx->m_local_pawn;
    if (!pawn)
        return;
    auto* movement = pawn->m_movement_services();
    if (!movement)
        return;
    const int original_flags = pawn->m_flags();
    const vec3_t original_origin = pawn->m_scene_node()->m_abs_origin();
    const vec3_t original_velocity = pawn->m_velocity();
    simulate_movement_for_jumpbug(cmd);
    const int predicted_flags = pawn->m_flags();
    pawn->m_flags() = original_flags;
    pawn->m_scene_node()->m_abs_origin() = original_origin;
    pawn->m_velocity() = original_velocity;
    const bool was_on_ground = (original_flags & FL_ONGROUND);
    const bool will_be_on_ground = (predicted_flags & FL_ONGROUND);
    bool edge_detected = false;
    if (was_on_ground && !will_be_on_ground)
    {
        auto* collision = pawn->m_collision();
        if (collision)
        {
            const vec3_t mins = collision->m_mins();
            const vec3_t maxs = collision->m_maxs();
            trace_filter_t filter;
            g_interfaces->m_trace->init_player_movement_trace_filter(&filter, pawn, 0x1C3003, COLLISION_GROUP_PLAYER_MOVEMENT);
            const float speed = original_velocity.length_2d();
            if (speed > 50.0f)
            {
                vec3_t forward_pos = original_origin + original_velocity.normalized() * 24.0f;
                forward_pos.z -= 32.0f;
                game_trace_t forward_trace;
                bbox_t bounds{ mins, maxs };
                if (g_interfaces->m_trace->trace_player_bbox(&original_origin, &forward_pos, &bounds, &filter, &forward_trace))
                {
                    edge_detected = forward_trace.m_fraction >= 0.95f;
                }
            }
            if (!edge_detected)
            {
                vec3_t down_pos = original_origin - vec3_t(0, 0, 32.0f);
                game_trace_t down_trace;
                bbox_t bounds{ mins, maxs };
                if (g_interfaces->m_trace->trace_player_bbox(&original_origin, &down_pos, &bounds, &filter, &down_trace))
                {
                    edge_detected = down_trace.m_fraction >= 0.9f;
                }
            }
        }
    }
    if ((was_on_ground && !will_be_on_ground) || edge_detected)
    {
        cmd->m_button_state.set_button_state(IN_DUCK, IN_BUTTON_DOWN);
        base_cmd->clear_subtick_moves();
        const float interval = g_interfaces->m_globals->m_interval_per_tick;
        const float tick_progress = fmodf(g_interfaces->m_globals->m_curtime, interval) / interval;
        float duck_timing = 0.0f;
        float jump_timing = 0.2f - (tick_progress * 0.1f);
        float unduck_timing = 0.85f + (tick_progress * 0.05f);
        float unjump_timing = unduck_timing + 0.03f;
        const float speed = original_velocity.length_2d();
        if (speed > 320.0f)
        {
            jump_timing *= 0.95f;
            unduck_timing *= 1.02f;
        }
        else if (speed < 200.0f)
        {
            jump_timing *= 1.05f;
            unduck_timing *= 0.98f;
        }
        auto add_subtick = [&](float time, int button, bool pressed)
        {
            if (auto move = g_interfaces->m_csgo_input->create_new_subtick_move_step(base_cmd->mutable_subtick_moves()))
            {
                move->set_when(time);
                move->set_button(button);
                move->set_pressed(pressed);
                base_cmd->mutable_subtick_moves()->AddAllocated(move);
            }
        };
        add_subtick(duck_timing, IN_DUCK, true);
        add_subtick(jump_timing, IN_JUMP, true);
        add_subtick(unduck_timing, IN_DUCK, false);
        add_subtick(unjump_timing, IN_JUMP, false);
    }
    else if (!(original_flags & FL_ONGROUND))
    {
        cmd->m_button_state.set_button_state(IN_DUCK, IN_BUTTON_UP);
    }
}