From 2e9373d530bca779b4070a79972f246ef41e4038 Mon Sep 17 00:00:00 2001 From: salmonik Date: Tue, 3 Nov 2026 22:03:01 +0100 Subject: GUI: recover invalid fonts; vischeck: props and bulletproof glass - Validate the GUI font handles with GetTextSize and recreate them when the local player entity or UI scale changes. Small scaled sizes fall back to the proven 16 px size, and the failure log/toast is emitted once instead of every frame. This fixes sessions where the menu rendered with no text after a level transition. - Rework visibility: trace with MASK_SHOT/TRACE_EVERYTHING while ignoring the local player and target, so static props, crates and other NPCs block. Non-solid glass and breakable-glass materials are skipped; solid non-break glass (bulletproof) blocks. Fix CONTENTS_WINDOW 0x2 (was 0x4) in the masks. - Pass self/target to is_visible from aimbot/silent/melee callers. --- src/vischeck.cpp | 113 +++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 88 insertions(+), 25 deletions(-) (limited to 'src/vischeck.cpp') diff --git a/src/vischeck.cpp b/src/vischeck.cpp index dcb5c3c..a568f31 100644 --- a/src/vischeck.cpp +++ b/src/vischeck.cpp @@ -1,14 +1,18 @@ -// vischeck.cpp — world-only line-of-sight test via EngineTraceClient003. +// vischeck.cpp — line-of-sight test via EngineTraceClient003. // // IEngineTrace::TraceRay (vtable slot 4) is given a ray from the eye to the -// aim point and a TRACE_WORLD_ONLY filter. MASK_VISIBLE ignores glass/windows -// (you can see through them) while still hitting opaque world geometry. -// Per the SDK, TRACE_WORLD_ONLY does not test static props; that is the -// deliberate trade-off for this build. +// aim point with TRACE_EVERYTHING/MASK_SHOT, ignoring the local player and the +// target entity. This catches world geometry, static props and temporary +// objects such as crates/NPCs. Glass is skipped only when it is non-solid or +// its material name marks it breakable; solid non-break glass (bulletproof) +// blocks the check. #include "hl2h.h" +#include #include #include +#include +#include namespace { @@ -75,11 +79,6 @@ struct ITraceFilterCompat { virtual int GetTraceType() const = 0; }; -struct WorldOnlyFilter : ITraceFilterCompat { - bool ShouldHitEntity(void *, int) override { return false; } - int GetTraceType() const override { return 1; } // TRACE_WORLD_ONLY -}; - struct EntityFilter : ITraceFilterCompat { void *ignore; explicit EntityFilter(void *ig) : ignore(ig) {} @@ -89,15 +88,24 @@ struct EntityFilter : ITraceFilterCompat { int GetTraceType() const override { return 0; } // TRACE_EVERYTHING }; -// MASK_VISIBLE = CONTENTS_SOLID | CONTENTS_MOVEABLE | CONTENTS_OPAQUE | -// CONTENTS_IGNORE_NODRAW_OPAQUE -constexpr unsigned MASK_VISIBLE = 0x1u | 0x4000u | 0x80u | 0x2000u; +struct VisibilityFilter : ITraceFilterCompat { + void *self; + void *target; + VisibilityFilter(void *s, void *t) : self(s), target(t) {} + bool ShouldHitEntity(void *pEntity, int) override { + return pEntity != self && pEntity != target; + } + int GetTraceType() const override { return 0; } // TRACE_EVERYTHING +}; + +constexpr unsigned CONTENTS_SOLID = 0x1u; +constexpr unsigned CONTENTS_WINDOW = 0x2u; // MASK_SHOT = SOLID|MOVEABLE|MONSTER|WINDOW|DEBRIS|HITBOX -constexpr unsigned MASK_SHOT = 0x1u | 0x4000u | 0x2000000u | 0x4u | +constexpr unsigned MASK_SHOT = 0x1u | 0x4000u | 0x2000000u | 0x2u | 0x4000000u | 0x40000000u; // MASK_PLAYERSOLID = SOLID|MOVEABLE|PLAYERCLIP|WINDOW|MONSTER|GRATE constexpr unsigned MASK_PLAYERSOLID = - 0x1u | 0x4000u | 0x10000u | 0x4u | 0x2000000u | 0x8u; + 0x1u | 0x4000u | 0x10000u | 0x2u | 0x2000000u | 0x8u; static void make_ray(RayCompat &ray, const Vector &start, const Vector &end) { float dx = end.x - start.x, dy = end.y - start.y, dz = end.z - start.z; @@ -127,7 +135,26 @@ static void make_standing_hull_ray(RayCompat &ray, const Vector &origin, } // namespace -bool is_visible(const Vector &eye, const Vector &point) { +static bool surface_contains(const SurfaceCompat &surf, const char *needle) { + if (!surf.name || !needle) + return false; + size_t n = strlen(needle); + for (const char *p = surf.name; *p; p++) + if (strncasecmp(p, needle, n) == 0) + return true; + return false; +} + +static bool surface_is_breakable_glass(const SurfaceCompat &surf) { + return surface_contains(surf, "break"); +} + +static bool surface_is_glass(const SurfaceCompat &surf) { + return surface_contains(surf, "glass") || surface_contains(surf, "window"); +} + +bool is_visible(const Vector &eye, const Vector &point, void *self, + void *target) { static bool warned = false; if (!g_iface.enginetrace) { if (!warned) { @@ -137,15 +164,51 @@ bool is_visible(const Vector &eye, const Vector &point) { return true; } - RayCompat ray; - make_ray(ray, eye, point); - GameTraceCompat tr{}; - WorldOnlyFilter filter; - using TraceRayFn = void (*)(void *, const RayCompat *, unsigned, void *, - GameTraceCompat *); - vfunc(g_iface.enginetrace, 4)(g_iface.enginetrace, &ray, - MASK_VISIBLE, &filter, &tr); - return tr.fraction > 0.97f && !tr.startsolid; + float dx = point.x - eye.x; + float dy = point.y - eye.y; + float dz = point.z - eye.z; + float len2 = dx * dx + dy * dy + dz * dz; + if (len2 < 0.0001f) + return true; + float inv_len = 1.0f / sqrtf(len2); + Vector dir{dx * inv_len, dy * inv_len, dz * inv_len}; + + // Trace everything that bullets care about: world, static props, crates and + // NPCs. Non-solid glass and glass whose material name marks it breakable is + // skipped; solid non-break glass (bulletproof) blocks. + Vector start = eye; + for (int pass = 0; pass < 5; pass++) { + RayCompat ray; + make_ray(ray, start, point); + GameTraceCompat tr{}; + VisibilityFilter filter(self, target); + using TraceRayFn = void (*)(void *, const RayCompat *, unsigned, void *, + GameTraceCompat *); + vfunc(g_iface.enginetrace, 4)(g_iface.enginetrace, &ray, + MASK_SHOT, &filter, &tr); + if (tr.startsolid || tr.allsolid) + return false; + if (tr.fraction >= 1.0f) + return true; + + bool glass = (tr.contents & CONTENTS_WINDOW) != 0 || + surface_is_glass(tr.surface); + bool solid = (tr.contents & CONTENTS_SOLID) != 0; + bool breakable_glass = glass && surface_is_breakable_glass(tr.surface); + if (glass && (!solid || breakable_glass)) { + // Step just past the pane and look for the next obstruction. + start = tr.endpos; + start.x += dir.x * 0.5f; + start.y += dir.y * 0.5f; + start.z += dir.z * 0.5f; + logd("vischeck: skipped glass surface=%s contents=0x%x solid=%d break=%d", + tr.surface.name ? tr.surface.name : "?", tr.contents, (int)solid, + (int)breakable_glass); + continue; + } + return false; + } + return false; } // Trace the crosshair ray and return the first entity hit (nullptr for world -- cgit v1.2.3