diff options
| author | salmonik <salmonik@tuta.io> | 2026-10-27 13:48:27 +0100 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-10-27 13:48:27 +0100 |
| commit | 471f022f449a18766fedb1ff0a4204a933b95b9c (patch) | |
| tree | 499fcd29f1c9f7933aade3f8ffad88b3ef10f377 /src/hl2h.cpp | |
| parent | script: change the map for tests (diff) | |
| download | hl2h-471f022f449a18766fedb1ff0a4204a933b95b9c.tar.gz hl2h-471f022f449a18766fedb1ff0a4204a933b95b9c.zip | |
Jumpbug: optional velocity-timed assist
The unduck window is only 2 units, while a 625-ups fall moves ~9.3 units per
66.7 Hz movement command, so pure jumpbug is ~20% reliable at that speed.
- Add Jumpbug -> Assist timing (default off). When the natural next command
would skip [36,38], pin the post-command ducked distance to 37 by writing
the vertical velocity for that command. Pure jumpbug remains available with
the option off.
- Resolve the client local m_vecAbsVelocity (0x11C) via the client datamap
and the server m_vecAbsVelocity (0x228) via the server datamap.
- Write both the authoritative server velocity (what SetupMove reads through
GetAbsVelocity) and the local prediction fields, so the next CreateMove sees
the assisted origin on both sides.
- Do not write the server m_vecVelocity directly: the datamap scan finds two
same-named entries (0x1C and 0x29C); SetAbsVelocity syncs it after movement.
Diffstat (limited to 'src/hl2h.cpp')
| -rw-r--r-- | src/hl2h.cpp | 82 |
1 files changed, 71 insertions, 11 deletions
diff --git a/src/hl2h.cpp b/src/hl2h.cpp index 125d94e..4ba30bc 100644 --- a/src/hl2h.cpp +++ b/src/hl2h.cpp | |||
| @@ -186,6 +186,8 @@ static void apply_autostrafe(CUserCmd *cmd, const float *vel) { | |||
| 186 | static constexpr float JB_SAFE_FALL = 526.5f; | 186 | static constexpr float JB_SAFE_FALL = 526.5f; |
| 187 | static constexpr float JB_GRAVITY = 600.0f; | 187 | static constexpr float JB_GRAVITY = 600.0f; |
| 188 | static constexpr float JB_DUCK_RAISE = 36.0f; | 188 | static constexpr float JB_DUCK_RAISE = 36.0f; |
| 189 | static constexpr float JB_TARGET_DIST = 37.0f; // middle of the unduck window | ||
| 190 | static constexpr float JB_TICK = 0.015f; // Source interval_per_tick | ||
| 189 | 191 | ||
| 190 | static int jb_state = 0; // 0 idle, 1 ducked/waiting for the unduck window | 192 | static int jb_state = 0; // 0 idle, 1 ducked/waiting for the unduck window |
| 191 | static int jb_local = -1; | 193 | static int jb_local = -1; |
| @@ -213,6 +215,30 @@ static void jumpbug_reset() { | |||
| 213 | jb_tracked = false; | 215 | jb_tracked = false; |
| 214 | } | 216 | } |
| 215 | 217 | ||
| 218 | // Choose a vertical velocity so this command's movement ends with the feet | ||
| 219 | // `target` units above the floor. The engine does StartGravity (half a tick), | ||
| 220 | // moves by velocity*dt, then FinishGravity; displacement is | ||
| 221 | // (vz - 0.5*g*dt)*dt. dist is the distance before the command's movement. | ||
| 222 | static bool jb_force_next_distance(int local, void *ent, const float *vel, | ||
| 223 | float dist, float target) { | ||
| 224 | if (g_net.absvelocity < 0 || g_net.velocity < 0) | ||
| 225 | return false; | ||
| 226 | float dz = target - dist; | ||
| 227 | float vz = dz / JB_TICK + 0.5f * JB_GRAVITY * JB_TICK; | ||
| 228 | if (vz < -2000.0f) | ||
| 229 | vz = -2000.0f; | ||
| 230 | if (vz > 2000.0f) | ||
| 231 | vz = 2000.0f; | ||
| 232 | if (!server_set_velocity_z(local, vz)) | ||
| 233 | return false; | ||
| 234 | // Keep the local prediction in step with the authoritative server entity: | ||
| 235 | // C_BaseEntity::GetAbsVelocity() reads the non-networked m_vecAbsVelocity, | ||
| 236 | // while the networked m_vecVelocity is what the rest of the client sees. | ||
| 237 | *(float *)((uintptr_t)ent + g_net.absvelocity + 8) = vz; | ||
| 238 | ((float *)vel)[2] = vz; | ||
| 239 | return true; | ||
| 240 | } | ||
| 241 | |||
| 216 | static bool jumpbug_apply(CUserCmd *cmd, int local, void *ent, int flags, | 242 | static bool jumpbug_apply(CUserCmd *cmd, int local, void *ent, int flags, |
| 217 | const float *vel, float frametime) { | 243 | const float *vel, float frametime) { |
| 218 | if (!feature_active(FEAT_JUMPBUG) || !g_iface.engine || | 244 | if (!feature_active(FEAT_JUMPBUG) || !g_iface.engine || |
| @@ -285,6 +311,10 @@ static bool jumpbug_apply(CUserCmd *cmd, int local, void *ent, int flags, | |||
| 285 | logf("jumpbug: tracking x=%.1f vz=%.1f impact=%.1f", x, vz, impact); | 311 | logf("jumpbug: tracking x=%.1f vz=%.1f impact=%.1f", x, vz, impact); |
| 286 | } | 312 | } |
| 287 | 313 | ||
| 314 | bool assist = jumpbug_assist_enabled(); | ||
| 315 | if (assist) | ||
| 316 | resolve_server_netvars(); | ||
| 317 | |||
| 288 | if (jb_drop <= 0.05f) { | 318 | if (jb_drop <= 0.05f) { |
| 289 | // No measured fall yet. Bias toward the default 15 ms Source tick when | 319 | // 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 | 320 | // the input frametime is smaller: setting the duck up early leaves us |
| @@ -310,29 +340,59 @@ static bool jumpbug_apply(CUserCmd *cmd, int local, void *ent, int flags, | |||
| 310 | trigger = true; | 340 | trigger = true; |
| 311 | jb_state = 0; | 341 | jb_state = 0; |
| 312 | jb_tracked = false; | 342 | jb_tracked = false; |
| 313 | if (jb_logs < 20) { | 343 | if (jb_logs < 30) { |
| 314 | jb_logs++; | 344 | jb_logs++; |
| 315 | logf("jumpbug: trigger x=%.2f drop=%.2f vz=%.1f", x, jb_drop, vz); | 345 | logf("jumpbug: trigger x=%.2f drop=%.2f vz=%.1f assist=%d", x, jb_drop, |
| 346 | vz, (int)assist); | ||
| 316 | } | 347 | } |
| 317 | } else { | 348 | } else { |
| 318 | // Hold the crouch and wait for the window. Staying ducked is required | 349 | if (assist) { |
| 319 | // for the unduck trick; releasing early would just add a phase shift | 350 | float next_x = x - jb_drop; |
| 320 | // and fight a user who is holding crouch. | 351 | if (next_x <= 38.0f && next_x < 36.0f) { |
| 352 | // The natural next command would skip the 2-unit window. Force the | ||
| 353 | // post-command distance to 37 so the next command can unduck+jump. | ||
| 354 | if (jb_force_next_distance(local, ent, vel, x, JB_TARGET_DIST)) { | ||
| 355 | if (jb_logs < 30) { | ||
| 356 | jb_logs++; | ||
| 357 | logf("jumpbug: assist duck x=%.2f next=%.2f -> vz=%.1f", x, | ||
| 358 | next_x, vel[2]); | ||
| 359 | } | ||
| 360 | } else { | ||
| 361 | static bool warned = false; | ||
| 362 | if (!warned) { | ||
| 363 | warned = true; | ||
| 364 | logf("jumpbug: assist unavailable (server velocity failed)"); | ||
| 365 | } | ||
| 366 | } | ||
| 367 | } | ||
| 368 | } | ||
| 369 | // Hold the crouch and wait for the window (or for the assisted | ||
| 370 | // position on the next command). | ||
| 321 | cmd->buttons |= IN_DUCK; | 371 | cmd->buttons |= IN_DUCK; |
| 322 | jb_state = 1; | 372 | jb_state = 1; |
| 323 | } | 373 | } |
| 324 | } else { | 374 | } else { |
| 325 | if (jb_state == 1) | 375 | if (jb_state == 1) |
| 326 | jb_state = 0; // expected to be ducked but is not | 376 | jb_state = 0; // expected to be ducked but is not |
| 327 | if (x <= jb_drop + 2.0f) { | 377 | float setup_limit = jb_drop + (assist ? 4.0f : 2.0f); |
| 328 | // Setup frame: raise the feet 36 units here so the next frame starts | 378 | if (x <= setup_limit) { |
| 329 | // near the middle of the unduck window. | 379 | // Setup frame: raise the feet 36 units here so the next command starts |
| 380 | // near the middle of the unduck window. With assist enabled, also pin | ||
| 381 | // the post-command distance to exactly 37. | ||
| 382 | bool forced = false; | ||
| 383 | if (assist) | ||
| 384 | forced = jb_force_next_distance(local, ent, vel, x + JB_DUCK_RAISE, | ||
| 385 | JB_TARGET_DIST); | ||
| 330 | cmd->buttons |= IN_DUCK; | 386 | cmd->buttons |= IN_DUCK; |
| 331 | jb_state = 1; | 387 | jb_state = 1; |
| 332 | if (jb_logs < 20) { | 388 | if (jb_logs < 30) { |
| 333 | jb_logs++; | 389 | jb_logs++; |
| 334 | logf("jumpbug: setup x=%.2f drop=%.2f pred=%.2f vz=%.1f", x, jb_drop, | 390 | if (assist) |
| 335 | x + JB_DUCK_RAISE - jb_drop, vz); | 391 | logf("jumpbug: setup assist x=%.2f drop=%.2f vz=%.1f ok=%d", x, |
| 392 | jb_drop, vel[2], (int)forced); | ||
| 393 | else | ||
| 394 | logf("jumpbug: setup x=%.2f drop=%.2f pred=%.2f vz=%.1f", x, | ||
| 395 | jb_drop, x + JB_DUCK_RAISE - jb_drop, vz); | ||
| 336 | } | 396 | } |
| 337 | } | 397 | } |
| 338 | } | 398 | } |
