diff --git a/tdkpin-rs/src/app.rs b/tdkpin-rs/src/app.rs index 3cf0928..e161057 100644 --- a/tdkpin-rs/src/app.rs +++ b/tdkpin-rs/src/app.rs @@ -135,9 +135,14 @@ impl App { self.assets.play(2012, self.saved.settings.sounds); } if is_key_pressed(KeyCode::Down) || is_key_pressed(KeyCode::Enter) { - self.game = Some(Game::new(self.player_count)); + let mut game = Game::new(self.player_count); + let launched = game.launch(); + self.game = Some(game); self.screen = Screen::Playing; self.assets.play(2008, self.saved.settings.sounds); + if launched { + self.assets.play(2016, self.saved.settings.sounds); + } } } @@ -196,6 +201,7 @@ impl App { Event::Media => 2022, Event::ExtraBall => 2019, Event::Nudge | Event::BallSearch => 2021, + Event::Tilt => 2015, Event::Drain => 2013, }; self.assets.play(id, self.saved.settings.sounds); @@ -481,8 +487,8 @@ impl App { ); self.draw_displays(game); - if game.tilt > 1.15 { - draw_centered("TILT", 263.0, 34, RED); + if game.tilted { + draw_centered_at("TILT", 160.0, 263.0, 30, RED); } draw_text("F1 HELP F2 SETUP", 345.0, 448.0, 11.0, BLACK); } diff --git a/tdkpin-rs/src/game.rs b/tdkpin-rs/src/game.rs index 847a841..e662553 100644 --- a/tdkpin-rs/src/game.rs +++ b/tdkpin-rs/src/game.rs @@ -7,6 +7,7 @@ const GRAVITY: f32 = 135.0; const MAX_SPEED: f32 = 430.0; const BALL_SEARCH_DELAY: f32 = 3.0; const BALL_SEARCH_SPEED: f32 = 24.0; +const TILT_THRESHOLD: f32 = 1.15; #[derive(Clone, Copy, Debug, Default)] pub struct Controls { @@ -28,6 +29,7 @@ pub enum Event { Media, ExtraBall, Nudge, + Tilt, BallSearch, Drain, } @@ -83,7 +85,7 @@ pub struct Game { pub top_targets: [bool; 3], pub lock_lights: u8, pub magnets: f32, - pub tilt: f32, + pub tilted: bool, pub left_flipper_angle: f32, pub right_flipper_angle: f32, pub finished: bool, @@ -91,6 +93,7 @@ pub struct Game { target_cooldown: f32, bumper_cooldown: f32, nudge_cooldown: f32, + nudge_meter: f32, stalled_for: f32, } @@ -106,7 +109,7 @@ impl Game { top_targets: [false; 3], lock_lights: 0, magnets: 0.0, - tilt: 0.0, + tilted: false, left_flipper_angle: -2.72, right_flipper_angle: -0.42, finished: false, @@ -114,6 +117,7 @@ impl Game { target_cooldown: 0.0, bumper_cooldown: 0.0, nudge_cooldown: 0.0, + nudge_meter: 0.0, stalled_for: 0.0, } } @@ -122,13 +126,31 @@ impl Game { &self.players[self.current_player] } + pub fn launch(&mut self) -> bool { + if !self.ball.in_launcher || self.tilted { + return false; + } + self.ball.in_launcher = false; + self.ball.velocity = vec2(12.0, -330.0); + self.stalled_for = 0.0; + true + } + pub fn update(&mut self, frame_time: f32, detail: u8, controls: Controls) -> Vec { if self.finished { return Vec::new(); } let mut events = Vec::new(); - let desired_left = if controls.left_flipper { -2.18 } else { -2.72 }; - let desired_right = if controls.right_flipper { -0.96 } else { -0.42 }; + let desired_left = if controls.left_flipper && !self.tilted { + -2.18 + } else { + -2.72 + }; + let desired_right = if controls.right_flipper && !self.tilted { + -0.96 + } else { + -0.42 + }; let flipper_rate = 14.0 * frame_time; let old_left = self.left_flipper_angle; let old_right = self.right_flipper_angle; @@ -136,23 +158,27 @@ impl Game { (desired_left - self.left_flipper_angle).clamp(-flipper_rate, flipper_rate); self.right_flipper_angle += (desired_right - self.right_flipper_angle).clamp(-flipper_rate, flipper_rate); - if (controls.left_flipper && old_left < -2.68) - || (controls.right_flipper && old_right > -0.46) + if !self.tilted + && ((controls.left_flipper && old_left < -2.68) + || (controls.right_flipper && old_right > -0.46)) { events.push(Event::Flipper); } - if controls.launch_pressed && self.ball.in_launcher { - self.ball.in_launcher = false; - self.ball.velocity = vec2(12.0, -330.0); - self.stalled_for = 0.0; + if controls.launch_pressed && self.launch() { events.push(Event::Launch); } - if controls.nudge.abs() > 0.1 && self.nudge_cooldown <= 0.0 { + if !self.tilted && controls.nudge.abs() > 0.1 && self.nudge_cooldown <= 0.0 { self.ball.velocity.x += controls.nudge * 55.0; - self.tilt += controls.nudge.abs() * 0.34; + self.nudge_meter += controls.nudge.abs() * 0.34; self.nudge_cooldown = 0.22; - events.push(Event::Nudge); + if self.nudge_meter >= TILT_THRESHOLD { + self.tilted = true; + self.bonus = 0; + events.push(Event::Tilt); + } else { + events.push(Event::Nudge); + } } self.accumulator = (self.accumulator + frame_time.min(0.05)).min(0.1); @@ -171,7 +197,9 @@ impl Game { self.bumper_cooldown = (self.bumper_cooldown - dt).max(0.0); self.nudge_cooldown = (self.nudge_cooldown - dt).max(0.0); self.magnets = (self.magnets - dt).max(0.0); - self.tilt = (self.tilt - dt * 0.08).max(0.0); + if !self.tilted { + self.nudge_meter = (self.nudge_meter - dt * 0.34).max(0.0); + } if self.ball.in_launcher { self.ball.position = vec2(157.0, 426.0); @@ -216,15 +244,15 @@ impl Game { } for center in [vec2(207.0, 109.0), vec2(167.0, 148.0), vec2(219.0, 167.0)] { - if circle_collision( + let hit = circle_collision( &mut self.ball.position, &mut self.ball.velocity, BALL_RADIUS, center, 14.0, 105.0, - ) && self.bumper_cooldown <= 0.0 - { + ); + if hit && !self.tilted && self.bumper_cooldown <= 0.0 { self.add_score(self.player().bumper_value); self.bonus = self.bonus.saturating_add(100); self.bumper_cooldown = 0.08; @@ -232,8 +260,10 @@ impl Game { } } - self.check_targets(events); - self.check_media(events); + if !self.tilted { + self.check_targets(events); + self.check_media(events); + } if self.ball.position.y > 454.0 { if self.magnets > 0.0 && (self.ball.position.x < 145.0 || self.ball.position.x > 175.0) @@ -408,7 +438,8 @@ impl Game { self.wheel_holes.fill(false); self.lock_lights = 0; self.magnets = 0.0; - self.tilt = 0.0; + self.tilted = false; + self.nudge_meter = 0.0; self.stalled_for = 0.0; let mut next = (self.current_player + 1) % self.players.len(); @@ -613,4 +644,56 @@ mod tests { assert!(events.contains(&Event::BallSearch)); assert!(game.ball.velocity.y < 0.0); } + + #[test] + fn tilt_latches_and_disables_flippers_and_scoring() { + let mut game = Game::new(1); + game.bonus = 4_000; + for _ in 0..4 { + game.nudge_cooldown = 0.0; + game.update( + 1.0 / 60.0, + 3, + Controls { + nudge: 1.0, + ..Controls::default() + }, + ); + } + assert!(game.tilted); + assert_eq!(game.bonus, 0); + + game.left_flipper_angle = -2.18; + game.right_flipper_angle = -0.96; + let score = game.player().score; + game.ball.in_launcher = false; + game.ball.position = vec2(201.0, 285.0); + game.ball.velocity = Vec2::ZERO; + game.update( + 1.0 / 30.0, + 5, + Controls { + left_flipper: true, + right_flipper: true, + ..Controls::default() + }, + ); + + assert!(game.left_flipper_angle < -2.18); + assert!(game.right_flipper_angle > -0.96); + assert_eq!(game.player().score, score); + } + + #[test] + fn drain_clears_latched_tilt() { + let mut game = Game::new(1); + game.tilted = true; + game.ball.in_launcher = false; + game.ball.position = vec2(157.0, 455.0); + + game.update(1.0 / 60.0, 3, Controls::default()); + + assert!(!game.tilted); + assert!(game.ball.in_launcher); + } }