From a161f01105edef7f7723116a424777a3c42fb6b8 Mon Sep 17 00:00:00 2001 From: salmonik Date: Tue, 27 Oct 2026 12:54:29 +0100 Subject: ESP: HUD speed graph (last 10 s) - 0.1 s sampling, 100-sample ring; horizontal speed of the local player - small (240x64) panel centered in the lower half of the screen - min 0, max auto-scales to the maximum in the 10 s window; shows max value - ESP -> Show speed graph toggle, persisted as speedgraph=1 --- CONTEXT.md | 1 - src/esp.cpp | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/hl2h.h | 3 ++- src/menu.cpp | 10 ++++++--- 4 files changed, 78 insertions(+), 5 deletions(-) diff --git a/CONTEXT.md b/CONTEXT.md index 077c7c2..cd3811d 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -173,7 +173,6 @@ Binds use SDL scancodes; mouse buttons are pseudo-scancodes 600..604. range, sorted by distance, only while the crowbar is held. - **Jumpbug**: automate the HL2 jumpbug movement glitch (trigger conditions to be defined/tested). -- **Speed graph**: HUD plot of horizontal speed over time. - **GUI**: replace/augment the VGUI ISurface menu with an immediate-mode GUI (Dear ImGui or a custom implementation); needs a design grill first. - **Distance weapon spread thesis**: TBD — user will explain. Likely models diff --git a/src/esp.cpp b/src/esp.cpp index c0edd2e..78a357c 100644 --- a/src/esp.cpp +++ b/src/esp.cpp @@ -29,6 +29,12 @@ static int g_ntargets = 0; static int g_scr_w = 0, g_scr_h = 0; static bool g_esp_logged = false; +// Speed graph: 100 samples at 0.1 s = the last 10 seconds. +static constexpr int SPEED_SAMPLES = 100; +static float g_speed_samples[SPEED_SAMPLES] = {}; +static int g_speed_head = 0; +static double g_speed_last_sample = 0.0; + static double esp_now() { timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); @@ -332,6 +338,67 @@ static void draw_fov_circle() { } } +// Speed graph: 100 samples at 0.1 s = the last 10 seconds; min is 0, max is +// the window maximum. Small and centered in the lower half of the screen. +static void speed_graph_sample() { + double now = esp_now(); + if (now - g_speed_last_sample < 0.1) + return; + g_speed_last_sample = now; + float speed = 0.0f; + int local = engine_get_local_player(); + if (local > 0 && g_net.velocity >= 0) { + void *ent = entitylist_get(local); + if (ent) { + const float *v = (const float *)((uintptr_t)ent + g_net.velocity); + speed = sqrtf(v[0] * v[0] + v[1] * v[1]); + } + } + g_speed_samples[g_speed_head] = speed; + g_speed_head = (g_speed_head + 1) % SPEED_SAMPLES; +} + +static void draw_speed_graph() { + if (!esp_option(ESP_OPT_SHOW_SPEEDGRAPH)) + return; + speed_graph_sample(); + + float maxv = 0.0f; + for (int i = 0; i < SPEED_SAMPLES; i++) + if (g_speed_samples[i] > maxv) + maxv = g_speed_samples[i]; + float scale = maxv > 1.0f ? maxv : 1.0f; + + const int w = 240, h = 64; + int x = (g_scr_w - w) / 2; + int y = (g_scr_h * 7) / 10 - h / 2; + if (x < 0) x = 0; + if (y < 0) y = 0; + + surf_color(0, 0, 0, 120); + surf_rect(1, x, y, x + w, y + h); + surf_color(80, 80, 80, 180); + surf_rect(0, x, y, x + w, y + h); + + surf_color(0, 255, 200, 220); + int px = 0, py = 0; + for (int i = 0; i < SPEED_SAMPLES; i++) { + int idx = (g_speed_head + i) % SPEED_SAMPLES; // oldest -> newest + float v = g_speed_samples[idx]; + int sx = x + 1 + (i * (w - 2)) / (SPEED_SAMPLES - 1); + int sy = y + h - 2 - (int)((v / scale) * (float)(h - 4)); + if (sy < y + 1) sy = y + 1; + if (i > 0) + surf_line(px, py, sx, sy); + px = sx; + py = sy; + } + + char label[32]; + snprintf(label, sizeof(label), "max %d", (int)maxv); + draw_text_col(x + 3, y + 2, label, 200, 200, 200); +} + // Draw the parent->child bone links. Bone snapshots are world-space matrices // from SetupBones; project each bone once and reuse for both endpoints. static void draw_skeleton(const EspTarget &t) { @@ -844,4 +911,6 @@ void esp_draw() { } } } + + draw_speed_graph(); } diff --git a/src/hl2h.h b/src/hl2h.h index 72cd21f..fdc88c1 100644 --- a/src/hl2h.h +++ b/src/hl2h.h @@ -161,7 +161,8 @@ enum EspOption { ESP_OPT_SHOW_NAME = 3, ESP_OPT_SHOW_SKELETON = 4, ESP_OPT_SHOW_BOX = 5, - ESP_OPT_COUNT = 6 + ESP_OPT_SHOW_SPEEDGRAPH = 6, + ESP_OPT_COUNT = 7 }; // --------------------------------------------------------------------------- diff --git a/src/menu.cpp b/src/menu.cpp index 49dd886..3e20cb3 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -87,7 +87,7 @@ static int g_silent_fov = 60; // ESP sub-options (menu "ESP" children). Defaults match the pre-option // behaviour: everything on. -static bool g_esp_opt[ESP_OPT_COUNT] = {true, true, true, true, true, true}; +static bool g_esp_opt[ESP_OPT_COUNT] = {true, true, true, true, true, true, true}; // tree static MenuNode g_root{NODE_ROOT, "HL2hake", false, 0, {}, nullptr, nullptr, nullptr}; @@ -105,6 +105,7 @@ static MenuNode g_esp_opt_nodes[ESP_OPT_COUNT] = { {NODE_TOGGLE, "Show name", false, 0, {}, &g_esp_opt[ESP_OPT_SHOW_NAME], nullptr, nullptr}, {NODE_TOGGLE, "Show skeleton", false, 0, {}, &g_esp_opt[ESP_OPT_SHOW_SKELETON], nullptr, nullptr}, {NODE_TOGGLE, "Show box", false, 0, {}, &g_esp_opt[ESP_OPT_SHOW_BOX], nullptr, nullptr}, + {NODE_TOGGLE, "Show speed graph", false, 0, {}, &g_esp_opt[ESP_OPT_SHOW_SPEEDGRAPH], nullptr, nullptr}, }; static MenuNode g_aimbot_feat{NODE_FEATURE, "Aimbot", false, 1, {}, nullptr, nullptr, nullptr}; static MenuNode g_aimbot_active{NODE_TOGGLE, "Active", false, 0, {}, &g_aimbot.active, nullptr, nullptr}; @@ -308,11 +309,12 @@ static void config_save() { fprintf(f, "[silentaim]\nactive=%d\nbind=%d\nfov=%d\n", g_silentaim.active, g_silentaim.bind, g_silent_fov); fprintf(f, "[esp]\nactive=%d\nbind=%d\nneutral=%d\nhealthbar=%d\n" - "healthnum=%d\nname=%d\nskeleton=%d\nbox=%d\n", + "healthnum=%d\nname=%d\nskeleton=%d\nbox=%d\nspeedgraph=%d\n", g_esp.active, g_esp.bind, g_esp_opt[ESP_OPT_SHOW_NEUTRAL], g_esp_opt[ESP_OPT_SHOW_HEALTHBAR], g_esp_opt[ESP_OPT_SHOW_HEALTHNUM], g_esp_opt[ESP_OPT_SHOW_NAME], - g_esp_opt[ESP_OPT_SHOW_SKELETON], g_esp_opt[ESP_OPT_SHOW_BOX]); + g_esp_opt[ESP_OPT_SHOW_SKELETON], g_esp_opt[ESP_OPT_SHOW_BOX], + g_esp_opt[ESP_OPT_SHOW_SPEEDGRAPH]); fclose(f); } @@ -401,6 +403,8 @@ static void config_load() { g_esp_opt[ESP_OPT_SHOW_SKELETON] = atoi(val) != 0; else if (strcmp(key, "box") == 0) g_esp_opt[ESP_OPT_SHOW_BOX] = atoi(val) != 0; + else if (strcmp(key, "speedgraph") == 0) + g_esp_opt[ESP_OPT_SHOW_SPEEDGRAPH] = atoi(val) != 0; } if (strcmp(key, "active") == 0) cur->active = atoi(val) != 0; -- cgit v1.2.3