From 5adb4303c8ac4969312fa1afd4f254cba65f6ecc Mon Sep 17 00:00:00 2001 From: salmonik Date: Tue, 27 Oct 2026 12:43:59 +0100 Subject: Aimbot/silent aim: settable FOV, FOV circle, target locking - Aimbot -> FOV and Silent aim -> FOV values (1..180 deg from the crosshair) - Aimbot -> Show FOV circle: projects the FOV cone through the engine's world->screen transform (hidden for >=89 deg since the boundary is behind the eye) - target lock: while a feature has a bind and the key is held, the acquired target is kept while it is alive and has a visible selected hitbox point; it only re-acquires when the target dies/leaves visibility or the key is released. With bind = None both features keep selecting the nearest target. --- src/esp.cpp | 204 ++++++++++++++++++++++++++++++++++++++++++++--------------- src/hl2h.cpp | 5 ++ src/hl2h.h | 7 ++ src/menu.cpp | 72 +++++++++++++++++---- 4 files changed, 224 insertions(+), 64 deletions(-) diff --git a/src/esp.cpp b/src/esp.cpp index 7583f26..945b234 100644 --- a/src/esp.cpp +++ b/src/esp.cpp @@ -274,6 +274,45 @@ static void surf_line(int x0, int y0, int x1, int y1) { vfunc(g_surface, SURF_DrawLine)(g_surface, x0, y0, x1, y1); } +// Screen projection of the aimbot FOV cone boundary, using the engine's own +// world->screen transform so it follows the actual aspect/fov. +static void draw_fov_circle() { + if (!aimbot_fov_circle()) + return; + float fov = (float)aimbot_fov(); + if (fov < 1.0f || fov >= 89.0f) // beyond this the "circle" is behind the eye + return; + float alpha = fov * PI_F / 180.0f; + float yaw = g_cam_view.y * PI_F / 180.0f; + float pitch = g_cam_view.x * PI_F / 180.0f; + Vector fwd{cosf(pitch) * cosf(yaw), cosf(pitch) * sinf(yaw), -sinf(pitch)}; + Vector right{sinf(yaw), -cosf(yaw), 0.0f}; + Vector up{right.y * fwd.z - right.z * fwd.y, + right.z * fwd.x - right.x * fwd.z, + right.x * fwd.y - right.y * fwd.x}; + constexpr int N = 48; + float xs[N], ys[N]; + bool ok[N]; + float ca = cosf(alpha), sa = sinf(alpha); + for (int i = 0; i < N; i++) { + float phi = (2.0f * PI_F * i) / N; + float cp = cosf(phi), sp = sinf(phi); + Vector d{fwd.x * ca + (right.x * cp + up.x * sp) * sa, + fwd.y * ca + (right.y * cp + up.y * sp) * sa, + fwd.z * ca + (right.z * cp + up.z * sp) * sa}; + Vector p{g_cam_eye.x + d.x * 1024.0f, g_cam_eye.y + d.y * 1024.0f, + g_cam_eye.z + d.z * 1024.0f}; + ok[i] = world_to_screen(p, xs[i], ys[i]) && std::isfinite(xs[i]) && + std::isfinite(ys[i]); + } + surf_color(255, 255, 255, 160); + for (int i = 0; i < N; i++) { + int j = (i + 1) % N; + if (ok[i] && ok[j]) + surf_line((int)xs[i], (int)ys[i], (int)xs[j], (int)ys[j]); + } +} + // Draw the parent->child bone links. Bone snapshots are world-space matrices // from SetupBones; project each bone once and reuse for both endpoints. static void draw_skeleton(const EspTarget &t) { @@ -332,11 +371,20 @@ static QAngle angle_to(const Vector &from, const Vector &to) { return a; } -static constexpr float AIM_MAX_FOV = 60.0f; +static int g_aim_lock = -1; +static int g_silent_lock = -1; +void aimbot_reset_lock() { g_aim_lock = -1; } +void silentaim_reset_lock() { g_silent_lock = -1; } + +// max_fov is the angular radius around the crosshair. If lock_idx is non-null +// and valid, that target is kept while it is alive and visible (the "do not +// switch while the bind is held" rule); a fresh acquisition is ranked by angle +// and the nearest visible selected hitbox point. 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) { + bool vischeck, float max_fov, const char *tag, + int *lock_idx, int &out_idx, Vector &out_point, + float &out_fov) { out_idx = -1; out_point = {}; out_fov = 0.0f; @@ -352,6 +400,71 @@ static bool select_aim_target(const QAngle &view, unsigned bone_mask, *(int *)((uintptr_t)local_ent + g_net.health) <= 0) return false; + auto try_target = [&](int idx, bool enforce_fov, Vector &pt, + float &fov) -> bool { + if (idx <= 0 || idx == local) + return false; + void *ent = entitylist_get(idx); + if (!ent) + return false; + void *net = entity_networkable(idx); + if (!net) + return false; + const char *cls = entity_class_name(net, nullptr); + if (!cls || !is_npc_class(cls)) + return false; + uintptr_t e = (uintptr_t)ent; + int life = g_net.lifestate >= 0 + ? (int)*(unsigned char *)(e + g_net.lifestate) + : -1; + if (life != LIFE_ALIVE) + return false; + const char *model = entity_model_name(ent); + if (model_is_friendly(model) || model_is_neutral(model)) + return false; + if (!bones_update(idx, ent)) + return false; + Vector pts[32]; + int npoints = aim_points(idx, bone_mask, pts, 32); + float best = 1e9f; + Vector best_pt{}; + for (int i = 0; i < npoints; i++) { + float f = getfov(view, angle_to(g_cam_eye, pts[i])); + if (enforce_fov && f > max_fov) + continue; + if (f >= best) + continue; + if (vischeck && !is_visible(g_cam_eye, pts[i])) + continue; + best = f; + best_pt = pts[i]; + } + if (best > 1e8f) + return false; + pt = best_pt; + fov = best; + return true; + }; + + // Keep the locked target while it is alive and has a visible selected point. + // No FOV limit on a locked target; only acquisition is FOV-limited. + if (lock_idx && *lock_idx >= 0) { + Vector pt; + float f; + if (try_target(*lock_idx, false, pt, f)) { + out_idx = *lock_idx; + out_point = pt; + out_fov = f; + static int lock_logs = 0; + if (lock_logs < 5) { + lock_logs++; + logf("%s: lock idx=%d fov=%.2f", tag, out_idx, f); + } + return true; + } + *lock_idx = -1; + } + using Fn = int (*)(void *); int hi = vfunc(g_iface.entitylist, 6)(g_iface.entitylist); if (hi < 0 || hi > 4096) @@ -397,7 +510,7 @@ static bool select_aim_target(const QAngle &view, unsigned bone_mask, 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 >= AIM_MAX_FOV) + if (fov > max_fov) continue; int pos = ncands; @@ -416,57 +529,37 @@ static bool select_aim_target(const QAngle &view, unsigned bone_mask, double now = esp_now(); if (now - last_none >= 2.0) { last_none = now; - logf("%s: no hostile target within %.0f deg", tag, AIM_MAX_FOV); + logf("%s: no hostile target within %.0f deg", tag, max_fov); } return false; } - int best_idx = -1; - 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); - if (!ent || !bones_update(idx, ent)) + for (int ci = 0; ci < ncands; ci++) { + Vector pt; + float f; + if (!try_target(cands[ci].idx, true, pt, f)) continue; - Vector pts[32]; - int npoints = aim_points(idx, bone_mask, pts, 32); - float aim_fov = 1e9f; - Vector aim_pt{}; - for (int i = 0; i < npoints; i++) { - float f = getfov(view, angle_to(g_cam_eye, pts[i])); - if (f >= aim_fov) - continue; - if (vischeck && !is_visible(g_cam_eye, pts[i])) - continue; - aim_fov = f; - aim_pt = pts[i]; - } - if (aim_fov <= AIM_MAX_FOV && aim_fov < 1e8f) { - best_idx = idx; - best_point = aim_pt; - best_fov = aim_fov; - static int sel_logs = 0; - if (sel_logs < 5) { - sel_logs++; - logf("%s: target idx=%d points=%d fov=%.2f vischeck=%d", tag, idx, - npoints, aim_fov, (int)vischeck); - } + out_idx = cands[ci].idx; + out_point = pt; + out_fov = f; + if (lock_idx) + *lock_idx = out_idx; + static int sel_logs = 0; + if (sel_logs < 5) { + sel_logs++; + logf("%s: target idx=%d fov=%.2f vischeck=%d", tag, out_idx, f, + (int)vischeck); } + return true; } - if (best_idx < 0) { - static double last_vis = 0.0; - double now = esp_now(); - if (now - last_vis >= 2.0) { - last_vis = now; - logf("%s: no visible aim point (vischeck=%d)", tag, (int)vischeck); - } - return false; + + static double last_vis = 0.0; + double now = esp_now(); + if (now - last_vis >= 2.0) { + last_vis = now; + logf("%s: no visible aim point (vischeck=%d)", tag, (int)vischeck); } - out_idx = best_idx; - out_point = best_point; - out_fov = best_fov; - return true; + return false; } bool aimbot_apply(CUserCmd *cmd) { @@ -477,8 +570,12 @@ bool aimbot_apply(CUserCmd *cmd) { int target = -1; Vector point{}; float fov = 0.0f; - if (!select_aim_target(view, bone_mask, aimbot_vischeck_enabled(), "aimbot", - target, point, fov)) + float max_fov = (float)aimbot_fov(); + if (max_fov < 1.0f) max_fov = 1.0f; + if (max_fov > 180.0f) max_fov = 180.0f; + int *lock = feature_has_bind(FEAT_AIMBOT) ? &g_aim_lock : nullptr; + if (!select_aim_target(view, bone_mask, aimbot_vischeck_enabled(), max_fov, + "aimbot", lock, target, point, fov)) return false; QAngle desired = angle_to(g_cam_eye, point); static int want_logs = 0; @@ -548,8 +645,12 @@ bool silent_aim_apply(CUserCmd *cmd) { int target = -1; Vector point{}; float fov = 0.0f; - if (!select_aim_target(view, bone_mask, true, "silentaim", target, point, - fov)) + float max_fov = (float)silentaim_fov(); + if (max_fov < 1.0f) max_fov = 1.0f; + if (max_fov > 180.0f) max_fov = 180.0f; + int *lock = feature_has_bind(FEAT_SILENTAIM) ? &g_silent_lock : nullptr; + if (!select_aim_target(view, bone_mask, true, max_fov, "silentaim", lock, + target, point, fov)) return false; QAngle desired = angle_to(g_cam_eye, point); float np = desired.x; @@ -643,6 +744,7 @@ void esp_draw() { logf("ESP: screen %dx%d", g_scr_w, g_scr_h); if (!g_scr_w) return; } + draw_fov_circle(); esp_collect(engine_get_local_player()); for (int i = 0; i < g_ntargets; i++) { const EspTarget &t = g_targets[i]; diff --git a/src/hl2h.cpp b/src/hl2h.cpp index a49acc2..4947b18 100644 --- a/src/hl2h.cpp +++ b/src/hl2h.cpp @@ -260,6 +260,11 @@ static void do_move(int seq) { } } + if (!feature_active(FEAT_AIMBOT) || !feature_has_bind(FEAT_AIMBOT)) + aimbot_reset_lock(); + if (!feature_active(FEAT_SILENTAIM) || !feature_has_bind(FEAT_SILENTAIM)) + silentaim_reset_lock(); + if (feature_active(FEAT_AIMBOT)) { static bool aim_feature_logged = false; if (!aim_feature_logged) { diff --git a/src/hl2h.h b/src/hl2h.h index b86fe84..72cd21f 100644 --- a/src/hl2h.h +++ b/src/hl2h.h @@ -179,11 +179,18 @@ int server_model_index(void *base); void find_modules(); void init_conmsg(); bool feature_active(int feature); +bool feature_has_bind(int feature); +bool feature_bind_held(int feature); bool esp_option(int opt); bool bhop_autostrafe_enabled(); unsigned aimbot_bone_mask(); int aimbot_smooth_step(); bool aimbot_vischeck_enabled(); +int aimbot_fov(); +int silentaim_fov(); +bool aimbot_fov_circle(); +void aimbot_reset_lock(); +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); diff --git a/src/menu.cpp b/src/menu.cpp index 41d3321..49dd886 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -81,6 +81,9 @@ static bool g_aim_bones[AIM_BONE_COUNT] = {true, false, false, false, false, false}; static int g_aim_smooth = 5; // degrees per tick static bool g_aim_vischeck = true; +static int g_aim_fov = 60; // angular distance from the crosshair +static bool g_aim_fov_circle = true; +static int g_silent_fov = 60; // ESP sub-options (menu "ESP" children). Defaults match the pre-option // behaviour: everything on. @@ -118,14 +121,19 @@ static MenuNode g_aimbot_bone_nodes[AIM_BONE_COUNT] = { }; static MenuNode g_aimbot_smooth_node{NODE_VALUE, "Smoothness", false, 0, {}, nullptr, nullptr, nullptr, &g_aim_smooth, 1, 45, 1}; +static MenuNode g_aimbot_fov_node{NODE_VALUE, "FOV", false, 0, {}, nullptr, + nullptr, nullptr, &g_aim_fov, 1, 180, 1}; +static MenuNode g_aimbot_fov_circle_node{NODE_TOGGLE, "Show FOV circle", false, 0, {}, &g_aim_fov_circle, nullptr, nullptr}; 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_silentaim_fov_node{NODE_VALUE, "FOV", false, 0, {}, nullptr, + nullptr, nullptr, &g_silent_fov, 1, 180, 1}; -static MenuNode *g_visible[32]; // flattened visible nodes for arrow navigation +static MenuNode *g_visible[48]; // flattened visible nodes for arrow navigation static int g_visible_count = 0; static int g_selected = 0; bool g_menu_open = false; @@ -142,7 +150,9 @@ static void build_tree() { g_aimbot_feat.children[2] = &g_aimbot_vischeck_node; g_aimbot_feat.children[3] = &g_aimbot_bones_feat; g_aimbot_feat.children[4] = &g_aimbot_smooth_node; - g_aimbot_feat.child_count = 5; + g_aimbot_feat.children[5] = &g_aimbot_fov_node; + g_aimbot_feat.children[6] = &g_aimbot_fov_circle_node; + g_aimbot_feat.child_count = 7; for (int i = 0; i < AIM_BONE_COUNT; i++) g_aimbot_bones_feat.children[i] = &g_aimbot_bone_nodes[i]; g_aimbot_bones_feat.child_count = AIM_BONE_COUNT; @@ -151,7 +161,8 @@ static void build_tree() { 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; + g_silentaim_feat.children[2] = &g_silentaim_fov_node; + g_silentaim_feat.child_count = 3; 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; @@ -164,10 +175,10 @@ static void build_tree() { g_root.child_count = 6; } -static int g_depth[32]; // depth of each visible row (for indentation) +static int g_depth[48]; // depth of each visible row (for indentation) static void flatten(MenuNode *node, int depth) { - if (g_visible_count >= 32) + if (g_visible_count >= 48) return; node->type = (depth == 0) ? NODE_ROOT : (node->child_count > 0) ? NODE_FEATURE @@ -284,17 +295,18 @@ static void config_save() { g_bhop.bind, g_bhop_autostrafe); fprintf(f, "[abh]\nactive=%d\nbind=%d\n", g_abh.active, g_abh.bind); fprintf(f, "[aimbot]\nactive=%d\nbind=%d\nsmooth=%d\nvischeck=%d\n" + "fov=%d\nfov_circle=%d\n" "bone_head=%d\nbone_torso=%d\nbone_larm=%d\nbone_rarm=%d\n" "bone_lleg=%d\nbone_rleg=%d\n", g_aimbot.active, g_aimbot.bind, g_aim_smooth, - (int)g_aim_vischeck, + (int)g_aim_vischeck, g_aim_fov, (int)g_aim_fov_circle, g_aim_bones[AIM_BONE_HEAD], g_aim_bones[AIM_BONE_TORSO], g_aim_bones[AIM_BONE_LARM], g_aim_bones[AIM_BONE_RARM], 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, "[silentaim]\nactive=%d\nbind=%d\nfov=%d\n", g_silentaim.active, + g_silentaim.bind, g_silent_fov); 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], @@ -310,7 +322,7 @@ static void config_load() { return; char line[256]; FeatureState *cur = nullptr; - bool in_esp = false, in_bhop = false, in_aim = false; + bool in_esp = false, in_bhop = false, in_aim = false, in_silent = false; while (fgets(line, sizeof(line), f)) { line[strcspn(line, "\r\n")] = 0; if (line[0] == '[') { @@ -331,6 +343,7 @@ static void config_load() { in_esp = (cur == &g_esp); in_bhop = (cur == &g_bhop); in_aim = (cur == &g_aimbot); + in_silent = (cur == &g_silentaim); } else if (cur) { char *eq = strchr(line, '='); if (!eq) @@ -360,8 +373,21 @@ static void config_load() { g_aim_bones[AIM_BONE_RLEG] = atoi(val) != 0; } else if (strcmp(key, "vischeck") == 0) { g_aim_vischeck = atoi(val) != 0; + } else if (strcmp(key, "fov") == 0) { + int v = atoi(val); + if (v < 1) v = 1; + if (v > 180) v = 180; + g_aim_fov = v; + } else if (strcmp(key, "fov_circle") == 0) { + g_aim_fov_circle = atoi(val) != 0; } } + if (in_silent && strcmp(key, "fov") == 0) { + int v = atoi(val); + if (v < 1) v = 1; + if (v > 180) v = 180; + g_silent_fov = v; + } if (in_esp) { if (strcmp(key, "neutral") == 0) g_esp_opt[ESP_OPT_SHOW_NEUTRAL] = atoi(val) != 0; @@ -485,18 +511,32 @@ 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 *feature_state(int feature) { 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) + return nullptr; + return feats[feature]; +} + +bool feature_active(int feature) { + FeatureState *f = feature_state(feature); + if (!f || !f->active) return false; return f->bind == 0 || key_held(f->bind); } +bool feature_has_bind(int feature) { + FeatureState *f = feature_state(feature); + return f && f->bind != 0; +} + +bool feature_bind_held(int feature) { + FeatureState *f = feature_state(feature); + return f && f->bind != 0 && key_held(f->bind); +} + bool esp_option(int opt) { return opt >= 0 && opt < ESP_OPT_COUNT && g_esp_opt[opt]; } @@ -524,6 +564,12 @@ int aimbot_smooth_step() { return g_aim_smooth; } bool aimbot_vischeck_enabled() { return g_aim_vischeck; } +int aimbot_fov() { return g_aim_fov; } + +int silentaim_fov() { return g_silent_fov; } + +bool aimbot_fov_circle() { return g_aim_fov_circle; } + static void menu_handle_input() { poll_keyboard(); -- cgit v1.2.3