Skip to content

Add Retro Bowl-style drag-aimed passing - #32

Merged
BrettKulp merged 3 commits into
BrettKulp:mainfrom
ChessMess:feature/drag-aimed-passing
Aug 12, 2026
Merged

BrettKulp merged 3 commits into
BrettKulp:mainfrom
ChessMess:feature/drag-aimed-passing

Conversation

@ChessMess

Copy link
Copy Markdown
Collaborator

Summary

Replaces the old click-a-receiver-for-a-70%-coin-flip pass mechanic with a Retro Bowl-style slingshot throw:

  • Pause the play, press and drag back from the QB to aim — a dotted preview and catch reticle show where the throw will land.
  • Hit Start and the ball flies the drawn path (a plain circle, no physics body — nothing collides with a pass in flight).
  • Outcome is decided on arrival by proximity, not a coin flip: the nearest eligible player to the landing spot catches it, a closer defender intercepts, and nobody in range is incomplete.
  • An interception ends the play immediately, spots the ball where it was picked off, and hands over possession on 1st down (reuses the existing turnoverOnDowns handshake so it survives save/resume).
  • Review Play now records and replays the ball's position and flight scale instead of teleporting it.

Also fixes, surfaced during review of this change:

  • Formation toggles (offense and defense) now clear a pending/in-flight pass, matching togglePlayType()'s existing behavior — repositioning players mid-aim or mid-flight could otherwise 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.

New passing config block (src/game/config.json) tunes drag sensitivity, range, ball speed, and catch radius.

Test plan

  • npm run lint passes
  • npm test passes (142/142, including 8 new PassManager unit 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:e2e passes (4/4 Playwright smoke tests)
  • Manually verified end-to-end in a live browser session (drag math, catch/interception/incomplete outcomes, mid-flight pause/resume, bare-click clearing a stale aim, the old receiver-click confirmed inert, and an interception surviving an actual page refresh) — no console errors

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.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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 PassManager to 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 (passing block) 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.

Comment thread src/game/PassManager.js
ChessMess and others added 2 commits August 12, 2026 11:51
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
BrettKulp merged commit c0c7f17 into BrettKulp:main Aug 12, 2026
1 check 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.

3 participants