fix(input): preserve Win16 flipper key latches
Normal ball-end handling keeps the original flipper key bytes and geometry unless the current player has finished. Rust instead reset both flippers on every drain, skipped the normal WAVE 2021 release edge, and treated a key still held after Tilt as a fresh press once Tilt cleared. Preserve flipper state across ordinary drains. Tilt and a player's final ball now clear only the key flags, let the timer epilogue lower raised geometry, and latch each physical key until key-up before accepting another press. This matches the original WM_KEYDOWN/WM_KEYUP lifetime while retaining modern per-frame input sampling. Test Plan: - `cargo test --workspace --all-targets --all-features` -- 119 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:
+68
-6
@@ -347,6 +347,7 @@ pub struct Game {
|
||||
player_entry: PlayerEntry,
|
||||
random: BorlandRandom,
|
||||
pending_flipper_edges: [i8; 2],
|
||||
flipper_release_latch: [bool; 2],
|
||||
record_contacts: [u16; 176],
|
||||
trigger_flags: [bool; 176],
|
||||
object_active: [bool; 176],
|
||||
@@ -390,6 +391,7 @@ impl Game {
|
||||
player_entry: PlayerEntry::Open,
|
||||
random: BorlandRandom::new(seed),
|
||||
pending_flipper_edges: [0; 2],
|
||||
flipper_release_latch: [false; 2],
|
||||
record_contacts: [0; 176],
|
||||
trigger_flags: [false; 176],
|
||||
object_active: initial_object_activity(),
|
||||
@@ -478,8 +480,22 @@ impl Game {
|
||||
self.last_collision_id = None;
|
||||
self.claw_frame_seconds = DETAIL_TIMER_SECONDS[usize::from(detail.clamp(1, 5) - 1)];
|
||||
let mut events = Vec::new();
|
||||
self.flipper_inputs.left_raised = controls.left_flipper && !self.tilted;
|
||||
self.flipper_inputs.right_raised = controls.right_flipper && !self.tilted;
|
||||
if !controls.left_flipper {
|
||||
self.flipper_release_latch[0] = false;
|
||||
} else if self.tilted {
|
||||
self.flipper_release_latch[0] = true;
|
||||
}
|
||||
if !controls.right_flipper {
|
||||
self.flipper_release_latch[1] = false;
|
||||
} else if self.tilted {
|
||||
self.flipper_release_latch[1] = true;
|
||||
}
|
||||
self.flipper_inputs.left_raised = controls.left_flipper
|
||||
&& !self.tilted
|
||||
&& !self.flipper_release_latch[0];
|
||||
self.flipper_inputs.right_raised = controls.right_flipper
|
||||
&& !self.tilted
|
||||
&& !self.flipper_release_latch[1];
|
||||
|
||||
if self.ball.in_launcher && !self.tilted {
|
||||
if controls.launch_down {
|
||||
@@ -716,6 +732,8 @@ impl Game {
|
||||
let threshold = self.random.below(10) + 30;
|
||||
if threshold < self.tilt_counter {
|
||||
self.tilted = true;
|
||||
self.flipper_release_latch[0] |= self.flipper_inputs.left_raised;
|
||||
self.flipper_release_latch[1] |= self.flipper_inputs.right_raised;
|
||||
self.flipper_inputs = Flippers::default();
|
||||
self.pending_flipper_edges.fill(0);
|
||||
events.push(Event::Tilt);
|
||||
@@ -2073,12 +2091,12 @@ impl Game {
|
||||
events.push(Event::Sound(2008));
|
||||
if let Some(score) = high_score_candidate {
|
||||
events.push(Event::HighScoreCandidate(score));
|
||||
self.flipper_release_latch[0] |= self.flipper_inputs.left_raised;
|
||||
self.flipper_release_latch[1] |= self.flipper_inputs.right_raised;
|
||||
self.flipper_inputs = Flippers::default();
|
||||
}
|
||||
self.tilted = false;
|
||||
self.tilt_counter = 0;
|
||||
self.flipper_inputs = Flippers::default();
|
||||
self.flippers = Flippers::default();
|
||||
self.pending_flipper_edges.fill(0);
|
||||
self.secondary_ball = None;
|
||||
self.score_mode = ScoreMode::Normal;
|
||||
self.launcher_charge = 0.0;
|
||||
@@ -4176,10 +4194,54 @@ mod tests {
|
||||
game.ball.position = vec2(157.0, 454.0);
|
||||
game.ball.velocity = vec2(0.0, 200.0);
|
||||
|
||||
game.update(0.030, 3, Controls::default());
|
||||
let held = Controls {
|
||||
left_flipper: true,
|
||||
..Controls::default()
|
||||
};
|
||||
game.update(0.030, 3, held);
|
||||
|
||||
assert!(!game.tilted);
|
||||
assert!(game.ball.in_launcher);
|
||||
assert!(game.flipper_release_latch[0]);
|
||||
game.update(0.0, 3, held);
|
||||
assert!(!game.flipper_inputs.left_raised);
|
||||
game.update(0.0, 3, Controls::default());
|
||||
game.update(0.0, 3, held);
|
||||
assert!(game.flipper_inputs.left_raised);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ball_end_preserves_or_releases_flippers_like_key_message_latches() {
|
||||
let mut live_player = Game::new(1);
|
||||
live_player.flipper_inputs.left_raised = true;
|
||||
live_player.flippers.left_raised = true;
|
||||
live_player.drain(&mut Vec::new());
|
||||
assert!(live_player.flipper_inputs.left_raised);
|
||||
assert!(live_player.flippers.left_raised);
|
||||
|
||||
let mut finished_player = Game::new(2);
|
||||
finished_player.players[0].balls = 1;
|
||||
finished_player.flipper_inputs.left_raised = true;
|
||||
finished_player.flippers.left_raised = true;
|
||||
finished_player.drain(&mut Vec::new());
|
||||
assert!(!finished_player.flipper_inputs.left_raised);
|
||||
assert!(finished_player.flippers.left_raised);
|
||||
assert!(finished_player.flipper_release_latch[0]);
|
||||
|
||||
let mut events = Vec::new();
|
||||
finished_player.timer_tick(0.030, 0, &mut events);
|
||||
assert!(!finished_player.flippers.left_raised);
|
||||
assert_eq!(events, [Event::FlipperMove, Event::Sound(2021)]);
|
||||
|
||||
let held = Controls {
|
||||
left_flipper: true,
|
||||
..Controls::default()
|
||||
};
|
||||
finished_player.update(0.0, 3, held);
|
||||
assert!(!finished_player.flipper_inputs.left_raised);
|
||||
finished_player.update(0.0, 3, Controls::default());
|
||||
finished_player.update(0.0, 3, held);
|
||||
assert!(finished_player.flipper_inputs.left_raised);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user