Загрузка данных
#include "Aimbot.hpp"
#ifdef USE_VMPROTECT
#include "VMProtectSDK.h"
#endif
// -----------------------------------------------------------------------
// SmoothStep переделан: теперь возвращает значение СТРОГО в [0.0, 1.0].
// SmoothFactor 1 → step ~0.01 (очень плавно, маленький шаг)
// SmoothFactor 100 → step 1.0 (мгновенно, за один тик)
// Формула: step = (SmoothFactor / 100) ^ 2 — квадратичная кривая,
// даёт хорошее ощущение smooth на низких значениях и резкость на высоких.
// -----------------------------------------------------------------------
__declspec(noinline) float GetSmoothStep(float SmoothFactor)
{
// Нормализуем в [0, 1]
float t = std::clamp(SmoothFactor, 0.0f, 100.0f) / 100.0f;
// Квадратичная кривая: плавный старт, резкий финиш
return t * t;
}
static bool IsValidWorldPosition(const D3DXVECTOR3& pos)
{
if (std::isnan(pos.x) || std::isnan(pos.y) || std::isnan(pos.z)) return false;
if (std::isinf(pos.x) || std::isinf(pos.y) || std::isinf(pos.z)) return false;
if (pos.x == 0.0f && pos.y == 0.0f && pos.z == 0.0f) return false;
if (pos.x < -10000.f || pos.x > 10000.f) return false;
if (pos.y < -10000.f || pos.y > 10000.f) return false;
if (pos.z < -300.f || pos.z > 3000.f) return false;
return true;
}
uintptr_t Core::Features::cAimbot::GetActiveCamera()
{
const uintptr_t base = Core::SDK::Pointers::pCamGamePlayDirector;
const uintptr_t ADSCam = Mem.Read<uintptr_t>(base + 0x2D8);
const uintptr_t FollowCam = Mem.Read<uintptr_t>(base + 0x2C0);
if (ADSCam)
{
const D3DXVECTOR3 pos = Mem.Read<D3DXVECTOR3>(ADSCam + 0x60);
if (IsValidWorldPosition(pos))
return ADSCam;
}
if (FollowCam)
{
const 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
D3DXVECTOR2 ScreenBonePos = Core::SDK::Game::WorldToScreen(BonePos);
if (!Core::SDK::Game::IsOnScreen(ScreenBonePos))
return;
float dx = ScreenBonePos.x - g_Variables.g_vGameWindowCenter.x;
float dy = ScreenBonePos.y - g_Variables.g_vGameWindowCenter.y;
if (std::isnan(dx) || std::isnan(dy) || std::isinf(dx) || std::isinf(dy))
return;
// step теперь строго в [0, 1] — никакого overshooting
const float SmoothFactor = std::clamp(static_cast<float>(g_Config.Aimbot->AimbotSpeed), 0.0f, 100.0f);
const float LocalSmoothStep = GetSmoothStep(SmoothFactor);
// Дробные пиксели накапливаем и отправляем целыми числами
// чтобы не терять точность на низких значениях smooth
m_AccumX += dx * LocalSmoothStep;
m_AccumY += dy * LocalSmoothStep;
const LONG moveX = static_cast<LONG>(m_AccumX);
const LONG moveY = static_cast<LONG>(m_AccumY);
// Вычитаем только то что реально отправили
m_AccumX -= static_cast<float>(moveX);
m_AccumY -= static_cast<float>(moveY);
if (moveX == 0 && moveY == 0)
return;
INPUT input = {};
input.type = INPUT_MOUSE;
input.mi.dwFlags = MOUSEEVENTF_MOVE;
input.mi.dx = moveX;
input.mi.dy = moveY;
SendInput(1, &input, sizeof(INPUT));
#ifdef USE_VMPROTECT
VMProtectEnd();
#endif
}
void Core::Features::cAimbot::Start()
{
#ifdef USE_VMPROTECT
VMProtectBeginMutation("AimbotLoop");
#endif
uintptr_t LastKnownCam = 0;
auto LastCamSwitchTime = std::chrono::steady_clock::now();
const int CAM_COOLDOWN_MS = 500;
// Таймер для ограничения частоты тиков аима.
// Аим должен работать синхронно с игровым рендером (~60-165 Гц),
// а не на максимальной скорости процессора.
// 2мс на тик = ~500 Гц — достаточно для любого монитора.
const auto TICK_INTERVAL = std::chrono::microseconds(2000); // 2мс = 500 Hz
auto lastTick = std::chrono::steady_clock::now();
while (true)
{
// --- Ограничение частоты тиков ---
auto now = std::chrono::steady_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(now - lastTick);
if (elapsed < TICK_INTERVAL)
{
// Спим оставшееся время с небольшим запасом
std::this_thread::sleep_for(TICK_INTERVAL - elapsed);
continue;
}
lastTick = std::chrono::steady_clock::now();
if (!g_Config.Aimbot->Enabled
|| !g_Config.Aimbot->KeyBind
|| !(GetAsyncKeyState(g_Config.Aimbot->KeyBind) & 0x8000)
|| GetForegroundWindow() == g_Variables.g_hCheatWindow)
{
// Сбрасываем аккумулятор пока кнопка не зажата
m_AccumX = 0.0f;
m_AccumY = 0.0f;
continue;
}
// Детект смены камеры (нажатие V / ADS)
uintptr_t CurrentCam = GetActiveCamera();
if (CurrentCam != LastKnownCam)
{
LastKnownCam = CurrentCam;
LastCamSwitchTime = std::chrono::steady_clock::now();
m_AccumX = 0.0f;
m_AccumY = 0.0f;
continue;
}
const auto msSinceSwitch = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - LastCamSwitchTime).count();
if (msSinceSwitch < CAM_COOLDOWN_MS)
continue;
CPed* Ped = Core::SDK::Game::GetClosestPed(
g_Config.Aimbot->MaxDistance,
g_Config.Aimbot->IgnoreNPCs,
g_Config.Aimbot->OnlyVisible);
if (!Ped)
{
m_AccumX = 0.0f;
m_AccumY = 0.0f;
continue;
}
D3DXVECTOR3 HeadPos = Ped->GetBonePosDefault(0 /*Head*/);
D3DXVECTOR2 ScreenHeadPos = Core::SDK::Game::WorldToScreen(HeadPos);
if (Core::SDK::Game::IsOnScreen(ScreenHeadPos))
{
const int Fov = static_cast<int>(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));
}
else
{
// Цель вышла за FOV — сбрасываем аккумулятор
m_AccumX = 0.0f;
m_AccumY = 0.0f;
}
}
else
{
m_AccumX = 0.0f;
m_AccumY = 0.0f;
}
}
#ifdef USE_VMPROTECT
VMProtectEnd();
#endif
}