summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-10-27 12:43:59 +0100
committersalmonik <salmonik@tuta.io>2026-10-27 12:43:59 +0100
commit5adb4303c8ac4969312fa1afd4f254cba65f6ecc (patch)
tree5a82f08cef4ae7b9d344826c7b96710eb46db101
parentBones: ignore uninitialized/zero bone matrices (diff)
downloadhl2h-5adb4303c8ac4969312fa1afd4f254cba65f6ecc.tar.gz
hl2h-5adb4303c8ac4969312fa1afd4f254cba65f6ecc.zip
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.
Diffstat (limited to '')
-rw-r--r--src/esp.cpp204
-rw-r--r--src/hl2h.cpp5
-rw-r--r--src/hl2h.h7
-rw-r--r--src/menu.cpp72
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) {
274 vfunc<Fn>(g_surface, SURF_DrawLine)(g_surface, x0, y0, x1, y1); 274 vfunc<Fn>(g_surface, SURF_DrawLine)(g_surface, x0, y0, x1, y1);
275} 275}
276 276
277// Screen projection of the aimbot FOV cone boundary, using the engine's own
278// world->screen transform so it follows the actual aspect/fov.
279static void draw_fov_circle() {
280 if (!aimbot_fov_circle())
281 return;
282 float fov = (float)aimbot_fov();
283 if (fov < 1.0f || fov >= 89.0f) // beyond this the "circle" is behind the eye
284 return;
285 float alpha = fov * PI_F / 180.0f;
286 float yaw = g_cam_view.y * PI_F / 180.0f;
287 float pitch = g_cam_view.x * PI_F / 180.0f;
288 Vector fwd{cosf(pitch) * cosf(yaw), cosf(pitch) * sinf(yaw), -sinf(pitch)};
289 Vector right{sinf(yaw), -cosf(yaw), 0.0f};
290 Vector up{right.y * fwd.z - right.z * fwd.y,
291 right.z * fwd.x - right.x * fwd.z,
292 right.x * fwd.y - right.y * fwd.x};
293 constexpr int N = 48;
294 float xs[N], ys[N];
295 bool ok[N];
296 float ca = cosf(alpha), sa = sinf(alpha);
297 for (int i = 0; i < N; i++) {
298 float phi = (2.0f * PI_F * i) / N;
299 float cp = cosf(phi), sp = sinf(phi);
300 Vector d{fwd.x * ca + (right.x * cp + up.x * sp) * sa,
301 fwd.y * ca + (right.y * cp + up.y * sp) * sa,
302 fwd.z * ca + (right.z * cp + up.z * sp) * sa};
303 Vector p{g_cam_eye.x + d.x * 1024.0f, g_cam_eye.y + d.y * 1024.0f,
304 g_cam_eye.z + d.z * 1024.0f};
305 ok[i] = world_to_screen(p, xs[i], ys[i]) && std::isfinite(xs[i]) &&
306 std::isfinite(ys[i]);
307 }
308 surf_color(255, 255, 255, 160);
309 for (int i = 0; i < N; i++) {
310 int j = (i + 1) % N;
311 if (ok[i] && ok[j])
312 surf_line((int)xs[i], (int)ys[i], (int)xs[j], (int)ys[j]);
313 }
314}
315
277// Draw the parent->child bone links. Bone snapshots are world-space matrices 316// Draw the parent->child bone links. Bone snapshots are world-space matrices
278// from SetupBones; project each bone once and reuse for both endpoints. 317// from SetupBones; project each bone once and reuse for both endpoints.
279static void draw_skeleton(const EspTarget &t) { 318static void draw_skeleton(const EspTarget &t) {
@@ -332,11 +371,20 @@ static QAngle angle_to(const Vector &from, const Vector &to) {
332 return a; 371 return a;
333} 372}
334 373
335static constexpr float AIM_MAX_FOV = 60.0f; 374static int g_aim_lock = -1;
375static int g_silent_lock = -1;
336 376
377void aimbot_reset_lock() { g_aim_lock = -1; }
378void silentaim_reset_lock() { g_silent_lock = -1; }
379
380// max_fov is the angular radius around the crosshair. If lock_idx is non-null
381// and valid, that target is kept while it is alive and visible (the "do not
382// switch while the bind is held" rule); a fresh acquisition is ranked by angle
383// and the nearest visible selected hitbox point.
337static bool select_aim_target(const QAngle &view, unsigned bone_mask, 384static bool select_aim_target(const QAngle &view, unsigned bone_mask,
338 bool vischeck, const char *tag, int &out_idx, 385 bool vischeck, float max_fov, const char *tag,
339 Vector &out_point, float &out_fov) { 386 int *lock_idx, int &out_idx, Vector &out_point,
387 float &out_fov) {
340 out_idx = -1; 388 out_idx = -1;
341 out_point = {}; 389 out_point = {};
342 out_fov = 0.0f; 390 out_fov = 0.0f;
@@ -352,6 +400,71 @@ static bool select_aim_target(const QAngle &view, unsigned bone_mask,
352 *(int *)((uintptr_t)local_ent + g_net.health) <= 0) 400 *(int *)((uintptr_t)local_ent + g_net.health) <= 0)
353 return false; 401 return false;
354 402
403 auto try_target = [&](int idx, bool enforce_fov, Vector &pt,
404 float &fov) -> bool {
405 if (idx <= 0 || idx == local)
406 return false;
407 void *ent = entitylist_get(idx);
408 if (!ent)
409 return false;
410 void *net = entity_networkable(idx);
411 if (!net)
412 return false;
413 const char *cls = entity_class_name(net, nullptr);
414 if (!cls || !is_npc_class(cls))
415 return false;
416 uintptr_t e = (uintptr_t)ent;
417 int life = g_net.lifestate >= 0
418 ? (int)*(unsigned char *)(e + g_net.lifestate)
419 : -1;
420 if (life != LIFE_ALIVE)
421 return false;
422 const char *model = entity_model_name(ent);
423 if (model_is_friendly(model) || model_is_neutral(model))
424 return false;
425 if (!bones_update(idx, ent))
426 return false;
427 Vector pts[32];
428 int npoints = aim_points(idx, bone_mask, pts, 32);
429 float best = 1e9f;
430 Vector best_pt{};
431 for (int i = 0; i < npoints; i++) {
432 float f = getfov(view, angle_to(g_cam_eye, pts[i]));
433 if (enforce_fov && f > max_fov)
434 continue;
435 if (f >= best)
436 continue;
437 if (vischeck && !is_visible(g_cam_eye, pts[i]))
438 continue;
439 best = f;
440 best_pt = pts[i];
441 }
442 if (best > 1e8f)
443 return false;
444 pt = best_pt;
445 fov = best;
446 return true;
447 };
448
449 // Keep the locked target while it is alive and has a visible selected point.
450 // No FOV limit on a locked target; only acquisition is FOV-limited.
451 if (lock_idx && *lock_idx >= 0) {
452 Vector pt;
453 float f;
454 if (try_target(*lock_idx, false, pt, f)) {
455 out_idx = *lock_idx;
456 out_point = pt;
457 out_fov = f;
458 static int lock_logs = 0;
459 if (lock_logs < 5) {
460 lock_logs++;
461 logf("%s: lock idx=%d fov=%.2f", tag, out_idx, f);
462 }
463 return true;
464 }
465 *lock_idx = -1;
466 }
467
355 using Fn = int (*)(void *); 468 using Fn = int (*)(void *);
356 int hi = vfunc<Fn>(g_iface.entitylist, 6)(g_iface.entitylist); 469 int hi = vfunc<Fn>(g_iface.entitylist, 6)(g_iface.entitylist);
357 if (hi < 0 || hi > 4096) 470 if (hi < 0 || hi > 4096)
@@ -397,7 +510,7 @@ static bool select_aim_target(const QAngle &view, unsigned bone_mask,
397 Vector approx{origin.x, origin.y, 510 Vector approx{origin.x, origin.y,
398 origin.z + maxz * ((bone_mask & (1u << 1)) ? 0.9f : 0.55f)}; 511 origin.z + maxz * ((bone_mask & (1u << 1)) ? 0.9f : 0.55f)};
399 float fov = getfov(view, angle_to(g_cam_eye, approx)); 512 float fov = getfov(view, angle_to(g_cam_eye, approx));
400 if (fov >= AIM_MAX_FOV) 513 if (fov > max_fov)
401 continue; 514 continue;
402 515
403 int pos = ncands; 516 int pos = ncands;
@@ -416,57 +529,37 @@ static bool select_aim_target(const QAngle &view, unsigned bone_mask,
416 double now = esp_now(); 529 double now = esp_now();
417 if (now - last_none >= 2.0) { 530 if (now - last_none >= 2.0) {
418 last_none = now; 531 last_none = now;
419 logf("%s: no hostile target within %.0f deg", tag, AIM_MAX_FOV); 532 logf("%s: no hostile target within %.0f deg", tag, max_fov);
420 } 533 }
421 return false; 534 return false;
422 } 535 }
423 536
424 int best_idx = -1; 537 for (int ci = 0; ci < ncands; ci++) {
425 Vector best_point{}; 538 Vector pt;
426 float best_fov = 0.0f; 539 float f;
427 for (int ci = 0; ci < ncands && best_idx < 0; ci++) { 540 if (!try_target(cands[ci].idx, true, pt, f))
428 int idx = cands[ci].idx;
429 void *ent = entitylist_get(idx);
430 if (!ent || !bones_update(idx, ent))
431 continue; 541 continue;
432 Vector pts[32]; 542 out_idx = cands[ci].idx;
433 int npoints = aim_points(idx, bone_mask, pts, 32); 543 out_point = pt;
434 float aim_fov = 1e9f; 544 out_fov = f;
435 Vector aim_pt{}; 545 if (lock_idx)
436 for (int i = 0; i < npoints; i++) { 546 *lock_idx = out_idx;
437 float f = getfov(view, angle_to(g_cam_eye, pts[i])); 547 static int sel_logs = 0;
438 if (f >= aim_fov) 548 if (sel_logs < 5) {
439 continue; 549 sel_logs++;
440 if (vischeck && !is_visible(g_cam_eye, pts[i])) 550 logf("%s: target idx=%d fov=%.2f vischeck=%d", tag, out_idx, f,
441 continue; 551 (int)vischeck);
442 aim_fov = f;
443 aim_pt = pts[i];
444 }
445 if (aim_fov <= AIM_MAX_FOV && aim_fov < 1e8f) {
446 best_idx = idx;
447 best_point = aim_pt;
448 best_fov = aim_fov;
449 static int sel_logs = 0;
450 if (sel_logs < 5) {
451 sel_logs++;
452 logf("%s: target idx=%d points=%d fov=%.2f vischeck=%d", tag, idx,
453 npoints, aim_fov, (int)vischeck);
454 }
455 } 552 }
553 return true;
456 } 554 }
457 if (best_idx < 0) { 555
458 static double last_vis = 0.0; 556 static double last_vis = 0.0;
459 double now = esp_now(); 557 double now = esp_now();
460 if (now - last_vis >= 2.0) { 558 if (now - last_vis >= 2.0) {
461 last_vis = now; 559 last_vis = now;
462 logf("%s: no visible aim point (vischeck=%d)", tag, (int)vischeck); 560 logf("%s: no visible aim point (vischeck=%d)", tag, (int)vischeck);
463 }
464 return false;
465 } 561 }
466 out_idx = best_idx; 562 return false;
467 out_point = best_point;
468 out_fov = best_fov;
469 return true;
470} 563}
471 564
472bool aimbot_apply(CUserCmd *cmd) { 565bool aimbot_apply(CUserCmd *cmd) {
@@ -477,8 +570,12 @@ bool aimbot_apply(CUserCmd *cmd) {
477 int target = -1; 570 int target = -1;
478 Vector point{}; 571 Vector point{};
479 float fov = 0.0f; 572 float fov = 0.0f;
480 if (!select_aim_target(view, bone_mask, aimbot_vischeck_enabled(), "aimbot", 573 float max_fov = (float)aimbot_fov();
481 target, point, fov)) 574 if (max_fov < 1.0f) max_fov = 1.0f;
575 if (max_fov > 180.0f) max_fov = 180.0f;
576 int *lock = feature_has_bind(FEAT_AIMBOT) ? &g_aim_lock : nullptr;
577 if (!select_aim_target(view, bone_mask, aimbot_vischeck_enabled(), max_fov,
578 "aimbot", lock, target, point, fov))
482 return false; 579 return false;
483 QAngle desired = angle_to(g_cam_eye, point); 580 QAngle desired = angle_to(g_cam_eye, point);
484 static int want_logs = 0; 581 static int want_logs = 0;
@@ -548,8 +645,12 @@ bool silent_aim_apply(CUserCmd *cmd) {
548 int target = -1; 645 int target = -1;
549 Vector point{}; 646 Vector point{};
550 float fov = 0.0f; 647 float fov = 0.0f;
551 if (!select_aim_target(view, bone_mask, true, "silentaim", target, point, 648 float max_fov = (float)silentaim_fov();
552 fov)) 649 if (max_fov < 1.0f) max_fov = 1.0f;
650 if (max_fov > 180.0f) max_fov = 180.0f;
651 int *lock = feature_has_bind(FEAT_SILENTAIM) ? &g_silent_lock : nullptr;
652 if (!select_aim_target(view, bone_mask, true, max_fov, "silentaim", lock,
653 target, point, fov))
553 return false; 654 return false;
554 QAngle desired = angle_to(g_cam_eye, point); 655 QAngle desired = angle_to(g_cam_eye, point);
555 float np = desired.x; 656 float np = desired.x;
@@ -643,6 +744,7 @@ void esp_draw() {
643 logf("ESP: screen %dx%d", g_scr_w, g_scr_h); 744 logf("ESP: screen %dx%d", g_scr_w, g_scr_h);
644 if (!g_scr_w) return; 745 if (!g_scr_w) return;
645 } 746 }
747 draw_fov_circle();
646 esp_collect(engine_get_local_player()); 748 esp_collect(engine_get_local_player());
647 for (int i = 0; i < g_ntargets; i++) { 749 for (int i = 0; i < g_ntargets; i++) {
648 const EspTarget &t = g_targets[i]; 750 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) {
260 } 260 }
261 } 261 }
262 262
263 if (!feature_active(FEAT_AIMBOT) || !feature_has_bind(FEAT_AIMBOT))
264 aimbot_reset_lock();
265 if (!feature_active(FEAT_SILENTAIM) || !feature_has_bind(FEAT_SILENTAIM))
266 silentaim_reset_lock();
267
263 if (feature_active(FEAT_AIMBOT)) { 268 if (feature_active(FEAT_AIMBOT)) {
264 static bool aim_feature_logged = false; 269 static bool aim_feature_logged = false;
265 if (!aim_feature_logged) { 270 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);
179void find_modules(); 179void find_modules();
180void init_conmsg(); 180void init_conmsg();
181bool feature_active(int feature); 181bool feature_active(int feature);
182bool feature_has_bind(int feature);
183bool feature_bind_held(int feature);
182bool esp_option(int opt); 184bool esp_option(int opt);
183bool bhop_autostrafe_enabled(); 185bool bhop_autostrafe_enabled();
184unsigned aimbot_bone_mask(); 186unsigned aimbot_bone_mask();
185int aimbot_smooth_step(); 187int aimbot_smooth_step();
186bool aimbot_vischeck_enabled(); 188bool aimbot_vischeck_enabled();
189int aimbot_fov();
190int silentaim_fov();
191bool aimbot_fov_circle();
192void aimbot_reset_lock();
193void silentaim_reset_lock();
187void *entitylist_get(int idx); 194void *entitylist_get(int idx);
188int engine_get_local_player(); 195int engine_get_local_player();
189void draw_text_col(int x, int y, const char *s, int r, int g, int b); 196void 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,
81 false, false, false}; 81 false, false, false};
82static int g_aim_smooth = 5; // degrees per tick 82static int g_aim_smooth = 5; // degrees per tick
83static bool g_aim_vischeck = true; 83static bool g_aim_vischeck = true;
84static int g_aim_fov = 60; // angular distance from the crosshair
85static bool g_aim_fov_circle = true;
86static int g_silent_fov = 60;
84 87
85// ESP sub-options (menu "ESP" children). Defaults match the pre-option 88// ESP sub-options (menu "ESP" children). Defaults match the pre-option
86// behaviour: everything on. 89// behaviour: everything on.
@@ -118,14 +121,19 @@ static MenuNode g_aimbot_bone_nodes[AIM_BONE_COUNT] = {
118}; 121};
119static MenuNode g_aimbot_smooth_node{NODE_VALUE, "Smoothness", false, 0, {}, nullptr, 122static MenuNode g_aimbot_smooth_node{NODE_VALUE, "Smoothness", false, 0, {}, nullptr,
120 nullptr, nullptr, &g_aim_smooth, 1, 45, 1}; 123 nullptr, nullptr, &g_aim_smooth, 1, 45, 1};
124static MenuNode g_aimbot_fov_node{NODE_VALUE, "FOV", false, 0, {}, nullptr,
125 nullptr, nullptr, &g_aim_fov, 1, 180, 1};
126static MenuNode g_aimbot_fov_circle_node{NODE_TOGGLE, "Show FOV circle", false, 0, {}, &g_aim_fov_circle, nullptr, nullptr};
121static MenuNode g_triggerbot_feat{NODE_FEATURE, "Triggerbot", false, 1, {}, nullptr, nullptr, nullptr}; 127static MenuNode g_triggerbot_feat{NODE_FEATURE, "Triggerbot", false, 1, {}, nullptr, nullptr, nullptr};
122static MenuNode g_triggerbot_active{NODE_TOGGLE, "Active", false, 0, {}, &g_triggerbot.active, nullptr, nullptr}; 128static MenuNode g_triggerbot_active{NODE_TOGGLE, "Active", false, 0, {}, &g_triggerbot.active, nullptr, nullptr};
123static MenuNode g_triggerbot_bind{NODE_BIND, "Bind", false, 0, {}, nullptr, &g_triggerbot.bind, nullptr}; 129static MenuNode g_triggerbot_bind{NODE_BIND, "Bind", false, 0, {}, nullptr, &g_triggerbot.bind, nullptr};
124static MenuNode g_silentaim_feat{NODE_FEATURE, "Silent aim", false, 1, {}, nullptr, nullptr, nullptr}; 130static MenuNode g_silentaim_feat{NODE_FEATURE, "Silent aim", false, 1, {}, nullptr, nullptr, nullptr};
125static MenuNode g_silentaim_active{NODE_TOGGLE, "Active", false, 0, {}, &g_silentaim.active, nullptr, nullptr}; 131static MenuNode g_silentaim_active{NODE_TOGGLE, "Active", false, 0, {}, &g_silentaim.active, nullptr, nullptr};
126static MenuNode g_silentaim_bind{NODE_BIND, "Bind", false, 0, {}, nullptr, &g_silentaim.bind, nullptr}; 132static MenuNode g_silentaim_bind{NODE_BIND, "Bind", false, 0, {}, nullptr, &g_silentaim.bind, nullptr};
133static MenuNode g_silentaim_fov_node{NODE_VALUE, "FOV", false, 0, {}, nullptr,
134 nullptr, nullptr, &g_silent_fov, 1, 180, 1};
127 135
128static MenuNode *g_visible[32]; // flattened visible nodes for arrow navigation 136static MenuNode *g_visible[48]; // flattened visible nodes for arrow navigation
129static int g_visible_count = 0; 137static int g_visible_count = 0;
130static int g_selected = 0; 138static int g_selected = 0;
131bool g_menu_open = false; 139bool g_menu_open = false;
@@ -142,7 +150,9 @@ static void build_tree() {
142 g_aimbot_feat.children[2] = &g_aimbot_vischeck_node; 150 g_aimbot_feat.children[2] = &g_aimbot_vischeck_node;
143 g_aimbot_feat.children[3] = &g_aimbot_bones_feat; 151 g_aimbot_feat.children[3] = &g_aimbot_bones_feat;
144 g_aimbot_feat.children[4] = &g_aimbot_smooth_node; 152 g_aimbot_feat.children[4] = &g_aimbot_smooth_node;
145 g_aimbot_feat.child_count = 5; 153 g_aimbot_feat.children[5] = &g_aimbot_fov_node;
154 g_aimbot_feat.children[6] = &g_aimbot_fov_circle_node;
155 g_aimbot_feat.child_count = 7;
146 for (int i = 0; i < AIM_BONE_COUNT; i++) 156 for (int i = 0; i < AIM_BONE_COUNT; i++)
147 g_aimbot_bones_feat.children[i] = &g_aimbot_bone_nodes[i]; 157 g_aimbot_bones_feat.children[i] = &g_aimbot_bone_nodes[i];
148 g_aimbot_bones_feat.child_count = AIM_BONE_COUNT; 158 g_aimbot_bones_feat.child_count = AIM_BONE_COUNT;
@@ -151,7 +161,8 @@ static void build_tree() {
151 g_triggerbot_feat.child_count = 2; 161 g_triggerbot_feat.child_count = 2;
152 g_silentaim_feat.children[0] = &g_silentaim_active; 162 g_silentaim_feat.children[0] = &g_silentaim_active;
153 g_silentaim_feat.children[1] = &g_silentaim_bind; 163 g_silentaim_feat.children[1] = &g_silentaim_bind;
154 g_silentaim_feat.child_count = 2; 164 g_silentaim_feat.children[2] = &g_silentaim_fov_node;
165 g_silentaim_feat.child_count = 3;
155 for (int i = 0; i < ESP_OPT_COUNT; i++) 166 for (int i = 0; i < ESP_OPT_COUNT; i++)
156 g_esp_feat.children[i + 1] = &g_esp_opt_nodes[i]; 167 g_esp_feat.children[i + 1] = &g_esp_opt_nodes[i];
157 g_esp_feat.child_count = ESP_OPT_COUNT + 1; 168 g_esp_feat.child_count = ESP_OPT_COUNT + 1;
@@ -164,10 +175,10 @@ static void build_tree() {
164 g_root.child_count = 6; 175 g_root.child_count = 6;
165} 176}
166 177
167static int g_depth[32]; // depth of each visible row (for indentation) 178static int g_depth[48]; // depth of each visible row (for indentation)
168 179
169static void flatten(MenuNode *node, int depth) { 180static void flatten(MenuNode *node, int depth) {
170 if (g_visible_count >= 32) 181 if (g_visible_count >= 48)
171 return; 182 return;
172 node->type = (depth == 0) ? NODE_ROOT 183 node->type = (depth == 0) ? NODE_ROOT
173 : (node->child_count > 0) ? NODE_FEATURE 184 : (node->child_count > 0) ? NODE_FEATURE
@@ -284,17 +295,18 @@ static void config_save() {
284 g_bhop.bind, g_bhop_autostrafe); 295 g_bhop.bind, g_bhop_autostrafe);
285 fprintf(f, "[abh]\nactive=%d\nbind=%d\n", g_abh.active, g_abh.bind); 296 fprintf(f, "[abh]\nactive=%d\nbind=%d\n", g_abh.active, g_abh.bind);
286 fprintf(f, "[aimbot]\nactive=%d\nbind=%d\nsmooth=%d\nvischeck=%d\n" 297 fprintf(f, "[aimbot]\nactive=%d\nbind=%d\nsmooth=%d\nvischeck=%d\n"
298 "fov=%d\nfov_circle=%d\n"
287 "bone_head=%d\nbone_torso=%d\nbone_larm=%d\nbone_rarm=%d\n" 299 "bone_head=%d\nbone_torso=%d\nbone_larm=%d\nbone_rarm=%d\n"
288 "bone_lleg=%d\nbone_rleg=%d\n", 300 "bone_lleg=%d\nbone_rleg=%d\n",
289 g_aimbot.active, g_aimbot.bind, g_aim_smooth, 301 g_aimbot.active, g_aimbot.bind, g_aim_smooth,
290 (int)g_aim_vischeck, 302 (int)g_aim_vischeck, g_aim_fov, (int)g_aim_fov_circle,
291 g_aim_bones[AIM_BONE_HEAD], g_aim_bones[AIM_BONE_TORSO], 303 g_aim_bones[AIM_BONE_HEAD], g_aim_bones[AIM_BONE_TORSO],
292 g_aim_bones[AIM_BONE_LARM], g_aim_bones[AIM_BONE_RARM], 304 g_aim_bones[AIM_BONE_LARM], g_aim_bones[AIM_BONE_RARM],
293 g_aim_bones[AIM_BONE_LLEG], g_aim_bones[AIM_BONE_RLEG]); 305 g_aim_bones[AIM_BONE_LLEG], g_aim_bones[AIM_BONE_RLEG]);
294 fprintf(f, "[triggerbot]\nactive=%d\nbind=%d\n", g_triggerbot.active, 306 fprintf(f, "[triggerbot]\nactive=%d\nbind=%d\n", g_triggerbot.active,
295 g_triggerbot.bind); 307 g_triggerbot.bind);
296 fprintf(f, "[silentaim]\nactive=%d\nbind=%d\n", g_silentaim.active, 308 fprintf(f, "[silentaim]\nactive=%d\nbind=%d\nfov=%d\n", g_silentaim.active,
297 g_silentaim.bind); 309 g_silentaim.bind, g_silent_fov);
298 fprintf(f, "[esp]\nactive=%d\nbind=%d\nneutral=%d\nhealthbar=%d\n" 310 fprintf(f, "[esp]\nactive=%d\nbind=%d\nneutral=%d\nhealthbar=%d\n"
299 "healthnum=%d\nname=%d\nskeleton=%d\nbox=%d\n", 311 "healthnum=%d\nname=%d\nskeleton=%d\nbox=%d\n",
300 g_esp.active, g_esp.bind, g_esp_opt[ESP_OPT_SHOW_NEUTRAL], 312 g_esp.active, g_esp.bind, g_esp_opt[ESP_OPT_SHOW_NEUTRAL],
@@ -310,7 +322,7 @@ static void config_load() {
310 return; 322 return;
311 char line[256]; 323 char line[256];
312 FeatureState *cur = nullptr; 324 FeatureState *cur = nullptr;
313 bool in_esp = false, in_bhop = false, in_aim = false; 325 bool in_esp = false, in_bhop = false, in_aim = false, in_silent = false;
314 while (fgets(line, sizeof(line), f)) { 326 while (fgets(line, sizeof(line), f)) {
315 line[strcspn(line, "\r\n")] = 0; 327 line[strcspn(line, "\r\n")] = 0;
316 if (line[0] == '[') { 328 if (line[0] == '[') {
@@ -331,6 +343,7 @@ static void config_load() {
331 in_esp = (cur == &g_esp); 343 in_esp = (cur == &g_esp);
332 in_bhop = (cur == &g_bhop); 344 in_bhop = (cur == &g_bhop);
333 in_aim = (cur == &g_aimbot); 345 in_aim = (cur == &g_aimbot);
346 in_silent = (cur == &g_silentaim);
334 } else if (cur) { 347 } else if (cur) {
335 char *eq = strchr(line, '='); 348 char *eq = strchr(line, '=');
336 if (!eq) 349 if (!eq)
@@ -360,8 +373,21 @@ static void config_load() {
360 g_aim_bones[AIM_BONE_RLEG] = atoi(val) != 0; 373 g_aim_bones[AIM_BONE_RLEG] = atoi(val) != 0;
361 } else if (strcmp(key, "vischeck") == 0) { 374 } else if (strcmp(key, "vischeck") == 0) {
362 g_aim_vischeck = atoi(val) != 0; 375 g_aim_vischeck = atoi(val) != 0;
376 } else if (strcmp(key, "fov") == 0) {
377 int v = atoi(val);
378 if (v < 1) v = 1;
379 if (v > 180) v = 180;
380 g_aim_fov = v;
381 } else if (strcmp(key, "fov_circle") == 0) {
382 g_aim_fov_circle = atoi(val) != 0;
363 } 383 }
364 } 384 }
385 if (in_silent && strcmp(key, "fov") == 0) {
386 int v = atoi(val);
387 if (v < 1) v = 1;
388 if (v > 180) v = 180;
389 g_silent_fov = v;
390 }
365 if (in_esp) { 391 if (in_esp) {
366 if (strcmp(key, "neutral") == 0) 392 if (strcmp(key, "neutral") == 0)
367 g_esp_opt[ESP_OPT_SHOW_NEUTRAL] = atoi(val) != 0; 393 g_esp_opt[ESP_OPT_SHOW_NEUTRAL] = atoi(val) != 0;
@@ -485,18 +511,32 @@ static bool key_pressed(int sc) {
485 511
486// feature gate queried from hl2h.cpp do_move(): feature must be Active AND 512// feature gate queried from hl2h.cpp do_move(): feature must be Active AND
487// its bind key held (always-on when unbound). 513// its bind key held (always-on when unbound).
488bool feature_active(int feature) { 514static FeatureState *feature_state(int feature) {
489 static FeatureState *feats[6] = {&g_bhop, &g_abh, &g_esp, 515 static FeatureState *feats[6] = {&g_bhop, &g_abh, &g_esp,
490 &g_aimbot, &g_triggerbot, 516 &g_aimbot, &g_triggerbot,
491 &g_silentaim}; 517 &g_silentaim};
492 if (feature < 0 || feature > 5) 518 if (feature < 0 || feature > 5)
493 return false; 519 return nullptr;
494 FeatureState *f = feats[feature]; 520 return feats[feature];
495 if (!f->active) 521}
522
523bool feature_active(int feature) {
524 FeatureState *f = feature_state(feature);
525 if (!f || !f->active)
496 return false; 526 return false;
497 return f->bind == 0 || key_held(f->bind); 527 return f->bind == 0 || key_held(f->bind);
498} 528}
499 529
530bool feature_has_bind(int feature) {
531 FeatureState *f = feature_state(feature);
532 return f && f->bind != 0;
533}
534
535bool feature_bind_held(int feature) {
536 FeatureState *f = feature_state(feature);
537 return f && f->bind != 0 && key_held(f->bind);
538}
539
500bool esp_option(int opt) { 540bool esp_option(int opt) {
501 return opt >= 0 && opt < ESP_OPT_COUNT && g_esp_opt[opt]; 541 return opt >= 0 && opt < ESP_OPT_COUNT && g_esp_opt[opt];
502} 542}
@@ -524,6 +564,12 @@ int aimbot_smooth_step() { return g_aim_smooth; }
524 564
525bool aimbot_vischeck_enabled() { return g_aim_vischeck; } 565bool aimbot_vischeck_enabled() { return g_aim_vischeck; }
526 566
567int aimbot_fov() { return g_aim_fov; }
568
569int silentaim_fov() { return g_silent_fov; }
570
571bool aimbot_fov_circle() { return g_aim_fov_circle; }
572
527static void menu_handle_input() { 573static void menu_handle_input() {
528 poll_keyboard(); 574 poll_keyboard();
529 575