Repository navigation
Time a reload by its own sequence, not a transition - #2249
Merged
AdamTadeusz merged 2 commits intoOct 26, 2026
Merged
Conversation
Contributor
Author
|
Demo comparison of the tachi case: case8e-tachi-sprint-reload.mp4 |
sunzenshen
marked this pull request as ready for review
October 16, 2026 09:41
This was referenced Oct 17, 2026
sunzenshen
force-pushed
the
fix-pistol-sprint-reload
branch
from
October 21, 2026 09:47
25edc86 to
0be1a51
Compare
AdamTadeusz
reviewed
Oct 23, 2026
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
force-pushed
the
fix-pistol-sprint-reload
branch
from
October 26, 2026 03:35
95193cc to
e8d8993
Compare
AdamTadeusz
approved these changes
Oct 26, 2026
Agiel
approved these changes
Oct 26, 2026
Agiel
left a comment
Contributor
There was a problem hiding this comment.
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?
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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:
idletolowgoes from node 1 to node 3,lowtoidlefrom 3 to 1, andlowidleis node 3. They inherited this from HL2'sv_pistol. Every other viewmodel keeps all its sequences on nodes 1 and 2.When a reload starts from the lowered pose,
CBaseCombatWeapon::SetIdealActivityfindslowtoidleas the transition from node 3 to the reload's node 1, and plays it first asACT_TRANSITION.DefaultReloadthen sets the reload's end tocurtime + SequenceDuration(). At that moment the sequence is the 0.367 s (Tachi) or 0.400 s (Milso) transition, not the 2.1 s reload, soCheckReloadrefills 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
SetIdealActivityalready skips transitions forACT_VM_DRAW. Under#ifdef NEO, it now skips them forACT_VM_RELOADtoo. The reload sequence then starts at once, crossfading from the lowered pose as on every other gun, andDefaultReloadtimes 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 theiridletolow/lowtoidletransitions when sprinting starts and ends.How to reproduce (master)
cl_autoreload_when_empty 0.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.
lowtoidle, then the magazine is fullreloadfrom the lowered poseThe 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_ctgandntre_skyline_ctg. The local player sprint-reloaded the Tachi three times, then spectated. It logged only known asserts:neo_root.cpp (378) localizedWszStrat startup, plus oneclientleafsystem.cpp (1025)onntre_rise_ctg.Trial merges with #2245 and the other open weapon PRs (empty idle, muzzle flash class change) are clean.