Valorant Aimbot using OpenCV

Raj Hammeer S. Hada
5 min readApr 22, 2023

--

I suck at Valorant, well that’s not the only reason why my mind vented into creating an aimbot for jumping some ranks. I also really wanted to know if it could be done? (I took it on my ego NGL)

The fact that hitting head every time being a Silver is very realistic and not that bad after all. (Catching my sarcasm low elo players? 😅)

The Start — 💚

Here is the map I wanted to follow after doing some experiments and scraping the internet. I am embarrassed to say that my internet history looks like a typical cheater lol, with search keywords like ‘Valorant Aimbot’ ‘Valo Aimbot Github’. (The guilt wasn’t stopping me to look at some code examples online). Most folks on the internet agreed to something like this —

  1. Get screen capture and feed it to the detection engine.
  2. Detect purple outline of enemy body on the screen.
  3. Trigger a mouse click on the detected blob.

Sounds easy right? I mean excluding the sleepless nights with false body detections and wanting to give up because mouse_move() and mouse_left_click() just won’t work (Valorant seems to have blocked SendInput() coming from the system calls on Windows, more on this later…)

Screen Capture —

This might be the worst screen capture code I could have written but but ✋. It gets the work done. It basically returns me a Mat object of the screen for my further processing, pretty cool right? (Pair this with ffmpeg and you have your own OBS ready 💁)

#include <iostream>
#include <Windows.h>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

Mat captureScreenMat(HWND hwnd)
{
HDC hwindowDC,hwindowCompatibleDC;

int height,width,srcheight,srcwidth;
HBITMAP hbwindow;
Mat src;
BITMAPINFOHEADER bi;

hwindowDC=GetDC(hwnd);
hwindowCompatibleDC=CreateCompatibleDC(hwindowDC);
SetStretchBltMode(hwindowCompatibleDC,COLORONCOLOR);

RECT windowsize; // get the height and width of the screen
GetClientRect(hwnd, &windowsize);

srcheight = windowsize.bottom;
srcwidth = windowsize.right;
height = windowsize.bottom/1; //change this to whatever size you want to resize to
width = windowsize.right/1;

src.create(height,width,CV_8UC4);

// create a bitmap
hbwindow = CreateCompatibleBitmap( hwindowDC, width, height);
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = width;
bi.biHeight = -height; //this is the line that makes it draw upside down or not
bi.biPlanes = 1;
bi.biBitCount = 32;
bi.biCompression = BI_RGB;
bi.biSizeImage = 0;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = 0;
bi.biClrImportant = 0;

// use the previously created device context with the bitmap
SelectObject(hwindowCompatibleDC, hbwindow);
// copy from the window device context to the bitmap device context
StretchBlt( hwindowCompatibleDC, 0,0, width, height, hwindowDC, 0, 0,srcwidth,srcheight, SRCCOPY); //change SRCCOPY to NOTSRCCOPY for wacky colors !
GetDIBits(hwindowCompatibleDC,hbwindow,0,height,src.data,(BITMAPINFO *)&bi,DIB_RGB_COLORS); //copy from hwindowCompatibleDC to hbwindow

// avoid memory leak
DeleteObject (hbwindow);
DeleteDC(hwindowCompatibleDC);
ReleaseDC(hwnd, hwindowDC);

return src;
}

int main()
{
namedWindow("Screen Capture", CV_WINDOW_NORMAL);
HWND hwnd = GetDesktopWindow();
int key = 0;
while(key != 27){
Mat src = captureScreenMat(hwnd);
key = waitKey(30);
}

return 0;
}

Body Detection —

After a bunch of hit and trial I was able to detect enemy bodies almost immediately as they show up on screen (At close to mid-range distances) and below are the settings/filters I have used from OpenCV.

Note — for people who haven’t played hefty Valorant, the outline of the enemy bodies is marked purple.

You see the purple outline?

Hence you would see the inRange() method being used to mask purple color in the source image.

Later I am detecting all the blobs larger than a particular area using findContours() and then only selecting the blob that is closest to the center (considering that a bit of manual aiming would be done by the user themselves).

void bodyDetector(Mat frame){
Mat image;
cvtColor(frame, image, COLOR_BGR2HSV);
inRange(image, Scalar(130, 50, 50), Scalar(150, 255, 255), image);

int morph_size = 1;
Mat element = getStructuringElement(
MORPH_DILATE, Size(2 * morph_size + 1,
2 * morph_size + 1),
Point(morph_size, morph_size));

//dilate to make everything bigger a bit
dilate(image, image, element, Point(-1, -1), 5, 1);

threshold(image, image, 60, 255, THRESH_BINARY);

vector<vector<Point> > contours;
findContours( image, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE );

vector<vector<Point> >hull( contours.size() );

for( size_t i = 0; i < contours.size(); i++ )
{
convexHull( contours[i], hull[i] );
}

vector<Moments> mu(hull.size()); //get moments
for (int i = 0; i < hull.size(); i++)
{
mu[i] = moments(hull[i], false);
}

vector<Point2f> mc(hull.size()); //get centers
for (int i = 0; i < hull.size(); i++)
{
mc[i] = Point2f(mu[i].m10 / mu[i].m00, mu[i].m01 / mu[i].m00);
}

vector<int> largeBlobIndexes;
for (int j = 0; j < hull.size(); j++) {
double newArea = contourArea(hull.at(j));
if (newArea > MAX_AREA_CAP) {
largeBlobIndexes.push_back(j);
}
}

int closestToCenterBlobDistance = 1920;
int closestToCenterBlobDistanceIndx = 0;
for(int i=0;i<largeBlobIndexes.size();i++){
Point2f frameCenter = Point2f(frame.cols/2, frame.rows/2);
if(sqrt(pow(frameCenter.x - mc[i].x, 2) + pow(frameCenter.y - mc[i].y, 2) * 1.0) < closestToCenterBlobDistance){
closestToCenterBlobDistance = sqrt(pow(frameCenter.x - mc[i].x, 2) + pow(frameCenter.y - mc[i].y, 2) * 1.0);
closestToCenterBlobDistanceIndx = i;
}
}

drawContours( frame, hull, closestToCenterBlobDistanceIndx, Scalar(255, 0, 0), CV_FILLED, 8 );

//trigger click at this x and y on SHIFT_PRESS
if(GetKeyState(VK_SHIFT) & 0x8000)
{
click_x = mc[closestToCenterBlobDistanceIndx].x;
click_y = mc[closestToCenterBlobDistanceIndx].y;
EnumDisplayMonitors(NULL,NULL, Monitorenumproc,0);
}

imshow("Head Hitter", frame);
}

I am being 100% honest when I say this piece of code doesn’t hit the body every time, let alone the required head hit to win a kill.

On finding the cvMoments(weighted center of the blob) of the closet blob to the center, we trigger a click event using the normal SendInput() WIN system call. The click events works just fine on the test video that I feed to the algorithm.

Body detection doing its job 👍

But But ✋What’s the catch here? (Aimbot works? 😶) —

As soon as I launch Valorant, the click events from SendInput() on Windows doesn't work. Looks like Valorant has worked a bit on their anti-cheat innit? I am suspecting Valorant blocks the SendInput() calls. I have tried a bunch of other methods including (mouse_event() calls on Windows, but that’s a failure too).

Well, I am really sorry for a disappointing end. I am still looking for a workaround, let’s see if I am able to figure it out. I will keep pushing updates here. Care to help me on this? Comment down and I will give you access to the source code.

--

--

Raj Hammeer S. Hada

Android Dev looking for quality text, sage scripts & efficient code