summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CONTEXT.md30
-rw-r--r--src/hl2h.cpp186
-rw-r--r--src/hl2h.h8
-rw-r--r--src/menu.cpp28
-rw-r--r--src/vischeck.cpp45
5 files changed, 279 insertions, 18 deletions
diff --git a/CONTEXT.md b/CONTEXT.md
index cd3811d..b1ec4e7 100644
--- a/CONTEXT.md
+++ b/CONTEXT.md
@@ -104,21 +104,26 @@ HL2hake
104 Uni-directional auto-strafe 104 Uni-directional auto-strafe
105 ABH 105 ABH
106 Active 106 Active
107 Jumpbug
108 Active
107 ESP 109 ESP
108 Active 110 Active
109 Show neutral / healthbar / healthnum / name / skeleton / box 111 Show neutral / healthbar / healthnum / name / skeleton / box / speed graph
110 Aimbot 112 Aimbot
111 Active 113 Active
112 Bind 114 Bind
113 Vischeck 115 Vischeck
114 Bones -> Head / Torso / Left arm / Right arm / Left leg / Right leg 116 Bones -> Head / Torso / Left arm / Right arm / Left leg / Right leg
115 Smoothness (1..45 deg/tick, Left/Right) 117 Smoothness (1..45 deg/tick, Left/Right)
118 FOV (1..180 deg, Left/Right)
119 Show FOV circle
116 Triggerbot 120 Triggerbot
117 Active 121 Active
118 Bind 122 Bind
119 Silent aim 123 Silent aim
120 Active 124 Active
121 Bind 125 Bind
126 FOV (1..180 deg, Left/Right)
122``` 127```
123 128
124Binds use SDL scancodes; mouse buttons are pseudo-scancodes 600..604. 129Binds use SDL scancodes; mouse buttons are pseudo-scancodes 600..604.
@@ -156,6 +161,27 @@ Binds use SDL scancodes; mouse buttons are pseudo-scancodes 600..604.
156 zero forward/side movement; HL2's jump speed-bound subtraction then 161 zero forward/side movement; HL2's jump speed-bound subtraction then
157 accelerates along the velocity. Camera restored at `FRAME_RENDER_START`. 162 accelerates along the velocity. Camera restored at `FRAME_RENDER_START`.
158 163
164## Jumpbug
165
166- Automatic while falling when the predicted landing speed
167 `sqrt(vz^2 + 2*600*x)` exceeds `PLAYER_MAX_SAFE_FALL_SPEED` (526.5).
168- The engine only applies fall damage when `CheckFalling` sees a ground entity
169 at the end of the frame. `PlayerMove` runs `Duck()` **before**
170 `FullWalkMove`'s `CheckJumpButton`, so a same-frame `FinishUnDuck` sets ground
171 via `CategorizePosition`, the jump executes, and the fall-damage test is
172 skipped.
173- In air, `FinishDuck` raises the origin by 36 (standing hull 72, duck hull 36)
174 and `CanUnduck` only succeeds when the floor is at least 36 below; after
175 unducking, `CategorizePosition` needs it within 2 more units. The unduck
176 window is therefore a 2-unit band `[36,38]` above the floor.
177- The feature presses duck when the standing distance `x` is within
178 `drop + [0,2]` (where `drop` is the previous command's measured fall), then
179 releases duck + `IN_JUMP` on the next command when `x` is in `[36,38]`.
180 It also clears `IN_JUMP` while waiting so the pogo latch is clear.
181- `drop` is measured from consecutive predicted origins (not a hard-coded tick
182 interval). At high fall speeds the 2-unit band can be skipped between
183 commands; a higher frame rate improves reliability. Best-effort by design.
184
159## Gotchas / lessons 185## Gotchas / lessons
160 186
161- Never pipe-mask build failures: 187- Never pipe-mask build failures:
@@ -171,8 +197,6 @@ Binds use SDL scancodes; mouse buttons are pseudo-scancodes 600..604.
171 197
172- **Melee killbot**: 360° silent aim for hostile NPCs inside crowbar/melee 198- **Melee killbot**: 360° silent aim for hostile NPCs inside crowbar/melee
173 range, sorted by distance, only while the crowbar is held. 199 range, sorted by distance, only while the crowbar is held.
174- **Jumpbug**: automate the HL2 jumpbug movement glitch (trigger conditions
175 to be defined/tested).
176- **GUI**: replace/augment the VGUI ISurface menu with an immediate-mode GUI 200- **GUI**: replace/augment the VGUI ISurface menu with an immediate-mode GUI
177 (Dear ImGui or a custom implementation); needs a design grill first. 201 (Dear ImGui or a custom implementation); needs a design grill first.
178- **Distance weapon spread thesis**: TBD — user will explain. Likely models 202- **Distance weapon spread thesis**: TBD — user will explain. Likely models
diff --git a/src/hl2h.cpp b/src/hl2h.cpp
index 4947b18..125d94e 100644
--- a/src/hl2h.cpp
+++ b/src/hl2h.cpp
@@ -166,7 +166,184 @@ static void apply_autostrafe(CUserCmd *cmd, const float *vel) {
166 cmd->sidemove = (dx * rx + dy * ry) * 450.0f; 166 cmd->sidemove = (dx * rx + dy * ry) * 450.0f;
167} 167}
168 168
169static void do_move(int seq) { 169// ---------------------------------------------------------------------------
170// jumpbug
171// ---------------------------------------------------------------------------
172//
173// Source jumpbug: while falling, hold duck; on the frame where unducking would
174// leave the standing hull within CategorizePosition's 2-unit ground probe,
175// release duck and press jump. PlayerMove calls Duck() before FullWalkMove's
176// CheckJumpButton, so FinishUnDuck -> CategorizePosition sets the ground
177// entity, the jump executes in the same frame, and CheckFalling never sees
178// ground for the fall-damage test.
179//
180// In air, FinishDuck raises the feet by 36 units (the standing hull is 72
181// tall, the duck hull 36). We exploit that as a one-frame phase shift: press
182// duck when the standing distance x to the floor is about `drop` units, so
183// after this frame's fall the ducked feet sit ~37 units above the floor (the
184// middle of the [36, 38] unduck window) and the next frame can jump.
185
186static constexpr float JB_SAFE_FALL = 526.5f;
187static constexpr float JB_GRAVITY = 600.0f;
188static constexpr float JB_DUCK_RAISE = 36.0f;
189
190static int jb_state = 0; // 0 idle, 1 ducked/waiting for the unduck window
191static int jb_local = -1;
192static float jb_prev_z = 0.0f;
193static bool jb_prev_valid = false;
194static bool jb_prev_ducked = false;
195static float jb_drop = 0.0f; // last measured vertical fall per command
196static int jb_logs = 0;
197static bool jb_tracked = false;
198
199static bool game_running() {
200 if (!g_iface.engine)
201 return true;
202 using BoolFn = bool (*)(void *);
203 if (!vfunc<BoolFn>(g_iface.engine, 26)(g_iface.engine))
204 return false;
205 if (vfunc<BoolFn>(g_iface.engine, 84)(g_iface.engine))
206 return false;
207 return true;
208}
209
210static void jumpbug_reset() {
211 jb_state = 0;
212 jb_prev_valid = false;
213 jb_tracked = false;
214}
215
216static bool jumpbug_apply(CUserCmd *cmd, int local, void *ent, int flags,
217 const float *vel, float frametime) {
218 if (!feature_active(FEAT_JUMPBUG) || !g_iface.engine ||
219 g_net.origin < 0 || g_net.velocity < 0) {
220 jumpbug_reset();
221 return false;
222 }
223 if (!game_running()) {
224 jumpbug_reset();
225 return false;
226 }
227
228 bool onground = (flags & FL_ONGROUND) != 0;
229 bool inwater = (flags & FL_INWATER) != 0;
230 bool ducked = (flags & FL_DUCKING) != 0;
231 uintptr_t e = (uintptr_t)ent;
232 int movetype =
233 g_net.movetype >= 0 ? *(int *)(e + g_net.movetype) : MOVETYPE_WALK;
234
235 if (onground || inwater || movetype != MOVETYPE_WALK ||
236 (g_net.health >= 0 && *(int *)(e + g_net.health) <= 0)) {
237 jumpbug_reset();
238 return false;
239 }
240
241 Vector origin{*(float *)(e + g_net.origin),
242 *(float *)(e + g_net.origin + 4),
243 *(float *)(e + g_net.origin + 8)};
244 float vz = vel[2];
245
246 if (local != jb_local) {
247 jb_local = local;
248 jumpbug_reset();
249 jb_drop = 0.0f;
250 jb_logs = 0;
251 }
252
253 // Measure the previous command's fall. Both endpoints must be airborne and
254 // standing: a duck transition moves the origin by 36 units and would
255 // otherwise look like a huge fall.
256 if (jb_prev_valid && !jb_prev_ducked && !ducked) {
257 float d = jb_prev_z - origin.z;
258 if (d > 0.05f && d < 250.0f)
259 jb_drop = d;
260 }
261 jb_prev_valid = true;
262 jb_prev_z = origin.z;
263 jb_prev_ducked = ducked;
264
265 if (vz > -10.0f) { // not falling
266 jb_state = 0;
267 jb_tracked = false;
268 return false;
269 }
270
271 float x = ground_distance(origin, ent, 2048.0f);
272 if (x < 0.0f) { // no standable surface below
273 jb_state = 0;
274 jb_tracked = false;
275 return false;
276 }
277 float impact = sqrtf(vz * vz + 2.0f * JB_GRAVITY * x);
278 if (impact <= JB_SAFE_FALL + 0.5f) {
279 jb_tracked = false;
280 jb_state = 0;
281 return false;
282 }
283 if (!jb_tracked) {
284 jb_tracked = true;
285 logf("jumpbug: tracking x=%.1f vz=%.1f impact=%.1f", x, vz, impact);
286 }
287
288 if (jb_drop <= 0.05f) {
289 // No measured fall yet. Bias toward the default 15 ms Source tick when
290 // the input frametime is smaller: setting the duck up early leaves us
291 // ducked above the window, where we can wait; setting it up late would
292 // put us below the unduck window and lose the jump.
293 float dt = frametime;
294 if (!(dt > 0.0f && dt < 0.05f) || dt < 0.015f)
295 dt = 0.015f;
296 jb_drop = -vz * dt + 0.5f * JB_GRAVITY * dt * dt;
297 if (jb_drop < 0.05f)
298 jb_drop = 0.05f;
299 }
300
301 int before = cmd->buttons;
302 bool trigger = false;
303
304 if (ducked) {
305 if (x >= JB_DUCK_RAISE && x <= JB_DUCK_RAISE + 2.0f) {
306 // In the window: unduck (CategorizePosition sets ground) and jump in
307 // the same command.
308 cmd->buttons &= ~IN_DUCK;
309 cmd->buttons |= IN_JUMP;
310 trigger = true;
311 jb_state = 0;
312 jb_tracked = false;
313 if (jb_logs < 20) {
314 jb_logs++;
315 logf("jumpbug: trigger x=%.2f drop=%.2f vz=%.1f", x, jb_drop, vz);
316 }
317 } else {
318 // Hold the crouch and wait for the window. Staying ducked is required
319 // for the unduck trick; releasing early would just add a phase shift
320 // and fight a user who is holding crouch.
321 cmd->buttons |= IN_DUCK;
322 jb_state = 1;
323 }
324 } else {
325 if (jb_state == 1)
326 jb_state = 0; // expected to be ducked but is not
327 if (x <= jb_drop + 2.0f) {
328 // Setup frame: raise the feet 36 units here so the next frame starts
329 // near the middle of the unduck window.
330 cmd->buttons |= IN_DUCK;
331 jb_state = 1;
332 if (jb_logs < 20) {
333 jb_logs++;
334 logf("jumpbug: setup x=%.2f drop=%.2f pred=%.2f vz=%.1f", x, jb_drop,
335 x + JB_DUCK_RAISE - jb_drop, vz);
336 }
337 }
338 }
339
340 if (!trigger && (cmd->buttons & IN_JUMP))
341 cmd->buttons &= ~IN_JUMP; // keep the pogo latch clear for the trigger
342
343 return cmd->buttons != before;
344}
345
346static void do_move(int seq, float frametime) {
170 static bool g_echo_sent = false; 347 static bool g_echo_sent = false;
171 if (!g_input || !g_net.ok()) 348 if (!g_input || !g_net.ok())
172 return; 349 return;
@@ -206,6 +383,7 @@ static void do_move(int seq) {
206 int flags = *(int *)(e + g_net.flags); 383 int flags = *(int *)(e + g_net.flags);
207 bool onground = (flags & FL_ONGROUND) != 0; 384 bool onground = (flags & FL_ONGROUND) != 0;
208 bool inwater = (flags & FL_INWATER) != 0; 385 bool inwater = (flags & FL_INWATER) != 0;
386 const float *vel = (const float *)(e + g_net.velocity);
209 387
210 bool bhop_on = feature_active(FEAT_BHOP); 388 bool bhop_on = feature_active(FEAT_BHOP);
211 bool abh_on = feature_active(FEAT_ABH); 389 bool abh_on = feature_active(FEAT_ABH);
@@ -217,7 +395,6 @@ static void do_move(int seq) {
217 lastground = onground; 395 lastground = onground;
218 return; 396 return;
219 } 397 }
220 const float *vel = (const float *)(e + g_net.velocity);
221 float speed = sqrtf(vel[0] * vel[0] + vel[1] * vel[1] + vel[2] * vel[2]); 398 float speed = sqrtf(vel[0] * vel[0] + vel[1] * vel[1] + vel[2] * vel[2]);
222 bool moving = speed > 0.1f; 399 bool moving = speed > 0.1f;
223 bool backmove = false; 400 bool backmove = false;
@@ -260,6 +437,9 @@ static void do_move(int seq) {
260 } 437 }
261 } 438 }
262 439
440 if (jumpbug_apply(cmd, localIdx, ent, flags, vel, frametime))
441 note_modified(cmd, flags, vel);
442
263 if (!feature_active(FEAT_AIMBOT) || !feature_has_bind(FEAT_AIMBOT)) 443 if (!feature_active(FEAT_AIMBOT) || !feature_has_bind(FEAT_AIMBOT))
264 aimbot_reset_lock(); 444 aimbot_reset_lock();
265 if (!feature_active(FEAT_SILENTAIM) || !feature_has_bind(FEAT_SILENTAIM)) 445 if (!feature_active(FEAT_SILENTAIM) || !feature_has_bind(FEAT_SILENTAIM))
@@ -361,7 +541,7 @@ static void hkdPaintTraverseEx(void *self, long panel, bool paintPopups) {
361 541
362static bool hkdCreateMove(void *self, int seq, float frametime, bool active) { 542static bool hkdCreateMove(void *self, int seq, float frametime, bool active) {
363 bool ret = oCreateMove(self, seq, frametime, active); 543 bool ret = oCreateMove(self, seq, frametime, active);
364 do_move(seq); 544 do_move(seq, frametime);
365 545
366 // Hook ISurface once: PaintTraverseEx (slot 114) fires every frame; keep 546 // Hook ISurface once: PaintTraverseEx (slot 114) fires every frame; keep
367 // DrawPrintText (slot 22) hooked as a fallback text-draw trigger. 547 // DrawPrintText (slot 22) hooked as a fallback text-draw trigger.
diff --git a/src/hl2h.h b/src/hl2h.h
index fdc88c1..92ae3f1 100644
--- a/src/hl2h.h
+++ b/src/hl2h.h
@@ -128,8 +128,8 @@ struct CUserCmd {
128// --------------------------------------------------------------------------- 128// ---------------------------------------------------------------------------
129 129
130enum { IN_ATTACK = 1 << 0, IN_JUMP = 1 << 1, IN_DUCK = 1 << 2, IN_USE = 1 << 5 }; 130enum { IN_ATTACK = 1 << 0, IN_JUMP = 1 << 1, IN_DUCK = 1 << 2, IN_USE = 1 << 5 };
131enum { FL_ONGROUND = 1 << 0, FL_INWATER = 1 << 9 }; 131enum { FL_ONGROUND = 1 << 0, FL_DUCKING = 1 << 1, FL_INWATER = 1 << 9 };
132enum { MOVETYPE_NOCLIP = 8, MOVETYPE_LADDER = 9 }; 132enum { MOVETYPE_WALK = 2, MOVETYPE_NOCLIP = 8, MOVETYPE_LADDER = 9 };
133 133
134// --------------------------------------------------------------------------- 134// ---------------------------------------------------------------------------
135// vector math 135// vector math
@@ -152,7 +152,8 @@ enum FeatureId {
152 FEAT_ESP = 2, 152 FEAT_ESP = 2,
153 FEAT_AIMBOT = 3, 153 FEAT_AIMBOT = 3,
154 FEAT_TRIGGERBOT = 4, 154 FEAT_TRIGGERBOT = 4,
155 FEAT_SILENTAIM = 5 155 FEAT_SILENTAIM = 5,
156 FEAT_JUMPBUG = 6
156}; 157};
157enum EspOption { 158enum EspOption {
158 ESP_OPT_SHOW_NEUTRAL = 0, 159 ESP_OPT_SHOW_NEUTRAL = 0,
@@ -200,6 +201,7 @@ bool world_to_screen(const Vector &w, float &sx, float &sy);
200void set_silent_restore(const QAngle &angles); 201void set_silent_restore(const QAngle &angles);
201bool is_visible(const Vector &eye, const Vector &point); 202bool is_visible(const Vector &eye, const Vector &point);
202void *trace_entity(const Vector &start, const Vector &end, void *ignore); 203void *trace_entity(const Vector &start, const Vector &end, void *ignore);
204float ground_distance(const Vector &origin, void *ignore, float max_dist);
203 205
204// bones.cpp — client-side animated skeleton / hitbox access (see there) 206// bones.cpp — client-side animated skeleton / hitbox access (see there)
205bool bones_update(int idx, void *ent); 207bool bones_update(int idx, void *ent);
diff --git a/src/menu.cpp b/src/menu.cpp
index 3e20cb3..0ed89d4 100644
--- a/src/menu.cpp
+++ b/src/menu.cpp
@@ -62,6 +62,7 @@ static FeatureState g_bhop{true, 0};
62static bool g_bhop_autostrafe = true; 62static bool g_bhop_autostrafe = true;
63 63
64static FeatureState g_abh{true, 0}; 64static FeatureState g_abh{true, 0};
65static FeatureState g_jumpbug{true, 0};
65static FeatureState g_esp{true, 0}; 66static FeatureState g_esp{true, 0};
66static FeatureState g_aimbot{true, 604}; // default Mouse5, rebindable in menu 67static FeatureState g_aimbot{true, 604}; // default Mouse5, rebindable in menu
67static FeatureState g_triggerbot{true, 603}; // default Mouse4 68static FeatureState g_triggerbot{true, 603}; // default Mouse4
@@ -96,6 +97,8 @@ static MenuNode g_bhop_active{NODE_TOGGLE, "Active", false, 0, {}, &g_bhop.activ
96static MenuNode g_bhop_autostrafe_node{NODE_TOGGLE, "Uni-directional auto-strafe", false, 0, {}, &g_bhop_autostrafe, nullptr, nullptr}; 97static MenuNode g_bhop_autostrafe_node{NODE_TOGGLE, "Uni-directional auto-strafe", false, 0, {}, &g_bhop_autostrafe, nullptr, nullptr};
97static MenuNode g_abh_feat{NODE_FEATURE, "ABH", false, 1, {}, nullptr, nullptr, nullptr}; 98static MenuNode g_abh_feat{NODE_FEATURE, "ABH", false, 1, {}, nullptr, nullptr, nullptr};
98static MenuNode g_abh_active{NODE_TOGGLE, "Active", false, 0, {}, &g_abh.active, nullptr, nullptr}; 99static MenuNode g_abh_active{NODE_TOGGLE, "Active", false, 0, {}, &g_abh.active, nullptr, nullptr};
100static MenuNode g_jumpbug_feat{NODE_FEATURE, "Jumpbug", false, 1, {}, nullptr, nullptr, nullptr};
101static MenuNode g_jumpbug_active{NODE_TOGGLE, "Active", false, 0, {}, &g_jumpbug.active, nullptr, nullptr};
99static MenuNode g_esp_feat{NODE_FEATURE, "ESP", false, 1, {}, nullptr, nullptr, nullptr}; 102static MenuNode g_esp_feat{NODE_FEATURE, "ESP", false, 1, {}, nullptr, nullptr, nullptr};
100static MenuNode g_esp_active{NODE_TOGGLE, "Active", false, 0, {}, &g_esp.active, nullptr, nullptr}; 103static MenuNode g_esp_active{NODE_TOGGLE, "Active", false, 0, {}, &g_esp.active, nullptr, nullptr};
101static MenuNode g_esp_opt_nodes[ESP_OPT_COUNT] = { 104static MenuNode g_esp_opt_nodes[ESP_OPT_COUNT] = {
@@ -145,6 +148,9 @@ static void build_tree() {
145 g_bhop_feat.children[1] = &g_bhop_autostrafe_node; 148 g_bhop_feat.children[1] = &g_bhop_autostrafe_node;
146 g_bhop_feat.child_count = 2; 149 g_bhop_feat.child_count = 2;
147 g_abh_feat.children[0] = &g_abh_active; 150 g_abh_feat.children[0] = &g_abh_active;
151 g_abh_feat.child_count = 1;
152 g_jumpbug_feat.children[0] = &g_jumpbug_active;
153 g_jumpbug_feat.child_count = 1;
148 g_esp_feat.children[0] = &g_esp_active; 154 g_esp_feat.children[0] = &g_esp_active;
149 g_aimbot_feat.children[0] = &g_aimbot_active; 155 g_aimbot_feat.children[0] = &g_aimbot_active;
150 g_aimbot_feat.children[1] = &g_aimbot_bind; 156 g_aimbot_feat.children[1] = &g_aimbot_bind;
@@ -169,11 +175,12 @@ static void build_tree() {
169 g_esp_feat.child_count = ESP_OPT_COUNT + 1; 175 g_esp_feat.child_count = ESP_OPT_COUNT + 1;
170 g_root.children[0] = &g_bhop_feat; 176 g_root.children[0] = &g_bhop_feat;
171 g_root.children[1] = &g_abh_feat; 177 g_root.children[1] = &g_abh_feat;
172 g_root.children[2] = &g_esp_feat; 178 g_root.children[2] = &g_jumpbug_feat;
173 g_root.children[3] = &g_aimbot_feat; 179 g_root.children[3] = &g_esp_feat;
174 g_root.children[4] = &g_triggerbot_feat; 180 g_root.children[4] = &g_aimbot_feat;
175 g_root.children[5] = &g_silentaim_feat; 181 g_root.children[5] = &g_triggerbot_feat;
176 g_root.child_count = 6; 182 g_root.children[6] = &g_silentaim_feat;
183 g_root.child_count = 7;
177} 184}
178 185
179static int g_depth[48]; // depth of each visible row (for indentation) 186static int g_depth[48]; // depth of each visible row (for indentation)
@@ -295,6 +302,7 @@ static void config_save() {
295 fprintf(f, "[bhop]\nactive=%d\nbind=%d\nautostrafe=%d\n", g_bhop.active, 302 fprintf(f, "[bhop]\nactive=%d\nbind=%d\nautostrafe=%d\n", g_bhop.active,
296 g_bhop.bind, g_bhop_autostrafe); 303 g_bhop.bind, g_bhop_autostrafe);
297 fprintf(f, "[abh]\nactive=%d\nbind=%d\n", g_abh.active, g_abh.bind); 304 fprintf(f, "[abh]\nactive=%d\nbind=%d\n", g_abh.active, g_abh.bind);
305 fprintf(f, "[jumpbug]\nactive=%d\n", g_jumpbug.active);
298 fprintf(f, "[aimbot]\nactive=%d\nbind=%d\nsmooth=%d\nvischeck=%d\n" 306 fprintf(f, "[aimbot]\nactive=%d\nbind=%d\nsmooth=%d\nvischeck=%d\n"
299 "fov=%d\nfov_circle=%d\n" 307 "fov=%d\nfov_circle=%d\n"
300 "bone_head=%d\nbone_torso=%d\nbone_larm=%d\nbone_rarm=%d\n" 308 "bone_head=%d\nbone_torso=%d\nbone_larm=%d\nbone_rarm=%d\n"
@@ -332,6 +340,8 @@ static void config_load() {
332 cur = &g_bhop; 340 cur = &g_bhop;
333 else if (strstr(line, "[abh]")) 341 else if (strstr(line, "[abh]"))
334 cur = &g_abh; 342 cur = &g_abh;
343 else if (strstr(line, "[jumpbug]"))
344 cur = &g_jumpbug;
335 else if (strstr(line, "[esp]")) 345 else if (strstr(line, "[esp]"))
336 cur = &g_esp; 346 cur = &g_esp;
337 else if (strstr(line, "[aimbot]")) 347 else if (strstr(line, "[aimbot]"))
@@ -516,10 +526,10 @@ static bool key_pressed(int sc) {
516// feature gate queried from hl2h.cpp do_move(): feature must be Active AND 526// feature gate queried from hl2h.cpp do_move(): feature must be Active AND
517// its bind key held (always-on when unbound). 527// its bind key held (always-on when unbound).
518static FeatureState *feature_state(int feature) { 528static FeatureState *feature_state(int feature) {
519 static FeatureState *feats[6] = {&g_bhop, &g_abh, &g_esp, 529 static FeatureState *feats[7] = {&g_bhop, &g_abh, &g_esp,
520 &g_aimbot, &g_triggerbot, 530 &g_aimbot, &g_triggerbot, &g_silentaim,
521 &g_silentaim}; 531 &g_jumpbug};
522 if (feature < 0 || feature > 5) 532 if (feature < 0 || feature > 6)
523 return nullptr; 533 return nullptr;
524 return feats[feature]; 534 return feats[feature];
525} 535}
diff --git a/src/vischeck.cpp b/src/vischeck.cpp
index 428f54f..6d453c5 100644
--- a/src/vischeck.cpp
+++ b/src/vischeck.cpp
@@ -95,6 +95,9 @@ constexpr unsigned MASK_VISIBLE = 0x1u | 0x4000u | 0x80u | 0x2000u;
95// MASK_SHOT = SOLID|MOVEABLE|MONSTER|WINDOW|DEBRIS|HITBOX 95// MASK_SHOT = SOLID|MOVEABLE|MONSTER|WINDOW|DEBRIS|HITBOX
96constexpr unsigned MASK_SHOT = 0x1u | 0x4000u | 0x2000000u | 0x4u | 96constexpr unsigned MASK_SHOT = 0x1u | 0x4000u | 0x2000000u | 0x4u |
97 0x4000000u | 0x40000000u; 97 0x4000000u | 0x40000000u;
98// MASK_PLAYERSOLID = SOLID|MOVEABLE|PLAYERCLIP|WINDOW|MONSTER|GRATE
99constexpr unsigned MASK_PLAYERSOLID =
100 0x1u | 0x4000u | 0x10000u | 0x4u | 0x2000000u | 0x8u;
98 101
99static void make_ray(RayCompat &ray, const Vector &start, const Vector &end) { 102static void make_ray(RayCompat &ray, const Vector &start, const Vector &end) {
100 float dx = end.x - start.x, dy = end.y - start.y, dz = end.z - start.z; 103 float dx = end.x - start.x, dy = end.y - start.y, dz = end.z - start.z;
@@ -107,6 +110,21 @@ static void make_ray(RayCompat &ray, const Vector &start, const Vector &end) {
107 ray.isSwept = (dx * dx + dy * dy + dz * dz) > 0.0f; 110 ray.isSwept = (dx * dx + dy * dy + dz * dz) > 0.0f;
108} 111}
109 112
113// Swept AABB matching the standing player hull used by CGameMovement.
114// Ray_t::Init shifts m_Start to the box center and stores the negated center
115// offset in m_StartOffset. With mins=(-16,-16,0), maxs=(16,16,72), the
116// extents are (16,16,36) and m_StartOffset is (0,0,-36).
117static void make_standing_hull_ray(RayCompat &ray, const Vector &origin,
118 float distance) {
119 ray = RayCompat{};
120 ray.start = {origin.x, origin.y, origin.z + 36.0f, 0.0f};
121 ray.delta = {0.0f, 0.0f, -distance, 0.0f};
122 ray.startOffset = {0.0f, 0.0f, -36.0f, 0.0f};
123 ray.extents = {16.0f, 16.0f, 36.0f, 0.0f};
124 ray.isRay = false;
125 ray.isSwept = true;
126}
127
110} // namespace 128} // namespace
111 129
112bool is_visible(const Vector &eye, const Vector &point) { 130bool is_visible(const Vector &eye, const Vector &point) {
@@ -147,3 +165,30 @@ void *trace_entity(const Vector &start, const Vector &end, void *ignore) {
147 return nullptr; 165 return nullptr;
148 return tr.ent; 166 return tr.ent;
149} 167}
168
169// Vertical distance from the standing player hull's feet to the first
170// standable surface below. Matches CategorizePosition's 2-unit probe and its
171// plane.normal.z >= 0.7 rule, but sweeps much farther so the caller can
172// predict the landing. Returns -1 when there is no usable ground.
173float ground_distance(const Vector &origin, void *ignore, float max_dist) {
174 if (!g_iface.enginetrace || max_dist <= 0.0f)
175 return -1.0f;
176 RayCompat ray;
177 make_standing_hull_ray(ray, origin, max_dist);
178 GameTraceCompat tr{};
179 EntityFilter filter(ignore);
180 using TraceRayFn = void (*)(void *, const RayCompat *, unsigned, void *,
181 GameTraceCompat *);
182 vfunc<TraceRayFn>(g_iface.enginetrace, 4)(g_iface.enginetrace, &ray,
183 MASK_PLAYERSOLID, &filter, &tr);
184 if (tr.startsolid || tr.fraction >= 1.0f || tr.normal.z < 0.7f) {
185 static int solid_logs = 0;
186 if (tr.startsolid && solid_logs < 4) {
187 solid_logs++;
188 logf("ground trace: startsolid at (%.1f %.1f %.1f)", origin.x,
189 origin.y, origin.z);
190 }
191 return -1.0f;
192 }
193 return tr.fraction * max_dist;
194}