summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/esp.cpp6
-rw-r--r--src/gui.cpp106
-rw-r--r--src/hl2h.h3
-rw-r--r--src/vischeck.cpp113
4 files changed, 175 insertions, 53 deletions
diff --git a/src/esp.cpp b/src/esp.cpp
index 3db25e6..5683704 100644
--- a/src/esp.cpp
+++ b/src/esp.cpp
@@ -541,7 +541,7 @@ static bool select_aim_target(const QAngle &view, unsigned bone_mask,
541 continue; 541 continue;
542 if (f >= best) 542 if (f >= best)
543 continue; 543 continue;
544 if (vischeck && !is_visible(g_cam_eye, pts[i])) 544 if (vischeck && !is_visible(g_cam_eye, pts[i], local_ent, ent))
545 continue; 545 continue;
546 best = f; 546 best = f;
547 best_pt = pts[i]; 547 best_pt = pts[i];
@@ -1061,7 +1061,7 @@ bool melee_aimbot_apply(CUserCmd *cmd) {
1061 float pd = vec_dist(g_cam_eye, pts[i]); 1061 float pd = vec_dist(g_cam_eye, pts[i]);
1062 if (pd > MELEE_RANGE || pd >= bestd) 1062 if (pd > MELEE_RANGE || pd >= bestd)
1063 continue; 1063 continue;
1064 if (!is_visible(g_cam_eye, pts[i])) 1064 if (!is_visible(g_cam_eye, pts[i], local_ent, tent))
1065 continue; 1065 continue;
1066 bestd = pd; 1066 bestd = pd;
1067 aim = pts[i]; 1067 aim = pts[i];
@@ -1083,7 +1083,7 @@ bool melee_aimbot_apply(CUserCmd *cmd) {
1083 if (ad > MELEE_RANGE) 1083 if (ad > MELEE_RANGE)
1084 aim = c.closest; 1084 aim = c.closest;
1085 } 1085 }
1086 if (!is_visible(g_cam_eye, aim)) 1086 if (!is_visible(g_cam_eye, aim, local_ent, tent))
1087 continue; 1087 continue;
1088 } 1088 }
1089 1089
diff --git a/src/gui.cpp b/src/gui.cpp
index b512ef3..417860f 100644
--- a/src/gui.cpp
+++ b/src/gui.cpp
@@ -39,6 +39,8 @@ static unsigned long g_font_title = 0;
39static int g_body_h = 15; 39static int g_body_h = 15;
40static int g_title_h = 20; 40static int g_title_h = 20;
41static float g_font_scale = 0.0f; 41static float g_font_scale = 0.0f;
42static void *g_font_local = (void *)-1;
43static bool g_font_fail_logged = false;
42static int g_scr_w = 0, g_scr_h = 0; 44static int g_scr_w = 0, g_scr_h = 0;
43 45
44static void s_color(int r, int g, int b, int a) { 46static void s_color(int r, int g, int b, int a) {
@@ -485,41 +487,97 @@ bool gui_toasts_active() {
485 return active; 487 return active;
486} 488}
487 489
488static void ensure_fonts() { 490static bool font_valid(unsigned long f) {
489 if (g_font_body && fabsf(g_font_scale - g_scale) < 0.001f) 491 if (!f || !g_surface)
490 return; 492 return false;
491 g_font_scale = g_scale; 493 int w = 0, h = 0;
492 g_body_h = (int)(15.0f * g_scale + 0.5f); 494 s_text_size(f, L"M", w, h);
493 g_title_h = (int)(20.0f * g_scale + 0.5f); 495 return w > 0 && h > 0;
494 if (g_body_h < 11) g_body_h = 11; 496}
495 if (g_title_h < 15) g_title_h = 15; 497
498static void gui_reset_fonts() {
499 g_font_body = 0;
500 g_font_title = 0;
501 g_font_scale = 0.0f;
502}
503
504static unsigned long create_gui_font(int tall) {
496 static const char *families[] = {"Liberation Sans", "DejaVu Sans", 505 static const char *families[] = {"Liberation Sans", "DejaVu Sans",
497 "Noto Sans", "Tahoma", "Verdana", "Arial", 506 "Noto Sans", "Tahoma", "Verdana", "Arial",
498 nullptr}; 507 nullptr};
508 for (int i = 0; families[i]; i++) {
509 unsigned long f = s_create_font();
510 if (!f)
511 break;
512 if (s_set_font(f, families[i], tall, 400, 0x400, 0x20, 0xFF) &&
513 font_valid(f))
514 return f;
515 }
516 return 0;
517}
518
519static void ensure_fonts() {
520 void *le = nullptr;
521 if (g_iface.engine && g_iface.entitylist) {
522 int local = engine_get_local_player();
523 if (local > 0)
524 le = entitylist_get(local);
525 }
526 if (le != g_font_local) {
527 g_font_local = le;
528 gui_reset_fonts();
529 }
530
531 bool scale_changed = fabsf(g_font_scale - g_scale) >= 0.001f;
532 if (g_font_body && g_font_title && !scale_changed &&
533 font_valid(g_font_body) && font_valid(g_font_title))
534 return;
535 // Level transitions can silently invalidate VGUI font handles. Recreate
536 // them instead of drawing invisible text.
537 if ((g_font_body && !font_valid(g_font_body)) ||
538 (g_font_title && !font_valid(g_font_title)))
539 gui_reset_fonts();
540 if (scale_changed)
541 gui_reset_fonts();
542
543 g_font_scale = g_scale;
544 int req_body = (int)(15.0f * g_scale + 0.5f);
545 int req_title = (int)(20.0f * g_scale + 0.5f);
546 if (req_body < 11) req_body = 11;
547 if (req_title < 15) req_title = 15;
548 g_body_h = req_body;
549 g_title_h = req_title;
550
551 if (!g_font_body)
552 g_font_body = create_gui_font(g_body_h);
553 if (!g_font_title)
554 g_font_title = create_gui_font(g_title_h);
555
556 // Small scaled sizes can fail in the font manager; 16 px is the proven ESP
557 // size, so use it as a readable fallback.
499 if (!g_font_body) { 558 if (!g_font_body) {
500 for (int i = 0; families[i] && !g_font_body; i++) { 559 g_body_h = 16;
501 unsigned long f = s_create_font(); 560 g_font_body = create_gui_font(g_body_h);
502 if (!f)
503 break;
504 if (s_set_font(f, families[i], g_body_h, 400, 0x400, 0x20, 0xFF))
505 g_font_body = f;
506 }
507 } 561 }
508 if (!g_font_title) { 562 if (!g_font_title) {
509 for (int i = 0; families[i] && !g_font_title; i++) { 563 g_title_h = 16;
510 unsigned long f = s_create_font(); 564 g_font_title = create_gui_font(g_title_h);
511 if (!f)
512 break;
513 if (s_set_font(f, families[i], g_title_h, 400, 0x400, 0x20, 0xFF))
514 g_font_title = f;
515 }
516 } 565 }
517 if (!g_font_title) 566 if (!g_font_title)
518 g_font_title = g_font_body; 567 g_font_title = g_font_body;
568
519 if (!g_font_body) { 569 if (!g_font_body) {
520 logf("gui: no usable body font"); 570 if (!g_font_fail_logged) {
521 toastf(TOAST_ERROR, "No usable GUI font"); 571 g_font_fail_logged = true;
572 logf("gui: no usable body font");
573 toastf(TOAST_ERROR, "No usable GUI font");
574 }
575 return;
522 } 576 }
577 g_font_fail_logged = false;
578 if (g_body_h != req_body || g_title_h != req_title)
579 logd("gui: font fallback body=%d title=%d (requested %d/%d)", g_body_h,
580 g_title_h, req_body, req_title);
523} 581}
524 582
525static int text_w(const char *s, int tall) { 583static int text_w(const char *s, int tall) {
diff --git a/src/hl2h.h b/src/hl2h.h
index 9c0440f..45a297e 100644
--- a/src/hl2h.h
+++ b/src/hl2h.h
@@ -272,7 +272,8 @@ void esp_draw();
272void esp_draw_items(); 272void esp_draw_items();
273bool world_to_screen(const Vector &w, float &sx, float &sy); 273bool world_to_screen(const Vector &w, float &sx, float &sy);
274void set_silent_restore(const QAngle &angles); 274void set_silent_restore(const QAngle &angles);
275bool is_visible(const Vector &eye, const Vector &point); 275bool is_visible(const Vector &eye, const Vector &point,
276 void *self = nullptr, void *target = nullptr);
276void *trace_entity(const Vector &start, const Vector &end, void *ignore); 277void *trace_entity(const Vector &start, const Vector &end, void *ignore);
277float ground_distance(const Vector &origin, void *ignore, float max_dist); 278float ground_distance(const Vector &origin, void *ignore, float max_dist);
278 279
diff --git a/src/vischeck.cpp b/src/vischeck.cpp
index dcb5c3c..a568f31 100644
--- a/src/vischeck.cpp
+++ b/src/vischeck.cpp
@@ -1,14 +1,18 @@
1// vischeck.cpp — world-only line-of-sight test via EngineTraceClient003. 1// vischeck.cpp — line-of-sight test via EngineTraceClient003.
2// 2//
3// IEngineTrace::TraceRay (vtable slot 4) is given a ray from the eye to the 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 4// aim point with TRACE_EVERYTHING/MASK_SHOT, ignoring the local player and the
5// (you can see through them) while still hitting opaque world geometry. 5// target entity. This catches world geometry, static props and temporary
6// Per the SDK, TRACE_WORLD_ONLY does not test static props; that is the 6// objects such as crates/NPCs. Glass is skipped only when it is non-solid or
7// deliberate trade-off for this build. 7// its material name marks it breakable; solid non-break glass (bulletproof)
8// blocks the check.
8 9
9#include "hl2h.h" 10#include "hl2h.h"
11#include <cmath>
10#include <cstddef> 12#include <cstddef>
11#include <cstdint> 13#include <cstdint>
14#include <cstring>
15#include <strings.h>
12 16
13namespace { 17namespace {
14 18
@@ -75,11 +79,6 @@ struct ITraceFilterCompat {
75 virtual int GetTraceType() const = 0; 79 virtual int GetTraceType() const = 0;
76}; 80};
77 81
78struct WorldOnlyFilter : ITraceFilterCompat {
79 bool ShouldHitEntity(void *, int) override { return false; }
80 int GetTraceType() const override { return 1; } // TRACE_WORLD_ONLY
81};
82
83struct EntityFilter : ITraceFilterCompat { 82struct EntityFilter : ITraceFilterCompat {
84 void *ignore; 83 void *ignore;
85 explicit EntityFilter(void *ig) : ignore(ig) {} 84 explicit EntityFilter(void *ig) : ignore(ig) {}
@@ -89,15 +88,24 @@ struct EntityFilter : ITraceFilterCompat {
89 int GetTraceType() const override { return 0; } // TRACE_EVERYTHING 88 int GetTraceType() const override { return 0; } // TRACE_EVERYTHING
90}; 89};
91 90
92// MASK_VISIBLE = CONTENTS_SOLID | CONTENTS_MOVEABLE | CONTENTS_OPAQUE | 91struct VisibilityFilter : ITraceFilterCompat {
93// CONTENTS_IGNORE_NODRAW_OPAQUE 92 void *self;
94constexpr unsigned MASK_VISIBLE = 0x1u | 0x4000u | 0x80u | 0x2000u; 93 void *target;
94 VisibilityFilter(void *s, void *t) : self(s), target(t) {}
95 bool ShouldHitEntity(void *pEntity, int) override {
96 return pEntity != self && pEntity != target;
97 }
98 int GetTraceType() const override { return 0; } // TRACE_EVERYTHING
99};
100
101constexpr unsigned CONTENTS_SOLID = 0x1u;
102constexpr unsigned CONTENTS_WINDOW = 0x2u;
95// MASK_SHOT = SOLID|MOVEABLE|MONSTER|WINDOW|DEBRIS|HITBOX 103// MASK_SHOT = SOLID|MOVEABLE|MONSTER|WINDOW|DEBRIS|HITBOX
96constexpr unsigned MASK_SHOT = 0x1u | 0x4000u | 0x2000000u | 0x4u | 104constexpr unsigned MASK_SHOT = 0x1u | 0x4000u | 0x2000000u | 0x2u |
97 0x4000000u | 0x40000000u; 105 0x4000000u | 0x40000000u;
98// MASK_PLAYERSOLID = SOLID|MOVEABLE|PLAYERCLIP|WINDOW|MONSTER|GRATE 106// MASK_PLAYERSOLID = SOLID|MOVEABLE|PLAYERCLIP|WINDOW|MONSTER|GRATE
99constexpr unsigned MASK_PLAYERSOLID = 107constexpr unsigned MASK_PLAYERSOLID =
100 0x1u | 0x4000u | 0x10000u | 0x4u | 0x2000000u | 0x8u; 108 0x1u | 0x4000u | 0x10000u | 0x2u | 0x2000000u | 0x8u;
101 109
102static void make_ray(RayCompat &ray, const Vector &start, const Vector &end) { 110static void make_ray(RayCompat &ray, const Vector &start, const Vector &end) {
103 float dx = end.x - start.x, dy = end.y - start.y, dz = end.z - start.z; 111 float dx = end.x - start.x, dy = end.y - start.y, dz = end.z - start.z;
@@ -127,7 +135,26 @@ static void make_standing_hull_ray(RayCompat &ray, const Vector &origin,
127 135
128} // namespace 136} // namespace
129 137
130bool is_visible(const Vector &eye, const Vector &point) { 138static bool surface_contains(const SurfaceCompat &surf, const char *needle) {
139 if (!surf.name || !needle)
140 return false;
141 size_t n = strlen(needle);
142 for (const char *p = surf.name; *p; p++)
143 if (strncasecmp(p, needle, n) == 0)
144 return true;
145 return false;
146}
147
148static bool surface_is_breakable_glass(const SurfaceCompat &surf) {
149 return surface_contains(surf, "break");
150}
151
152static bool surface_is_glass(const SurfaceCompat &surf) {
153 return surface_contains(surf, "glass") || surface_contains(surf, "window");
154}
155
156bool is_visible(const Vector &eye, const Vector &point, void *self,
157 void *target) {
131 static bool warned = false; 158 static bool warned = false;
132 if (!g_iface.enginetrace) { 159 if (!g_iface.enginetrace) {
133 if (!warned) { 160 if (!warned) {
@@ -137,15 +164,51 @@ bool is_visible(const Vector &eye, const Vector &point) {
137 return true; 164 return true;
138 } 165 }
139 166
140 RayCompat ray; 167 float dx = point.x - eye.x;
141 make_ray(ray, eye, point); 168 float dy = point.y - eye.y;
142 GameTraceCompat tr{}; 169 float dz = point.z - eye.z;
143 WorldOnlyFilter filter; 170 float len2 = dx * dx + dy * dy + dz * dz;
144 using TraceRayFn = void (*)(void *, const RayCompat *, unsigned, void *, 171 if (len2 < 0.0001f)
145 GameTraceCompat *); 172 return true;
146 vfunc<TraceRayFn>(g_iface.enginetrace, 4)(g_iface.enginetrace, &ray, 173 float inv_len = 1.0f / sqrtf(len2);
147 MASK_VISIBLE, &filter, &tr); 174 Vector dir{dx * inv_len, dy * inv_len, dz * inv_len};
148 return tr.fraction > 0.97f && !tr.startsolid; 175
176 // Trace everything that bullets care about: world, static props, crates and
177 // NPCs. Non-solid glass and glass whose material name marks it breakable is
178 // skipped; solid non-break glass (bulletproof) blocks.
179 Vector start = eye;
180 for (int pass = 0; pass < 5; pass++) {
181 RayCompat ray;
182 make_ray(ray, start, point);
183 GameTraceCompat tr{};
184 VisibilityFilter filter(self, target);
185 using TraceRayFn = void (*)(void *, const RayCompat *, unsigned, void *,
186 GameTraceCompat *);
187 vfunc<TraceRayFn>(g_iface.enginetrace, 4)(g_iface.enginetrace, &ray,
188 MASK_SHOT, &filter, &tr);
189 if (tr.startsolid || tr.allsolid)
190 return false;
191 if (tr.fraction >= 1.0f)
192 return true;
193
194 bool glass = (tr.contents & CONTENTS_WINDOW) != 0 ||
195 surface_is_glass(tr.surface);
196 bool solid = (tr.contents & CONTENTS_SOLID) != 0;
197 bool breakable_glass = glass && surface_is_breakable_glass(tr.surface);
198 if (glass && (!solid || breakable_glass)) {
199 // Step just past the pane and look for the next obstruction.
200 start = tr.endpos;
201 start.x += dir.x * 0.5f;
202 start.y += dir.y * 0.5f;
203 start.z += dir.z * 0.5f;
204 logd("vischeck: skipped glass surface=%s contents=0x%x solid=%d break=%d",
205 tr.surface.name ? tr.surface.name : "?", tr.contents, (int)solid,
206 (int)breakable_glass);
207 continue;
208 }
209 return false;
210 }
211 return false;
149} 212}
150 213
151// Trace the crosshair ray and return the first entity hit (nullptr for world 214// Trace the crosshair ray and return the first entity hit (nullptr for world