From ab3d725cacbe8cd7153c02faf2d9fe6df7e37949 Mon Sep 17 00:00:00 2001 From: salmonik Date: Mon, 26 Oct 2026 17:02:45 +0100 Subject: Aimbot v2: bone checklist and numeric smoothness - Aimbot -> Bones: Head, Torso (chest+stomach), Left/Right arm, Left/Right leg; the aimbot aims at the nearest selected hitbox point on the chosen target (head uses the head hitbox/bone fallback) - Aimbot -> Smoothness: value 1..45 deg/tick, Left/Right in the menu to adjust, persisted as smooth=N; default 5 (previous behaviour) - config: bone_* toggles + smooth under [aimbot] - menu gains a NODE_VALUE type (right-aligned value, Left/Right edit) --- src/bones.cpp | 32 ++++++++++++++++++ src/esp.cpp | 40 +++++++++++++++------- src/hl2h.h | 3 ++ src/menu.cpp | 107 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 164 insertions(+), 18 deletions(-) diff --git a/src/bones.cpp b/src/bones.cpp index abcaac6..12c7406 100644 --- a/src/bones.cpp +++ b/src/bones.cpp @@ -460,3 +460,35 @@ bool head_position(int idx, Vector &out) { } return false; } + +// Collect world-space aim points for the selected hit groups (HITGROUP_*). +// Group 1 (head) uses head_position for its bone fallback; other groups are +// the hitbox centers. The caller picks the point nearest to the crosshair. +int aim_points(int idx, unsigned group_mask, Vector *out, int max) { + if (!out || max <= 0) + return 0; + BoneEntry *e = find_entry(idx); + if (!e || !e->valid || !e->hdr) + return 0; + int n = 0; + if ((group_mask & (1u << 1)) && n < max) { + Vector head; + if (head_position(idx, head)) + out[n++] = head; + } + const HitboxSet *s = hitbox_set0(e->hdr); + if (!s) + return n; + const Hitbox *boxes = hitboxes(s); + for (int i = 0; i < s->numhitboxes && n < max; i++) { + int g = boxes[i].group; + if (g <= 1 || g > 7) + continue; + if (!(group_mask & (1u << g))) + continue; + Vector p; + if (hitbox_world(e, boxes[i], p)) + out[n++] = p; + } + return n; +} diff --git a/src/esp.cpp b/src/esp.cpp index 2987508..fbeb234 100644 --- a/src/esp.cpp +++ b/src/esp.cpp @@ -337,6 +337,9 @@ static QAngle angle_to(const Vector &from, const Vector &to) { bool aimbot_apply(CUserCmd *cmd) { 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; @@ -388,7 +391,8 @@ bool aimbot_apply(CUserCmd *cmd) { 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}; + 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 >= best_fov) continue; @@ -416,24 +420,33 @@ bool aimbot_apply(CUserCmd *cmd) { best_head.x, best_head.y, best_head.z); } - // Refine to the actual head hitbox/bone for the chosen target only. - bool exact_head = false; + // Refine to the nearest selected hitbox/bone point on the chosen target. + Vector pts[32]; + int npoints = 0; 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; - exact_head = true; + if (best_ent && bones_update(best_idx, best_ent)) + npoints = aim_points(best_idx, bone_mask, pts, 32); + if (npoints <= 0) + return false; + 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) { + aim_fov = f; + aim_pt = pts[i]; } } + if (aim_fov > MAX_FOV) + return false; + best_head = aim_pt; QAngle desired = angle_to(g_cam_eye, best_head); static int want_logs = 0; if (want_logs < 10) { want_logs++; - logf("aimbot: head=(%.1f %.1f %.1f) exact=%d want=%.2f/%.2f", - best_head.x, best_head.y, best_head.z, (int)exact_head, desired.x, + logf("aimbot: aim=(%.1f %.1f %.1f) points=%d fov=%.2f want=%.2f/%.2f", + best_head.x, best_head.y, best_head.z, npoints, aim_fov, desired.x, desired.y); } float dp = desired.x - cmd->viewangles[0]; @@ -442,12 +455,13 @@ bool aimbot_apply(CUserCmd *cmd) { while (dy < -180.0f) dy += 360.0f; constexpr float DEADZONE = 0.05f; - constexpr float MAX_STEP = 5.0f; + float max_step = (float)aimbot_smooth_step(); + if (max_step < 1.0f) max_step = 1.0f; float maxd = fmaxf(fabsf(dp), fabsf(dy)); if (maxd <= DEADZONE) return false; - float scale = (maxd > MAX_STEP) ? (MAX_STEP / maxd) : 1.0f; + float scale = (maxd > max_step) ? (max_step / maxd) : 1.0f; float old_pitch = cmd->viewangles[0]; float old_yaw = cmd->viewangles[1]; float np = cmd->viewangles[0] + dp * scale; diff --git a/src/hl2h.h b/src/hl2h.h index f92b919..e27433f 100644 --- a/src/hl2h.h +++ b/src/hl2h.h @@ -165,6 +165,8 @@ void init_conmsg(); bool feature_active(int feature); bool esp_option(int opt); bool bhop_autostrafe_enabled(); +unsigned aimbot_bone_mask(); +int aimbot_smooth_step(); 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); @@ -182,6 +184,7 @@ int bone_nearest_core_parent(int idx, int bone); bool bones_is_humanoid(int idx); bool hitbox_center(int idx, const char *hitbox_name, Vector &out); bool head_position(int idx, Vector &out); +int aim_points(int idx, unsigned group_mask, Vector *out, int max); void outer_draw(); // raw ISurface slot calls used by the PaintTraverseEx hook (hl2h.cpp) diff --git a/src/menu.cpp b/src/menu.cpp index 269174d..56f8c95 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -30,7 +30,7 @@ void init_conmsg() { // menu tree structure // --------------------------------------------------------------------------- -enum NodeType { NODE_ROOT, NODE_FEATURE, NODE_TOGGLE, NODE_BIND }; +enum NodeType { NODE_ROOT, NODE_FEATURE, NODE_TOGGLE, NODE_BIND, NODE_VALUE }; struct MenuNode { NodeType type; @@ -43,6 +43,10 @@ struct MenuNode { bool *active_ptr; // NODE_TOGGLE: pointer to the active flag int *bind_ptr; // NODE_BIND: pointer to the key binding (SDL scancode) const char *bind_name; // NODE_BIND: current bind display name + int *value_ptr = nullptr; // NODE_VALUE: pointer to the value + int value_min = 0; + int value_max = 0; + int value_step = 0; }; // feature data @@ -58,6 +62,20 @@ static FeatureState g_abh{true, 0}; static FeatureState g_esp{true, 0}; static FeatureState g_aimbot{true, 604}; // default Mouse5, rebindable in menu +// Aimbot bone checklist. Head defaults on (v1 behaviour). +enum { + AIM_BONE_HEAD = 0, + AIM_BONE_TORSO, + AIM_BONE_LARM, + AIM_BONE_RARM, + AIM_BONE_LLEG, + AIM_BONE_RLEG, + AIM_BONE_COUNT +}; +static bool g_aim_bones[AIM_BONE_COUNT] = {true, false, false, + false, false, false}; +static int g_aim_smooth = 5; // degrees per tick + // ESP sub-options (menu "ESP" children). Defaults match the pre-option // behaviour: everything on. static bool g_esp_opt[ESP_OPT_COUNT] = {true, true, true, true, true}; @@ -81,6 +99,17 @@ static MenuNode g_esp_opt_nodes[ESP_OPT_COUNT] = { 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_aimbot_bones_feat{NODE_FEATURE, "Bones", false, 0, {}, nullptr, nullptr, nullptr}; +static MenuNode g_aimbot_bone_nodes[AIM_BONE_COUNT] = { + {NODE_TOGGLE, "Head", false, 0, {}, &g_aim_bones[AIM_BONE_HEAD], nullptr, nullptr}, + {NODE_TOGGLE, "Torso", false, 0, {}, &g_aim_bones[AIM_BONE_TORSO], nullptr, nullptr}, + {NODE_TOGGLE, "Left arm", false, 0, {}, &g_aim_bones[AIM_BONE_LARM], nullptr, nullptr}, + {NODE_TOGGLE, "Right arm", false, 0, {}, &g_aim_bones[AIM_BONE_RARM], nullptr, nullptr}, + {NODE_TOGGLE, "Left leg", false, 0, {}, &g_aim_bones[AIM_BONE_LLEG], nullptr, nullptr}, + {NODE_TOGGLE, "Right leg", false, 0, {}, &g_aim_bones[AIM_BONE_RLEG], nullptr, nullptr}, +}; +static MenuNode g_aimbot_smooth_node{NODE_VALUE, "Smoothness", false, 0, {}, nullptr, + nullptr, nullptr, &g_aim_smooth, 1, 45, 1}; static MenuNode *g_visible[32]; // flattened visible nodes for arrow navigation static int g_visible_count = 0; @@ -96,7 +125,12 @@ static void build_tree() { 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; + g_aimbot_feat.children[2] = &g_aimbot_bones_feat; + g_aimbot_feat.children[3] = &g_aimbot_smooth_node; + g_aimbot_feat.child_count = 4; + 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; 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; @@ -115,6 +149,7 @@ static void flatten(MenuNode *node, int depth) { node->type = (depth == 0) ? NODE_ROOT : (node->child_count > 0) ? NODE_FEATURE : (node->active_ptr) ? NODE_TOGGLE + : (node->value_ptr) ? NODE_VALUE : NODE_BIND; g_depth[g_visible_count] = depth; g_visible[g_visible_count++] = node; @@ -167,8 +202,13 @@ 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, "[aimbot]\nactive=%d\nbind=%d\nsmooth=%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, + 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, "[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,7 +224,7 @@ static void config_load() { return; char line[256]; FeatureState *cur = nullptr; - bool in_esp = false, in_bhop = false; + bool in_esp = false, in_bhop = false, in_aim = false; while (fgets(line, sizeof(line), f)) { line[strcspn(line, "\r\n")] = 0; if (line[0] == '[') { @@ -200,6 +240,7 @@ static void config_load() { cur = nullptr; in_esp = (cur == &g_esp); in_bhop = (cur == &g_bhop); + in_aim = (cur == &g_aimbot); } else if (cur) { char *eq = strchr(line, '='); if (!eq) @@ -209,6 +250,26 @@ static void config_load() { if (in_bhop && strcmp(key, "autostrafe") == 0) { g_bhop_autostrafe = atoi(val) != 0; } + if (in_aim) { + if (strcmp(key, "smooth") == 0) { + int v = atoi(val); + if (v < 1) v = 1; + if (v > 45) v = 45; + g_aim_smooth = v; + } else if (strcmp(key, "bone_head") == 0) { + g_aim_bones[AIM_BONE_HEAD] = atoi(val) != 0; + } else if (strcmp(key, "bone_torso") == 0) { + g_aim_bones[AIM_BONE_TORSO] = atoi(val) != 0; + } else if (strcmp(key, "bone_larm") == 0) { + g_aim_bones[AIM_BONE_LARM] = atoi(val) != 0; + } else if (strcmp(key, "bone_rarm") == 0) { + g_aim_bones[AIM_BONE_RARM] = atoi(val) != 0; + } else if (strcmp(key, "bone_lleg") == 0) { + g_aim_bones[AIM_BONE_LLEG] = atoi(val) != 0; + } else if (strcmp(key, "bone_rleg") == 0) { + g_aim_bones[AIM_BONE_RLEG] = atoi(val) != 0; + } + } if (in_esp) { if (strcmp(key, "neutral") == 0) g_esp_opt[ESP_OPT_SHOW_NEUTRAL] = atoi(val) != 0; @@ -346,6 +407,25 @@ bool esp_option(int opt) { bool bhop_autostrafe_enabled() { return g_bhop_autostrafe; } +unsigned aimbot_bone_mask() { + unsigned m = 0; + if (g_aim_bones[AIM_BONE_HEAD]) + m |= 1u << 1; // HITGROUP_HEAD + if (g_aim_bones[AIM_BONE_TORSO]) + m |= (1u << 2) | (1u << 3); // chest + stomach + if (g_aim_bones[AIM_BONE_LARM]) + m |= 1u << 4; + if (g_aim_bones[AIM_BONE_RARM]) + m |= 1u << 5; + if (g_aim_bones[AIM_BONE_LLEG]) + m |= 1u << 6; + if (g_aim_bones[AIM_BONE_RLEG]) + m |= 1u << 7; + return m; +} + +int aimbot_smooth_step() { return g_aim_smooth; } + static void menu_handle_input() { poll_keyboard(); @@ -415,6 +495,19 @@ static void menu_handle_input() { g_selected++; return; } + if (key_pressed(SCAN_LEFT) || key_pressed(SCAN_RIGHT)) { + MenuNode *n = g_visible[g_selected]; + if (n->type == NODE_VALUE && n->value_ptr) { + int dir = key_pressed(SCAN_RIGHT) ? 1 : -1; + int step = n->value_step ? n->value_step : 1; + int v = *n->value_ptr + dir * step; + if (v < n->value_min) v = n->value_min; + if (v > n->value_max) v = n->value_max; + *n->value_ptr = v; + config_save(); + } + return; + } if (key_pressed(SCAN_ENTER)) { MenuNode *n = g_visible[g_selected]; if (n->child_count > 0) { @@ -599,6 +692,10 @@ void outer_draw() { snprintf(bn[fi], 32, "[%s]", scancode_name(*n->bind_ptr)); draw_text(270, 10 + i * 20, bn[fi]); } + } else if (n->type == NODE_VALUE) { + char vb[32]; + snprintf(vb, sizeof(vb), "%d deg/tick", *n->value_ptr); + draw_text(270, 10 + i * 20, vb); } } surf_pop_make_current(panel); -- cgit v1.2.3