Загрузка данных
#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
}