summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/esp.cpp96
-rw-r--r--src/hl2h.cpp6
-rw-r--r--src/hl2h.h3
-rw-r--r--src/menu.cpp16
-rw-r--r--src/vischeck.cpp98
5 files changed, 178 insertions, 41 deletions
diff --git a/src/esp.cpp b/src/esp.cpp
index 57f67ae..2c033ea 100644
--- a/src/esp.cpp
+++ b/src/esp.cpp
@@ -358,9 +358,13 @@ bool aimbot_apply(CUserCmd *cmd) {
358 hi = 4096; 358 hi = 4096;
359 359
360 constexpr float MAX_FOV = 60.0f; // only things near the crosshair 360 constexpr float MAX_FOV = 60.0f; // only things near the crosshair
361 float best_fov = MAX_FOV; 361 struct Candidate {
362 int best_idx = -1; 362 int idx;
363 Vector best_head{}; 363 float fov;
364 Vector approx;
365 };
366 Candidate cands[16];
367 int ncands = 0;
364 368
365 for (int idx = 1; idx <= hi; idx++) { 369 for (int idx = 1; idx <= hi; idx++) {
366 if (idx == local) 370 if (idx == local)
@@ -394,14 +398,22 @@ bool aimbot_apply(CUserCmd *cmd) {
394 Vector approx{origin.x, origin.y, 398 Vector approx{origin.x, origin.y,
395 origin.z + maxz * ((bone_mask & (1u << 1)) ? 0.9f : 0.55f)}; 399 origin.z + maxz * ((bone_mask & (1u << 1)) ? 0.9f : 0.55f)};
396 float fov = getfov(view, angle_to(g_cam_eye, approx)); 400 float fov = getfov(view, angle_to(g_cam_eye, approx));
397 if (fov >= best_fov) 401 if (fov >= MAX_FOV)
398 continue; 402 continue;
399 best_fov = fov; 403
400 best_idx = idx; 404 // keep candidates sorted by approximate angle, nearest first
401 best_head = approx; 405 int pos = ncands;
406 while (pos > 0 && cands[pos - 1].fov > fov)
407 pos--;
408 if (ncands < 16)
409 ncands++;
410 for (int j = ncands - 1; j > pos; j--)
411 cands[j] = cands[j - 1];
412 if (pos < 16)
413 cands[pos] = {idx, fov, approx};
402 } 414 }
403 415
404 if (best_idx < 0) { 416 if (ncands == 0) {
405 static double last_none = 0.0; 417 static double last_none = 0.0;
406 double now = esp_now(); 418 double now = esp_now();
407 if (now - last_none >= 2.0) { 419 if (now - last_none >= 2.0) {
@@ -411,43 +423,57 @@ bool aimbot_apply(CUserCmd *cmd) {
411 return false; 423 return false;
412 } 424 }
413 425
414 static int sel_logs = 0; 426 // Walk targets nearest-first. On each, pick the nearest selected hitbox
415 if (sel_logs < 5) { 427 // point that passes vischeck; skip to the next target if all are blocked.
416 sel_logs++; 428 int best_idx = -1;
417 logf("aimbot: target idx=%d fov=%.2f eye=(%.1f %.1f %.1f) " 429 Vector best_head{};
418 "approx_head=(%.1f %.1f %.1f)", 430 for (int ci = 0; ci < ncands && best_idx < 0; ci++) {
419 best_idx, best_fov, g_cam_eye.x, g_cam_eye.y, g_cam_eye.z, 431 int idx = cands[ci].idx;
420 best_head.x, best_head.y, best_head.z); 432 void *ent = entitylist_get(idx);
421 } 433 if (!ent || !bones_update(idx, ent))
422 434 continue;
423 // Refine to the nearest selected hitbox/bone point on the chosen target. 435 Vector pts[32];
424 Vector pts[32]; 436 int npoints = aim_points(idx, bone_mask, pts, 32);
425 int npoints = 0; 437 float aim_fov = 1e9f;
426 void *best_ent = entitylist_get(best_idx); 438 Vector aim_pt{};
427 if (best_ent && bones_update(best_idx, best_ent)) 439 for (int i = 0; i < npoints; i++) {
428 npoints = aim_points(best_idx, bone_mask, pts, 32); 440 float f = getfov(view, angle_to(g_cam_eye, pts[i]));
429 if (npoints <= 0) 441 if (f >= aim_fov)
430 return false; 442 continue;
431 float aim_fov = 1e9f; 443 if (aimbot_vischeck_enabled() && !is_visible(g_cam_eye, pts[i]))
432 Vector aim_pt{}; 444 continue;
433 for (int i = 0; i < npoints; i++) {
434 float f = getfov(view, angle_to(g_cam_eye, pts[i]));
435 if (f < aim_fov) {
436 aim_fov = f; 445 aim_fov = f;
437 aim_pt = pts[i]; 446 aim_pt = pts[i];
438 } 447 }
448 if (aim_fov <= MAX_FOV && aim_fov < 1e8f) {
449 best_idx = idx;
450 best_head = aim_pt;
451 static int sel_logs = 0;
452 if (sel_logs < 5) {
453 sel_logs++;
454 logf("aimbot: target idx=%d points=%d fov=%.2f vischeck=%d", idx,
455 npoints, aim_fov, (int)aimbot_vischeck_enabled());
456 }
457 }
439 } 458 }
440 if (aim_fov > MAX_FOV) 459
460 if (best_idx < 0) {
461 static double last_vis = 0.0;
462 double now = esp_now();
463 if (now - last_vis >= 2.0) {
464 last_vis = now;
465 logf("aimbot: no visible aim point (vischeck=%d)",
466 (int)aimbot_vischeck_enabled());
467 }
441 return false; 468 return false;
442 best_head = aim_pt; 469 }
443 470
444 QAngle desired = angle_to(g_cam_eye, best_head); 471 QAngle desired = angle_to(g_cam_eye, best_head);
445 static int want_logs = 0; 472 static int want_logs = 0;
446 if (want_logs < 10) { 473 if (want_logs < 10) {
447 want_logs++; 474 want_logs++;
448 logf("aimbot: aim=(%.1f %.1f %.1f) points=%d fov=%.2f want=%.2f/%.2f", 475 logf("aimbot: aim=(%.1f %.1f %.1f) want=%.2f/%.2f", best_head.x,
449 best_head.x, best_head.y, best_head.z, npoints, aim_fov, desired.x, 476 best_head.y, best_head.z, desired.x, desired.y);
450 desired.y);
451 } 477 }
452 float dp = desired.x - cmd->viewangles[0]; 478 float dp = desired.x - cmd->viewangles[0];
453 float dy = desired.y - cmd->viewangles[1]; 479 float dy = desired.y - cmd->viewangles[1];
diff --git a/src/hl2h.cpp b/src/hl2h.cpp
index c3b94fe..85012a0 100644
--- a/src/hl2h.cpp
+++ b/src/hl2h.cpp
@@ -408,9 +408,11 @@ static void *setup_thread(void *) {
408 g_iface.modelinfo = ef("VModelInfoClient006", nullptr); 408 g_iface.modelinfo = ef("VModelInfoClient006", nullptr);
409 g_iface.engineserver = ef("VEngineServer021", nullptr); 409 g_iface.engineserver = ef("VEngineServer021", nullptr);
410 g_iface.debugoverlay = ef("VDebugOverlay003", nullptr); 410 g_iface.debugoverlay = ef("VDebugOverlay003", nullptr);
411 g_iface.enginetrace = ef("EngineTraceClient003", nullptr);
411 logf("modelinfo: %p (VModelInfoClient006)", g_iface.modelinfo); 412 logf("modelinfo: %p (VModelInfoClient006)", g_iface.modelinfo);
412 logf("engineserver: %p (VEngineServer021), debugoverlay: %p", 413 logf("engineserver: %p (VEngineServer021), debugoverlay: %p, "
413 g_iface.engineserver, g_iface.debugoverlay); 414 "enginetrace: %p (EngineTraceClient003)",
415 g_iface.engineserver, g_iface.debugoverlay, g_iface.enginetrace);
414 416
415 resolve_netvars(); 417 resolve_netvars();
416 if (!find_input_global()) 418 if (!find_input_global())
diff --git a/src/hl2h.h b/src/hl2h.h
index 13af3d2..98d93f4 100644
--- a/src/hl2h.h
+++ b/src/hl2h.h
@@ -51,6 +51,7 @@ struct GameInterfaces {
51 void *modelinfo = nullptr; // IVModelInfoClient006 (model index -> model name) 51 void *modelinfo = nullptr; // IVModelInfoClient006 (model index -> model name)
52 void *engineserver = nullptr; // IVEngineServer021 (edict access, incl. server entities) 52 void *engineserver = nullptr; // IVEngineServer021 (edict access, incl. server entities)
53 void *debugoverlay = nullptr; // IVDebugOverlay003 (engine-accurate world->screen) 53 void *debugoverlay = nullptr; // IVDebugOverlay003 (engine-accurate world->screen)
54 void *enginetrace = nullptr; // EngineTraceClient003 (world vischeck)
54}; 55};
55extern GameInterfaces g_iface; 56extern GameInterfaces g_iface;
56 57
@@ -168,11 +169,13 @@ bool esp_option(int opt);
168bool bhop_autostrafe_enabled(); 169bool bhop_autostrafe_enabled();
169unsigned aimbot_bone_mask(); 170unsigned aimbot_bone_mask();
170int aimbot_smooth_step(); 171int aimbot_smooth_step();
172bool aimbot_vischeck_enabled();
171void *entitylist_get(int idx); 173void *entitylist_get(int idx);
172int engine_get_local_player(); 174int engine_get_local_player();
173void draw_text_col(int x, int y, const char *s, int r, int g, int b); 175void draw_text_col(int x, int y, const char *s, int r, int g, int b);
174void esp_draw(); 176void esp_draw();
175bool world_to_screen(const Vector &w, float &sx, float &sy); 177bool world_to_screen(const Vector &w, float &sx, float &sy);
178bool is_visible(const Vector &eye, const Vector &point);
176 179
177// bones.cpp — client-side animated skeleton / hitbox access (see there) 180// bones.cpp — client-side animated skeleton / hitbox access (see there)
178bool bones_update(int idx, void *ent); 181bool bones_update(int idx, void *ent);
diff --git a/src/menu.cpp b/src/menu.cpp
index db52fd2..d26b62e 100644
--- a/src/menu.cpp
+++ b/src/menu.cpp
@@ -75,6 +75,7 @@ enum {
75static bool g_aim_bones[AIM_BONE_COUNT] = {true, false, false, 75static bool g_aim_bones[AIM_BONE_COUNT] = {true, false, false,
76 false, false, false}; 76 false, false, false};
77static int g_aim_smooth = 5; // degrees per tick 77static int g_aim_smooth = 5; // degrees per tick
78static bool g_aim_vischeck = true;
78 79
79// ESP sub-options (menu "ESP" children). Defaults match the pre-option 80// ESP sub-options (menu "ESP" children). Defaults match the pre-option
80// behaviour: everything on. 81// behaviour: everything on.
@@ -101,6 +102,7 @@ static MenuNode g_aimbot_feat{NODE_FEATURE, "Aimbot", false, 1, {}, nullptr, nul
101static MenuNode g_aimbot_active{NODE_TOGGLE, "Active", false, 0, {}, &g_aimbot.active, nullptr, nullptr}; 102static MenuNode g_aimbot_active{NODE_TOGGLE, "Active", false, 0, {}, &g_aimbot.active, nullptr, nullptr};
102static MenuNode g_aimbot_bind{NODE_BIND, "Bind", false, 0, {}, nullptr, &g_aimbot.bind, nullptr}; 103static MenuNode g_aimbot_bind{NODE_BIND, "Bind", false, 0, {}, nullptr, &g_aimbot.bind, nullptr};
103static MenuNode g_aimbot_bones_feat{NODE_FEATURE, "Bones", false, 0, {}, nullptr, nullptr, nullptr}; 104static MenuNode g_aimbot_bones_feat{NODE_FEATURE, "Bones", false, 0, {}, nullptr, nullptr, nullptr};
105static MenuNode g_aimbot_vischeck_node{NODE_TOGGLE, "Vischeck", false, 0, {}, &g_aim_vischeck, nullptr, nullptr};
104static MenuNode g_aimbot_bone_nodes[AIM_BONE_COUNT] = { 106static MenuNode g_aimbot_bone_nodes[AIM_BONE_COUNT] = {
105 {NODE_TOGGLE, "Head", false, 0, {}, &g_aim_bones[AIM_BONE_HEAD], nullptr, nullptr}, 107 {NODE_TOGGLE, "Head", false, 0, {}, &g_aim_bones[AIM_BONE_HEAD], nullptr, nullptr},
106 {NODE_TOGGLE, "Torso", false, 0, {}, &g_aim_bones[AIM_BONE_TORSO], nullptr, nullptr}, 108 {NODE_TOGGLE, "Torso", false, 0, {}, &g_aim_bones[AIM_BONE_TORSO], nullptr, nullptr},
@@ -126,9 +128,10 @@ static void build_tree() {
126 g_esp_feat.children[0] = &g_esp_active; 128 g_esp_feat.children[0] = &g_esp_active;
127 g_aimbot_feat.children[0] = &g_aimbot_active; 129 g_aimbot_feat.children[0] = &g_aimbot_active;
128 g_aimbot_feat.children[1] = &g_aimbot_bind; 130 g_aimbot_feat.children[1] = &g_aimbot_bind;
129 g_aimbot_feat.children[2] = &g_aimbot_bones_feat; 131 g_aimbot_feat.children[2] = &g_aimbot_vischeck_node;
130 g_aimbot_feat.children[3] = &g_aimbot_smooth_node; 132 g_aimbot_feat.children[3] = &g_aimbot_bones_feat;
131 g_aimbot_feat.child_count = 4; 133 g_aimbot_feat.children[4] = &g_aimbot_smooth_node;
134 g_aimbot_feat.child_count = 5;
132 for (int i = 0; i < AIM_BONE_COUNT; i++) 135 for (int i = 0; i < AIM_BONE_COUNT; i++)
133 g_aimbot_bones_feat.children[i] = &g_aimbot_bone_nodes[i]; 136 g_aimbot_bones_feat.children[i] = &g_aimbot_bone_nodes[i];
134 g_aimbot_bones_feat.child_count = AIM_BONE_COUNT; 137 g_aimbot_bones_feat.child_count = AIM_BONE_COUNT;
@@ -203,10 +206,11 @@ static void config_save() {
203 fprintf(f, "[bhop]\nactive=%d\nbind=%d\nautostrafe=%d\n", g_bhop.active, 206 fprintf(f, "[bhop]\nactive=%d\nbind=%d\nautostrafe=%d\n", g_bhop.active,
204 g_bhop.bind, g_bhop_autostrafe); 207 g_bhop.bind, g_bhop_autostrafe);
205 fprintf(f, "[abh]\nactive=%d\nbind=%d\n", g_abh.active, g_abh.bind); 208 fprintf(f, "[abh]\nactive=%d\nbind=%d\n", g_abh.active, g_abh.bind);
206 fprintf(f, "[aimbot]\nactive=%d\nbind=%d\nsmooth=%d\n" 209 fprintf(f, "[aimbot]\nactive=%d\nbind=%d\nsmooth=%d\nvischeck=%d\n"
207 "bone_head=%d\nbone_torso=%d\nbone_larm=%d\nbone_rarm=%d\n" 210 "bone_head=%d\nbone_torso=%d\nbone_larm=%d\nbone_rarm=%d\n"
208 "bone_lleg=%d\nbone_rleg=%d\n", 211 "bone_lleg=%d\nbone_rleg=%d\n",
209 g_aimbot.active, g_aimbot.bind, g_aim_smooth, 212 g_aimbot.active, g_aimbot.bind, g_aim_smooth,
213 (int)g_aim_vischeck,
210 g_aim_bones[AIM_BONE_HEAD], g_aim_bones[AIM_BONE_TORSO], 214 g_aim_bones[AIM_BONE_HEAD], g_aim_bones[AIM_BONE_TORSO],
211 g_aim_bones[AIM_BONE_LARM], g_aim_bones[AIM_BONE_RARM], 215 g_aim_bones[AIM_BONE_LARM], g_aim_bones[AIM_BONE_RARM],
212 g_aim_bones[AIM_BONE_LLEG], g_aim_bones[AIM_BONE_RLEG]); 216 g_aim_bones[AIM_BONE_LLEG], g_aim_bones[AIM_BONE_RLEG]);
@@ -269,6 +273,8 @@ static void config_load() {
269 g_aim_bones[AIM_BONE_LLEG] = atoi(val) != 0; 273 g_aim_bones[AIM_BONE_LLEG] = atoi(val) != 0;
270 } else if (strcmp(key, "bone_rleg") == 0) { 274 } else if (strcmp(key, "bone_rleg") == 0) {
271 g_aim_bones[AIM_BONE_RLEG] = atoi(val) != 0; 275 g_aim_bones[AIM_BONE_RLEG] = atoi(val) != 0;
276 } else if (strcmp(key, "vischeck") == 0) {
277 g_aim_vischeck = atoi(val) != 0;
272 } 278 }
273 } 279 }
274 if (in_esp) { 280 if (in_esp) {
@@ -429,6 +435,8 @@ unsigned aimbot_bone_mask() {
429 435
430int aimbot_smooth_step() { return g_aim_smooth; } 436int aimbot_smooth_step() { return g_aim_smooth; }
431 437
438bool aimbot_vischeck_enabled() { return g_aim_vischeck; }
439
432static void menu_handle_input() { 440static void menu_handle_input() {
433 poll_keyboard(); 441 poll_keyboard();
434 442
diff --git a/src/vischeck.cpp b/src/vischeck.cpp
new file mode 100644
index 0000000..92d194b
--- /dev/null
+++ b/src/vischeck.cpp
@@ -0,0 +1,98 @@
1// vischeck.cpp — world-only line-of-sight test via EngineTraceClient003.
2//
3// IEngineTrace::TraceRay (vtable slot 4) is given a ray from the eye to the
4// aim point and a TRACE_WORLD_ONLY filter. MASK_VISIBLE ignores glass/windows
5// (you can see through them) while still hitting opaque world geometry.
6// Per the SDK, TRACE_WORLD_ONLY does not test static props; that is the
7// deliberate trade-off for this build.
8
9#include "hl2h.h"
10#include <cstddef>
11#include <cstdint>
12
13namespace {
14
15struct alignas(16) VectorAlignedCompat {
16 float x, y, z, w;
17};
18
19// Ray_t: four VectorAligned (16 bytes each) followed by two bools.
20struct alignas(16) RayCompat {
21 VectorAlignedCompat start;
22 VectorAlignedCompat delta;
23 VectorAlignedCompat startOffset;
24 VectorAlignedCompat extents;
25 bool isRay;
26 bool isSwept;
27};
28
29static_assert(sizeof(VectorAlignedCompat) == 16, "VectorAligned layout");
30static_assert(offsetof(RayCompat, delta) == 16, "Ray_t delta offset");
31static_assert(offsetof(RayCompat, isRay) == 64, "Ray_t isRay offset");
32
33// trace_t / CBaseTrace prefix. cplane_t is Vector + float + type + signbits
34// + 2 pad bytes, so fraction lands at 0x2C and startsolid at 0x37.
35struct TraceCompat {
36 Vector startpos;
37 Vector endpos;
38 Vector normal;
39 float dist;
40 uint8_t type;
41 uint8_t signbits;
42 uint8_t pad[2];
43 float fraction;
44 int contents;
45 uint16_t dispFlags;
46 bool allsolid;
47 bool startsolid;
48 uint8_t rest[128]; // CGameTrace tail (surface, ent, hitbox, ...)
49};
50
51static_assert(offsetof(TraceCompat, fraction) == 44, "trace_t fraction");
52static_assert(offsetof(TraceCompat, startsolid) == 55, "trace_t startsolid");
53
54// ITraceFilter has no virtual destructor: vtable[0] = ShouldHitEntity,
55// vtable[1] = GetTraceType.
56struct ITraceFilterCompat {
57 virtual bool ShouldHitEntity(void *pEntity, int contentsMask) = 0;
58 virtual int GetTraceType() const = 0;
59};
60
61struct WorldOnlyFilter : ITraceFilterCompat {
62 bool ShouldHitEntity(void *, int) override { return false; }
63 int GetTraceType() const override { return 1; } // TRACE_WORLD_ONLY
64};
65
66// MASK_VISIBLE = CONTENTS_SOLID | CONTENTS_MOVEABLE | CONTENTS_OPAQUE |
67// CONTENTS_IGNORE_NODRAW_OPAQUE
68constexpr unsigned MASK_VISIBLE = 0x1u | 0x4000u | 0x80u | 0x2000u;
69
70} // namespace
71
72bool is_visible(const Vector &eye, const Vector &point) {
73 static bool warned = false;
74 if (!g_iface.enginetrace) {
75 if (!warned) {
76 warned = true;
77 logf("vischeck: EngineTraceClient003 unavailable; failing open");
78 }
79 return true;
80 }
81
82 float dx = point.x - eye.x, dy = point.y - eye.y, dz = point.z - eye.z;
83 RayCompat ray{};
84 ray.start = {eye.x, eye.y, eye.z, 0.0f};
85 ray.delta = {dx, dy, dz, 0.0f};
86 ray.startOffset = {0.0f, 0.0f, 0.0f, 0.0f};
87 ray.extents = {0.0f, 0.0f, 0.0f, 0.0f};
88 ray.isRay = true;
89 ray.isSwept = (dx * dx + dy * dy + dz * dz) > 0.0f;
90
91 TraceCompat tr{};
92 WorldOnlyFilter filter;
93 using TraceRayFn = void (*)(void *, const RayCompat *, unsigned, void *,
94 TraceCompat *);
95 vfunc<TraceRayFn>(g_iface.enginetrace, 4)(g_iface.enginetrace, &ray,
96 MASK_VISIBLE, &filter, &tr);
97 return tr.fraction > 0.97f && !tr.startsolid;
98}