summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-10-28 12:50:11 +0100
committersalmonik <salmonik@tuta.io>2026-10-28 12:50:11 +0100
commit6618d19660c42f4a6ae48cdaec23324e6e79def1 (patch)
treec4fb9f08c96f86c455a600e6862e43e53d0be6f1
parentItem ESP: make 0 = unlimited explicit and persistent (diff)
downloadhl2h-6618d19660c42f4a6ae48cdaec23324e6e79def1.tar.gz
hl2h-6618d19660c42f4a6ae48cdaec23324e6e79def1.zip
Combat: fix silent-aim spread seed and lead projectiles
- Silent aim and triggerbot set IN_ATTACK after CInput::CreateMove, so the minimal-spread seed was never applied to their shots. Re-apply spread_control_apply after setting the attack button, then update the CRC. - Crossbow bolts and RPG rockets are not hitscan. Lead the selected hitbox by target velocity * travel time; the crossbow bolt also gets its 0.05 gravity drop (30 u/s^2). Apply the same correction to the smooth aimbot. - Document both paths in CONTEXT.
-rw-r--r--CONTEXT.md7
-rw-r--r--src/esp.cpp61
-rw-r--r--src/hl2h.cpp12
3 files changed, 77 insertions, 3 deletions
diff --git a/CONTEXT.md b/CONTEXT.md
index 0ecf80c..a9e34d8 100644
--- a/CONTEXT.md
+++ b/CONTEXT.md
@@ -176,7 +176,10 @@ vocabulary / decisions.
176- Silent aim: exact target angle written to `cmd->viewangles`, `IN_ATTACK`, 176- Silent aim: exact target angle written to `cmd->viewangles`, `IN_ATTACK`,
177 then the rendered view is restored immediately **and** at 177 then the rendered view is restored immediately **and** at
178 `FRAME_RENDER_START`; `forwardmove`/`sidemove` are rewritten to preserve the 178 `FRAME_RENDER_START`; `forwardmove`/`sidemove` are rewritten to preserve the
179 world-space movement wish vector. 179 world-space movement wish vector. Because `IN_ATTACK` is set after
180 `CInput::CreateMove`, the minimal-spread seed is applied again in `do_move`
181 (same for triggerbot). Crossbow bolts and RPG rockets are led by target
182 velocity and travel time; the crossbow also gets its 0.05 gravity drop.
180- ABH: on the landing/jump tick, face opposite the horizontal velocity with 183- ABH: on the landing/jump tick, face opposite the horizontal velocity with
181 zero forward/side movement; HL2's jump speed-bound subtraction then 184 zero forward/side movement; HL2's jump speed-bound subtraction then
182 accelerates along the velocity. Camera restored at `FRAME_RENDER_START`. 185 accelerates along the velocity. Camera restored at `FRAME_RENDER_START`.
@@ -212,6 +215,8 @@ vocabulary / decisions.
212 `RandomSeed`/`RandomFloat`, the 8-bit seed whose 215 `RandomSeed`/`RandomFloat`, the 8-bit seed whose
213 `CShotManipulator::ApplySpread` offset is smallest. It sets 216 `CShotManipulator::ApplySpread` offset is smallest. It sets
214 `cmd->random_seed` on attack commands; `note_modified` repairs the CRC. 217 `cmd->random_seed` on attack commands; `note_modified` repairs the CRC.
218 Silent aim and triggerbot set `IN_ATTACK` after the CInput hook, so they
219 re-apply the best seed in `do_move`.
215- Shotguns use consecutive seeds per pellet. The primary's first pellet is 220- Shotguns use consecutive seeds per pellet. The primary's first pellet is
216 `FIRE_BULLETS_FIRST_SHOT_ACCURATE`, so the table excludes it; the secondary 221 `FIRE_BULLETS_FIRST_SHOT_ACCURATE`, so the table excludes it; the secondary
217 uses all 12. Separate best starting seeds are precomputed for 1, 8 and 12 222 uses all 12. Separate best starting seeds are precomputed for 1, 8 and 12
diff --git a/src/esp.cpp b/src/esp.cpp
index 4d2345d..06bafaf 100644
--- a/src/esp.cpp
+++ b/src/esp.cpp
@@ -669,6 +669,61 @@ static bool select_aim_target(const QAngle &view, unsigned bone_mask,
669 return false; 669 return false;
670} 670}
671 671
672static bool active_weapon_contains(void *local_ent, const char *needle) {
673 if (!local_ent || g_net.activeweapon < 0 || !needle)
674 return false;
675 uint32_t h = *(uint32_t *)((uintptr_t)local_ent + g_net.activeweapon);
676 if (h == 0xFFFFFFFFu)
677 return false;
678 int idx = (int)(h & 0xFFFF);
679 if (idx <= 0 || idx > 4096)
680 return false;
681 void *wep = entitylist_get(idx);
682 void *net = entity_networkable(idx);
683 const char *cls = net ? entity_class_name(net, nullptr) : nullptr;
684 const char *model = wep ? entity_model_name(wep) : nullptr;
685 return (cls && strstr(cls, needle)) || (model && strstr(model, needle));
686}
687
688// Crossbow bolts and RPG rockets are not hitscan. Lead moving targets by the
689// projectile travel time; the crossbow bolt also has gravity 0.05 (30 u/s^2).
690static bool projectile_lead_point(int target_idx, const Vector &point,
691 Vector &out) {
692 int local = engine_get_local_player();
693 if (local <= 0)
694 return false;
695 void *local_ent = entitylist_get(local);
696 if (!local_ent)
697 return false;
698 float speed = 0.0f, gravity = 0.0f;
699 if (active_weapon_contains(local_ent, "crossbow") ||
700 active_weapon_contains(local_ent, "Crossbow")) {
701 speed = 2500.0f;
702 gravity = 30.0f;
703 } else if (active_weapon_contains(local_ent, "rocket_launcher") ||
704 active_weapon_contains(local_ent, "w_rpg") ||
705 active_weapon_contains(local_ent, "RPG")) {
706 speed = 1500.0f;
707 gravity = 0.0f;
708 } else {
709 return false;
710 }
711 void *target = entitylist_get(target_idx);
712 if (!target || g_net.velocity < 0)
713 return false;
714 const float *v = (const float *)((uintptr_t)target + g_net.velocity);
715 float dx = point.x - g_cam_eye.x;
716 float dy = point.y - g_cam_eye.y;
717 float dz = point.z - g_cam_eye.z;
718 float dist = sqrtf(dx * dx + dy * dy + dz * dz);
719 float t = dist / speed;
720 out = point;
721 out.x += v[0] * t;
722 out.y += v[1] * t;
723 out.z += v[2] * t + 0.5f * gravity * t * t;
724 return true;
725}
726
672bool aimbot_apply(CUserCmd *cmd) { 727bool aimbot_apply(CUserCmd *cmd) {
673 unsigned bone_mask = aimbot_bone_mask(); 728 unsigned bone_mask = aimbot_bone_mask();
674 if (!bone_mask) 729 if (!bone_mask)
@@ -684,6 +739,9 @@ bool aimbot_apply(CUserCmd *cmd) {
684 if (!select_aim_target(view, bone_mask, aimbot_vischeck_enabled(), max_fov, 739 if (!select_aim_target(view, bone_mask, aimbot_vischeck_enabled(), max_fov,
685 "aimbot", lock, target, point, fov)) 740 "aimbot", lock, target, point, fov))
686 return false; 741 return false;
742 Vector lead;
743 if (projectile_lead_point(target, point, lead))
744 point = lead;
687 QAngle desired = angle_to(g_cam_eye, point); 745 QAngle desired = angle_to(g_cam_eye, point);
688 static int want_logs = 0; 746 static int want_logs = 0;
689 if (want_logs < 10) { 747 if (want_logs < 10) {
@@ -759,6 +817,9 @@ bool silent_aim_apply(CUserCmd *cmd) {
759 if (!select_aim_target(view, bone_mask, true, max_fov, "silentaim", lock, 817 if (!select_aim_target(view, bone_mask, true, max_fov, "silentaim", lock,
760 target, point, fov)) 818 target, point, fov))
761 return false; 819 return false;
820 Vector lead;
821 if (projectile_lead_point(target, point, lead))
822 point = lead;
762 QAngle desired = angle_to(g_cam_eye, point); 823 QAngle desired = angle_to(g_cam_eye, point);
763 float np = desired.x; 824 float np = desired.x;
764 if (np > 89.0f) np = 89.0f; 825 if (np > 89.0f) np = 89.0f;
diff --git a/src/hl2h.cpp b/src/hl2h.cpp
index 256b9ab..4f5673f 100644
--- a/src/hl2h.cpp
+++ b/src/hl2h.cpp
@@ -689,13 +689,21 @@ static void do_move(int seq, float frametime) {
689 } 689 }
690 690
691 if (feature_active(FEAT_TRIGGERBOT)) { 691 if (feature_active(FEAT_TRIGGERBOT)) {
692 if (triggerbot_apply(cmd)) 692 if (triggerbot_apply(cmd)) {
693 // Triggerbot sets IN_ATTACK after CInput's spread hook, so the best
694 // seed has to be applied here as well.
695 spread_control_apply(cmd, ent);
693 note_modified(cmd, flags, vel); 696 note_modified(cmd, flags, vel);
697 }
694 } 698 }
695 699
696 if (feature_active(FEAT_SILENTAIM)) { 700 if (feature_active(FEAT_SILENTAIM)) {
697 if (silent_aim_apply(cmd)) 701 if (silent_aim_apply(cmd)) {
702 // Silent aim also sets IN_ATTACK late; without this the shot keeps
703 // the random CInput seed instead of the minimal-spread seed.
704 spread_control_apply(cmd, ent);
698 note_modified(cmd, flags, vel); 705 note_modified(cmd, flags, vel);
706 }
699 } 707 }
700 } 708 }
701} 709}