diff options
| author | salmonik <salmonik@tuta.io> | 2026-10-26 17:30:29 +0100 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-10-26 17:30:29 +0100 |
| commit | 92d388116cc19ef728d66ac67eaa4f584e63d895 (patch) | |
| tree | 5b9438cf8c2f1b4f01eef3eec4a6d91b42ac1acb /src/esp.cpp | |
| parent | Triggerbot: crosshair entity trace, hostile-only (diff) | |
| download | hl2h-92d388116cc19ef728d66ac67eaa4f584e63d895.tar.gz hl2h-92d388116cc19ef728d66ac67eaa4f584e63d895.zip | |
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
Diffstat (limited to 'src/esp.cpp')
| -rw-r--r-- | src/esp.cpp | 106 |
1 files changed, 78 insertions, 28 deletions
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) { | |||
| 334 | return a; | 334 | return a; |
| 335 | } | 335 | } |
| 336 | 336 | ||
| 337 | bool aimbot_apply(CUserCmd *cmd) { | 337 | static constexpr float AIM_MAX_FOV = 60.0f; |
| 338 | |||
| 339 | static bool select_aim_target(const QAngle &view, unsigned bone_mask, | ||
| 340 | bool vischeck, const char *tag, int &out_idx, | ||
| 341 | Vector &out_point, float &out_fov) { | ||
| 342 | out_idx = -1; | ||
| 343 | out_point = {}; | ||
| 344 | out_fov = 0.0f; | ||
| 338 | if (!g_iface.entitylist || g_net.origin < 0 || g_net.maxs < 0) | 345 | if (!g_iface.entitylist || g_net.origin < 0 || g_net.maxs < 0) |
| 339 | return false; | 346 | return false; |
| 340 | unsigned bone_mask = aimbot_bone_mask(); | ||
| 341 | if (!bone_mask) | ||
| 342 | return false; | ||
| 343 | int local = engine_get_local_player(); | 347 | int local = engine_get_local_player(); |
| 344 | if (local <= 0) | 348 | if (local <= 0) |
| 345 | return false; | 349 | return false; |
| @@ -350,14 +354,11 @@ bool aimbot_apply(CUserCmd *cmd) { | |||
| 350 | *(int *)((uintptr_t)local_ent + g_net.health) <= 0) | 354 | *(int *)((uintptr_t)local_ent + g_net.health) <= 0) |
| 351 | return false; | 355 | return false; |
| 352 | 356 | ||
| 353 | QAngle view{cmd->viewangles[0], cmd->viewangles[1], 0.0f}; | ||
| 354 | |||
| 355 | using Fn = int (*)(void *); | 357 | using Fn = int (*)(void *); |
| 356 | int hi = vfunc<Fn>(g_iface.entitylist, 6)(g_iface.entitylist); | 358 | int hi = vfunc<Fn>(g_iface.entitylist, 6)(g_iface.entitylist); |
| 357 | if (hi < 0 || hi > 4096) | 359 | if (hi < 0 || hi > 4096) |
| 358 | hi = 4096; | 360 | hi = 4096; |
| 359 | 361 | ||
| 360 | constexpr float MAX_FOV = 60.0f; // only things near the crosshair | ||
| 361 | struct Candidate { | 362 | struct Candidate { |
| 362 | int idx; | 363 | int idx; |
| 363 | float fov; | 364 | float fov; |
| @@ -398,10 +399,9 @@ bool aimbot_apply(CUserCmd *cmd) { | |||
| 398 | Vector approx{origin.x, origin.y, | 399 | Vector approx{origin.x, origin.y, |
| 399 | origin.z + maxz * ((bone_mask & (1u << 1)) ? 0.9f : 0.55f)}; | 400 | origin.z + maxz * ((bone_mask & (1u << 1)) ? 0.9f : 0.55f)}; |
| 400 | float fov = getfov(view, angle_to(g_cam_eye, approx)); | 401 | float fov = getfov(view, angle_to(g_cam_eye, approx)); |
| 401 | if (fov >= MAX_FOV) | 402 | if (fov >= AIM_MAX_FOV) |
| 402 | continue; | 403 | continue; |
| 403 | 404 | ||
| 404 | // keep candidates sorted by approximate angle, nearest first | ||
| 405 | int pos = ncands; | 405 | int pos = ncands; |
| 406 | while (pos > 0 && cands[pos - 1].fov > fov) | 406 | while (pos > 0 && cands[pos - 1].fov > fov) |
| 407 | pos--; | 407 | pos--; |
| @@ -418,15 +418,14 @@ bool aimbot_apply(CUserCmd *cmd) { | |||
| 418 | double now = esp_now(); | 418 | double now = esp_now(); |
| 419 | if (now - last_none >= 2.0) { | 419 | if (now - last_none >= 2.0) { |
| 420 | last_none = now; | 420 | last_none = now; |
| 421 | logf("aimbot: no hostile target within %.0f deg", MAX_FOV); | 421 | logf("%s: no hostile target within %.0f deg", tag, AIM_MAX_FOV); |
| 422 | } | 422 | } |
| 423 | return false; | 423 | return false; |
| 424 | } | 424 | } |
| 425 | 425 | ||
| 426 | // Walk targets nearest-first. On each, pick the nearest selected hitbox | ||
| 427 | // point that passes vischeck; skip to the next target if all are blocked. | ||
| 428 | int best_idx = -1; | 426 | int best_idx = -1; |
| 429 | Vector best_head{}; | 427 | Vector best_point{}; |
| 428 | float best_fov = 0.0f; | ||
| 430 | for (int ci = 0; ci < ncands && best_idx < 0; ci++) { | 429 | for (int ci = 0; ci < ncands && best_idx < 0; ci++) { |
| 431 | int idx = cands[ci].idx; | 430 | int idx = cands[ci].idx; |
| 432 | void *ent = entitylist_get(idx); | 431 | void *ent = entitylist_get(idx); |
| @@ -440,40 +439,55 @@ bool aimbot_apply(CUserCmd *cmd) { | |||
| 440 | float f = getfov(view, angle_to(g_cam_eye, pts[i])); | 439 | float f = getfov(view, angle_to(g_cam_eye, pts[i])); |
| 441 | if (f >= aim_fov) | 440 | if (f >= aim_fov) |
| 442 | continue; | 441 | continue; |
| 443 | if (aimbot_vischeck_enabled() && !is_visible(g_cam_eye, pts[i])) | 442 | if (vischeck && !is_visible(g_cam_eye, pts[i])) |
| 444 | continue; | 443 | continue; |
| 445 | aim_fov = f; | 444 | aim_fov = f; |
| 446 | aim_pt = pts[i]; | 445 | aim_pt = pts[i]; |
| 447 | } | 446 | } |
| 448 | if (aim_fov <= MAX_FOV && aim_fov < 1e8f) { | 447 | if (aim_fov <= AIM_MAX_FOV && aim_fov < 1e8f) { |
| 449 | best_idx = idx; | 448 | best_idx = idx; |
| 450 | best_head = aim_pt; | 449 | best_point = aim_pt; |
| 450 | best_fov = aim_fov; | ||
| 451 | static int sel_logs = 0; | 451 | static int sel_logs = 0; |
| 452 | if (sel_logs < 5) { | 452 | if (sel_logs < 5) { |
| 453 | sel_logs++; | 453 | sel_logs++; |
| 454 | logf("aimbot: target idx=%d points=%d fov=%.2f vischeck=%d", idx, | 454 | logf("%s: target idx=%d points=%d fov=%.2f vischeck=%d", tag, idx, |
| 455 | npoints, aim_fov, (int)aimbot_vischeck_enabled()); | 455 | npoints, aim_fov, (int)vischeck); |
| 456 | } | 456 | } |
| 457 | } | 457 | } |
| 458 | } | 458 | } |
| 459 | |||
| 460 | if (best_idx < 0) { | 459 | if (best_idx < 0) { |
| 461 | static double last_vis = 0.0; | 460 | static double last_vis = 0.0; |
| 462 | double now = esp_now(); | 461 | double now = esp_now(); |
| 463 | if (now - last_vis >= 2.0) { | 462 | if (now - last_vis >= 2.0) { |
| 464 | last_vis = now; | 463 | last_vis = now; |
| 465 | logf("aimbot: no visible aim point (vischeck=%d)", | 464 | logf("%s: no visible aim point (vischeck=%d)", tag, (int)vischeck); |
| 466 | (int)aimbot_vischeck_enabled()); | ||
| 467 | } | 465 | } |
| 468 | return false; | 466 | return false; |
| 469 | } | 467 | } |
| 468 | out_idx = best_idx; | ||
| 469 | out_point = best_point; | ||
| 470 | out_fov = best_fov; | ||
| 471 | return true; | ||
| 472 | } | ||
| 470 | 473 | ||
| 471 | QAngle desired = angle_to(g_cam_eye, best_head); | 474 | bool aimbot_apply(CUserCmd *cmd) { |
| 475 | unsigned bone_mask = aimbot_bone_mask(); | ||
| 476 | if (!bone_mask) | ||
| 477 | return false; | ||
| 478 | QAngle view{cmd->viewangles[0], cmd->viewangles[1], 0.0f}; | ||
| 479 | int target = -1; | ||
| 480 | Vector point{}; | ||
| 481 | float fov = 0.0f; | ||
| 482 | if (!select_aim_target(view, bone_mask, aimbot_vischeck_enabled(), "aimbot", | ||
| 483 | target, point, fov)) | ||
| 484 | return false; | ||
| 485 | QAngle desired = angle_to(g_cam_eye, point); | ||
| 472 | static int want_logs = 0; | 486 | static int want_logs = 0; |
| 473 | if (want_logs < 10) { | 487 | if (want_logs < 10) { |
| 474 | want_logs++; | 488 | want_logs++; |
| 475 | logf("aimbot: aim=(%.1f %.1f %.1f) want=%.2f/%.2f", best_head.x, | 489 | logf("aimbot: target=%d aim=(%.1f %.1f %.1f) fov=%.2f want=%.2f/%.2f", |
| 476 | best_head.y, best_head.z, desired.x, desired.y); | 490 | target, point.x, point.y, point.z, fov, desired.x, desired.y); |
| 477 | } | 491 | } |
| 478 | float dp = desired.x - cmd->viewangles[0]; | 492 | float dp = desired.x - cmd->viewangles[0]; |
| 479 | float dy = desired.y - cmd->viewangles[1]; | 493 | float dy = desired.y - cmd->viewangles[1]; |
| @@ -499,15 +513,11 @@ bool aimbot_apply(CUserCmd *cmd) { | |||
| 499 | cmd->viewangles[0] = np; | 513 | cmd->viewangles[0] = np; |
| 500 | cmd->viewangles[1] = ny; | 514 | cmd->viewangles[1] = ny; |
| 501 | 515 | ||
| 502 | // cmd->viewangles drives usercmd/prediction, but the rendered camera can | ||
| 503 | // stay on the pre-hook angles. Also push the smoothed angles into the | ||
| 504 | // engine's view (IVEngineClient013 slot 20 = SetViewAngles). | ||
| 505 | if (g_iface.engine) { | 516 | if (g_iface.engine) { |
| 506 | QAngle out{np, ny, 0.0f}; | 517 | QAngle out{np, ny, 0.0f}; |
| 507 | using SetViewFn = void (*)(void *, QAngle *); | 518 | using SetViewFn = void (*)(void *, QAngle *); |
| 508 | vfunc<SetViewFn>(g_iface.engine, 20)(g_iface.engine, &out); | 519 | vfunc<SetViewFn>(g_iface.engine, 20)(g_iface.engine, &out); |
| 509 | } | 520 | } |
| 510 | |||
| 511 | static int mod_logs = 0; | 521 | static int mod_logs = 0; |
| 512 | if (mod_logs < 10) { | 522 | if (mod_logs < 10) { |
| 513 | mod_logs++; | 523 | mod_logs++; |
| @@ -516,6 +526,46 @@ bool aimbot_apply(CUserCmd *cmd) { | |||
| 516 | return true; | 526 | return true; |
| 517 | } | 527 | } |
| 518 | 528 | ||
| 529 | bool silent_aim_apply(CUserCmd *cmd) { | ||
| 530 | unsigned bone_mask = aimbot_bone_mask(); | ||
| 531 | if (!bone_mask) | ||
| 532 | return false; | ||
| 533 | QAngle orig{g_cam_view.x, g_cam_view.y, g_cam_view.z}; | ||
| 534 | QAngle view{orig.x, orig.y, 0.0f}; | ||
| 535 | int target = -1; | ||
| 536 | Vector point{}; | ||
| 537 | float fov = 0.0f; | ||
| 538 | if (!select_aim_target(view, bone_mask, true, "silentaim", target, point, | ||
| 539 | fov)) | ||
| 540 | return false; | ||
| 541 | QAngle desired = angle_to(g_cam_eye, point); | ||
| 542 | float np = desired.x; | ||
| 543 | if (np > 89.0f) np = 89.0f; | ||
| 544 | if (np < -89.0f) np = -89.0f; | ||
| 545 | float ny = desired.y; | ||
| 546 | while (ny > 180.0f) ny -= 360.0f; | ||
| 547 | while (ny < -180.0f) ny += 360.0f; | ||
| 548 | |||
| 549 | cmd->viewangles[0] = np; | ||
| 550 | cmd->viewangles[1] = ny; | ||
| 551 | cmd->viewangles[2] = orig.z; | ||
| 552 | |||
| 553 | // The usercmd keeps the aim angles (shot/prediction); the rendered camera | ||
| 554 | // is restored to where the player was actually looking. | ||
| 555 | if (g_iface.engine) { | ||
| 556 | QAngle restore{orig.x, orig.y, orig.z}; | ||
| 557 | using SetViewFn = void (*)(void *, QAngle *); | ||
| 558 | vfunc<SetViewFn>(g_iface.engine, 20)(g_iface.engine, &restore); | ||
| 559 | } | ||
| 560 | cmd->buttons |= IN_ATTACK; | ||
| 561 | static int logs = 0; | ||
| 562 | if (logs < 5) { | ||
| 563 | logs++; | ||
| 564 | logf("silentaim: fire idx=%d fov=%.2f aim=%.2f/%.2f", target, fov, np, ny); | ||
| 565 | } | ||
| 566 | return true; | ||
| 567 | } | ||
| 568 | |||
| 519 | bool triggerbot_apply(CUserCmd *cmd) { | 569 | bool triggerbot_apply(CUserCmd *cmd) { |
| 520 | if (!g_iface.enginetrace || !g_iface.entitylist) | 570 | if (!g_iface.enginetrace || !g_iface.entitylist) |
| 521 | return false; | 571 | return false; |
