summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-10-26 16:39:11 +0100
committersalmonik <salmonik@tuta.io>2026-10-26 16:39:11 +0100
commitccdcd4ad0483b98c9c20bd1304ad062ef8af3a4b (patch)
tree5a766a3b72ac442a4e7f1537afb07b6cb09e90bd /src
parentESP: generic full-bone skeleton for non-humanoid NPCs (headcrabs, crows, ...) (diff)
downloadhl2h-ccdcd4ad0483b98c9c20bd1304ad062ef8af3a4b.tar.gz
hl2h-ccdcd4ad0483b98c9c20bd1304ad062ef8af3a4b.zip
Aimbot: head-only nearest-to-crosshair with bounded smoothing
- select the hostile NPC whose (approximate) head is closest in angle to the crosshair, then refine to its actual head hitbox/bone - aimbot menu feature (Active + Bind, default Mouse5, rebindable via the existing bind chooser); config [aimbot] active/bind - never snap: move at most 5 degrees per tick toward the target, shortest-path yaw, pitch clamped to +/-89, 0.05 degree deadzone; no overshoot/oscillation - fixes the usercmd CRC after modifying viewangles - skip when dead; ignores friendly/neutral entities (crows, citizens, ...)
Diffstat (limited to '')
-rw-r--r--src/esp.cpp117
-rw-r--r--src/hl2h.cpp7
-rw-r--r--src/hl2h.h4
-rw-r--r--src/menu.cpp26
4 files changed, 146 insertions, 8 deletions
diff --git a/src/esp.cpp b/src/esp.cpp
index 34a702a..1cb8b70 100644
--- a/src/esp.cpp
+++ b/src/esp.cpp
@@ -316,6 +316,123 @@ static void draw_skeleton(const EspTarget &t) {
316 } 316 }
317} 317}
318 318
319// ---------------------------------------------------------------------------
320// aimbot v1: nearest hostile NPC head to the crosshair.
321// Bounded smoothing: never snap, never overshoot. Each tick moves at most
322// MAX_STEP degrees toward the target and stops inside DEADZONE.
323// ---------------------------------------------------------------------------
324
325static QAngle angle_to(const Vector &from, const Vector &to) {
326 float dx = to.x - from.x, dy = to.y - from.y, dz = to.z - from.z;
327 float horiz = sqrtf(dx * dx + dy * dy);
328 if (horiz < 0.001f)
329 horiz = 0.001f;
330 QAngle a{};
331 a.x = -atan2f(dz, horiz) * 180.0f / PI_F; // Source: positive pitch = down
332 a.y = atan2f(dy, dx) * 180.0f / PI_F;
333 a.z = 0.0f;
334 return a;
335}
336
337bool aimbot_apply(CUserCmd *cmd) {
338 if (!g_iface.entitylist || g_net.origin < 0 || g_net.maxs < 0)
339 return false;
340 int local = engine_get_local_player();
341 if (local <= 0)
342 return false;
343 void *local_ent = entitylist_get(local);
344 if (!local_ent)
345 return false;
346 if (g_net.health >= 0 &&
347 *(int *)((uintptr_t)local_ent + g_net.health) <= 0)
348 return false;
349
350 QAngle view{cmd->viewangles[0], cmd->viewangles[1], 0.0f};
351
352 using Fn = int (*)(void *);
353 int hi = vfunc<Fn>(g_iface.entitylist, 6)(g_iface.entitylist);
354 if (hi < 0 || hi > 4096)
355 hi = 4096;
356
357 constexpr float MAX_FOV = 60.0f; // only things near the crosshair
358 float best_fov = MAX_FOV;
359 int best_idx = -1;
360 Vector best_head{};
361
362 for (int idx = 1; idx <= hi; idx++) {
363 if (idx == local)
364 continue;
365 void *ent = entitylist_get(idx);
366 if (!ent)
367 continue;
368 void *net = entity_networkable(idx);
369 if (!net)
370 continue;
371 const char *cls = entity_class_name(net, nullptr);
372 if (!cls || !is_npc_class(cls))
373 continue;
374
375 uintptr_t e = (uintptr_t)ent;
376 int life = g_net.lifestate >= 0
377 ? (int)*(unsigned char *)(e + g_net.lifestate)
378 : -1;
379 if (life != LIFE_ALIVE)
380 continue;
381 const char *model = entity_model_name(ent);
382 if (model_is_friendly(model) || model_is_neutral(model))
383 continue;
384
385 float maxz = g_net.maxs >= 0 ? *(float *)(e + g_net.maxs + 8) : 72.0f;
386 if (!(maxz > 4.0f && maxz < 512.0f))
387 maxz = 72.0f;
388 Vector origin{*(float *)(e + g_net.origin),
389 *(float *)(e + g_net.origin + 4),
390 *(float *)(e + g_net.origin + 8)};
391 Vector approx{origin.x, origin.y, origin.z + maxz * 0.9f};
392 float fov = getfov(view, angle_to(g_cam_eye, approx));
393 if (fov >= best_fov)
394 continue;
395 best_fov = fov;
396 best_idx = idx;
397 best_head = approx;
398 }
399
400 if (best_idx < 0)
401 return false;
402
403 // Refine to the actual head hitbox/bone for the chosen target only.
404 void *best_ent = entitylist_get(best_idx);
405 if (best_ent && bones_update(best_idx, best_ent)) {
406 Vector head;
407 if (head_position(best_idx, head) &&
408 getfov(view, angle_to(g_cam_eye, head)) <= MAX_FOV)
409 best_head = head;
410 }
411
412 QAngle desired = angle_to(g_cam_eye, best_head);
413 float dp = desired.x - cmd->viewangles[0];
414 float dy = desired.y - cmd->viewangles[1];
415 while (dy > 180.0f) dy -= 360.0f;
416 while (dy < -180.0f) dy += 360.0f;
417
418 constexpr float DEADZONE = 0.05f;
419 constexpr float MAX_STEP = 5.0f;
420 float maxd = fmaxf(fabsf(dp), fabsf(dy));
421 if (maxd <= DEADZONE)
422 return false;
423
424 float scale = (maxd > MAX_STEP) ? (MAX_STEP / maxd) : 1.0f;
425 float np = cmd->viewangles[0] + dp * scale;
426 float ny = cmd->viewangles[1] + dy * scale;
427 if (np > 89.0f) np = 89.0f;
428 if (np < -89.0f) np = -89.0f;
429 while (ny > 180.0f) ny -= 360.0f;
430 while (ny < -180.0f) ny += 360.0f;
431 cmd->viewangles[0] = np;
432 cmd->viewangles[1] = ny;
433 return true;
434}
435
319void esp_draw() { 436void esp_draw() {
320 if (!g_surface || !g_iface.entitylist || !g_net.esp_ok()) return; 437 if (!g_surface || !g_iface.entitylist || !g_net.esp_ok()) return;
321 if (!g_scr_w) { 438 if (!g_scr_w) {
diff --git a/src/hl2h.cpp b/src/hl2h.cpp
index b666585..21fa7ae 100644
--- a/src/hl2h.cpp
+++ b/src/hl2h.cpp
@@ -241,6 +241,13 @@ static void do_move(int seq) {
241 note_modified(cmd, flags, vel); 241 note_modified(cmd, flags, vel);
242 } 242 }
243 } 243 }
244
245 if (feature_active(FEAT_AIMBOT)) {
246 if (aimbot_apply(cmd)) {
247 const float *vel = (const float *)(e + g_net.velocity);
248 note_modified(cmd, flags, vel);
249 }
250 }
244} 251}
245 252
246// --------------------------------------------------------------------------- 253// ---------------------------------------------------------------------------
diff --git a/src/hl2h.h b/src/hl2h.h
index 289e4e6..f92b919 100644
--- a/src/hl2h.h
+++ b/src/hl2h.h
@@ -135,7 +135,9 @@ struct QAngle { float x, y, z; };
135extern Vector g_cam_eye; 135extern Vector g_cam_eye;
136extern Vector g_cam_view; 136extern Vector g_cam_view;
137QAngle vector_angles(const Vector &forward); 137QAngle vector_angles(const Vector &forward);
138enum FeatureId { FEAT_BHOP = 0, FEAT_ABH = 1, FEAT_ESP = 2 }; 138float getfov(const QAngle &my_view, const QAngle &aim_ang);
139bool aimbot_apply(CUserCmd *cmd);
140enum FeatureId { FEAT_BHOP = 0, FEAT_ABH = 1, FEAT_ESP = 2, FEAT_AIMBOT = 3 };
139enum EspOption { 141enum EspOption {
140 ESP_OPT_SHOW_NEUTRAL = 0, 142 ESP_OPT_SHOW_NEUTRAL = 0,
141 ESP_OPT_SHOW_HEALTHBAR = 1, 143 ESP_OPT_SHOW_HEALTHBAR = 1,
diff --git a/src/menu.cpp b/src/menu.cpp
index 6474d69..269174d 100644
--- a/src/menu.cpp
+++ b/src/menu.cpp
@@ -56,6 +56,7 @@ static bool g_bhop_autostrafe = true;
56 56
57static FeatureState g_abh{true, 0}; 57static FeatureState g_abh{true, 0};
58static FeatureState g_esp{true, 0}; 58static FeatureState g_esp{true, 0};
59static FeatureState g_aimbot{true, 604}; // default Mouse5, rebindable in menu
59 60
60// ESP sub-options (menu "ESP" children). Defaults match the pre-option 61// ESP sub-options (menu "ESP" children). Defaults match the pre-option
61// behaviour: everything on. 62// behaviour: everything on.
@@ -77,6 +78,9 @@ static MenuNode g_esp_opt_nodes[ESP_OPT_COUNT] = {
77 {NODE_TOGGLE, "Show name", false, 0, {}, &g_esp_opt[ESP_OPT_SHOW_NAME], nullptr, nullptr}, 78 {NODE_TOGGLE, "Show name", false, 0, {}, &g_esp_opt[ESP_OPT_SHOW_NAME], nullptr, nullptr},
78 {NODE_TOGGLE, "Show skeleton", false, 0, {}, &g_esp_opt[ESP_OPT_SHOW_SKELETON], nullptr, nullptr}, 79 {NODE_TOGGLE, "Show skeleton", false, 0, {}, &g_esp_opt[ESP_OPT_SHOW_SKELETON], nullptr, nullptr},
79}; 80};
81static MenuNode g_aimbot_feat{NODE_FEATURE, "Aimbot", false, 1, {}, nullptr, nullptr, nullptr};
82static MenuNode g_aimbot_active{NODE_TOGGLE, "Active", false, 0, {}, &g_aimbot.active, nullptr, nullptr};
83static MenuNode g_aimbot_bind{NODE_BIND, "Bind", false, 0, {}, nullptr, &g_aimbot.bind, nullptr};
80 84
81static MenuNode *g_visible[32]; // flattened visible nodes for arrow navigation 85static MenuNode *g_visible[32]; // flattened visible nodes for arrow navigation
82static int g_visible_count = 0; 86static int g_visible_count = 0;
@@ -90,13 +94,17 @@ static void build_tree() {
90 g_bhop_feat.child_count = 2; 94 g_bhop_feat.child_count = 2;
91 g_abh_feat.children[0] = &g_abh_active; 95 g_abh_feat.children[0] = &g_abh_active;
92 g_esp_feat.children[0] = &g_esp_active; 96 g_esp_feat.children[0] = &g_esp_active;
97 g_aimbot_feat.children[0] = &g_aimbot_active;
98 g_aimbot_feat.children[1] = &g_aimbot_bind;
99 g_aimbot_feat.child_count = 2;
93 for (int i = 0; i < ESP_OPT_COUNT; i++) 100 for (int i = 0; i < ESP_OPT_COUNT; i++)
94 g_esp_feat.children[i + 1] = &g_esp_opt_nodes[i]; 101 g_esp_feat.children[i + 1] = &g_esp_opt_nodes[i];
95 g_esp_feat.child_count = ESP_OPT_COUNT + 1; 102 g_esp_feat.child_count = ESP_OPT_COUNT + 1;
96 g_root.children[0] = &g_bhop_feat; 103 g_root.children[0] = &g_bhop_feat;
97 g_root.children[1] = &g_abh_feat; 104 g_root.children[1] = &g_abh_feat;
98 g_root.children[2] = &g_esp_feat; 105 g_root.children[2] = &g_esp_feat;
99 g_root.child_count = 3; 106 g_root.children[3] = &g_aimbot_feat;
107 g_root.child_count = 4;
100} 108}
101 109
102static int g_depth[32]; // depth of each visible row (for indentation) 110static int g_depth[32]; // depth of each visible row (for indentation)
@@ -159,6 +167,8 @@ static void config_save() {
159 fprintf(f, "[bhop]\nactive=%d\nbind=%d\nautostrafe=%d\n", g_bhop.active, 167 fprintf(f, "[bhop]\nactive=%d\nbind=%d\nautostrafe=%d\n", g_bhop.active,
160 g_bhop.bind, g_bhop_autostrafe); 168 g_bhop.bind, g_bhop_autostrafe);
161 fprintf(f, "[abh]\nactive=%d\nbind=%d\n", g_abh.active, g_abh.bind); 169 fprintf(f, "[abh]\nactive=%d\nbind=%d\n", g_abh.active, g_abh.bind);
170 fprintf(f, "[aimbot]\nactive=%d\nbind=%d\n", g_aimbot.active,
171 g_aimbot.bind);
162 fprintf(f, "[esp]\nactive=%d\nbind=%d\nneutral=%d\nhealthbar=%d\n" 172 fprintf(f, "[esp]\nactive=%d\nbind=%d\nneutral=%d\nhealthbar=%d\n"
163 "healthnum=%d\nname=%d\nskeleton=%d\n", 173 "healthnum=%d\nname=%d\nskeleton=%d\n",
164 g_esp.active, g_esp.bind, g_esp_opt[ESP_OPT_SHOW_NEUTRAL], 174 g_esp.active, g_esp.bind, g_esp_opt[ESP_OPT_SHOW_NEUTRAL],
@@ -184,6 +194,8 @@ static void config_load() {
184 cur = &g_abh; 194 cur = &g_abh;
185 else if (strstr(line, "[esp]")) 195 else if (strstr(line, "[esp]"))
186 cur = &g_esp; 196 cur = &g_esp;
197 else if (strstr(line, "[aimbot]"))
198 cur = &g_aimbot;
187 else 199 else
188 cur = nullptr; 200 cur = nullptr;
189 in_esp = (cur == &g_esp); 201 in_esp = (cur == &g_esp);
@@ -319,8 +331,8 @@ static bool key_pressed(int sc) {
319// feature gate queried from hl2h.cpp do_move(): feature must be Active AND 331// feature gate queried from hl2h.cpp do_move(): feature must be Active AND
320// its bind key held (always-on when unbound). 332// its bind key held (always-on when unbound).
321bool feature_active(int feature) { 333bool feature_active(int feature) {
322 static FeatureState *feats[3] = {&g_bhop, &g_abh, &g_esp}; 334 static FeatureState *feats[4] = {&g_bhop, &g_abh, &g_esp, &g_aimbot};
323 if (feature < 0 || feature > 2) 335 if (feature < 0 || feature > 3)
324 return false; 336 return false;
325 FeatureState *f = feats[feature]; 337 FeatureState *f = feats[feature];
326 if (!f->active) 338 if (!f->active)
@@ -376,7 +388,7 @@ static void menu_handle_input() {
376 MenuNode *n = g_visible[g_selected]; 388 MenuNode *n = g_visible[g_selected];
377 if (n->bind_ptr) { 389 if (n->bind_ptr) {
378 // clear same bind from other features 390 // clear same bind from other features
379 FeatureState *features[] = {&g_bhop, &g_abh}; 391 FeatureState *features[] = {&g_bhop, &g_abh, &g_aimbot};
380 for (auto *f : features) 392 for (auto *f : features)
381 if (&f->bind != n->bind_ptr && f->bind == i) 393 if (&f->bind != n->bind_ptr && f->bind == i)
382 f->bind = 0; 394 f->bind = 0;
@@ -578,10 +590,10 @@ void outer_draw() {
578 draw_text(270, 10 + i * 20, "[press key]"); 590 draw_text(270, 10 + i * 20, "[press key]");
579 else { 591 else {
580 /* stable name for current bind */ 592 /* stable name for current bind */
581 static char bn[2][32]; 593 static char bn[3][32];
582 static FeatureState *feats[] = {&g_bhop, &g_abh}; 594 static FeatureState *feats[] = {&g_bhop, &g_abh, &g_aimbot};
583 int fi = 0; 595 int fi = 0;
584 for (int k = 0; k < 2; k++) 596 for (int k = 0; k < 3; k++)
585 if (n->bind_ptr == &feats[k]->bind) 597 if (n->bind_ptr == &feats[k]->bind)
586 fi = k; 598 fi = k;
587 snprintf(bn[fi], 32, "[%s]", scancode_name(*n->bind_ptr)); 599 snprintf(bn[fi], 32, "[%s]", scancode_name(*n->bind_ptr));