fix(game): restore deliberate plunger charging
Advance the recovered eleven-frame plunger over a full one-second hold and use that same frame selection for rendering. This makes a short tap visibly and physically weaker while a complete pull still clears the shooter lane. Test Plan: - cargo fmt --check - cargo test - cargo clippy --all-targets --all-features -- -D warnings - git diff --check
This commit is contained in:
@@ -438,8 +438,6 @@ impl App {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if game.ball.in_launcher {
|
if game.ball.in_launcher {
|
||||||
let frame = [0.05, 0.15, 0.25, 0.35, 0.45, 0.55, 0.65, 0.75, 0.85, 0.95]
|
|
||||||
.partition_point(|threshold| game.launcher_charge >= *threshold);
|
|
||||||
draw_texture_ex(
|
draw_texture_ex(
|
||||||
&self.assets.plunger,
|
&self.assets.plunger,
|
||||||
314.0,
|
314.0,
|
||||||
@@ -447,7 +445,12 @@ impl App {
|
|||||||
WHITE,
|
WHITE,
|
||||||
DrawTextureParams {
|
DrawTextureParams {
|
||||||
dest_size: Some(vec2(21.0, 18.0)),
|
dest_size: Some(vec2(21.0, 18.0)),
|
||||||
source: Some(Rect::new(frame as f32 * 21.0, 0.0, 20.0, 17.0)),
|
source: Some(Rect::new(
|
||||||
|
game.launcher_frame() as f32 * 21.0,
|
||||||
|
0.0,
|
||||||
|
20.0,
|
||||||
|
17.0,
|
||||||
|
)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
+39
-4
@@ -22,7 +22,12 @@ const BALL_SEARCH_DELAY: f32 = 3.0;
|
|||||||
const BALL_SEARCH_SPEED: f32 = 24.0;
|
const BALL_SEARCH_SPEED: f32 = 24.0;
|
||||||
const TILT_THRESHOLD: f32 = 1.15;
|
const TILT_THRESHOLD: f32 = 1.15;
|
||||||
const LAUNCHER_POSITION: Vec2 = Vec2::new(325.0, 413.0);
|
const LAUNCHER_POSITION: Vec2 = Vec2::new(325.0, 413.0);
|
||||||
const LAUNCHER_CHARGE_SECONDS: f32 = 0.55;
|
const LAUNCHER_FRAME_THRESHOLDS: [f32; 10] =
|
||||||
|
[0.05, 0.15, 0.25, 0.35, 0.45, 0.55, 0.65, 0.75, 0.85, 0.95];
|
||||||
|
// The original advances resource 901 through repeated Down-key events. A
|
||||||
|
// fixed duration keeps that deliberate hold consistent across modern systems
|
||||||
|
// whose keyboard-repeat delay and rate differ.
|
||||||
|
const LAUNCHER_CHARGE_SECONDS: f32 = 1.0;
|
||||||
const LAUNCH_SPEED_MIN: f32 = 330.0;
|
const LAUNCH_SPEED_MIN: f32 = 330.0;
|
||||||
const LAUNCH_SPEED_RANGE: f32 = 100.0;
|
const LAUNCH_SPEED_RANGE: f32 = 100.0;
|
||||||
|
|
||||||
@@ -159,6 +164,10 @@ impl Game {
|
|||||||
&self.players[self.current_player]
|
&self.players[self.current_player]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn launcher_frame(&self) -> usize {
|
||||||
|
LAUNCHER_FRAME_THRESHOLDS.partition_point(|threshold| self.launcher_charge >= *threshold)
|
||||||
|
}
|
||||||
|
|
||||||
fn fire_launcher(&mut self) {
|
fn fire_launcher(&mut self) {
|
||||||
let launch_speed = LAUNCH_SPEED_MIN + self.launcher_charge * LAUNCH_SPEED_RANGE;
|
let launch_speed = LAUNCH_SPEED_MIN + self.launcher_charge * LAUNCH_SPEED_RANGE;
|
||||||
self.ball.in_launcher = false;
|
self.ball.in_launcher = false;
|
||||||
@@ -185,7 +194,12 @@ impl Game {
|
|||||||
if self.ball.in_launcher && !self.tilted {
|
if self.ball.in_launcher && !self.tilted {
|
||||||
if controls.launch_down {
|
if controls.launch_down {
|
||||||
let charge_step = frame_time.min(0.05) / LAUNCHER_CHARGE_SECONDS;
|
let charge_step = frame_time.min(0.05) / LAUNCHER_CHARGE_SECONDS;
|
||||||
self.launcher_charge = (self.launcher_charge + charge_step).min(1.0);
|
let next_charge = self.launcher_charge + charge_step;
|
||||||
|
self.launcher_charge = if next_charge >= 1.0 - f32::EPSILON * 4.0 {
|
||||||
|
1.0
|
||||||
|
} else {
|
||||||
|
next_charge
|
||||||
|
};
|
||||||
self.launcher_was_down = true;
|
self.launcher_was_down = true;
|
||||||
} else if self.launcher_was_down {
|
} else if self.launcher_was_down {
|
||||||
self.fire_launcher();
|
self.fire_launcher();
|
||||||
@@ -649,17 +663,38 @@ mod tests {
|
|||||||
let quick_speed = -quick.ball.velocity.y;
|
let quick_speed = -quick.ball.velocity.y;
|
||||||
|
|
||||||
let mut charged = Game::new(1);
|
let mut charged = Game::new(1);
|
||||||
launch_ball(&mut charged, 33);
|
launch_ball(&mut charged, 60);
|
||||||
let charged_speed = -charged.ball.velocity.y;
|
let charged_speed = -charged.ball.velocity.y;
|
||||||
|
|
||||||
assert!(charged_speed > quick_speed + 80.0);
|
assert!(charged_speed > quick_speed + 80.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn maximum_launch_requires_a_deliberate_hold() {
|
||||||
|
let mut game = Game::new(1);
|
||||||
|
let held = Controls {
|
||||||
|
launch_down: true,
|
||||||
|
..Controls::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
for _ in 0..30 {
|
||||||
|
game.update(1.0 / 60.0, 3, held);
|
||||||
|
}
|
||||||
|
assert!((0.45..0.55).contains(&game.launcher_charge));
|
||||||
|
assert_eq!(game.launcher_frame(), 5);
|
||||||
|
|
||||||
|
for _ in 0..30 {
|
||||||
|
game.update(1.0 / 60.0, 3, held);
|
||||||
|
}
|
||||||
|
assert!((game.launcher_charge - 1.0).abs() <= f32::EPSILON);
|
||||||
|
assert_eq!(game.launcher_frame(), 10);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn charged_ball_clears_the_shooter_lane() {
|
fn charged_ball_clears_the_shooter_lane() {
|
||||||
for detail in 1..=5 {
|
for detail in 1..=5 {
|
||||||
let mut game = Game::new(1);
|
let mut game = Game::new(1);
|
||||||
launch_ball(&mut game, 33);
|
launch_ball(&mut game, 60);
|
||||||
let mut entered_table = false;
|
let mut entered_table = false;
|
||||||
let mut returned_to_launcher = false;
|
let mut returned_to_launcher = false;
|
||||||
let mut minimum_y = game.ball.position.y;
|
let mut minimum_y = game.ball.position.y;
|
||||||
|
|||||||
Reference in New Issue
Block a user