From 92d388116cc19ef728d66ac67eaa4f584e63d895 Mon Sep 17 00:00:00 2001 From: salmonik Date: Mon, 26 Oct 2026 17:30:29 +0100 Subject: Silent aim: exact usercmd angle + fire, restore rendered view - new top-level Silent aim feature (Active + Bind, default Mouse3) - reuses the shared aim-target selection (bone checklist, nearest selected point, world-only MASK_VISIBLE vischeck) - writes the exact target angle into cmd->viewangles, sets IN_ATTACK for the shot, then immediately restores engine view angles to the player's input view (g_cam_view) so the camera stays where it was - shares the vischeck/bone selection with the aimbot; independent of the aimbot/triggerbot binds - config [silentaim] active/bind --- src/esp.cpp | 106 +++++++++++++++++++++++++++++++++++++++++++---------------- src/hl2h.cpp | 7 ++++ src/hl2h.h | 4 ++- src/menu.cpp | 30 ++++++++++++----- 4 files changed, 110 insertions(+), 37 deletions(-) (limited to 'src') diff --git a/src/esp.cpp b/src/esp.cpp index e6981f7..f7c24ce 100644 --- a/src/esp.cpp +++ b/src/esp.cpp @@ -334,12 +334,16 @@ static QAngle angle_to(const Vector &from, const Vector &to) { return a; } -bool aimbot_apply(CUserCmd *cmd) { +static constexpr float AIM_MAX_FOV = 60.0f; + +static bool select_aim_target(const QAngle &view, unsigned bone_mask, + bool vischeck, const char *tag, int &out_idx, + Vector &out_point, float &out_fov) { + out_idx = -1; + out_point = {}; + out_fov = 0.0f; if (!g_iface.entitylist || g_net.origin < 0 || g_net.maxs < 0) return false; - unsigned bone_mask = aimbot_bone_mask(); - if (!bone_mask) - return false; int local = engine_get_local_player(); if (local <= 0) return false; @@ -350,14 +354,11 @@ bool aimbot_apply(CUserCmd *cmd) { *(int *)((uintptr_t)local_ent + g_net.health) <= 0) return false; - QAngle view{cmd->viewangles[0], cmd->viewangles[1], 0.0f}; - using Fn = int (*)(void *); int hi = vfunc(g_iface.entitylist, 6)(g_iface.entitylist); if (hi < 0 || hi > 4096) hi = 4096; - constexpr float MAX_FOV = 60.0f; // only things near the crosshair struct Candidate { int idx; float fov; @@ -398,10 +399,9 @@ bool aimbot_apply(CUserCmd *cmd) { Vector approx{origin.x, origin.y, origin.z + maxz * ((bone_mask & (1u << 1)) ? 0.9f : 0.55f)}; float fov = getfov(view, angle_to(g_cam_eye, approx)); - if (fov >= MAX_FOV) + if (fov >= AIM_MAX_FOV) continue; - // keep candidates sorted by approximate angle, nearest first int pos = ncands; while (pos > 0 && cands[pos - 1].fov > fov) pos--; @@ -418,15 +418,14 @@ bool aimbot_apply(CUserCmd *cmd) { double now = esp_now(); if (now - last_none >= 2.0) { last_none = now; - logf("aimbot: no hostile target within %.0f deg", MAX_FOV); + logf("%s: no hostile target within %.0f deg", tag, AIM_MAX_FOV); } return false; } - // Walk targets nearest-first. On each, pick the nearest selected hitbox - // point that passes vischeck; skip to the next target if all are blocked. int best_idx = -1; - Vector best_head{}; + Vector best_point{}; + float best_fov = 0.0f; for (int ci = 0; ci < ncands && best_idx < 0; ci++) { int idx = cands[ci].idx; void *ent = entitylist_get(idx); @@ -440,40 +439,55 @@ bool aimbot_apply(CUserCmd *cmd) { float f = getfov(view, angle_to(g_cam_eye, pts[i])); if (f >= aim_fov) continue; - if (aimbot_vischeck_enabled() && !is_visible(g_cam_eye, pts[i])) + if (vischeck && !is_visible(g_cam_eye, pts[i])) continue; aim_fov = f; aim_pt = pts[i]; } - if (aim_fov <= MAX_FOV && aim_fov < 1e8f) { + if (aim_fov <= AIM_MAX_FOV && aim_fov < 1e8f) { best_idx = idx; - best_head = aim_pt; + best_point = aim_pt; + best_fov = aim_fov; static int sel_logs = 0; if (sel_logs < 5) { sel_logs++; - logf("aimbot: target idx=%d points=%d fov=%.2f vischeck=%d", idx, - npoints, aim_fov, (int)aimbot_vischeck_enabled()); + logf("%s: target idx=%d points=%d fov=%.2f vischeck=%d", tag, idx, + npoints, aim_fov, (int)vischeck); } } } - if (best_idx < 0) { static double last_vis = 0.0; double now = esp_now(); if (now - last_vis >= 2.0) { last_vis = now; - logf("aimbot: no visible aim point (vischeck=%d)", - (int)aimbot_vischeck_enabled()); + logf("%s: no visible aim point (vischeck=%d)", tag, (int)vischeck); } return false; } + out_idx = best_idx; + out_point = best_point; + out_fov = best_fov; + return true; +} - QAngle desired = angle_to(g_cam_eye, best_head); +bool aimbot_apply(CUserCmd *cmd) { + unsigned bone_mask = aimbot_bone_mask(); + if (!bone_mask) + return false; + QAngle view{cmd->viewangles[0], cmd->viewangles[1], 0.0f}; + int target = -1; + Vector point{}; + float fov = 0.0f; + if (!select_aim_target(view, bone_mask, aimbot_vischeck_enabled(), "aimbot", + target, point, fov)) + return false; + QAngle desired = angle_to(g_cam_eye, point); static int want_logs = 0; if (want_logs < 10) { want_logs++; - logf("aimbot: aim=(%.1f %.1f %.1f) want=%.2f/%.2f", best_head.x, - best_head.y, best_head.z, desired.x, desired.y); + logf("aimbot: target=%d aim=(%.1f %.1f %.1f) fov=%.2f want=%.2f/%.2f", + target, point.x, point.y, point.z, fov, desired.x, desired.y); } float dp = desired.x - cmd->viewangles[0]; float dy = desired.y - cmd->viewangles[1]; @@ -499,15 +513,11 @@ bool aimbot_apply(CUserCmd *cmd) { cmd->viewangles[0] = np; cmd->viewangles[1] = ny; - // cmd->viewangles drives usercmd/prediction, but the rendered camera can - // stay on the pre-hook angles. Also push the smoothed angles into the - // engine's view (IVEngineClient013 slot 20 = SetViewAngles). if (g_iface.engine) { QAngle out{np, ny, 0.0f}; using SetViewFn = void (*)(void *, QAngle *); vfunc(g_iface.engine, 20)(g_iface.engine, &out); } - static int mod_logs = 0; if (mod_logs < 10) { mod_logs++; @@ -516,6 +526,46 @@ bool aimbot_apply(CUserCmd *cmd) { return true; } +bool silent_aim_apply(CUserCmd *cmd) { + unsigned bone_mask = aimbot_bone_mask(); + if (!bone_mask) + return false; + QAngle orig{g_cam_view.x, g_cam_view.y, g_cam_view.z}; + QAngle view{orig.x, orig.y, 0.0f}; + int target = -1; + Vector point{}; + float fov = 0.0f; + if (!select_aim_target(view, bone_mask, true, "silentaim", target, point, + fov)) + return false; + QAngle desired = angle_to(g_cam_eye, point); + float np = desired.x; + if (np > 89.0f) np = 89.0f; + if (np < -89.0f) np = -89.0f; + float ny = desired.y; + while (ny > 180.0f) ny -= 360.0f; + while (ny < -180.0f) ny += 360.0f; + + 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. + if (g_iface.engine) { + QAngle restore{orig.x, orig.y, orig.z}; + using SetViewFn = void (*)(void *, QAngle *); + vfunc(g_iface.engine, 20)(g_iface.engine, &restore); + } + cmd->buttons |= IN_ATTACK; + static int logs = 0; + if (logs < 5) { + logs++; + logf("silentaim: fire idx=%d fov=%.2f aim=%.2f/%.2f", target, fov, np, ny); + } + return true; +} + bool triggerbot_apply(CUserCmd *cmd) { if (!g_iface.enginetrace || !g_iface.entitylist) return false; diff --git a/src/hl2h.cpp b/src/hl2h.cpp index 7a7d32e..89d6b89 100644 --- a/src/hl2h.cpp +++ b/src/hl2h.cpp @@ -262,6 +262,13 @@ static void do_move(int seq) { note_modified(cmd, flags, vel); } } + + if (feature_active(FEAT_SILENTAIM)) { + if (silent_aim_apply(cmd)) { + const float *vel = (const float *)(e + g_net.velocity); + note_modified(cmd, flags, vel); + } + } } // --------------------------------------------------------------------------- diff --git a/src/hl2h.h b/src/hl2h.h index 28970b0..02be393 100644 --- a/src/hl2h.h +++ b/src/hl2h.h @@ -140,12 +140,14 @@ Vector angle_forward_vector(const QAngle &angles); float getfov(const QAngle &my_view, const QAngle &aim_ang); bool aimbot_apply(CUserCmd *cmd); bool triggerbot_apply(CUserCmd *cmd); +bool silent_aim_apply(CUserCmd *cmd); enum FeatureId { FEAT_BHOP = 0, FEAT_ABH = 1, FEAT_ESP = 2, FEAT_AIMBOT = 3, - FEAT_TRIGGERBOT = 4 + FEAT_TRIGGERBOT = 4, + FEAT_SILENTAIM = 5 }; enum EspOption { ESP_OPT_SHOW_NEUTRAL = 0, diff --git a/src/menu.cpp b/src/menu.cpp index 585695c..11ed222 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -62,6 +62,7 @@ static FeatureState g_abh{true, 0}; static FeatureState g_esp{true, 0}; static FeatureState g_aimbot{true, 604}; // default Mouse5, rebindable in menu static FeatureState g_triggerbot{true, 603}; // default Mouse4 +static FeatureState g_silentaim{true, 602}; // default Mouse3 // Aimbot bone checklist. Head defaults on (v1 behaviour). enum { @@ -117,6 +118,9 @@ static MenuNode g_aimbot_smooth_node{NODE_VALUE, "Smoothness", false, 0, {}, nul static MenuNode g_triggerbot_feat{NODE_FEATURE, "Triggerbot", false, 1, {}, nullptr, nullptr, nullptr}; static MenuNode g_triggerbot_active{NODE_TOGGLE, "Active", false, 0, {}, &g_triggerbot.active, nullptr, nullptr}; static MenuNode g_triggerbot_bind{NODE_BIND, "Bind", false, 0, {}, nullptr, &g_triggerbot.bind, nullptr}; +static MenuNode g_silentaim_feat{NODE_FEATURE, "Silent aim", false, 1, {}, nullptr, nullptr, nullptr}; +static MenuNode g_silentaim_active{NODE_TOGGLE, "Active", false, 0, {}, &g_silentaim.active, nullptr, nullptr}; +static MenuNode g_silentaim_bind{NODE_BIND, "Bind", false, 0, {}, nullptr, &g_silentaim.bind, nullptr}; static MenuNode *g_visible[32]; // flattened visible nodes for arrow navigation static int g_visible_count = 0; @@ -142,6 +146,9 @@ static void build_tree() { g_triggerbot_feat.children[0] = &g_triggerbot_active; g_triggerbot_feat.children[1] = &g_triggerbot_bind; g_triggerbot_feat.child_count = 2; + g_silentaim_feat.children[0] = &g_silentaim_active; + g_silentaim_feat.children[1] = &g_silentaim_bind; + g_silentaim_feat.child_count = 2; for (int i = 0; i < ESP_OPT_COUNT; i++) g_esp_feat.children[i + 1] = &g_esp_opt_nodes[i]; g_esp_feat.child_count = ESP_OPT_COUNT + 1; @@ -150,7 +157,8 @@ static void build_tree() { g_root.children[2] = &g_esp_feat; g_root.children[3] = &g_aimbot_feat; g_root.children[4] = &g_triggerbot_feat; - g_root.child_count = 5; + g_root.children[5] = &g_silentaim_feat; + g_root.child_count = 6; } static int g_depth[32]; // depth of each visible row (for indentation) @@ -224,6 +232,8 @@ static void config_save() { g_aim_bones[AIM_BONE_LLEG], g_aim_bones[AIM_BONE_RLEG]); fprintf(f, "[triggerbot]\nactive=%d\nbind=%d\n", g_triggerbot.active, g_triggerbot.bind); + fprintf(f, "[silentaim]\nactive=%d\nbind=%d\n", g_silentaim.active, + g_silentaim.bind); fprintf(f, "[esp]\nactive=%d\nbind=%d\nneutral=%d\nhealthbar=%d\n" "healthnum=%d\nname=%d\nskeleton=%d\nbox=%d\n", g_esp.active, g_esp.bind, g_esp_opt[ESP_OPT_SHOW_NEUTRAL], @@ -253,6 +263,8 @@ static void config_load() { cur = &g_aimbot; else if (strstr(line, "[triggerbot]")) cur = &g_triggerbot; + else if (strstr(line, "[silentaim]")) + cur = &g_silentaim; else cur = nullptr; in_esp = (cur == &g_esp); @@ -413,9 +425,10 @@ static bool key_pressed(int sc) { // feature gate queried from hl2h.cpp do_move(): feature must be Active AND // its bind key held (always-on when unbound). bool feature_active(int feature) { - static FeatureState *feats[5] = {&g_bhop, &g_abh, &g_esp, &g_aimbot, - &g_triggerbot}; - if (feature < 0 || feature > 4) + static FeatureState *feats[6] = {&g_bhop, &g_abh, &g_esp, + &g_aimbot, &g_triggerbot, + &g_silentaim}; + if (feature < 0 || feature > 5) return false; FeatureState *f = feats[feature]; if (!f->active) @@ -492,7 +505,8 @@ static void menu_handle_input() { MenuNode *n = g_visible[g_selected]; if (n->bind_ptr) { // clear same bind from other features - FeatureState *features[] = {&g_bhop, &g_abh, &g_aimbot, &g_triggerbot}; + FeatureState *features[] = {&g_bhop, &g_abh, &g_aimbot, &g_triggerbot, + &g_silentaim}; for (auto *f : features) if (&f->bind != n->bind_ptr && f->bind == i) f->bind = 0; @@ -707,11 +721,11 @@ void outer_draw() { draw_text(270, 10 + i * 20, "[press key]"); else { /* stable name for current bind */ - static char bn[4][32]; + static char bn[5][32]; static FeatureState *feats[] = {&g_bhop, &g_abh, &g_aimbot, - &g_triggerbot}; + &g_triggerbot, &g_silentaim}; int fi = 0; - for (int k = 0; k < 4; k++) + for (int k = 0; k < 5; k++) if (n->bind_ptr == &feats[k]->bind) fi = k; snprintf(bn[fi], 32, "[%s]", scancode_name(*n->bind_ptr)); -- cgit v1.2.3