Загрузка данных


#include "Aimbot.hpp"

#ifdef USE_VMPROTECT
#include "VMProtectSDK.h"
#endif

__declspec(noinline) float GetSmoothStep(float SmoothFactor) {
    if (SmoothFactor > 0) {
        if (SmoothFactor >= 98 && SmoothFactor <= 100) return 0.5f / SmoothFactor;
        if (SmoothFactor >= 80 && SmoothFactor < 98)  return 1.8f / SmoothFactor;
        if (SmoothFactor >= 60 && SmoothFactor < 80)  return 2.0f / SmoothFactor;
        if (SmoothFactor >= 40 && SmoothFactor < 60)  return 2.2f / SmoothFactor;
        if (SmoothFactor >= 20 && SmoothFactor < 40)  return 2.5f / SmoothFactor;
        return 3.0f / SmoothFactor;
    }
    return 1.0f;
}

// -----------------------------------------------------------------------
// Проверяет, что позиция находится в пределах игрового мира GTA5.
// Карта примерно -10000..10000 по X/Y, высота -200..2000 по Z.
// Если координата вышла за эти границы — указатель протух.
// -----------------------------------------------------------------------
static bool IsValidWorldPosition(const D3DXVECTOR3& pos)
{
    const float MAP_LIMIT = 10000.0f;
    const float Z_MIN     = -300.0f;
    const float Z_MAX     =  3000.0f;

    if (pos.x < -MAP_LIMIT || pos.x > MAP_LIMIT) return false;
    if (pos.y < -MAP_LIMIT || pos.y > MAP_LIMIT) return false;
    if (pos.z < Z_MIN      || pos.z > Z_MAX)     return false;

    // Нулевой вектор тоже невалиден — камера никогда не стоит в (0,0,0)
    if (pos.x == 0.0f && pos.y == 0.0f && pos.z == 0.0f) return false;

    return true;
}

// -----------------------------------------------------------------------
// Проверяет, что направляющий вектор нормализован (длина ~1.0).
// Протухший указатель даст здесь огромные или нулевые значения.
// -----------------------------------------------------------------------
static bool IsValidDirectionVector(const D3DXVECTOR3& v)
{
    float len = D3DXVec3Length(&v);
    // После нормализации длина должна быть строго около 1.0
    return (len > 0.9f && len < 1.1f);
}

// -----------------------------------------------------------------------
// Возвращает указатель на активную камеру.
// Проверяет ADS-камеру (+0x2D8), затем обычную (+0x2C0).
// Валидирует позицию камеры перед возвратом — защита от stale pointer.
// -----------------------------------------------------------------------
uintptr_t Core::Features::cAimbot::GetActiveCamera()
{
    uintptr_t base = Core::SDK::Pointers::pCamGamePlayDirector;

    uintptr_t ADSCam    = Mem.Read<uintptr_t>(base + 0x2D8);
    uintptr_t FollowCam = Mem.Read<uintptr_t>(base + 0x2C0);

    // Пробуем ADS-камеру
    if (ADSCam)
    {
        D3DXVECTOR3 pos = Mem.Read<D3DXVECTOR3>(ADSCam + 0x60);
        if (IsValidWorldPosition(pos))
            return ADSCam;
    }

    // Падаем на обычную камеру
    if (FollowCam)
    {
        D3DXVECTOR3 pos = Mem.Read<D3DXVECTOR3>(FollowCam + 0x60);
        if (IsValidWorldPosition(pos))
            return FollowCam;
    }

    return 0; // Оба указателя протухли — не стреляем
}

