Add Retro Bowl-style drag-aimed passing - #32
Merged
Merged
Conversation
Replaces the old click-a-receiver-for-a-70%-coin-flip pass mechanic with a slingshot-style throw: pause the play, drag back from the QB to aim (a dotted preview and catch reticle show the landing spot), then hit Start and the ball flies the drawn path. Outcome is decided on arrival by proximity -- nearest eligible player to the landing spot catches it, a closer defender intercepts, nobody in range is incomplete -- rather than a coin flip. - New src/game/PassManager.js owns aim state, the ball, and its flight. - New PlayStateManager.handleInterception(), reusing the turnoverOnDowns handshake so it survives save/resume. - PlayRecorder now records/replays the ball's position and flight scale, so Review Play shows the throw instead of teleporting the ball. - Formation toggles (offense and defense) now clear a pending/in-flight pass, matching togglePlayType()'s existing behavior, so repositioning players mid-aim or mid-flight can't leave a throw resolving against stale positions. - Catching a pass re-anchors the stuck-ball-carrier trackers to the catch spot, so a checkdown/screen behind the QB's snap position isn't immediately ruled "Stuck" when the backward-drift house rule is enabled.
There was a problem hiding this comment.
Pull request overview
This PR replaces the old click-to-pass (70% coin-flip) mechanic with a Retro Bowl-style drag-aim “slingshot” passing system built around a new PassManager, and updates play resolution/replay recording to support in-flight ball visualization, catch/interception/incomplete outcomes, and interception-driven possession changes.
Changes:
- Added
PassManagerto manage aiming state, pass preview UI, ball flight, and deterministic catch/interception/incomplete resolution by proximity. - Updated play flow (
PlayStateManager,FormationManager,BaseGameScene) to integrate aiming/launching, clear pending/in-flight passes on state changes (formations/play type/possession/next play), and add interception handling/UI. - Extended replay recording (
PlayRecorder) to capture and replay ball position/scale during flight; added config tuning (passingblock) and comprehensive unit/integration tests.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| tests/unit/PlayStateManager.test.js | Adds interception and stuck-tracking regression coverage. |
| tests/unit/PassManager.test.js | New unit tests for aim projection/clamping and catch/interception/incomplete outcome logic. |
| tests/unit/FormationManager.test.js | Verifies formation toggles clear pending/in-flight pass state. |
| tests/integration/scene-boot.test.js | Integration coverage for aim state machine, flight/pause/resume, outcomes, replay recording, and regressions. |
| tests/fakes/makeFakeGame.js | Updates fake game API surface for interception UI and pass clearing. |
| src/game/scenes/BaseGameScene.js | Wires PassManager into input handlers, update loop, UI, and play/review lifecycle. |
| src/game/PlayStateManager.js | Adds interception handling and ball-carrier tracking reset; clears pass on key transitions. |
| src/game/PlayRecorder.js | Records/replays ball state per frame (position + scale) alongside players. |
| src/game/PassManager.js | New module implementing drag-aim pass preview, flight, and resolution logic. |
| src/game/FormationManager.js | Clears pending/in-flight passes when toggling formations or play type. |
| src/game/config.json | Adds passing tuning parameters, new colors, and a pass debug category. |
| CHANGELOG.md | Documents the new passing system plus related fixes and removals. |
Suppressed comments (1)
src/game/PassManager.js:175
- After the play resumes, the dotted aim preview and catch reticle should be paused-only UI. launchIfAimed() currently leaves aimGraphics drawn during the live play/ball flight until the pass resolves and clearPass() runs.
);
this.aimTarget = null;
}
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Addresses Copilot review feedback on PR BrettKulp#32: launchIfAimed() nulled aimTarget on both exit paths but never cleared aimGraphics, so the dotted preview and catch reticle stayed drawn through the entire live flight (only clearPass()/resolve() cleared them), and would persist indefinitely in the rare case where the carrier lookup fails.
BrettKulp
approved these changes
Aug 12, 2026
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.
Summary
Replaces the old click-a-receiver-for-a-70%-coin-flip pass mechanic with a Retro Bowl-style slingshot throw:
turnoverOnDownshandshake so it survives save/resume).Also fixes, surfaced during review of this change:
togglePlayType()'s existing behavior — repositioning players mid-aim or mid-flight could otherwise leave a throw resolving against stale positions.New
passingconfig block (src/game/config.json) tunes drag sensitivity, range, ball speed, and catch radius.Test plan
npm run lintpassesnpm testpasses (142/142, including 8 newPassManagerunit tests, 3 new interception tests, and 10 new integration tests covering the aim state machine, catch/interception/incomplete resolution, mid-flight pause/resume, the review-leak fix, and the two follow-up fixes above)npm run test:e2epasses (4/4 Playwright smoke tests)