Skip to content

fix(fp): a hit-stop never ends before its second frame (low fps caps) - #122

Merged
KunalSiyag merged 2 commits into
mainfrom
droid/round4-hitstop-floor
Oct 28, 2026
Merged

KunalSiyag merged 2 commits into
mainfrom
droid/round4-hitstop-floor

Conversation

@KunalSiyag

Copy link
Copy Markdown
Owner

What

Round-4 impact item 7 — the hit-feel review at the 20-25 fps browser cap, and the one fix it found: short hit-stops vanished at coarse frame times. No flag (a feel fix, no gameplay change); everything else in the hit-feel path already holds up at coarse dt.

The review

Piece At 20-25 fps?
Hit-stop clock Was broken for short stops — real-time charged per rendered frame, and the frame the blow lands on charges the whole frame's span, so stops of 0.03-0.045 s (bite, weak animal hit, weakest punch at 25 fps) were spent before one frozen frame rendered
Shake trauma decay Frame-rate independent (linear in dt)
View kick decay Frame-rate independent (exponential half-life)
Frame hitch dt clamped to MAX_FRAME_DELTA (0.1 s)
Shake wobble 9 Hz + 15.6 Hz components alias past the 10 Hz Nyquist at 20 fps — reads as harsher jitter, not a lost effect. Noted; not changed (taste call, wants eyes on the windowed build)

The fix

A stop never ends before its second tick(): one frame's overdraft (1e-4 s) holds it open until then. Every stop now spans the frame it lands on plus one full frozen frame at any frame rate. At 60 fps behavior is unchanged — three 16 ms ticks already span two frames, and the stop durations are untouched.

Tests

  • New _check_low_fps: at 50 ms frames a 0.04 s stop still holds after the landing frame and ends a frame later; at 60 fps the same stop still reads as a few frames.
  • _real() now ticks in frame-sized steps, as production does (a single coarse call previously let one "frame" charge a whole second).
  • fp_hitfeel green; run_all + pre-commit green; docs/api regenerated for the hitfeel page; ARCHITECTURE's hit-feel row carries the clause. Gate on CI per the freeze terms.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>

Review of hit feel at the browser's 20-25 fps cap: the stop's clock is real
time charged per rendered frame, and the frame a blow lands on charges the
whole frame's span — so at 40-50 ms frames the short stops (a bite's 0.04 s,
a weak animal hit's 0.03 s, at 25 fps even the weakest punch's 0.045 s) were
spent before one frozen frame was drawn, and the stop vanished. Now a stop
never ends before its second tick: one frame's overdraft holds it open, so
every stop spans the frame it lands on plus one full frozen one. At 60 fps
nothing changes (three 16 ms ticks already span two frames); at 20-25 fps
every stop reads. Shake and kick decay were already frame-rate independent
(exponential and linear in dt, clamped to MAX_FRAME_DELTA); the shake's
9 Hz/15.6 Hz wobble aliases past Nyquist at 20 fps, which reads as harsher
jitter, not a lost effect — noted, not changed.

fp_hitfeel's _real helper now ticks per frame, as production does, and a new
_check_low_fps pins the floor at 20 fps and the unchanged few-frames read at
60 fps.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
@github-actions

Copy link
Copy Markdown

Automated review

No new findings on the 4 changed files (gdlint, complexity, TODO policy, dead code).

Run python3 tools/checks/run_all.py locally for the full set of checks.

fp_animal_hits drives the stop clock with one coarse tick(1.0) after a blow;
the two-frame floor held that stop open and the scale never came back. A
single frame longer than STOP_MAX has already displayed the frozen world for
longer than any stop asks (a real hitch does the same), so it ends the stop
on its own. The floor still covers the case it was for: frame-sized ticks at
a 20-25 fps cap.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
@KunalSiyag
KunalSiyag merged commit 8f6038a into main Oct 28, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant