From 849d205a3afb553eb49296393fad44671cc03494 Mon Sep 17 00:00:00 2001 From: salmonik Date: Mon, 26 Oct 2026 17:35:13 +0100 Subject: Silent aim: restore view at FRAME_RENDER_START; preserve movement - hook CHLClient::FrameStageNotify (vtable slot 35) and at FRAME_RENDER_START re-apply the player's original view angles after the engine has consumed the aimed usercmd; this fixes the slow pull with high-RPM weapons and the revolver leaving the view on the target - silent aim also rewrites forwardmove/sidemove so the world-space movement wish vector is unchanged by the temporary aim angles --- src/esp.cpp | 22 ++++++++++++++++++++-- src/hl2h.cpp | 33 +++++++++++++++++++++++++++++++-- src/hl2h.h | 1 + 3 files changed, 52 insertions(+), 4 deletions(-) diff --git a/src/esp.cpp b/src/esp.cpp index f7c24ce..cd0ff36 100644 --- a/src/esp.cpp +++ b/src/esp.cpp @@ -526,6 +526,21 @@ bool aimbot_apply(CUserCmd *cmd) { return true; } +// Keep the world-space movement wish direction unchanged when the usercmd +// view angles are temporarily replaced by the silent-aim angles. +static void preserve_movement(CUserCmd *cmd, float old_yaw, float new_yaw) { + float oy = old_yaw * PI_F / 180.0f; + float ay = new_yaw * PI_F / 180.0f; + float ofx = cosf(oy), ofy = sinf(oy); + float orx = sinf(oy), ory = -cosf(oy); + float afx = cosf(ay), afy = sinf(ay); + float arx = sinf(ay), ary = -cosf(ay); + float wx = ofx * cmd->forwardmove + orx * cmd->sidemove; + float wy = ofy * cmd->forwardmove + ory * cmd->sidemove; + cmd->forwardmove = wx * afx + wy * afy; + cmd->sidemove = wx * arx + wy * ary; +} + bool silent_aim_apply(CUserCmd *cmd) { unsigned bone_mask = aimbot_bone_mask(); if (!bone_mask) @@ -546,12 +561,15 @@ bool silent_aim_apply(CUserCmd *cmd) { while (ny > 180.0f) ny -= 360.0f; while (ny < -180.0f) ny += 360.0f; + preserve_movement(cmd, orig.y, ny); + cmd->viewangles[0] = np; cmd->viewangles[1] = ny; cmd->viewangles[2] = orig.z; - // The usercmd keeps the aim angles (shot/prediction); the rendered camera - // is restored to where the player was actually looking. + // Restore now and again at FRAME_RENDER_START (after the engine consumed + // the aimed usercmd); the frame-stage restore is the one that sticks. + set_silent_restore(orig); if (g_iface.engine) { QAngle restore{orig.x, orig.y, orig.z}; using SetViewFn = void (*)(void *, QAngle *); diff --git a/src/hl2h.cpp b/src/hl2h.cpp index 89d6b89..0491fd9 100644 --- a/src/hl2h.cpp +++ b/src/hl2h.cpp @@ -278,6 +278,31 @@ static void do_move(int seq) { using CreateMoveFn = bool (*)(void *, int, float, bool); static CreateMoveFn oCreateMove = nullptr; +using FrameStageFn = void (*)(void *, int); +static FrameStageFn oFrameStageNotify = nullptr; +static QAngle g_silent_restore_angles{}; +static bool g_silent_restore_pending = false; + +void set_silent_restore(const QAngle &angles) { + g_silent_restore_angles = angles; + g_silent_restore_pending = true; +} + +// FRAME_RENDER_START (ClientFrameStage_t: -1 undefined, 0 START, ..., +// 4 NET_UPDATE_END, 5 RENDER_START). +static void hkdFrameStageNotify(void *self, int stage) { + if (oFrameStageNotify) + oFrameStageNotify(self, stage); + if (stage == 5 && g_silent_restore_pending) { + g_silent_restore_pending = false; + if (g_iface.engine) { + QAngle a = g_silent_restore_angles; + using SetViewFn = void (*)(void *, QAngle *); + vfunc(g_iface.engine, 20)(g_iface.engine, &a); + } + } +} + using DrawPrintTextFn = void (*)(void *self, const wchar_t *text, int len, int drawType); static DrawPrintTextFn oDrawPrintText = nullptr; using PaintTraverseExFn = void (*)(void *, long, bool); @@ -345,9 +370,13 @@ static void install_hook() { memcpy(newvt, vt, SLOTS * sizeof(void *)); oCreateMove = (CreateMoveFn)vt[21]; newvt[21] = (void *)&hkdCreateMove; + oFrameStageNotify = (FrameStageFn)vt[35]; + newvt[35] = (void *)&hkdFrameStageNotify; VTABLE(g_iface.client) = newvt; - logf("hook installed: CreateMove %p -> %p (orig %p)", vt[21], - (void *)&hkdCreateMove, (void *)oCreateMove); + logf("hook installed: CreateMove %p -> %p (orig %p), FrameStageNotify %p " + "-> %p (orig %p)", + vt[21], (void *)&hkdCreateMove, (void *)oCreateMove, vt[35], + (void *)&hkdFrameStageNotify, (void *)oFrameStageNotify); } // --------------------------------------------------------------------------- diff --git a/src/hl2h.h b/src/hl2h.h index 02be393..a08f029 100644 --- a/src/hl2h.h +++ b/src/hl2h.h @@ -185,6 +185,7 @@ int engine_get_local_player(); void draw_text_col(int x, int y, const char *s, int r, int g, int b); void esp_draw(); bool world_to_screen(const Vector &w, float &sx, float &sy); +void set_silent_restore(const QAngle &angles); bool is_visible(const Vector &eye, const Vector &point); void *trace_entity(const Vector &start, const Vector &end, void *ignore); -- cgit v1.2.3