Skip to content

Time a reload by its own sequence, not a transition - #2249

Merged
AdamTadeusz merged 2 commits into
NeotokyoRebuild:masterfrom
sunzenshen:fix-pistol-sprint-reload
Oct 26, 2026
Merged

AdamTadeusz merged 2 commits into
NeotokyoRebuild:masterfrom
sunzenshen:fix-pistol-sprint-reload

Conversation

@sunzenshen

@sunzenshen sunzenshen commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Description

Fix for a tachi/milso reload time bypass exploit through a timed sprint.


Fixes a fast reload on the Tachi and Milso. If you press reload during a sprint, or just as it ends, the magazine is refilled after about 0.6 s instead of 2.1 s, and the reload animation does not play. Players reported it as "if you sprint right before reloading the Tachi or Milso you get an ultra fast reload with no animation", and testers found it on those two pistols only.
Review page (case 8 of the animation review, first-person weapons): exported review page (Dropbox) · live review page (opening it lets you request access with your email or Claude account).

Cause

The Tachi and Milso viewmodels (both teams, and the silenced Milso) are the only NT viewmodels with a sequence node graph: idletolow goes from node 1 to node 3, lowtoidle from 3 to 1, and lowidle is node 3. They inherited this from HL2's v_pistol. Every other viewmodel keeps all its sequences on nodes 1 and 2.

When a reload starts from the lowered pose, CBaseCombatWeapon::SetIdealActivity finds lowtoidle as the transition from node 3 to the reload's node 1, and plays it first as ACT_TRANSITION. DefaultReload then sets the reload's end to curtime + SequenceDuration(). At that moment the sequence is the 0.367 s (Tachi) or 0.400 s (Milso) transition, not the 2.1 s reload, so CheckReload refills the magazine at that time on the server.

The client usually is not in the lowered sequence at that tick, so it predicts a 2.1 s reload and is then corrected by the server. A switch away and back after that early refill also brings the pistol back with a full magazine and no reload. That may explain the other report, "switch away and suddenly back, the reload animation is skipped and you can shoot immediately".

Change

SetIdealActivity already skips transitions for ACT_VM_DRAW. Under #ifdef NEO, it now skips them for ACT_VM_RELOAD too. The reload sequence then starts at once, crossfading from the lowered pose as on every other gun, and DefaultReload times the refill by the reload's own length on the server and client alike.

This is +6 lines in basecombatweapon_shared.cpp. The pistols still play their idletolow / lowtoidle transitions when sprinting starts and ends.

How to reproduce (master)

  1. Take the Tachi or Milso with a partly empty magazine, and set cl_autoreload_when_empty 0.
  2. Sprint forward, and press reload while still sprinting.
  3. The ammo count refills about 0.6 s later, with little or no reload animation.
  4. Compare with a ZR68S: the same steps give a normal 2.7 s reload.

Results

Reload pressed 0.30 or 0.10 s before the sprint key is released, or 0, 0.05, 0.10, 0.15 and 0.20 s after it. Release builds of master (f426c22) and this PR, listen server, with a harness-only reload log and the per-frame viewmodel log.

master this PR
Tachi, reload during the sprint: server reload length / refill after the start 0.367 s / 0.59-0.63 s 2.100 s / 2.31 s
Milso, reload during the sprint 0.400 s / 0.62-0.65 s 2.100 s / 2.31 s
Tachi / Milso, reload after the sprint 2.10 s 2.10 s
ZR68S, reload during the sprint (control) 2.70 s / 2.91 s the same
Tachi sequence shown lowtoidle, then the magazine is full reload from the lowered pose

The extra 0.21 s on reloads that start during a sprint comes from the existing attack block while lowered. It is the same on every gun, as the ZR68S row shows.

With net_fakelag 75, the client and server agree: both start a 2.1 s reload and refill at the same tick, in all three runs.

A Debug client with 12 bots ran 210 s each on ntre_rise_ctg and ntre_skyline_ctg. The local player sprint-reloaded the Tachi three times, then spectated. It logged only known asserts: neo_root.cpp (378) localizedWszStr at startup, plus one clientleafsystem.cpp (1025) on ntre_rise_ctg.

Trial merges with #2245 and the other open weapon PRs (empty idle, muzzle flash class change) are clean.

@sunzenshen sunzenshen added the Gunplay Gunplay elements in Neotokyo. Grenades, TTK, etc. label Oct 16, 2026
@sunzenshen

Copy link
Copy Markdown
Contributor Author

Demo comparison of the tachi case:

case8e-tachi-sprint-reload.mp4

Comment thread src/game/shared/basecombatweapon_shared.cpp Outdated
The Tachi and Milso viewmodels carry a node graph from HL2's pistol,
so a reload started from the lowered pose (during a sprint) played
the lowtoidle transition first. DefaultReload took that 0.37-0.40 s
transition as the reload length, and the server refilled the
magazine after about 0.6 s with no reload animation. Skip
transitions for ACT_VM_RELOAD as SetIdealActivity already does for
the draw, so the reload plays at once and lasts its own 2.1 s.
The reload-empty sequence is affected by the same bug as the regular
reload: if a sprint-lowered pistol starts a reload when the magazine is
empty, the lowtoidle transition would be timed instead of the full
reload-empty sequence. Guard against it now even though no weapon
currently calls SetIdealActivity(ACT_VM_RELOAD_EMPTY).
@sunzenshen
sunzenshen force-pushed the fix-pistol-sprint-reload branch from 95193cc to e8d8993 Compare October 26, 2026 03:35
@sunzenshen
sunzenshen requested review from a team and AdamTadeusz October 26, 2026 03:35
@AdamTadeusz
AdamTadeusz requested a review from a team October 26, 2026 17:39

@Agiel Agiel left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels a bit smelly to solve this here in the base class, but I guess HL2 doesn't lower the weapon on sprint so there was never a need to consider that case?

@AdamTadeusz
AdamTadeusz merged commit f246a90 into NeotokyoRebuild:master Oct 26, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Gunplay Gunplay elements in Neotokyo. Grenades, TTK, etc.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants