From 669d6c37306ca728e64836b61e9e466020129f48 Mon Sep 17 00:00:00 2001 From: salmonik Date: Wed, 4 Nov 2026 12:52:03 +0100 Subject: ESP: render damage tags as small left-side labels Draw ARM/VULN/IMM on the left edge of the box with a 10px font and a black 1px shadow, instead of full-size text above the name. The small font is a second HFont created next to the 16px body font. CONTEXT: document the DrawModel argument fix and the tag styling. --- CONTEXT.md | 14 ++++++++++++-- src/esp.cpp | 18 +++++++++++++----- src/hl2h.h | 1 + src/menu.cpp | 21 +++++++++++++++++++++ 4 files changed, 47 insertions(+), 7 deletions(-) diff --git a/CONTEXT.md b/CONTEXT.md index 7f8b131..5e36328 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -198,8 +198,10 @@ vocabulary / decisions. - `VULNERABLE` — headcrab, antlion, manhack: punting them with the gravity gun / physics impact damage kills them. - `NORMAL` — everything else. -- ESP draws `ARMORED` (orange), `VULN` (cyan) or `IMMUNE` (grey) above the - name; toggle is `ESP → Damage tags` (`[esp] dmg=1`). +- ESP draws a small 10 px label on the left edge of the box with a black 1 px + shadow: `ARM` (orange), `VULN` (cyan) or `IMM` (grey). Toggle is + `ESP → Damage tags` (`[esp] dmg=1`). The small font is a second `HFont` + created at glyph size 10 alongside the 16 px body font (`g_font_small`). - Aimbot/silent aim/triggerbot/melee skip `IMMUNE` targets always and `ARMORED` targets unless the active weapon is the RPG (`active_weapon_contains` for `rocket_launcher`/`rpg`). @@ -365,6 +367,14 @@ vocabulary / decisions. `SetColorModulation(1,1,1)` *before* `DrawModel`. Do not force `SetAlphaModulation` there: the earlier attempt produced error-material props and a material-system crash. +- `DrawModel` takes seven arguments after `this`: results, info, boneToWorld, + flexWeights, flexDelayedWeights, `const Vector &modelOrigin` (arrives as a + pointer) and `flags`. The hook must forward all of them; omitting + `modelOrigin` shifts `flags` into its slot and leaves the real `flags` as + caller stack garbage, which corrupted roughly every other prop draw + (checkerboard / error-material props). Verified against `studiorender.so` + 0x83ad0: the callee reads `0x20(%ebp)` (modelOrigin) and `0x24(%ebp)` + (flags). `DrawModelStaticProp` takes info/modelToWorld/flags only. - `prop_tint_update()` also writes `m_clrRender` on prop entities (dynamic props) as a secondary path; the netvar resolver rejects offset 0 (an embedded-table false positive) and only accepts 0x20..0x2000. diff --git a/src/esp.cpp b/src/esp.cpp index aa9f645..51f46bc 100644 --- a/src/esp.cpp +++ b/src/esp.cpp @@ -1573,18 +1573,26 @@ void esp_draw() { const char *tag = nullptr; int tr = 255, tg = 255, tb = 255; if (t.damage == NPC_DMG_ARMORED) { - tag = "ARMORED"; + tag = "ARM"; tr = 255; tg = 170; tb = 60; } else if (t.damage == NPC_DMG_VULNERABLE) { tag = "VULN"; tr = 90; tg = 220; tb = 255; } else if (t.damage == NPC_DMG_IMMUNE) { - tag = "IMMUNE"; + tag = "IMM"; tr = 180; tg = 180; tb = 180; } - if (tag) - draw_text_col(left, top - (esp_option(ESP_OPT_SHOW_NAME) ? 28 : 14), - tag, tr, tg, tb); + if (tag) { + // Small label on the left edge of the box, black 1px shadow for + // readability against the world (the 10px ESP font). + int tw = (int)strlen(tag) * 6; + int tx = left - tw - 3; + if (tx < 2) + tx = 2; + int ty = top + 2; + draw_text_small_col(tx + 1, ty + 1, tag, 0, 0, 0); + draw_text_small_col(tx, ty, tag, tr, tg, tb); + } } } diff --git a/src/hl2h.h b/src/hl2h.h index 287563c..e35e737 100644 --- a/src/hl2h.h +++ b/src/hl2h.h @@ -297,6 +297,7 @@ void silentaim_reset_lock(); 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 draw_text_small_col(int x, int y, const char *s, int r, int g, int b); void esp_draw(); void esp_draw_items(); void prop_tint_update(); diff --git a/src/menu.cpp b/src/menu.cpp index eb1075a..00aa189 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -683,6 +683,7 @@ using HFont = unsigned long; ISurface *g_surface = nullptr; static HFont g_font = 0; +static HFont g_font_small = 0; // VGUI_Surface030 vtable slots (confirmed via binary disassembly of vguimatsurface.so): // 8 = PushMakeCurrent(VPANEL, bool useInsets) — reads args at 0xc/0x10(%ebp) @@ -778,6 +779,16 @@ void draw_text_col(int x, int y, const char *text, int r, int g, int b) { surf_draw_print_text(wtext, (int)wcslen(wtext)); } +void draw_text_small_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_small ? g_font_small : 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)); +} + // --------------------------------------------------------------------------- // mat_fullbright // --------------------------------------------------------------------------- @@ -849,6 +860,7 @@ void outer_draw() { } if (font_reset && g_font) { g_font = 0; + g_font_small = 0; logd("font: reset (local entity change)"); } @@ -858,6 +870,7 @@ void outer_draw() { "Liberation Sans", "DejaVu Sans", "Noto Sans", "Tahoma", "Verdana", "Arial", nullptr}; bool font_ok = false; + const char *chosen = nullptr; for (int fi = 0; families[fi] && !font_ok; fi++) { g_font = surf_create_font(); if (!g_font) @@ -866,10 +879,18 @@ void outer_draw() { 0x20, 0xFF)) { logd("outer_draw: font family %s", families[fi]); font_ok = true; + chosen = families[fi]; } else { g_font = 0; } } + if (font_ok && chosen && !g_font_small) { + g_font_small = surf_create_font(); + if (g_font_small && + !surf_set_font_glyph_set(g_font_small, chosen, 10, 400, 0, 0, 0x400, + 0x20, 0xFF)) + g_font_small = 0; + } if (!font_ok) { logf("outer_draw: no usable font"); toastf(TOAST_ERROR, "No usable overlay font"); -- cgit v1.2.3