fix(game): restore physical target-bank rules
Attach recovered score and rule flags to type-2 collision records. Deactivate complete three-line groups through their contact owner, rearm all five bumper-value groups after completion, and rearm all four central groups after advancing the TDK diamond. Remove the broad side and colored-target coordinate checks those physical records replace. Preserve exact 5,000, 100,000, bank, and wheel-trigger scores from the initialized object ledger. Test Plan: - `cargo test --all-targets` -- 46 passed - `cargo clippy --all-targets -- -D warnings` -- passed - `cargo build --profile production` -- passed - target-bank deactivation, completion, and rearm test -- passed - `git diff --cached --check` -- passed
This commit is contained in:
+89
-33
@@ -172,6 +172,15 @@ enum PlayerEntry {
|
||||
Closed,
|
||||
}
|
||||
|
||||
fn initial_object_activity() -> [bool; 176] {
|
||||
let mut active = [true; 176];
|
||||
active[0] = false;
|
||||
for object_id in [6, 87, 88, 149, 153, 154, 175] {
|
||||
active[object_id] = false;
|
||||
}
|
||||
active
|
||||
}
|
||||
|
||||
impl Default for Ball {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
@@ -215,6 +224,7 @@ pub struct Game {
|
||||
claw_rng_state: u32,
|
||||
pending_flipper_kicks: [bool; 2],
|
||||
trigger_contacts: [bool; 176],
|
||||
object_active: [bool; 176],
|
||||
}
|
||||
|
||||
impl Game {
|
||||
@@ -255,6 +265,7 @@ impl Game {
|
||||
claw_rng_state: seed.max(1),
|
||||
pending_flipper_kicks: [false; 2],
|
||||
trigger_contacts: [false; 176],
|
||||
object_active: initial_object_activity(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -407,6 +418,9 @@ impl Game {
|
||||
let mut position = old_position.add(velocity);
|
||||
let mut best_collision: Option<(u8, bool, CollisionResponse)> = None;
|
||||
for object_id in 1..=175 {
|
||||
if !self.object_active[usize::from(object_id)] {
|
||||
continue;
|
||||
}
|
||||
if self.claw.active && (12..=20).contains(&object_id) {
|
||||
continue;
|
||||
}
|
||||
@@ -480,6 +494,7 @@ impl Game {
|
||||
self.launcher_was_down = false;
|
||||
return;
|
||||
}
|
||||
self.apply_wall_rule(wall_id, events);
|
||||
}
|
||||
|
||||
if let Some(index) = hit_circle
|
||||
@@ -522,26 +537,6 @@ impl Game {
|
||||
}
|
||||
let position = self.ball.position;
|
||||
|
||||
let side = [180.0, 195.0, 210.0, 225.0, 240.0];
|
||||
for (index, y) in side.into_iter().enumerate() {
|
||||
if position.distance_squared(vec2(286.0, y)) < 9.0_f32.powi(2)
|
||||
&& !self.side_targets[index]
|
||||
{
|
||||
self.side_targets[index] = true;
|
||||
self.add_score(2_000 + u32::try_from(index).unwrap_or(0) * 1_000);
|
||||
self.ball.velocity.x = -self.ball.velocity.x.abs() - 55.0;
|
||||
self.target_cooldown = 0.10;
|
||||
events.push(Event::Target);
|
||||
}
|
||||
}
|
||||
if self.side_targets.iter().all(|target| *target) {
|
||||
self.side_targets.fill(false);
|
||||
let player = &mut self.players[self.current_player];
|
||||
player.bumper_value = (player.bumper_value + 1_000).min(10_000);
|
||||
player.diamond_segments = (player.diamond_segments + 1).min(9);
|
||||
self.add_score(10_000);
|
||||
}
|
||||
|
||||
let wheel_center = vec2(114.0, 79.0);
|
||||
let from_wheel = position - wheel_center;
|
||||
if (25.0..=43.0).contains(&from_wheel.length()) {
|
||||
@@ -579,22 +574,64 @@ impl Game {
|
||||
self.target_cooldown = 0.4;
|
||||
events.push(Event::Lock);
|
||||
}
|
||||
}
|
||||
|
||||
for (center, value) in [
|
||||
(vec2(113.0, 285.0), 2_000),
|
||||
(vec2(132.0, 270.0), 3_000),
|
||||
(vec2(155.0, 258.0), 4_000),
|
||||
(vec2(181.0, 270.0), 5_000),
|
||||
(vec2(201.0, 285.0), 6_000),
|
||||
] {
|
||||
if position.distance_squared(center) < 10.0_f32.powi(2) {
|
||||
self.add_score(value);
|
||||
self.ball.velocity.y = -self.ball.velocity.y.abs() - 40.0;
|
||||
self.target_cooldown = 0.13;
|
||||
events.push(Event::Target);
|
||||
break;
|
||||
fn apply_wall_rule(&mut self, object_id: u8, events: &mut Vec<Event>) {
|
||||
let wall = WALLS
|
||||
.iter()
|
||||
.find(|wall| wall.id == object_id)
|
||||
.expect("a wall collision id must reference a recovered wall");
|
||||
if !self.tilted && wall.score != 0 {
|
||||
self.add_score(wall.score);
|
||||
events.push(if object_id == 121 {
|
||||
Event::Wheel
|
||||
} else {
|
||||
Event::Target
|
||||
});
|
||||
}
|
||||
|
||||
if wall.flags & 0x0002 != 0 {
|
||||
let group = usize::from(wall.contact_group);
|
||||
self.object_active[group] = false;
|
||||
if wall.flags & 0x2000 != 0 {
|
||||
self.object_active[group + 1] = false;
|
||||
self.object_active[group + 2] = false;
|
||||
}
|
||||
}
|
||||
if wall.flags & 0x0008 != 0
|
||||
&& [90, 93, 96, 99, 102]
|
||||
.into_iter()
|
||||
.all(|id| !self.object_active[id])
|
||||
{
|
||||
let player = &mut self.players[self.current_player];
|
||||
let completion_bonus = if player.bumper_value >= 6_000 {
|
||||
50_000
|
||||
} else {
|
||||
player.bumper_value += 1_000;
|
||||
0
|
||||
};
|
||||
self.add_score(completion_bonus);
|
||||
self.side_targets.fill(false);
|
||||
for active in &mut self.object_active[90..=104] {
|
||||
*active = true;
|
||||
}
|
||||
}
|
||||
if wall.flags & 0x0010 != 0
|
||||
&& [109, 112, 115, 118]
|
||||
.into_iter()
|
||||
.all(|id| !self.object_active[id])
|
||||
{
|
||||
let player = &mut self.players[self.current_player];
|
||||
player.diamond_segments = (player.diamond_segments + 1).min(9);
|
||||
let value = u32::from(player.diamond_segments) * 10_000;
|
||||
self.add_score(value);
|
||||
for active in &mut self.object_active[109..=120] {
|
||||
*active = true;
|
||||
}
|
||||
}
|
||||
if wall.flags & 0x0400 != 0 {
|
||||
self.wheel_animation = 0.65;
|
||||
}
|
||||
}
|
||||
|
||||
fn check_media(&mut self, events: &mut Vec<Event>) {
|
||||
@@ -799,6 +836,7 @@ impl Game {
|
||||
self.wheel_animation = 0.0;
|
||||
self.claw = Claw::default();
|
||||
self.trigger_contacts.fill(false);
|
||||
self.object_active = initial_object_activity();
|
||||
self.nudge_shake = 0.0;
|
||||
self.launcher_charge = 0.0;
|
||||
self.launcher_was_down = false;
|
||||
@@ -1039,6 +1077,24 @@ mod tests {
|
||||
assert_eq!(game.players[0].score, 2_000);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn recovered_target_banks_deactivate_and_rearm_their_line_groups() {
|
||||
let mut game = Game::new(1);
|
||||
let mut events = Vec::new();
|
||||
|
||||
for object_id in [90, 93, 96, 99, 102] {
|
||||
game.apply_wall_rule(object_id, &mut events);
|
||||
}
|
||||
assert_eq!(game.player().bumper_value, 2_000);
|
||||
assert!(game.object_active[90..=104].iter().all(|active| *active));
|
||||
|
||||
for object_id in [109, 112, 115, 118] {
|
||||
game.apply_wall_rule(object_id, &mut events);
|
||||
}
|
||||
assert_eq!(game.player().diamond_segments, 1);
|
||||
assert!(game.object_active[109..=120].iter().all(|active| *active));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn center_drain_advances_to_a_fresh_ball() {
|
||||
let mut game = Game::new(1);
|
||||
|
||||
Reference in New Issue
Block a user