diff options
| author | salmonik <salmonik@tuta.io> | 2026-10-27 12:54:29 +0100 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-10-27 12:54:40 +0100 |
| commit | a161f01105edef7f7723116a424777a3c42fb6b8 (patch) | |
| tree | d05b52e1f3a29e8c20976edb6b2c5dd6148a56a0 /src | |
| parent | Docs: add roadmap including distance weapon spread thesis (diff) | |
| download | hl2h-a161f01105edef7f7723116a424777a3c42fb6b8.tar.gz hl2h-a161f01105edef7f7723116a424777a3c42fb6b8.zip | |
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
Diffstat (limited to '')
| -rw-r--r-- | src/esp.cpp | 69 | ||||
| -rw-r--r-- | src/hl2h.h | 3 | ||||
| -rw-r--r-- | src/menu.cpp | 10 |
3 files changed, 78 insertions, 4 deletions
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; | |||
| 29 | static int g_scr_w = 0, g_scr_h = 0; | 29 | static int g_scr_w = 0, g_scr_h = 0; |
| 30 | static bool g_esp_logged = false; | 30 | static bool g_esp_logged = false; |
| 31 | 31 | ||
| 32 | // Speed graph: 100 samples at 0.1 s = the last 10 seconds. | ||
| 33 | static constexpr int SPEED_SAMPLES = 100; | ||
| 34 | static float g_speed_samples[SPEED_SAMPLES] = {}; | ||
| 35 | static int g_speed_head = 0; | ||
| 36 | static double g_speed_last_sample = 0.0; | ||
| 37 | |||
| 32 | static double esp_now() { | 38 | static double esp_now() { |
| 33 | timespec ts; | 39 | timespec ts; |
| 34 | clock_gettime(CLOCK_MONOTONIC, &ts); | 40 | clock_gettime(CLOCK_MONOTONIC, &ts); |
| @@ -332,6 +338,67 @@ static void draw_fov_circle() { | |||
| 332 | } | 338 | } |
| 333 | } | 339 | } |
| 334 | 340 | ||
| 341 | // Speed graph: 100 samples at 0.1 s = the last 10 seconds; min is 0, max is | ||
| 342 | // the window maximum. Small and centered in the lower half of the screen. | ||
| 343 | static void speed_graph_sample() { | ||
| 344 | double now = esp_now(); | ||
| 345 | if (now - g_speed_last_sample < 0.1) | ||
| 346 | return; | ||
| 347 | g_speed_last_sample = now; | ||
| 348 | float speed = 0.0f; | ||
| 349 | int local = engine_get_local_player(); | ||
| 350 | if (local > 0 && g_net.velocity >= 0) { | ||
| 351 | void *ent = entitylist_get(local); | ||
| 352 | if (ent) { | ||
| 353 | const float *v = (const float *)((uintptr_t)ent + g_net.velocity); | ||
| 354 | speed = sqrtf(v[0] * v[0] + v[1] * v[1]); | ||
| 355 | } | ||
| 356 | } | ||
| 357 | g_speed_samples[g_speed_head] = speed; | ||
| 358 | g_speed_head = (g_speed_head + 1) % SPEED_SAMPLES; | ||
| 359 | } | ||
| 360 | |||
| 361 | static void draw_speed_graph() { | ||
| 362 | if (!esp_option(ESP_OPT_SHOW_SPEEDGRAPH)) | ||
| 363 | return; | ||
| 364 | speed_graph_sample(); | ||
| 365 | |||
| 366 | float maxv = 0.0f; | ||
| 367 | for (int i = 0; i < SPEED_SAMPLES; i++) | ||
| 368 | if (g_speed_samples[i] > maxv) | ||
| 369 | maxv = g_speed_samples[i]; | ||
| 370 | float scale = maxv > 1.0f ? maxv : 1.0f; | ||
| 371 | |||
| 372 | const int w = 240, h = 64; | ||
| 373 | int x = (g_scr_w - w) / 2; | ||
| 374 | int y = (g_scr_h * 7) / 10 - h / 2; | ||
| 375 | if (x < 0) x = 0; | ||
| 376 | if (y < 0) y = 0; | ||
| 377 | |||
| 378 | surf_color(0, 0, 0, 120); | ||
| 379 | surf_rect(1, x, y, x + w, y + h); | ||
| 380 | surf_color(80, 80, 80, 180); | ||
| 381 | surf_rect(0, x, y, x + w, y + h); | ||
| 382 | |||
| 383 | surf_color(0, 255, 200, 220); | ||
| 384 | int px = 0, py = 0; | ||
| 385 | for (int i = 0; i < SPEED_SAMPLES; i++) { | ||
| 386 | int idx = (g_speed_head + i) % SPEED_SAMPLES; // oldest -> newest | ||
| 387 | float v = g_speed_samples[idx]; | ||
| 388 | int sx = x + 1 + (i * (w - 2)) / (SPEED_SAMPLES - 1); | ||
| 389 | int sy = y + h - 2 - (int)((v / scale) * (float)(h - 4)); | ||
| 390 | if (sy < y + 1) sy = y + 1; | ||
| 391 | if (i > 0) | ||
| 392 | surf_line(px, py, sx, sy); | ||
| 393 | px = sx; | ||
| 394 | py = sy; | ||
| 395 | } | ||
| 396 | |||
| 397 | char label[32]; | ||
| 398 | snprintf(label, sizeof(label), "max %d", (int)maxv); | ||
| 399 | draw_text_col(x + 3, y + 2, label, 200, 200, 200); | ||
| 400 | } | ||
| 401 | |||
| 335 | // Draw the parent->child bone links. Bone snapshots are world-space matrices | 402 | // Draw the parent->child bone links. Bone snapshots are world-space matrices |
| 336 | // from SetupBones; project each bone once and reuse for both endpoints. | 403 | // from SetupBones; project each bone once and reuse for both endpoints. |
| 337 | static void draw_skeleton(const EspTarget &t) { | 404 | static void draw_skeleton(const EspTarget &t) { |
| @@ -844,4 +911,6 @@ void esp_draw() { | |||
| 844 | } | 911 | } |
| 845 | } | 912 | } |
| 846 | } | 913 | } |
| 914 | |||
| 915 | draw_speed_graph(); | ||
| 847 | } | 916 | } |
| @@ -161,7 +161,8 @@ enum EspOption { | |||
| 161 | ESP_OPT_SHOW_NAME = 3, | 161 | ESP_OPT_SHOW_NAME = 3, |
| 162 | ESP_OPT_SHOW_SKELETON = 4, | 162 | ESP_OPT_SHOW_SKELETON = 4, |
| 163 | ESP_OPT_SHOW_BOX = 5, | 163 | ESP_OPT_SHOW_BOX = 5, |
| 164 | ESP_OPT_COUNT = 6 | 164 | ESP_OPT_SHOW_SPEEDGRAPH = 6, |
| 165 | ESP_OPT_COUNT = 7 | ||
| 165 | }; | 166 | }; |
| 166 | 167 | ||
| 167 | // --------------------------------------------------------------------------- | 168 | // --------------------------------------------------------------------------- |
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; | |||
| 87 | 87 | ||
| 88 | // ESP sub-options (menu "ESP" children). Defaults match the pre-option | 88 | // ESP sub-options (menu "ESP" children). Defaults match the pre-option |
| 89 | // behaviour: everything on. | 89 | // behaviour: everything on. |
| 90 | static bool g_esp_opt[ESP_OPT_COUNT] = {true, true, true, true, true, true}; | 90 | static bool g_esp_opt[ESP_OPT_COUNT] = {true, true, true, true, true, true, true}; |
| 91 | 91 | ||
| 92 | // tree | 92 | // tree |
| 93 | static MenuNode g_root{NODE_ROOT, "HL2hake", false, 0, {}, nullptr, nullptr, nullptr}; | 93 | 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] = { | |||
| 105 | {NODE_TOGGLE, "Show name", false, 0, {}, &g_esp_opt[ESP_OPT_SHOW_NAME], nullptr, nullptr}, | 105 | {NODE_TOGGLE, "Show name", false, 0, {}, &g_esp_opt[ESP_OPT_SHOW_NAME], nullptr, nullptr}, |
| 106 | {NODE_TOGGLE, "Show skeleton", false, 0, {}, &g_esp_opt[ESP_OPT_SHOW_SKELETON], nullptr, nullptr}, | 106 | {NODE_TOGGLE, "Show skeleton", false, 0, {}, &g_esp_opt[ESP_OPT_SHOW_SKELETON], nullptr, nullptr}, |
| 107 | {NODE_TOGGLE, "Show box", false, 0, {}, &g_esp_opt[ESP_OPT_SHOW_BOX], nullptr, nullptr}, | 107 | {NODE_TOGGLE, "Show box", false, 0, {}, &g_esp_opt[ESP_OPT_SHOW_BOX], nullptr, nullptr}, |
| 108 | {NODE_TOGGLE, "Show speed graph", false, 0, {}, &g_esp_opt[ESP_OPT_SHOW_SPEEDGRAPH], nullptr, nullptr}, | ||
| 108 | }; | 109 | }; |
| 109 | static MenuNode g_aimbot_feat{NODE_FEATURE, "Aimbot", false, 1, {}, nullptr, nullptr, nullptr}; | 110 | static MenuNode g_aimbot_feat{NODE_FEATURE, "Aimbot", false, 1, {}, nullptr, nullptr, nullptr}; |
| 110 | static MenuNode g_aimbot_active{NODE_TOGGLE, "Active", false, 0, {}, &g_aimbot.active, nullptr, nullptr}; | 111 | static MenuNode g_aimbot_active{NODE_TOGGLE, "Active", false, 0, {}, &g_aimbot.active, nullptr, nullptr}; |
| @@ -308,11 +309,12 @@ static void config_save() { | |||
| 308 | fprintf(f, "[silentaim]\nactive=%d\nbind=%d\nfov=%d\n", g_silentaim.active, | 309 | fprintf(f, "[silentaim]\nactive=%d\nbind=%d\nfov=%d\n", g_silentaim.active, |
| 309 | g_silentaim.bind, g_silent_fov); | 310 | g_silentaim.bind, g_silent_fov); |
| 310 | fprintf(f, "[esp]\nactive=%d\nbind=%d\nneutral=%d\nhealthbar=%d\n" | 311 | fprintf(f, "[esp]\nactive=%d\nbind=%d\nneutral=%d\nhealthbar=%d\n" |
| 311 | "healthnum=%d\nname=%d\nskeleton=%d\nbox=%d\n", | 312 | "healthnum=%d\nname=%d\nskeleton=%d\nbox=%d\nspeedgraph=%d\n", |
| 312 | g_esp.active, g_esp.bind, g_esp_opt[ESP_OPT_SHOW_NEUTRAL], | 313 | g_esp.active, g_esp.bind, g_esp_opt[ESP_OPT_SHOW_NEUTRAL], |
| 313 | g_esp_opt[ESP_OPT_SHOW_HEALTHBAR], | 314 | g_esp_opt[ESP_OPT_SHOW_HEALTHBAR], |
| 314 | g_esp_opt[ESP_OPT_SHOW_HEALTHNUM], g_esp_opt[ESP_OPT_SHOW_NAME], | 315 | g_esp_opt[ESP_OPT_SHOW_HEALTHNUM], g_esp_opt[ESP_OPT_SHOW_NAME], |
| 315 | g_esp_opt[ESP_OPT_SHOW_SKELETON], g_esp_opt[ESP_OPT_SHOW_BOX]); | 316 | g_esp_opt[ESP_OPT_SHOW_SKELETON], g_esp_opt[ESP_OPT_SHOW_BOX], |
| 317 | g_esp_opt[ESP_OPT_SHOW_SPEEDGRAPH]); | ||
| 316 | fclose(f); | 318 | fclose(f); |
| 317 | } | 319 | } |
| 318 | 320 | ||
| @@ -401,6 +403,8 @@ static void config_load() { | |||
| 401 | g_esp_opt[ESP_OPT_SHOW_SKELETON] = atoi(val) != 0; | 403 | g_esp_opt[ESP_OPT_SHOW_SKELETON] = atoi(val) != 0; |
| 402 | else if (strcmp(key, "box") == 0) | 404 | else if (strcmp(key, "box") == 0) |
| 403 | g_esp_opt[ESP_OPT_SHOW_BOX] = atoi(val) != 0; | 405 | g_esp_opt[ESP_OPT_SHOW_BOX] = atoi(val) != 0; |
| 406 | else if (strcmp(key, "speedgraph") == 0) | ||
| 407 | g_esp_opt[ESP_OPT_SHOW_SPEEDGRAPH] = atoi(val) != 0; | ||
| 404 | } | 408 | } |
| 405 | if (strcmp(key, "active") == 0) | 409 | if (strcmp(key, "active") == 0) |
| 406 | cur->active = atoi(val) != 0; | 410 | cur->active = atoi(val) != 0; |