void Core::Features::cAimbot::SetViewAngles(CPed* Ped, D3DXVECTOR3 BonePos)
{
#ifdef USE_VMPROTECT
    VMProtectBeginMutation("SetViewAngles");
#endif

    uintptr_t ActiveCam = GetActiveCamera();
    if (!ActiveCam) return;

    D3DXVECTOR3 CamPos            = Mem.Read<D3DXVECTOR3>(ActiveCam + 0x60);
    D3DXVECTOR3 CurrentViewAngles = Mem.Read<D3DXVECTOR3>(ActiveCam + 0x40);

    // Санити-чек: текущие углы камеры должны быть нормализованным вектором.
    // Если нет — указатель только что протух, пропускаем тик.
    if (!IsValidDirectionVector(CurrentViewAngles))
        return;

    // Санити-чек: кость цели тоже должна быть в пределах мира
    if (!IsValidWorldPosition(BonePos))
        return;

    D3DXVECTOR3 TargetViewAngles = BonePos - CamPos;
    D3DXVec3Normalize(&TargetViewAngles, &TargetViewAngles);

    // Санити-чек нормализованного результата
    if (!IsValidDirectionVector(TargetViewAngles))
        return;

    // -----------------------------------------------------------------------
    // Защита от "прыжка" аима за FOV при смене камеры:
    // ограничиваем максимальное изменение угла за один тик.
    // dot(A,B) == 1.0 → векторы совпадают, == 0.0 → 90°, < 0 → > 90°.
    // Если угол между текущим и целевым > MAX_ANGLE_DELTA — пропускаем тик.
    // -----------------------------------------------------------------------
    const float MAX_ANGLE_DELTA = 0.35f; // ~70° за тик — больше не бывает при нормальной игре
    float dot = D3DXVec3Dot(&CurrentViewAngles, &TargetViewAngles);
    if (dot < MAX_ANGLE_DELTA)
        return;

    float SmoothFactor = std::clamp(static_cast<float>(g_Config.Aimbot->AimbotSpeed), 0.0f, 100.0f);
    volatile float LocalSmoothStep = GetSmoothStep(SmoothFactor);

    D3DXVECTOR3 FinalAngles;
    FinalAngles.x = CurrentViewAngles.x + (TargetViewAngles.x - CurrentViewAngles.x) * LocalSmoothStep;
    FinalAngles.y = CurrentViewAngles.y + (TargetViewAngles.y - CurrentViewAngles.y) * LocalSmoothStep;
    FinalAngles.z = CurrentViewAngles.z + (TargetViewAngles.z - CurrentViewAngles.z) * LocalSmoothStep;

    D3DXVec3Normalize(&FinalAngles, &FinalAngles);

    // Финальная проверка перед записью
    if (!IsValidDirectionVector(FinalAngles))
        return;

    Mem.Write<D3DXVECTOR3>(ActiveCam + 0x40,  FinalAngles);
    Mem.Write<D3DXVECTOR3>(ActiveCam + 0x3D0, FinalAngles);

#ifdef USE_VMPROTECT
    VMProtectEnd();
#endif
}

void Core::Features::cAimbot::Start()
{
#ifdef USE_VMPROTECT
    VMProtectBeginMutation("AimbotLoop");
#endif

    // Запоминаем последний валидный указатель камеры.
    // При смене вида (V) он изменится — это сигнал для временной паузы аима,
    // чтобы не поймать момент, когда старый адрес ещё не зануlen, а новый ещё не готов.
    uintptr_t LastKnownCam  = 0;
    auto      LastCamSwitch = std::chrono::steady_clock::now();
    const int CAM_SWITCH_COOLDOWN_MS = 500; // пауза после смены камеры

    while (true)
    {
        if (g_Config.Aimbot->Enabled
            && g_Config.Aimbot->KeyBind
            && (GetAsyncKeyState(g_Config.Aimbot->KeyBind) & 0x8000)
            && GetForegroundWindow() != g_Variables.g_hCheatWindow)
        {
            uintptr_t CurrentCam = GetActiveCamera();

            // Детект смены камеры (нажатие V или ADS)
            if (CurrentCam != LastKnownCam)
            {
                LastKnownCam  = CurrentCam;
                LastCamSwitch = std::chrono::steady_clock::now();
                std::this_thread::sleep_for(std::chrono::nanoseconds(1));
                continue; // Даём игре время инициализировать новую камеру
            }

            // Пауза сразу после смены — новый объект камеры ещё может быть не полностью инициализирован
            auto msSinceSwitch = std::chrono::duration_cast<std::chrono::milliseconds>(
                std::chrono::steady_clock::now() - LastCamSwitch).count();

            if (msSinceSwitch < CAM_SWITCH_COOLDOWN_MS)
            {
                std::this_thread::sleep_for(std::chrono::nanoseconds(1));
                continue;
            }

            CPed* Ped = Core::SDK::Game::GetClosestPed(
                g_Config.Aimbot->MaxDistance,
                g_Config.Aimbot->IgnoreNPCs,
                g_Config.Aimbot->OnlyVisible);

            if (!Ped)
            {
                std::this_thread::sleep_for(std::chrono::nanoseconds(1));
                continue;
            }

            D3DXVECTOR3 HeadPos       = Ped->GetBonePosDefault(0 /*Head*/);
            D3DXVECTOR2 ScreenHeadPos = Core::SDK::Game::WorldToScreen(HeadPos);

            if (Core::SDK::Game::IsOnScreen(ScreenHeadPos))
            {
                int Fov = std::hypot(
                    ScreenHeadPos.x - g_Variables.g_vGameWindowCenter.x,
                    ScreenHeadPos.y - g_Variables.g_vGameWindowCenter.y);

                if (Fov < g_Config.Aimbot->FOV)
                {
                    SetViewAngles(Ped, HeadPos + D3DXVECTOR3(0, 0, 0.08f));
                }
            }
        }

        std::this_thread::sleep_for(std::chrono::nanoseconds(1));
    }

#ifdef USE_VMPROTECT
    VMProtectEnd();
#endif
}