fix(divergence): release the parked ball when the last ball in play leaves

Bug: with crazy-mode balls on the table and a ball parked in the special
hole, the parked ball stayed there after every other ball had drained.
It was reported after a tilt, which drains all balls quickly because the
flippers are dead.

Cause: the special respawn only fired when the drained ball was the very
last ball in Game::balls. In crazy mode a ball is often still waiting in
the launcher (crazy mode pressed before launching, or a ball that rolled
back onto the shooter stop). That waiting ball kept the list non-empty,
so each drain simply removed its ball and the parked ball never came out.
Reproduced deterministically: 30/30 seeds ended with only the launcher
ball left and the special hole still armed.

Fix:
- special_respawn_pending(leaving) now asks whether the leaving ball is
  the last one *in play*: every other ball waits in the launcher, and no
  effect-seven ball is about to be published. It no longer looks at the
  score mode; in the original, normal scoring there meant "no second
  ball", which this expresses directly.
- lose_ball: with other balls still in the list, the drained ball is
  removed and, if it was the last one in play, the parked ball is
  released onto the table (same position, velocity, contact clearing,
  and gate suppression as the original respawn). With no other ball it
  respawns in place as before, keeping the original's continuation of
  the drained ball's response.
- A ball that leaves because it came to rest on the shooter stop while
  the launcher was occupied also releases the parked ball if it was the
  last one in play.

Tilt is untouched: it still ends only with the player's ball, and the
released ball plays under it like the original respawned ball.

Test Plan:
- cargo test parked_ball_is_released_when_only_a_waiting_ball_remains:
  a tilted game with a launcher ball and one ball in play; draining it
  releases the parked ball without costing a ball. A second case with
  another ball still in play keeps the parked ball.
- The temporary reproduction (30 tilted crazy-mode games) now releases
  the parked ball in 30/30; a 40-game crazy-mode stress run still
  finishes every game.
This commit is contained in:
ddidderr committed 2026-10-10 12:01:15 +02:00
1 parent 158936577a
commit 27d40504cc
3 files changed
+101 -23

No files matched your search

+4
View File
@@ -100,6 +100,10 @@ differences follow from treating every ball alike:
from the hole center, radially away from the hub. A capture that removes
the last ball of a multiball callback resets it to the launcher like a
single-ball capture instead of ending the ball.
- The special-hole respawn triggers when the last ball *in play* drains; balls
waiting in the launcher do not count. The original checked for exactly one
ball and normal scoring, which is the same thing when only one or two balls
exist.
- High-score entries carry a `crazy` flag that both local storage and the
shared service persist; older entries default to normal scores.