From ccdcd4ad0483b98c9c20bd1304ad062ef8af3a4b Mon Sep 17 00:00:00 2001 From: salmonik Date: Mon, 26 Oct 2026 16:39:11 +0100 Subject: Aimbot: head-only nearest-to-crosshair with bounded smoothing - select the hostile NPC whose (approximate) head is closest in angle to the crosshair, then refine to its actual head hitbox/bone - aimbot menu feature (Active + Bind, default Mouse5, rebindable via the existing bind chooser); config [aimbot] active/bind - never snap: move at most 5 degrees per tick toward the target, shortest-path yaw, pitch clamped to +/-89, 0.05 degree deadzone; no overshoot/oscillation - fixes the usercmd CRC after modifying viewangles - skip when dead; ignores friendly/neutral entities (crows, citizens, ...) --- src/esp.cpp | 117 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/hl2h.cpp | 7 ++++ src/hl2h.h | 4 +- src/menu.cpp | 26 +++++++++---- 4 files changed, 146 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/esp.cpp b/src/esp.cpp index 34a702a..1cb8b70 100644 --- a/src/esp.cpp +++ b/src/esp.cpp @@ -316,6 +316,123 @@ static void draw_skeleton(const EspTarget &t) { } } +// --------------------------------------------------------------------------- +// aimbot v1: nearest hostile NPC head to the crosshair. +// Bounded smoothing: never snap, never overshoot. Each tick moves at most +// MAX_STEP degrees toward the target and stops inside DEADZONE. +// --------------------------------------------------------------------------- + +static QAngle angle_to(const Vector &from, const Vector &to) { + float dx = to.x - from.x, dy = to.y - from.y, dz = to.z - from.z; + float horiz = sqrtf(dx * dx + dy * dy); + if (horiz < 0.001f) + horiz = 0.001f; + QAngle a{}; + a.x = -atan2f(dz, horiz) * 180.0f / PI_F; // Source: positive pitch = down + a.y = atan2f(dy, dx) * 180.0f / PI_F; + a.z = 0.0f; + return a; +} + +bool aimbot_apply(CUserCmd *cmd) { + if (!g_iface.entitylist || g_net.origin < 0 || g_net.maxs < 0) + return false; + int local = engine_get_local_player(); + if (local <= 0) + return false; + void *local_ent = entitylist_get(local); + if (!local_ent) + return false; + if (g_net.health >= 0 && + *(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 + float best_fov = MAX_FOV; + int best_idx = -1; + Vector best_head{}; + + for (int idx = 1; idx <= hi; idx++) { + if (idx == local) + continue; + void *ent = entitylist_get(idx); + if (!ent) + continue; + void *net = entity_networkable(idx); + if (!net) + continue; + const char *cls = entity_class_name(net, nullptr); + if (!cls || !is_npc_class(cls)) + continue; + + uintptr_t e = (uintptr_t)ent; + int life = g_net.lifestate >= 0 + ? (int)*(unsigned char *)(e + g_net.lifestate) + : -1; + if (life != LIFE_ALIVE) + continue; + const char *model = entity_model_name(ent); + if (model_is_friendly(model) || model_is_neutral(model)) + continue; + + float maxz = g_net.maxs >= 0 ? *(float *)(e + g_net.maxs + 8) : 72.0f; + if (!(maxz > 4.0f && maxz < 512.0f)) + maxz = 72.0f; + Vector origin{*(float *)(e + g_net.origin), + *(float *)(e + g_net.origin + 4), + *(float *)(e + g_net.origin + 8)}; + Vector approx{origin.x, origin.y, origin.z + maxz * 0.9f}; + float fov = getfov(view, angle_to(g_cam_eye, approx)); + if (fov >= best_fov) + continue; + best_fov = fov; + best_idx = idx; + best_head = approx; + } + + if (best_idx < 0) + return false; + + // Refine to the actual head hitbox/bone for the chosen target only. + void *best_ent = entitylist_get(best_idx); + if (best_ent && bones_update(best_idx, best_ent)) { + Vector head; + if (head_position(best_idx, head) && + getfov(view, angle_to(g_cam_eye, head)) <= MAX_FOV) + best_head = head; + } + + QAngle desired = angle_to(g_cam_eye, best_head); + float dp = desired.x - cmd->viewangles[0]; + float dy = desired.y - cmd->viewangles[1]; + while (dy > 180.0f) dy -= 360.0f; + while (dy < -180.0f) dy += 360.0f; + + constexpr float DEADZONE = 0.05f; + constexpr float MAX_STEP = 5.0f; + float maxd = fmaxf(fabsf(dp), fabsf(dy)); + if (maxd <= DEADZONE) + return false; + + float scale = (maxd > MAX_STEP) ? (MAX_STEP / maxd) : 1.0f; + float np = cmd->viewangles[0] + dp * scale; + float ny = cmd->viewangles[1] + dy * scale; + if (np > 89.0f) np = 89.0f; + if (np < -89.0f) np = -89.0f; + while (ny > 180.0f) ny -= 360.0f; + while (ny < -180.0f) ny += 360.0f; + cmd->viewangles[0] = np; + cmd->viewangles[1] = ny; + return true; +} + void esp_draw() { if (!g_surface || !g_iface.entitylist || !g_net.esp_ok()) return; if (!g_scr_w) { diff --git a/src/hl2h.cpp b/src/hl2h.cpp index b666585..21fa7ae 100644 --- a/src/hl2h.cpp +++ b/src/hl2h.cpp @@ -241,6 +241,13 @@ static void do_move(int seq) { note_modified(cmd, flags, vel); } } + + if (feature_active(FEAT_AIMBOT)) { + if (aimbot_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 289e4e6..f92b919 100644 --- a/src/hl2h.h +++ b/src/hl2h.h @@ -135,7 +135,9 @@ struct QAngle { float x, y, z; }; extern Vector g_cam_eye; extern Vector g_cam_view; QAngle vector_angles(const Vector &forward); -enum FeatureId { FEAT_BHOP = 0, FEAT_ABH = 1, FEAT_ESP = 2 }; +float getfov(const QAngle &my_view, const QAngle &aim_ang); +bool aimbot_apply(CUserCmd *cmd); +enum FeatureId { FEAT_BHOP = 0, FEAT_ABH = 1, FEAT_ESP = 2, FEAT_AIMBOT = 3 }; enum EspOption { ESP_OPT_SHOW_NEUTRAL = 0, ESP_OPT_SHOW_HEALTHBAR = 1, diff --git a/src/menu.cpp b/src/menu.cpp index 6474d69..269174d 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -56,6 +56,7 @@ static bool g_bhop_autostrafe = true; static FeatureState g_abh{true, 0}; static FeatureState g_esp{true, 0}; +static FeatureState g_aimbot{true, 604}; // default Mouse5, rebindable in menu // ESP sub-options (menu "ESP" children). Defaults match the pre-option // behaviour: everything on. @@ -77,6 +78,9 @@ static MenuNode g_esp_opt_nodes[ESP_OPT_COUNT] = { {NODE_TOGGLE, "Show name", false, 0, {}, &g_esp_opt[ESP_OPT_SHOW_NAME], nullptr, nullptr}, {NODE_TOGGLE, "Show skeleton", false, 0, {}, &g_esp_opt[ESP_OPT_SHOW_SKELETON], nullptr, nullptr}, }; +static MenuNode g_aimbot_feat{NODE_FEATURE, "Aimbot", false, 1, {}, nullptr, nullptr, nullptr}; +static MenuNode g_aimbot_active{NODE_TOGGLE, "Active", false, 0, {}, &g_aimbot.active, nullptr, nullptr}; +static MenuNode g_aimbot_bind{NODE_BIND, "Bind", false, 0, {}, nullptr, &g_aimbot.bind, nullptr}; static MenuNode *g_visible[32]; // flattened visible nodes for arrow navigation static int g_visible_count = 0; @@ -90,13 +94,17 @@ static void build_tree() { g_bhop_feat.child_count = 2; g_abh_feat.children[0] = &g_abh_active; g_esp_feat.children[0] = &g_esp_active; + g_aimbot_feat.children[0] = &g_aimbot_active; + g_aimbot_feat.children[1] = &g_aimbot_bind; + g_aimbot_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; g_root.children[0] = &g_bhop_feat; g_root.children[1] = &g_abh_feat; g_root.children[2] = &g_esp_feat; - g_root.child_count = 3; + g_root.children[3] = &g_aimbot_feat; + g_root.child_count = 4; } static int g_depth[32]; // depth of each visible row (for indentation) @@ -159,6 +167,8 @@ static void config_save() { fprintf(f, "[bhop]\nactive=%d\nbind=%d\nautostrafe=%d\n", g_bhop.active, 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\n", g_aimbot.active, + g_aimbot.bind); fprintf(f, "[esp]\nactive=%d\nbind=%d\nneutral=%d\nhealthbar=%d\n" "healthnum=%d\nname=%d\nskeleton=%d\n", g_esp.active, g_esp.bind, g_esp_opt[ESP_OPT_SHOW_NEUTRAL], @@ -184,6 +194,8 @@ static void config_load() { cur = &g_abh; else if (strstr(line, "[esp]")) cur = &g_esp; + else if (strstr(line, "[aimbot]")) + cur = &g_aimbot; else cur = nullptr; in_esp = (cur == &g_esp); @@ -319,8 +331,8 @@ 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[3] = {&g_bhop, &g_abh, &g_esp}; - if (feature < 0 || feature > 2) + static FeatureState *feats[4] = {&g_bhop, &g_abh, &g_esp, &g_aimbot}; + if (feature < 0 || feature > 3) return false; FeatureState *f = feats[feature]; if (!f->active) @@ -376,7 +388,7 @@ 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}; + FeatureState *features[] = {&g_bhop, &g_abh, &g_aimbot}; for (auto *f : features) if (&f->bind != n->bind_ptr && f->bind == i) f->bind = 0; @@ -578,10 +590,10 @@ void outer_draw() { draw_text(270, 10 + i * 20, "[press key]"); else { /* stable name for current bind */ - static char bn[2][32]; - static FeatureState *feats[] = {&g_bhop, &g_abh}; + static char bn[3][32]; + static FeatureState *feats[] = {&g_bhop, &g_abh, &g_aimbot}; int fi = 0; - for (int k = 0; k < 2; k++) + for (int k = 0; k < 3; k++) if (n->bind_ptr == &feats[k]->bind) fi = k; snprintf(bn[fi], 32, "[%s]", scancode_name(*n->bind_ptr)); -- cgit v1.2.3