From 8af245ae414cb548a2188030ed8a52b31986252f Mon Sep 17 00:00:00 2001 From: salmonik Date: Mon, 14 Sep 2026 16:47:32 +0200 Subject: Default map d1_town_01 (intro-less, enemies spawned) --- build.sh | 2 +- play.sh | 2 +- src/ISURFACE_STATUS.md | 6 +-- src/esp.cpp | 124 +++++++++++++++++++++++++++++++++++++++++++++++++ src/hl2h.cpp | 15 +++--- src/hl2h.h | 25 +++++++--- src/menu.cpp | 35 ++++++++++---- src/netvars.cpp | 56 ++++++++++++++++------ 8 files changed, 226 insertions(+), 39 deletions(-) create mode 100644 src/esp.cpp diff --git a/build.sh b/build.sh index daee3f8..6e54ccf 100755 --- a/build.sh +++ b/build.sh @@ -2,6 +2,6 @@ set -e cd "$(dirname "$0")" g++ -m32 -O2 -std=c++17 -Wall -Wextra -Werror -fPIC -shared -fvisibility=hidden \ - src/hl2h.cpp src/modules.cpp src/cinput.cpp src/netvars.cpp src/menu.cpp \ + src/hl2h.cpp src/modules.cpp src/cinput.cpp src/netvars.cpp src/menu.cpp src/esp.cpp \ -ldl file libhl2hake.so diff --git a/play.sh b/play.sh index 3d791b0..3ff58c2 100755 --- a/play.sh +++ b/play.sh @@ -11,4 +11,4 @@ if [ ! -f "$LIB" ]; then fi export LD_PRELOAD="$LIB" -exec "$HOME/.steam/steam/ubuntu12_32/steam-runtime/run.sh" -- "$GAME/hl2.sh" -steam -novid -console "$@" +exec "$HOME/.steam/steam/ubuntu12_32/steam-runtime/run.sh" -- "$GAME/hl2.sh" -steam -novid -console +map d1_town_01 "$@" diff --git a/src/ISURFACE_STATUS.md b/src/ISURFACE_STATUS.md index 32a27bb..de952cd 100644 --- a/src/ISURFACE_STATUS.md +++ b/src/ISURFACE_STATUS.md @@ -65,13 +65,13 @@ fc-match findings (system fontconfig): ## Menu -- Tree: HL2hake → Climb/Bunnyhop/ABH, children indented (`g_depth[]`, +12px/level). +- Tree: HL2hake → Bunnyhop/ABH, children indented (`g_depth[]`, +12px/level). - Right-aligned state at x=200: `[on]`/`[off]` for Active rows, `[keyname]` for Bind rows, `[press key]` while binding. - Feature gating: `feature_active(int)` (menu.cpp) — active AND (bind==0 or key held). Called from `do_move` (hl2h.cpp) to gate - climb (middle-mouse) and bhop (jump) branches. -- Config: INI at `~/.hl2hake.ini` (climb/bhop/abh active+bind). + bhop (jump) and ABH branches. +- Config: INI at `~/.hl2hake.ini` (bhop/abh active+bind). ## Build / Run 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 @@ +// esp.cpp — ESP drawing + shared world_to_screen / entity gather (aimbot reuses these) +#include "hl2h.h" +#include +#include +#include + +static constexpr float PI_F = 3.14159265358979323846f; + +// ISurface slots (binary = SDK header + 4, disassembly-confirmed) +enum SurfSlot { + SURF_DrawSetColor = 10, SURF_FilledRect = 12, SURF_OutlinedRect = 14, + SURF_GetScreenSize = 38, +}; + +struct EspTarget { + float sx, sy; // screen pos of feet + float h; // box height px + float health, maxhealth; + int teamnum; + char name[48]; +}; +static EspTarget g_targets[128]; +static int g_ntargets = 0; +static int g_scr_w = 0, g_scr_h = 0; +static bool g_esp_logged = false; + +bool world_to_screen(const Vector &w, float &sx, float &sy) { + float pitch = g_cam_view.x * PI_F / 180.0f; + float yaw = g_cam_view.y * PI_F / 180.0f; + float sp = sinf(pitch), cp = cosf(pitch); + float syy = sinf(yaw), cy = cosf(yaw); + float fwd[3] = {cp * cy, cp * syy, -sp}; + float right[3] = {syy, -cy, 0}; + float up[3] = {sp * cy, sp * syy, cp}; + float dx = w.x - g_cam_eye.x, dy = w.y - g_cam_eye.y, dz = w.z - g_cam_eye.z; + float z = dx * fwd[0] + dy * fwd[1] + dz * fwd[2]; + if (z < 1.0f) return false; + float x = dx * right[0] + dy * right[1] + dz * right[2]; + float y = dx * up[0] + dy * up[1] + dz * up[2]; + float tanfov = tanf(75.0f * 0.5f * PI_F / 180.0f); + sx = (float)g_scr_w * 0.5f + (x / z) * ((float)g_scr_w * 0.5f / tanfov); + sy = (float)g_scr_h * 0.5f - (y / z) * ((float)g_scr_w * 0.5f / tanfov); + return true; +} + +static void esp_collect(int local_idx) { + g_ntargets = 0; + using Fn = int (*)(void *); + int maxent = vfunc(g_iface.entitylist, 5)(g_iface.entitylist); // GetHighestEntityIndex + if (maxent < 0 || maxent > 512) maxent = 512; + for (int idx = 0; idx <= maxent && g_ntargets < 128; idx++) { + if (idx == local_idx) continue; + void *ent = entitylist_get(idx); + if (!ent) continue; + uintptr_t e = (uintptr_t)ent; + float hpv = (float)*(int *)(e + g_net.health); // m_iHealth is int + if (!(hpv > 0)) continue; + Vector origin{*(float *)(e + g_net.origin), *(float *)(e + g_net.origin + 4), + *(float *)(e + g_net.origin + 8)}; + // ponytail: maxs netvar may be unresolved; fixed 72-unit torso fallback + float maxsz = g_net.maxs >= 0 ? *(float *)(e + g_net.maxs) : 72.0f; + Vector top{origin.x, origin.y, origin.z + maxsz}; + float sx, sy, tx, ty; + if (!world_to_screen(origin, sx, sy) || !world_to_screen(top, tx, ty)) + continue; + float h = sy - ty; + if (h < 4.0f || sy < 0 || ty > (float)g_scr_h) continue; // behind / offscreen + EspTarget &t = g_targets[g_ntargets++]; + t.sx = sx; t.sy = sy; t.h = h; + t.health = hpv; + t.maxhealth = (float)*(int *)(e + g_net.maxhealth); + if (t.maxhealth <= 0) t.maxhealth = hpv; + // classname via IClientNetworkable::GetClientClass (slot 2) -> m_pNetworkName @ +8 + using CCFn = void *(*)(void *); + void *cc = vfunc(ent, 2)(ent); + const char *nn = cc ? *(const char **)((uintptr_t)cc + 8) : nullptr; + strncpy(t.name, nn ? nn : "?", sizeof(t.name) - 1); + t.name[sizeof(t.name) - 1] = 0; + } +} + +static void surf_rect(int filled, int x0, int y0, int x1, int y1) { + using Fn = void (*)(void *, int, int, int, int); + vfunc(g_surface, filled ? SURF_FilledRect : SURF_OutlinedRect)(g_surface, x0, y0, x1, y1); +} +static void surf_color(int r, int g, int b, int a) { + using Fn = void (*)(void *, int, int, int, int); + vfunc(g_surface, SURF_DrawSetColor)(g_surface, r, g, b, a); +} + +void esp_draw() { + if (!g_surface || !g_iface.entitylist || !g_net.esp_ok()) return; + if (!g_scr_w) { + using Fn = void (*)(void *, int &, int &); + vfunc(g_surface, SURF_GetScreenSize)(g_surface, g_scr_w, g_scr_h); + logf("ESP: screen %dx%d", g_scr_w, g_scr_h); + if (!g_scr_w) return; + } + esp_collect(engine_get_local_player()); + if (g_ntargets && !g_esp_logged) { + g_esp_logged = true; + logf("ESP: first draw, %d targets", g_ntargets); + } + for (int i = 0; i < g_ntargets; i++) { + const EspTarget &t = g_targets[i]; + // team color: 3 combine red, 2 rebels green, else white + if (t.teamnum == 3) surf_color(255, 60, 60, 255); + else if (t.teamnum == 2) surf_color(60, 255, 60, 255); + else surf_color(255, 255, 255, 255); + float w = t.h * 0.45f; // ponytail: constant aspect, no OBB width — swap for m_vecMins/maxs span later + surf_rect(0, (int)(t.sx - w / 2), (int)(t.sy - t.h), (int)(t.sx + w / 2), (int)t.sy); + // health bar under box + float frac = t.health / t.maxhealth; + if (frac > 1) frac = 1; + surf_color(0, 0, 0, 200); + surf_rect(1, (int)(t.sx - w / 2), (int)t.sy + 1, (int)(t.sx + w / 2), (int)t.sy + 4); + if (frac > 0.5f) surf_color(40, 255, 40, 255); + else if (frac > 0.25f) surf_color(255, 255, 0, 255); + else surf_color(255, 40, 40, 255); + surf_rect(1, (int)(t.sx - w / 2), (int)t.sy + 2, + (int)(t.sx - w / 2 + w * frac), (int)t.sy + 3); + draw_text_col((int)(t.sx - w / 2), (int)(t.sy - t.h) - 14, t.name, 255, 255, 255); + } +} diff --git a/src/hl2h.cpp b/src/hl2h.cpp index c6c0aeb..01fdb0f 100644 --- a/src/hl2h.cpp +++ b/src/hl2h.cpp @@ -32,7 +32,8 @@ void logf(const char *fmt, ...) { Module g_client, g_engine; GameInterfaces g_iface; NetVars g_net; -uintptr_t g_input = 0; +Vector g_cam_eye{0, 0, 0}; +Vector g_cam_view{0, 0, 0}; // --------------------------------------------------------------------------- // vector math @@ -81,7 +82,7 @@ float getfov(const QAngle &my_view, const QAngle &aim_ang) { } // --------------------------------------------------------------------------- -// feature logic (bhop, ABH, climb) +// feature logic (bhop, ABH) // --------------------------------------------------------------------------- static int g_modified_logged = 0; @@ -103,19 +104,16 @@ void note_modified(const CUserCmd *cmd, int flags, const float *vel) { static bool g_logged_local = false; -static int engine_get_local_player() { +int engine_get_local_player() { using Fn = int (*)(void *); return vfunc(g_iface.engine, 12)(g_iface.engine); } -static void *entitylist_get(int idx) { +void *entitylist_get(int idx) { using Fn = void *(*)(void *, int); return vfunc(g_iface.entitylist, 3)(g_iface.entitylist, idx); } - - - static void do_move(int seq) { static bool g_echo_sent = false; if (!g_input || !g_net.ok()) @@ -148,6 +146,9 @@ static void do_move(int seq) { return; uintptr_t e = (uintptr_t)ent; + g_cam_eye = {*(float *)(e + g_net.origin), *(float *)(e + g_net.origin + 4), *(float *)(e + g_net.origin + 8)}; + g_cam_eye.z += *(float *)(e + g_net.viewoffset); + g_cam_view = {cmd->viewangles[0], cmd->viewangles[1], cmd->viewangles[2]}; int flags = *(int *)(e + g_net.flags); bool onground = (flags & FL_ONGROUND) != 0; bool inwater = (flags & FL_INWATER) != 0; diff --git a/src/hl2h.h b/src/hl2h.h index 88c6c0d..d6a005e 100644 --- a/src/hl2h.h +++ b/src/hl2h.h @@ -59,12 +59,20 @@ template inline Fn vfunc(void *obj, size_t idx) { // --------------------------------------------------------------------------- // netvars // --------------------------------------------------------------------------- - struct NetVars { int flags = -1; int velocity = -1; int movetype = -1; + int health = -1; + int maxhealth = -1; + int teamnum = -1; + int origin = -1; + int viewoffset = -1; + int mins = -1; + int maxs = -1; + // ponytail: mins/maxs may be absent from some HL2 netvar tables; esp.cpp falls back to h=72 bool ok() const { return flags >= 0 && velocity >= 0 && movetype >= 0; } + bool esp_ok() const { return ok() && health >= 0 && maxhealth >= 0 && teamnum >= 0 && origin >= 0 && viewoffset >= 0; } }; extern NetVars g_net; @@ -116,16 +124,15 @@ enum { MOVETYPE_NOCLIP = 8, MOVETYPE_LADDER = 9 }; struct Vector { float x, y, z; }; struct QAngle { float x, y, z; }; +// camera state captured each CreateMove (used by ESP / future aimbot) +extern Vector g_cam_eye; +extern Vector g_cam_view; QAngle vector_angles(const Vector &forward); -Vector angle_forward_vector(const QAngle &angles); -float getfov(const QAngle &my_view, const QAngle &aim_ang); +enum FeatureId { FEAT_BHOP = 0, FEAT_ABH = 1, FEAT_ESP = 2 }; // --------------------------------------------------------------------------- // cross-file declarations // --------------------------------------------------------------------------- - -// feature ids shared by menu (feature_active) and hl2h.cpp (do_move) -enum FeatureId { FEAT_BHOP = 0, FEAT_ABH = 1 }; void note_modified(const CUserCmd *cmd, int flags, const float *vel); CUserCmd *get_usercmd(int seq); void fix_verified_crc(int seq, uint32_t crc); @@ -135,6 +142,12 @@ void resolve_netvars(); void find_modules(); void init_conmsg(); bool feature_active(int feature); +void *entitylist_get(int idx); +int engine_get_local_player(); +void draw_text_col(int x, int y, const char *s, int r, int g, int b); +void esp_draw(); +bool world_to_screen(const Vector &w, float &sx, float &sy); + void outer_draw(); // raw ISurface slot calls used by the PaintTraverseEx hook (hl2h.cpp) using SurfFn = void (*)(void *, ...); diff --git a/src/menu.cpp b/src/menu.cpp index 6dccf1c..302eab3 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -52,29 +52,32 @@ struct FeatureState { }; static FeatureState g_bhop{true, 0}; -static FeatureState g_abh{true, 0}; +static FeatureState g_abh{true, 0}; +static FeatureState g_esp{true, 0}; // tree static MenuNode g_root{NODE_ROOT, "HL2hake", true, 0, {}, nullptr, nullptr, nullptr}; static MenuNode g_bhop_feat{NODE_FEATURE, "Bunnyhop", true, 1, {}, nullptr, nullptr, nullptr}; static MenuNode g_bhop_active{NODE_TOGGLE, "Active", false, 0, {}, &g_bhop.active, nullptr, nullptr}; static MenuNode g_abh_feat{NODE_FEATURE, "ABH", true, 1, {}, nullptr, nullptr, nullptr}; static MenuNode g_abh_active{NODE_TOGGLE, "Active", false, 0, {}, &g_abh.active, nullptr, nullptr}; +static MenuNode g_esp_feat{NODE_FEATURE, "ESP", true, 1, {}, nullptr, nullptr, nullptr}; +static MenuNode g_esp_active{NODE_TOGGLE, "Active", false, 0, {}, &g_esp.active, nullptr, nullptr}; static MenuNode *g_visible[16]; // flattened visible nodes for arrow navigation static int g_visible_count = 0; static int g_selected = 0; bool g_menu_open = false; -// bind-select state -static bool g_bind_mode = false; // true = waiting for key press - +static bool g_bind_mode = false; static void build_tree() { g_bhop_feat.children[0] = &g_bhop_active; g_abh_feat.children[0] = &g_abh_active; + g_esp_feat.children[0] = &g_esp_active; g_root.children[0] = &g_bhop_feat; g_root.children[1] = &g_abh_feat; - g_root.child_count = 2; + g_root.children[2] = &g_esp_feat; + g_root.child_count = 3; } static int g_depth[16]; // depth of each visible row (for indentation) @@ -136,6 +139,7 @@ static void config_save() { return; fprintf(f, "[bhop]\nactive=%d\nbind=%d\n", g_bhop.active, g_bhop.bind); fprintf(f, "[abh]\nactive=%d\nbind=%d\n", g_abh.active, g_abh.bind); + fprintf(f, "[esp]\nactive=%d\nbind=%d\n", g_esp.active, g_esp.bind); fclose(f); } @@ -152,6 +156,8 @@ static void config_load() { cur = &g_bhop; else if (strstr(line, "[abh]")) cur = &g_abh; + else if (strstr(line, "[esp]")) + cur = &g_esp; else cur = nullptr; } else if (cur) { @@ -234,6 +240,7 @@ static void poll_mouse() { return; int mx, my; unsigned mask = SDL_GetMouseState_real(&mx, &my); + for (int b = 0; b < 5; b++) { bool down = (mask & (1u << b)) != 0; // SDL buttons 1..5 = bits 0..4 int sc = MOUSE_BASE + b; @@ -269,8 +276,8 @@ static bool key_pressed(int sc) { // feature gate queried from hl2h.cpp do_move(): feature must be Active AND // its bind key held (always-on when unbound). bool feature_active(int feature) { - static FeatureState *feats[2] = {&g_bhop, &g_abh}; - if (feature < 0 || feature > 1) + static FeatureState *feats[3] = {&g_bhop, &g_abh, &g_esp}; + if (feature < 0 || feature > 2) return false; FeatureState *f = feats[feature]; if (!f->active) @@ -456,6 +463,15 @@ static void draw_text(int x, int y, const char *text) { surf_draw_set_text_pos(x, y); surf_draw_print_text(wtext, len); } +void draw_text_col(int x, int y, const char *text, int r, int g, int b) { + wchar_t wtext[256]; + mbstowcs(wtext, text, 255); + wtext[255] = 0; + surf_draw_set_text_font(g_font); + surf_draw_set_text_color(r, g, b, 255); + surf_draw_set_text_pos(x, y); + surf_draw_print_text(wtext, (int)wcslen(wtext)); +} // Draw overlay. MUST be called from inside the ISurface paint path // (see hkdDrawPrintText hook in hl2h.cpp): we wrap our draws in @@ -464,7 +480,8 @@ void outer_draw() { menu_handle_input(); // HL2H_DRAW_TEST=1 draws a probe rect without needing the menu open static bool draw_test = getenv("HL2H_DRAW_TEST") != nullptr; - if (!g_surface || (!g_menu_open && !draw_test)) + bool esp_on = feature_active(FEAT_ESP); + if (!g_surface || (!g_menu_open && !draw_test && !esp_on)) return; if (!g_font) { g_font = surf_create_font(); @@ -485,6 +502,8 @@ void outer_draw() { if (!panel) return; surf_push_make_current(panel, false); + if (esp_on) + esp_draw(); if (draw_test) { surf_draw_set_color(255, 0, 0, 255); surf_draw_filled_rect(20, 20, 180, 60); diff --git a/src/netvars.cpp b/src/netvars.cpp index 41070c7..5c8c296 100644 --- a/src/netvars.cpp +++ b/src/netvars.cpp @@ -82,10 +82,44 @@ static void collect_netvars(uintptr_t table, uintptr_t base_off, int depth) { } else { g_net.velocity = (int)(base_off + (uint32_t)poff); } + } else if (strcmp(pname2, "m_iHealth") == 0 && g_net.health < 0) { + g_net.health = (int)(base_off + (uint32_t)poff); + } else if (strcmp(pname2, "m_iMaxHealth") == 0 && g_net.maxhealth < 0) { + g_net.maxhealth = (int)(base_off + (uint32_t)poff); + } else if (strcmp(pname2, "m_iTeamNum") == 0 && g_net.teamnum < 0) { + g_net.teamnum = (int)(base_off + (uint32_t)poff); + } else if (strcmp(pname2, "m_vecOrigin") == 0 && g_net.origin < 0) { + if (parray && g_client.contains(parray)) { + int32_t eoff = *(int32_t *)(parray + 0x2C); + g_net.origin = (int)(base_off + (uint32_t)(eoff ? eoff : poff)); + } else { + g_net.origin = (int)(base_off + (uint32_t)poff); + } + } else if (strcmp(pname2, "m_vecViewOffset") == 0 && g_net.viewoffset < 0) { + if (parray && g_client.contains(parray)) { + int32_t eoff = *(int32_t *)(parray + 0x2C); + g_net.viewoffset = (int)(base_off + (uint32_t)(eoff ? eoff : poff)); + } else { + g_net.viewoffset = (int)(base_off + (uint32_t)poff); + } + } else if (strcmp(pname2, "m_vecMins") == 0 && g_net.mins < 0) { + if (parray && g_client.contains(parray)) { + int32_t eoff = *(int32_t *)(parray + 0x2C); + g_net.mins = (int)(base_off + (uint32_t)(eoff ? eoff : poff)); + } else { + g_net.mins = (int)(base_off + (uint32_t)poff); + } + } else if (strcmp(pname2, "m_vecMaxs") == 0 && g_net.maxs < 0) { + if (parray && g_client.contains(parray)) { + int32_t eoff = *(int32_t *)(parray + 0x2C); + g_net.maxs = (int)(base_off + (uint32_t)(eoff ? eoff : poff)); + } else { + g_net.maxs = (int)(base_off + (uint32_t)poff); + } } - if (g_net.ok()) + if (g_net.esp_ok()) return; - } +} } // prediction datamap scan for m_MoveType (FIELD_CHARACTER, fieldOffset in range) @@ -161,11 +195,9 @@ void resolve_netvars() { for (uintptr_t cc = head; cc && g_client.contains(cc) && classes < 1024; cc = *(uintptr_t *)(cc + 0x10)) { classes++; - char name[64]; - if (!module_string(*(uintptr_t *)(cc + 0x08), name, sizeof(name))) - break; collect_netvars(*(uintptr_t *)(cc + 0x0C), 0, 0); - if (g_net.flags >= 0 && g_net.velocity >= 0) + if (g_net.esp_ok() || (g_net.flags >= 0 && g_net.velocity >= 0 && + g_net.health < 0)) break; } logf("client classes walked: %d", classes); @@ -175,11 +207,9 @@ void resolve_netvars() { logf("movetype guard disabled"); } - if (g_net.ok()) { - logf("netvars: m_fFlags=+0x%X m_vecVelocity[0]=+0x%X m_MoveType=+0x%X", - g_net.flags, g_net.velocity, g_net.movetype); - } else { - logf("netvar resolution incomplete: flags=%d velocity=%d movetype=%d", - g_net.flags, g_net.velocity, g_net.movetype); - } + logf("netvars: flags=%d velocity=%d movetype=%d health=%d maxhealth=%d " + "teamnum=%d origin=%d viewoffset=%d mins=%d maxs=%d", + g_net.flags, g_net.velocity, g_net.movetype, g_net.health, + g_net.maxhealth, g_net.teamnum, g_net.origin, g_net.viewoffset, + g_net.mins, g_net.maxs); } -- cgit v1.2.3