summaryrefslogtreecommitdiffstats
path: root/src/esp.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/esp.cpp124
1 files changed, 124 insertions, 0 deletions
diff --git a/src/esp.cpp b/src/esp.cpp
new file mode 100644
index 0000000..87d0b80
--- /dev/null
+++ b/src/esp.cpp
@@ -0,0 +1,124 @@
1// esp.cpp — ESP drawing + shared world_to_screen / entity gather (aimbot reuses these)
2#include "hl2h.h"
3#include <cmath>
4#include <cstring>
5#include <cstddef>
6
7static constexpr float PI_F = 3.14159265358979323846f;
8
9// ISurface slots (binary = SDK header + 4, disassembly-confirmed)
10enum SurfSlot {
11 SURF_DrawSetColor = 10, SURF_FilledRect = 12, SURF_OutlinedRect = 14,
12 SURF_GetScreenSize = 38,
13};
14
15struct EspTarget {
16 float sx, sy; // screen pos of feet
17 float h; // box height px
18 float health, maxhealth;
19 int teamnum;
20 char name[48];
21};
22static EspTarget g_targets[128];
23static int g_ntargets = 0;
24static int g_scr_w = 0, g_scr_h = 0;
25static bool g_esp_logged = false;
26
27bool world_to_screen(const Vector &w, float &sx, float &sy) {
28 float pitch = g_cam_view.x * PI_F / 180.0f;
29 float yaw = g_cam_view.y * PI_F / 180.0f;
30 float sp = sinf(pitch), cp = cosf(pitch);
31 float syy = sinf(yaw), cy = cosf(yaw);
32 float fwd[3] = {cp * cy, cp * syy, -sp};
33 float right[3] = {syy, -cy, 0};
34 float up[3] = {sp * cy, sp * syy, cp};
35 float dx = w.x - g_cam_eye.x, dy = w.y - g_cam_eye.y, dz = w.z - g_cam_eye.z;
36 float z = dx * fwd[0] + dy * fwd[1] + dz * fwd[2];
37 if (z < 1.0f) return false;
38 float x = dx * right[0] + dy * right[1] + dz * right[2];
39 float y = dx * up[0] + dy * up[1] + dz * up[2];
40 float tanfov = tanf(75.0f * 0.5f * PI_F / 180.0f);
41 sx = (float)g_scr_w * 0.5f + (x / z) * ((float)g_scr_w * 0.5f / tanfov);
42 sy = (float)g_scr_h * 0.5f - (y / z) * ((float)g_scr_w * 0.5f / tanfov);
43 return true;
44}
45
46static void esp_collect(int local_idx) {
47 g_ntargets = 0;
48 using Fn = int (*)(void *);
49 int maxent = vfunc<Fn>(g_iface.entitylist, 5)(g_iface.entitylist); // GetHighestEntityIndex
50 if (maxent < 0 || maxent > 512) maxent = 512;
51 for (int idx = 0; idx <= maxent && g_ntargets < 128; idx++) {
52 if (idx == local_idx) continue;
53 void *ent = entitylist_get(idx);
54 if (!ent) continue;
55 uintptr_t e = (uintptr_t)ent;
56 float hpv = (float)*(int *)(e + g_net.health); // m_iHealth is int
57 if (!(hpv > 0)) continue;
58 Vector origin{*(float *)(e + g_net.origin), *(float *)(e + g_net.origin + 4),
59 *(float *)(e + g_net.origin + 8)};
60 // ponytail: maxs netvar may be unresolved; fixed 72-unit torso fallback
61 float maxsz = g_net.maxs >= 0 ? *(float *)(e + g_net.maxs) : 72.0f;
62 Vector top{origin.x, origin.y, origin.z + maxsz};
63 float sx, sy, tx, ty;
64 if (!world_to_screen(origin, sx, sy) || !world_to_screen(top, tx, ty))
65 continue;
66 float h = sy - ty;
67 if (h < 4.0f || sy < 0 || ty > (float)g_scr_h) continue; // behind / offscreen
68 EspTarget &t = g_targets[g_ntargets++];
69 t.sx = sx; t.sy = sy; t.h = h;
70 t.health = hpv;
71 t.maxhealth = (float)*(int *)(e + g_net.maxhealth);
72 if (t.maxhealth <= 0) t.maxhealth = hpv;
73 // classname via IClientNetworkable::GetClientClass (slot 2) -> m_pNetworkName @ +8
74 using CCFn = void *(*)(void *);
75 void *cc = vfunc<CCFn>(ent, 2)(ent);
76 const char *nn = cc ? *(const char **)((uintptr_t)cc + 8) : nullptr;
77 strncpy(t.name, nn ? nn : "?", sizeof(t.name) - 1);
78 t.name[sizeof(t.name) - 1] = 0;
79 }
80}
81
82static void surf_rect(int filled, int x0, int y0, int x1, int y1) {
83 using Fn = void (*)(void *, int, int, int, int);
84 vfunc<Fn>(g_surface, filled ? SURF_FilledRect : SURF_OutlinedRect)(g_surface, x0, y0, x1, y1);
85}
86static void surf_color(int r, int g, int b, int a) {
87 using Fn = void (*)(void *, int, int, int, int);
88 vfunc<Fn>(g_surface, SURF_DrawSetColor)(g_surface, r, g, b, a);
89}
90
91void esp_draw() {
92 if (!g_surface || !g_iface.entitylist || !g_net.esp_ok()) return;
93 if (!g_scr_w) {
94 using Fn = void (*)(void *, int &, int &);
95 vfunc<Fn>(g_surface, SURF_GetScreenSize)(g_surface, g_scr_w, g_scr_h);
96 logf("ESP: screen %dx%d", g_scr_w, g_scr_h);
97 if (!g_scr_w) return;
98 }
99 esp_collect(engine_get_local_player());
100 if (g_ntargets && !g_esp_logged) {
101 g_esp_logged = true;
102 logf("ESP: first draw, %d targets", g_ntargets);
103 }
104 for (int i = 0; i < g_ntargets; i++) {
105 const EspTarget &t = g_targets[i];
106 // team color: 3 combine red, 2 rebels green, else white
107 if (t.teamnum == 3) surf_color(255, 60, 60, 255);
108 else if (t.teamnum == 2) surf_color(60, 255, 60, 255);
109 else surf_color(255, 255, 255, 255);
110 float w = t.h * 0.45f; // ponytail: constant aspect, no OBB width — swap for m_vecMins/maxs span later
111 surf_rect(0, (int)(t.sx - w / 2), (int)(t.sy - t.h), (int)(t.sx + w / 2), (int)t.sy);
112 // health bar under box
113 float frac = t.health / t.maxhealth;
114 if (frac > 1) frac = 1;
115 surf_color(0, 0, 0, 200);
116 surf_rect(1, (int)(t.sx - w / 2), (int)t.sy + 1, (int)(t.sx + w / 2), (int)t.sy + 4);
117 if (frac > 0.5f) surf_color(40, 255, 40, 255);
118 else if (frac > 0.25f) surf_color(255, 255, 0, 255);
119 else surf_color(255, 40, 40, 255);
120 surf_rect(1, (int)(t.sx - w / 2), (int)t.sy + 2,
121 (int)(t.sx - w / 2 + w * frac), (int)t.sy + 3);
122 draw_text_col((int)(t.sx - w / 2), (int)(t.sy - t.h) - 14, t.name, 255, 255, 255);
123 }
124}