fix(input): restore multiball nudge slot updates

Preserve the Win16 key path's two-stage multiball behavior: consume the active-window random formula, then apply one separately recovered slot impulse to both saved ball velocities. This restores the original RNG advancement and prevents ball one and ball two from receiving invented different nudges.

Test Plan:
- cargo test --all-targets
- cargo clippy --all-targets --all-features -- -D warnings
- rumdl check CHANGELOG.md RECONSTRUCTION.md README.md
- git diff --check
This commit is contained in:
ddidderr committed 2026-08-23 18:16:09 +02:00
1 parent 3d5a105c10
commit 0661665374
3 files changed
+66 -12

No files matched your search

+62 -11
View File
@@ -622,24 +622,34 @@ impl Game {
}
Nudge::None => return,
}
self.ball.velocity = velocity.to_velocity_per_second();
if let Some(secondary) = &mut self.secondary_ball {
let mut slot_velocity = MilliVec::from_velocity_per_second(secondary.velocity);
match nudge {
let slot_impulse = match nudge {
Nudge::Left | Nudge::Right => {
let amount = if nudge == Nudge::Left { -600 } else { 600 };
slot_velocity.x = slot_velocity.x.wrapping_add(amount);
MilliVec {
x: if nudge == Nudge::Left { -600 } else { 600 },
y: 0,
}
}
Nudge::Center => {
let horizontal = (i32::from(self.random.below(21)) - 10) * SCALAR;
let vertical = -(i32::from(self.random.below(100)) + 50) * SCALAR;
slot_velocity.x = slot_velocity.x.wrapping_add(horizontal);
slot_velocity.y = slot_velocity.y.wrapping_add(vertical);
MilliVec {
x: horizontal,
y: vertical,
}
}
Nudge::None => {}
}
secondary.velocity = slot_velocity.to_velocity_per_second();
Nudge::None => MilliVec::default(),
};
let mut primary_slot = MilliVec::from_velocity_per_second(self.ball.velocity);
primary_slot.x = primary_slot.x.wrapping_add(slot_impulse.x);
primary_slot.y = primary_slot.y.wrapping_add(slot_impulse.y);
self.ball.velocity = primary_slot.to_velocity_per_second();
let mut secondary_slot = MilliVec::from_velocity_per_second(secondary.velocity);
secondary_slot.x = secondary_slot.x.wrapping_add(slot_impulse.x);
secondary_slot.y = secondary_slot.y.wrapping_add(slot_impulse.y);
secondary.velocity = secondary_slot.to_velocity_per_second();
} else {
self.ball.velocity = velocity.to_velocity_per_second();
}
self.nudge_shake = 0.18;
@@ -2690,6 +2700,47 @@ mod tests {
assert_eq!(game.tilt_counter, 24);
}
#[test]
fn multiball_nudge_consumes_active_draws_then_updates_both_saved_slots() {
let mut game = Game::new_with_seed(1, 7);
game.ball.in_launcher = false;
game.ball.velocity = MilliVec { x: 100, y: 200 }.to_velocity_per_second();
game.secondary_ball = Some(Ball {
position: vec2(200.0, 200.0),
velocity: MilliVec { x: -300, y: 400 }.to_velocity_per_second(),
in_launcher: false,
spin: Real48::ZERO,
capture_age: 0,
});
let mut reference = BorlandRandom::new(7);
let _discarded_active_x = reference.real48();
let _discarded_active_y = reference.below(20);
let shared_impulse = MilliVec {
x: (i32::from(reference.below(21)) - 10) * 15,
y: -(i32::from(reference.below(100)) + 50) * 15,
};
let _tilt_threshold = reference.below(10);
game.apply_nudge(Nudge::Center, &mut Vec::new());
assert_eq!(
MilliVec::from_velocity_per_second(game.ball.velocity),
MilliVec {
x: 100 + shared_impulse.x,
y: 200 + shared_impulse.y,
}
);
let secondary = game.secondary_ball.expect("slot two must remain active");
assert_eq!(
MilliVec::from_velocity_per_second(secondary.velocity),
MilliVec {
x: -300 + shared_impulse.x,
y: 400 + shared_impulse.y,
}
);
assert_eq!(game.random.seed(), reference.seed());
}
#[test]
fn claw_sprite_rects_follow_the_recovered_four_row_sheet() {
let source = |frame, bank| {