diff --git a/tdkpin-rs/src/game.rs b/tdkpin-rs/src/game.rs index edb2730..f6b9277 100644 --- a/tdkpin-rs/src/game.rs +++ b/tdkpin-rs/src/game.rs @@ -540,7 +540,10 @@ impl Game { self.flipper_inputs.right_raised = controls.right_flipper && !self.tilted && !self.flipper_release_latch[1]; - if self.ball.in_launcher && !self.tilted { + // The original key handlers keep accepting the launcher key while + // tilted; Tilt disables flippers and scoring, but must not strand a + // ball that is still waiting in the shooter lane. + if self.ball.in_launcher { if controls.launch_down { if self.launcher_was_down { self.launcher_hold_seconds += frame_time.min(0.05); @@ -563,7 +566,9 @@ impl Game { } else if self.launcher_was_down { self.fire_launcher(); events.push(Event::Launch); - events.push(Event::Sound(2002)); + if !self.tilted { + events.push(Event::Sound(2002)); + } } } if !self.tilted && controls.nudge != Nudge::None { @@ -4928,4 +4933,38 @@ mod tests { assert!(game.tilted); assert!(game.ball.in_launcher); } + + #[test] + fn tilted_waiting_ball_can_still_be_launched() { + let mut game = Game::new_with_seed(1, 7); + + for _ in 0..2 { + game.update( + 0.0, + 3, + Controls { + nudge: Nudge::Right, + ..Controls::default() + }, + ); + } + assert!(game.tilted); + assert!(game.ball.in_launcher); + + game.update( + 0.0, + 3, + Controls { + launch_down: true, + ..Controls::default() + }, + ); + assert!(game.launcher_frame() > 0); + + let events = game.update(0.0, 3, Controls::default()); + assert!(events.contains(&Event::Launch)); + assert!(!events.contains(&Event::Sound(2002))); + assert!(!game.ball.in_launcher); + assert!(game.tilted); + } }