fix(rules): retain active target rotation across drains

The original timer advances DAT600 target rotation only from active_ball_tick.
A normal ball end does not clear its phase, so a pending rotation pauses while
the next ball waits in the launcher and resumes after launch. Rust advanced the
phase during launcher idle and then discarded it on drain.

Gate rotation updates with the active-ball state and stop clearing rotation,
panel, or claw mechanism state in the normal drain path where the binary leaves
them untouched. Seed the deterministic target scenario with an active ball so
it continues to exercise all six recovered phases.

Test Plan:
- `cargo test --workspace --all-targets --all-features` -- 118 passed
- `cargo clippy --workspace --all-targets --all-features -- -D warnings` -- passed
- `rumdl check --flavor commonmark RECONSTRUCTION.md CHANGELOG.md` -- passed
- `git diff --cached --check` -- passed
This commit is contained in:
2026-08-23 20:01:00 +02:00
parent 6e6b7f4c58
commit a7c2779f73
4 changed files with 25 additions and 6 deletions
+20 -4
View File
@@ -525,8 +525,8 @@ impl Game {
self.update_claw(elapsed, events);
if !self.ball.in_launcher {
self.update_record_countdowns();
self.update_target_rotation(events);
}
self.update_target_rotation(events);
let panel_active = self.update_panel_completion(events);
if !self.claw.ball_suspended && !panel_active {
let had_secondary_ball = self.secondary_ball.is_some();
@@ -2075,9 +2075,6 @@ impl Game {
}
self.tilted = false;
self.tilt_counter = 0;
self.target_rotation_state = None;
self.panel_frame = None;
self.claw = Claw::default();
self.flipper_inputs = Flippers::default();
self.flippers = Flippers::default();
self.pending_flipper_edges.fill(0);
@@ -4084,6 +4081,7 @@ mod tests {
#[test]
fn target_rotation_uses_six_callbacks_and_rotates_player_state() {
let mut game = Game::new(1);
game.ball.in_launcher = false;
game.wheel_holes = [true, false, true, false, false];
game.record_contacts[129..=133].copy_from_slice(&[11, 22, 33, 44, 55]);
game.target_rotation_state = Some(0);
@@ -4101,6 +4099,24 @@ mod tests {
assert_eq!(game.target_rotation_state, None);
}
#[test]
fn target_rotation_pauses_in_launcher_and_survives_normal_drain() {
let mut game = Game::new(1);
game.target_rotation_state = Some(2);
game.timer_tick(0.030, 0, &mut Vec::new());
assert_eq!(game.target_rotation_state, Some(2));
game.ball.in_launcher = false;
game.timer_tick(0.030, 0, &mut Vec::new());
assert_eq!(game.target_rotation_state, Some(3));
game.drain(&mut Vec::new());
assert_eq!(game.target_rotation_state, Some(3));
game.timer_tick(0.030, 0, &mut Vec::new());
assert_eq!(game.target_rotation_state, Some(3));
}
#[test]
fn record_countdowns_advance_once_per_active_timer_callback() {
let mut game = Game::new(1);