diff options
| author | salmonik <salmonik@tuta.io> | 2026-10-26 17:22:25 +0100 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-10-26 17:22:25 +0100 |
| commit | e9944657c7ddefb3b8d2c8373c97fa47629ea1b8 (patch) | |
| tree | d79bb7b262b5791d05f1a087b0db4515ce69b47d /src/vischeck.cpp | |
| parent | Aimbot v3: world-only vischeck (diff) | |
| download | hl2h-e9944657c7ddefb3b8d2c8373c97fa47629ea1b8.tar.gz hl2h-e9944657c7ddefb3b8d2c8373c97fa47629ea1b8.zip | |
Triggerbot: crosshair entity trace, hostile-only
- top-level Triggerbot feature (Active + Bind, default Mouse4, rebindable)
- trace the crosshair ray from the eye with MASK_SHOT and TRACE_EVERYTHING;
the filter ignores the local player
- if the first entity hit is a live hostile NPC, press IN_ATTACK and fix the
usercmd CRC; walls/props/friendly/neutral/corpses do not trigger
- independent of Aimbot; config [triggerbot] active/bind
Diffstat (limited to 'src/vischeck.cpp')
| -rw-r--r-- | src/vischeck.cpp | 85 |
1 files changed, 68 insertions, 17 deletions
diff --git a/src/vischeck.cpp b/src/vischeck.cpp index 92d194b..428f54f 100644 --- a/src/vischeck.cpp +++ b/src/vischeck.cpp | |||
| @@ -30,9 +30,19 @@ static_assert(sizeof(VectorAlignedCompat) == 16, "VectorAligned layout"); | |||
| 30 | static_assert(offsetof(RayCompat, delta) == 16, "Ray_t delta offset"); | 30 | static_assert(offsetof(RayCompat, delta) == 16, "Ray_t delta offset"); |
| 31 | static_assert(offsetof(RayCompat, isRay) == 64, "Ray_t isRay offset"); | 31 | static_assert(offsetof(RayCompat, isRay) == 64, "Ray_t isRay offset"); |
| 32 | 32 | ||
| 33 | // trace_t / CBaseTrace prefix. cplane_t is Vector + float + type + signbits | 33 | // trace_t / CGameTrace layout (Source 2013, 32-bit): |
| 34 | // + 2 pad bytes, so fraction lands at 0x2C and startsolid at 0x37. | 34 | // CBaseTrace: startpos 0, endpos 12, cplane_t plane 24 (20 bytes), |
| 35 | struct TraceCompat { | 35 | // fraction 44, contents 48, dispFlags 52, allsolid 54, |
| 36 | // startsolid 55 | ||
| 37 | // CGameTrace: fractionleftsolid 56, csurface_t surface 60 (8 bytes), | ||
| 38 | // hitgroup 68, physicsbone 72, m_pEnt 76, hitbox 80 | ||
| 39 | struct SurfaceCompat { | ||
| 40 | const char *name; | ||
| 41 | short surfaceProps; | ||
| 42 | unsigned short flags; | ||
| 43 | }; | ||
| 44 | |||
| 45 | struct GameTraceCompat { | ||
| 36 | Vector startpos; | 46 | Vector startpos; |
| 37 | Vector endpos; | 47 | Vector endpos; |
| 38 | Vector normal; | 48 | Vector normal; |
| @@ -45,11 +55,18 @@ struct TraceCompat { | |||
| 45 | uint16_t dispFlags; | 55 | uint16_t dispFlags; |
| 46 | bool allsolid; | 56 | bool allsolid; |
| 47 | bool startsolid; | 57 | bool startsolid; |
| 48 | uint8_t rest[128]; // CGameTrace tail (surface, ent, hitbox, ...) | 58 | float fractionleftsolid; |
| 59 | SurfaceCompat surface; | ||
| 60 | int hitgroup; | ||
| 61 | short physicsbone; | ||
| 62 | void *ent; | ||
| 63 | int hitbox; | ||
| 64 | uint8_t rest[64]; // future-proof / avoid short writes | ||
| 49 | }; | 65 | }; |
| 50 | 66 | ||
| 51 | static_assert(offsetof(TraceCompat, fraction) == 44, "trace_t fraction"); | 67 | static_assert(offsetof(GameTraceCompat, fraction) == 44, "trace_t fraction"); |
| 52 | static_assert(offsetof(TraceCompat, startsolid) == 55, "trace_t startsolid"); | 68 | static_assert(offsetof(GameTraceCompat, startsolid) == 55, "trace_t startsolid"); |
| 69 | static_assert(offsetof(GameTraceCompat, ent) == 76, "trace_t m_pEnt"); | ||
| 53 | 70 | ||
| 54 | // ITraceFilter has no virtual destructor: vtable[0] = ShouldHitEntity, | 71 | // ITraceFilter has no virtual destructor: vtable[0] = ShouldHitEntity, |
| 55 | // vtable[1] = GetTraceType. | 72 | // vtable[1] = GetTraceType. |
| @@ -63,9 +80,32 @@ struct WorldOnlyFilter : ITraceFilterCompat { | |||
| 63 | int GetTraceType() const override { return 1; } // TRACE_WORLD_ONLY | 80 | int GetTraceType() const override { return 1; } // TRACE_WORLD_ONLY |
| 64 | }; | 81 | }; |
| 65 | 82 | ||
| 83 | struct EntityFilter : ITraceFilterCompat { | ||
| 84 | void *ignore; | ||
| 85 | explicit EntityFilter(void *ig) : ignore(ig) {} | ||
| 86 | bool ShouldHitEntity(void *pEntity, int) override { | ||
| 87 | return pEntity != ignore; | ||
| 88 | } | ||
| 89 | int GetTraceType() const override { return 0; } // TRACE_EVERYTHING | ||
| 90 | }; | ||
| 91 | |||
| 66 | // MASK_VISIBLE = CONTENTS_SOLID | CONTENTS_MOVEABLE | CONTENTS_OPAQUE | | 92 | // MASK_VISIBLE = CONTENTS_SOLID | CONTENTS_MOVEABLE | CONTENTS_OPAQUE | |
| 67 | // CONTENTS_IGNORE_NODRAW_OPAQUE | 93 | // CONTENTS_IGNORE_NODRAW_OPAQUE |
| 68 | constexpr unsigned MASK_VISIBLE = 0x1u | 0x4000u | 0x80u | 0x2000u; | 94 | constexpr unsigned MASK_VISIBLE = 0x1u | 0x4000u | 0x80u | 0x2000u; |
| 95 | // MASK_SHOT = SOLID|MOVEABLE|MONSTER|WINDOW|DEBRIS|HITBOX | ||
| 96 | constexpr unsigned MASK_SHOT = 0x1u | 0x4000u | 0x2000000u | 0x4u | | ||
| 97 | 0x4000000u | 0x40000000u; | ||
| 98 | |||
| 99 | static void make_ray(RayCompat &ray, const Vector &start, const Vector &end) { | ||
| 100 | float dx = end.x - start.x, dy = end.y - start.y, dz = end.z - start.z; | ||
| 101 | ray = RayCompat{}; | ||
| 102 | ray.start = {start.x, start.y, start.z, 0.0f}; | ||
| 103 | ray.delta = {dx, dy, dz, 0.0f}; | ||
| 104 | ray.startOffset = {0.0f, 0.0f, 0.0f, 0.0f}; | ||
| 105 | ray.extents = {0.0f, 0.0f, 0.0f, 0.0f}; | ||
| 106 | ray.isRay = true; | ||
| 107 | ray.isSwept = (dx * dx + dy * dy + dz * dz) > 0.0f; | ||
| 108 | } | ||
| 69 | 109 | ||
| 70 | } // namespace | 110 | } // namespace |
| 71 | 111 | ||
| @@ -79,20 +119,31 @@ bool is_visible(const Vector &eye, const Vector &point) { | |||
| 79 | return true; | 119 | return true; |
| 80 | } | 120 | } |
| 81 | 121 | ||
| 82 | float dx = point.x - eye.x, dy = point.y - eye.y, dz = point.z - eye.z; | 122 | RayCompat ray; |
| 83 | RayCompat ray{}; | 123 | make_ray(ray, eye, point); |
| 84 | ray.start = {eye.x, eye.y, eye.z, 0.0f}; | 124 | GameTraceCompat tr{}; |
| 85 | ray.delta = {dx, dy, dz, 0.0f}; | ||
| 86 | ray.startOffset = {0.0f, 0.0f, 0.0f, 0.0f}; | ||
| 87 | ray.extents = {0.0f, 0.0f, 0.0f, 0.0f}; | ||
| 88 | ray.isRay = true; | ||
| 89 | ray.isSwept = (dx * dx + dy * dy + dz * dz) > 0.0f; | ||
| 90 | |||
| 91 | TraceCompat tr{}; | ||
| 92 | WorldOnlyFilter filter; | 125 | WorldOnlyFilter filter; |
| 93 | using TraceRayFn = void (*)(void *, const RayCompat *, unsigned, void *, | 126 | using TraceRayFn = void (*)(void *, const RayCompat *, unsigned, void *, |
| 94 | TraceCompat *); | 127 | GameTraceCompat *); |
| 95 | vfunc<TraceRayFn>(g_iface.enginetrace, 4)(g_iface.enginetrace, &ray, | 128 | vfunc<TraceRayFn>(g_iface.enginetrace, 4)(g_iface.enginetrace, &ray, |
| 96 | MASK_VISIBLE, &filter, &tr); | 129 | MASK_VISIBLE, &filter, &tr); |
| 97 | return tr.fraction > 0.97f && !tr.startsolid; | 130 | return tr.fraction > 0.97f && !tr.startsolid; |
| 98 | } | 131 | } |
| 132 | |||
| 133 | // Trace the crosshair ray and return the first entity hit (nullptr for world | ||
| 134 | // or no hit). `ignore` is the local player entity pointer. | ||
| 135 | void *trace_entity(const Vector &start, const Vector &end, void *ignore) { | ||
| 136 | if (!g_iface.enginetrace) | ||
| 137 | return nullptr; | ||
| 138 | RayCompat ray; | ||
| 139 | make_ray(ray, start, end); | ||
| 140 | GameTraceCompat tr{}; | ||
| 141 | EntityFilter filter(ignore); | ||
| 142 | using TraceRayFn = void (*)(void *, const RayCompat *, unsigned, void *, | ||
| 143 | GameTraceCompat *); | ||
| 144 | vfunc<TraceRayFn>(g_iface.enginetrace, 4)(g_iface.enginetrace, &ray, | ||
| 145 | MASK_SHOT, &filter, &tr); | ||
| 146 | if (tr.fraction >= 1.0f || tr.startsolid) | ||
| 147 | return nullptr; | ||
| 148 | return tr.ent; | ||
| 149 | } | ||
