fmt: just fmt (rust only)
Build TDK Pinball / build (macos-latest) (push) Canceled after 0s
Build TDK Pinball / build (ubuntu-latest) (push) Canceled after 0s
Build TDK Pinball / build (windows-latest) (push) Canceled after 0s

This commit is contained in:
2026-08-29 09:57:39 +02:00
parent ee58525011
commit c2f1443436
10 changed files with 386 additions and 328 deletions
+142 -172
View File
@@ -1,20 +1,34 @@
use macroquad::prelude::{Rect, Vec2, vec2};
use crate::{
borland_random::BorlandRandom,
flipper_physics::{FlipperSide, moving_flipper_response},
geometry::Segment,
original_physics::{
CollisionMaterial, CollisionResponse, GRAVITY_MILLI_PER_STEP,
MAXIMUM_SPEED_MILLI_PER_STEP, MilliVec, StaticCollisionCandidate,
ball_collision_response, capture_collision_candidate, circle_collision_candidate_at,
line_collision_candidate_at, milli_distance, path_intersects_circle,
CollisionMaterial,
CollisionResponse,
GRAVITY_MILLI_PER_STEP,
MAXIMUM_SPEED_MILLI_PER_STEP,
MilliVec,
StaticCollisionCandidate,
ball_collision_response,
capture_collision_candidate,
circle_collision_candidate_at,
line_collision_candidate_at,
milli_distance,
path_intersects_circle,
},
real48::Real48,
table::{
BUMPERS, EFFECT_SENSOR, LOCK_HOLES, PASSIVE_CIRCLES, TARGET_SENSORS, WALLS,
BUMPERS,
EFFECT_SENSOR,
LOCK_HOLES,
PASSIVE_CIRCLES,
SPECIAL_HOLE_SENSOR,
TARGET_SENSORS,
WALLS,
},
};
use macroquad::prelude::{Rect, Vec2, vec2};
const LEFT_FLIPPER_RAISED_TIP: Vec2 = Vec2::new(133.0, 377.0);
const RIGHT_FLIPPER_RAISED_TIP: Vec2 = Vec2::new(181.0, 376.0);
@@ -483,7 +497,8 @@ impl Game {
let variation = self.random.below(3_800) / 40;
launch_velocity = -MAXIMUM_SPEED_MILLI_PER_STEP + i32::from(variation);
} else if launch_velocity > -2_280 {
launch_velocity = launch_velocity.wrapping_sub(i32::from(self.random.below(3_800) / 40));
launch_velocity =
launch_velocity.wrapping_sub(i32::from(self.random.below(3_800) / 40));
}
self.ball.in_launcher = false;
self.ball.velocity = MilliVec {
@@ -516,12 +531,10 @@ impl Game {
} 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];
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 {
@@ -576,10 +589,7 @@ impl Game {
self.score_mode = ScoreMode::Multiball;
}
for _ in 0..substeps {
if self.fixed_update(events)
|| self.finished
|| self.claw.ball_suspended
{
if self.fixed_update(events) || self.finished || self.claw.ball_suspended {
break;
}
}
@@ -609,22 +619,18 @@ impl Game {
}
}
self.pending_flipper_edges[0] = match (
self.flipper_inputs.left_raised,
self.flippers.left_raised,
) {
(true, false) => -1,
(false, true) => 1,
_ => 0,
};
self.pending_flipper_edges[1] = match (
self.flipper_inputs.right_raised,
self.flippers.right_raised,
) {
(true, false) => -1,
(false, true) => 1,
_ => 0,
};
self.pending_flipper_edges[0] =
match (self.flipper_inputs.left_raised, self.flippers.left_raised) {
(true, false) => -1,
(false, true) => 1,
_ => 0,
};
self.pending_flipper_edges[1] =
match (self.flipper_inputs.right_raised, self.flippers.right_raised) {
(true, false) => -1,
(false, true) => 1,
_ => 0,
};
self.flippers = self.flipper_inputs;
for edge in self.pending_flipper_edges {
if edge != 0 {
@@ -706,7 +712,11 @@ impl Game {
match nudge {
Nudge::Left | Nudge::Right => {
let amount = (50 - i32::from(self.random.below(20))) * SCALAR;
let signed = if nudge == Nudge::Left { -amount } else { amount };
let signed = if nudge == Nudge::Left {
-amount
} else {
amount
};
velocity.x = velocity.x.wrapping_add(signed);
}
Nudge::Center => {
@@ -726,12 +736,10 @@ impl Game {
}
if let Some(secondary) = &mut self.secondary_ball {
let slot_impulse = match nudge {
Nudge::Left | Nudge::Right => {
MilliVec {
x: if nudge == Nudge::Left { -600 } else { 600 },
y: 0,
}
}
Nudge::Left | Nudge::Right => 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;
@@ -1070,13 +1078,7 @@ impl Game {
best = Some((id, false, candidate));
}
ball.velocity = velocity.to_velocity_per_second();
(
best,
SensorScanResult {
action,
},
predicted,
)
(best, SensorScanResult { action }, predicted)
}
fn find_static_collision_candidate_in_range(
@@ -1317,12 +1319,7 @@ impl Game {
collided || scan.action == BallAction::Suspend
}
fn apply_bumper_rule(
&mut self,
object_id: u8,
auxiliary_fired: bool,
events: &mut Vec<Event>,
) {
fn apply_bumper_rule(&mut self, object_id: u8, auxiliary_fired: bool, events: &mut Vec<Event>) {
if self.tilted || !BUMPERS.iter().any(|bumper| bumper.id == object_id) {
return;
}
@@ -1504,11 +1501,7 @@ impl Game {
if !self.tilted
&& (TARGET_SENSORS.into_iter().any(|sensor| {
self.object_active[usize::from(sensor.id)]
&& trigger_broadphase_contains(
predicted_position,
sensor.center,
sensor.radius,
)
&& trigger_broadphase_contains(predicted_position, sensor.center, sensor.radius)
}) || self.object_active[usize::from(EFFECT_SENSOR.id)]
&& trigger_broadphase_contains(
predicted_position,
@@ -1549,56 +1542,32 @@ impl Game {
}
}
if let Some(completed_action) =
self.check_lock_holes(
ball,
ball_number,
ball_count,
old_position,
predicted_position,
&mut capture_candidate,
events,
)
{
action = completed_action;
}
self.check_target_sensors(
ball,
old_position,
movement_velocity,
140..=147,
events,
);
if let Some(completed_action) =
self.check_special_hole(
ball,
ball_number,
ball_count,
old_position,
predicted_position,
&mut capture_candidate,
events,
)
{
action = completed_action;
}
self.check_effect_sensor(
if let Some(completed_action) = self.check_lock_holes(
ball,
ball_number,
ball_count,
old_position,
movement_velocity,
predicted_position,
&mut capture_candidate,
events,
);
self.check_target_sensors(
ball,
old_position,
movement_velocity,
150..=152,
events,
);
SensorScanResult {
action,
) {
action = completed_action;
}
self.check_target_sensors(ball, old_position, movement_velocity, 140..=147, events);
if let Some(completed_action) = self.check_special_hole(
ball,
ball_number,
ball_count,
old_position,
predicted_position,
&mut capture_candidate,
events,
) {
action = completed_action;
}
self.check_effect_sensor(ball, ball_number, old_position, movement_velocity, events);
self.check_target_sensors(ball, old_position, movement_velocity, 150..=152, events);
SensorScanResult { action }
}
#[allow(clippy::cast_possible_truncation, clippy::too_many_arguments)]
@@ -1858,7 +1827,8 @@ impl Game {
self.players[self.current_player].secondary_score = 0;
}
7 if self.record_contacts[usize::from(SPECIAL_HOLE_SENSOR.id)] != 0
&& self.record_contacts[usize::from(SPECIAL_HOLE_SENSOR.id)] != ball_number
&& self.record_contacts[usize::from(SPECIAL_HOLE_SENSOR.id)]
!= ball_number
&& self.secondary_ball.is_none()
&& matches!(
self.multiball_state,
@@ -1999,10 +1969,7 @@ impl Game {
.round_i32()
.wrapping_neg();
let predicted = old_position.add(*velocity);
if predicted.x < min_x
|| predicted.x > max_x
|| predicted.y < min_y
|| predicted.y > max_y
if predicted.x < min_x || predicted.x > max_x || predicted.y < min_y || predicted.y > max_y
{
self.object_active[object_id] = false;
}
@@ -2225,7 +2192,10 @@ impl Game {
fn special_respawn_pending(&self) -> bool {
self.secondary_ball.is_none()
&& self.score_mode == ScoreMode::Normal
&& matches!(self.multiball_state, MultiballState::Ready | MultiballState::Active)
&& matches!(
self.multiball_state,
MultiballState::Ready | MultiballState::Active
)
}
fn save_current_rule_state(&mut self) {
@@ -2336,16 +2306,14 @@ fn claw_release(frame: u8) -> (Vec2, Vec2) {
fn apply_flipper_response_to_ball(ball: &mut Ball, delta: i32, side: FlipperSide) {
let position = MilliVec::from_position(ball.position);
let velocity = MilliVec::from_velocity_per_second(ball.velocity);
if let Some(response) =
moving_flipper_response(
position,
velocity,
delta,
side,
Real48::from_bytes([0x80, 0, 0, 0, 0, 0]),
MAXIMUM_SPEED_MILLI_PER_STEP,
)
{
if let Some(response) = moving_flipper_response(
position,
velocity,
delta,
side,
Real48::from_bytes([0x80, 0, 0, 0, 0, 0]),
MAXIMUM_SPEED_MILLI_PER_STEP,
) {
ball.position = position.add(response.movement).to_position();
ball.velocity = response.velocity.to_velocity_per_second();
}
@@ -2379,11 +2347,7 @@ mod tests {
game.ball.in_launcher = false;
game.ball.position = center;
game.ball.velocity = Vec2::ZERO;
game.check_sensor_objects(
MilliVec::from_position(center),
MilliVec::default(),
events,
);
game.check_sensor_objects(MilliVec::from_position(center), MilliVec::default(), events);
}
}
@@ -2397,7 +2361,8 @@ mod tests {
) -> CaptureStep {
let mut ball = game.ball;
let mut capture_candidate = None;
let predicted_position = previous_position.add(MilliVec::from_velocity_per_second(ball.velocity));
let predicted_position =
previous_position.add(MilliVec::from_velocity_per_second(ball.velocity));
let result = game.capture_record_step(
&mut ball,
1,
@@ -2456,8 +2421,8 @@ mod tests {
game.object_active[usize::from(wall.id)] = true;
game.ball.in_launcher = false;
game.ball.position = midpoint - normal;
game.ball.velocity = MilliVec::from_velocity_per_second(normal * 200.0)
.to_velocity_per_second();
game.ball.velocity =
MilliVec::from_velocity_per_second(normal * 200.0).to_velocity_per_second();
game.fixed_update(&mut Vec::new());
if game.last_collision_id != Some(wall.id) {
missed_walls.push(wall.id);
@@ -2478,7 +2443,10 @@ mod tests {
}
}
assert!(missed_walls.is_empty(), "missed production wall records: {missed_walls:?}");
assert!(
missed_walls.is_empty(),
"missed production wall records: {missed_walls:?}"
);
assert!(
missed_circles.is_empty(),
"missed production circle records: {missed_circles:?}"
@@ -2869,9 +2837,11 @@ mod tests {
assert_eq!(special.multiball_state, MultiballState::Unavailable);
assert_eq!(special.record_contacts[148], 0);
assert!(!special_events.contains(&Event::Drain));
assert!(!special_events
.iter()
.any(|event| event.sound_resource().is_some()));
assert!(
!special_events
.iter()
.any(|event| event.sound_resource().is_some())
);
}
#[test]
@@ -3179,10 +3149,7 @@ mod tests {
);
assert_eq!(
MilliVec::from_velocity_per_second(game.ball.velocity),
MilliVec {
x: 0,
y: 3_055,
}
MilliVec { x: 0, y: 3_055 }
);
assert_eq!(game.special_hole_gate, SpecialHoleGate::Suppressed);
@@ -3255,10 +3222,7 @@ mod tests {
);
assert_eq!(
MilliVec::from_velocity_per_second(game.ball.velocity),
MilliVec {
x: -302,
y: -1_809,
}
MilliVec { x: -302, y: -1_809 }
);
assert_eq!(game.multiball_state, MultiballState::Unavailable);
assert_eq!(game.special_hole_gate, SpecialHoleGate::Suppressed);
@@ -3297,9 +3261,7 @@ mod tests {
spin: Real48::ZERO,
capture_age: 0,
};
let old_position = MilliVec::from_position(
sensor.center - vec2(sensor.radius + 1.0, 0.0),
);
let old_position = MilliVec::from_position(sensor.center - vec2(sensor.radius + 1.0, 0.0));
let current_position = MilliVec::from_position(sensor.center);
let movement = MilliVec {
x: current_position.x.wrapping_sub(old_position.x),
@@ -3391,7 +3353,10 @@ mod tests {
BallAction::Keep
);
assert_eq!(game.secondary_ball.map(|ball| ball.position), Some(secondary.position));
assert_eq!(
game.secondary_ball.map(|ball| ball.position),
Some(secondary.position)
);
assert_eq!(game.multiball_state, MultiballState::Active);
assert_eq!(game.target_effect, 0);
}
@@ -3511,7 +3476,6 @@ mod tests {
assert!(game.secondary_ball.is_none());
assert_eq!(game.player().balls, 3);
assert_eq!(game.score_mode, ScoreMode::Normal);
}
#[test]
@@ -3605,10 +3569,7 @@ mod tests {
assert!(game.fixed_update(&mut events));
assert_eq!(game.last_collision_id, Some(57));
assert_eq!(
game.ball.spin.bytes(),
[0x83, 0x5d, 0x8f, 0xc2, 0xf5, 0xa8]
);
assert_eq!(game.ball.spin.bytes(), [0x83, 0x5d, 0x8f, 0xc2, 0xf5, 0xa8]);
assert_eq!(
MilliVec::from_position(game.ball.position),
MilliVec {
@@ -3645,10 +3606,7 @@ mod tests {
assert!(game.fixed_update(&mut events));
assert_eq!(game.last_collision_id, Some(72));
assert_eq!(
game.ball.spin.bytes(),
[0x83, 0x86, 0xeb, 0x51, 0xb8, 0x2e]
);
assert_eq!(game.ball.spin.bytes(), [0x83, 0x86, 0xeb, 0x51, 0xb8, 0x2e]);
assert_eq!(
MilliVec::from_position(game.ball.position),
MilliVec {
@@ -3658,7 +3616,10 @@ mod tests {
);
assert_eq!(
MilliVec::from_velocity_per_second(game.ball.velocity),
MilliVec { x: -2_095, y: 1_669 }
MilliVec {
x: -2_095,
y: 1_669
}
);
}
@@ -3928,8 +3889,7 @@ mod tests {
#[test]
fn raised_flipper_tips_use_their_asymmetric_swept_bounds() {
for (object_id, center) in [(67_u8, vec2(133.0, 377.0)), (82, vec2(181.0, 376.0))]
{
for (object_id, center) in [(67_u8, vec2(133.0, 377.0)), (82, vec2(181.0, 376.0))] {
let mut game = Game::new(1);
game.object_active.fill(false);
game.object_active[usize::from(object_id)] = true;
@@ -4012,7 +3972,13 @@ mod tests {
let velocity_after_edge = game.ball.velocity;
assert_eq!(game.ball.position, vec2(138.124, 355.0));
let raw_velocity = MilliVec::from_velocity_per_second(velocity_after_edge);
assert_eq!(raw_velocity, MilliVec { x: 2_209, y: -5_891 });
assert_eq!(
raw_velocity,
MilliVec {
x: 2_209,
y: -5_891
}
);
assert_eq!(game.pending_flipper_edges, [0, 0]);
game.apply_flipper_kicks();
@@ -4102,20 +4068,20 @@ mod tests {
#[test]
fn tilt_counter_decays_once_per_selected_detail_callback() {
for (detail, interval) in [
(1, 0.050),
(2, 0.040),
(3, 0.030),
(4, 0.020),
(5, 0.010),
] {
for (detail, interval) in [(1, 0.050), (2, 0.040), (3, 0.030), (4, 0.020), (5, 0.010)] {
let mut game = Game::new(1);
game.tilt_counter = 2;
game.update(interval - 0.001, detail, Controls::default());
assert_eq!(game.tilt_counter, 2, "detail {detail} decayed before its callback");
assert_eq!(
game.tilt_counter, 2,
"detail {detail} decayed before its callback"
);
game.update(0.001_1, detail, Controls::default());
assert_eq!(game.tilt_counter, 1, "detail {detail} did not decay at its callback");
assert_eq!(
game.tilt_counter, 1,
"detail {detail} did not decay at its callback"
);
}
}
@@ -4462,11 +4428,7 @@ mod tests {
5
);
complete_stationary_type_three_capture(
&mut game,
SPECIAL_HOLE_SENSOR.center,
&mut events,
);
complete_stationary_type_three_capture(&mut game, SPECIAL_HOLE_SENSOR.center, &mut events);
assert!(game.wheel_holes.iter().all(|filled| *filled));
assert_eq!(game.multiball_state, MultiballState::Ready);
assert_eq!(game.record_contacts[148], 2);
@@ -4696,7 +4658,11 @@ mod tests {
assert_eq!(game.panel_frame, None);
assert_eq!(game.wheel_holes, [false; 5]);
assert!(game.record_contacts[129..=133].iter().all(|contact| *contact == 0));
assert!(
game.record_contacts[129..=133]
.iter()
.all(|contact| *contact == 0)
);
assert_eq!(
events,
[
@@ -4758,7 +4724,11 @@ mod tests {
game.record_countdowns[51] = 5;
game.timer_tick(0.030, 0, &mut Vec::new());
assert_eq!(game.record_countdown(51), 5, "launcher idle pauses countdowns");
assert_eq!(
game.record_countdown(51),
5,
"launcher idle pauses countdowns"
);
game.ball.in_launcher = false;
for expected in (0..5).rev() {