fix(rules): restore ninth-diamond completion order

The reconstructed collision response dispatches record flags before adding the
selected record's static score. Rust previously scored first and delayed the
permanent-double transition until a later completed bank, leaving the three
magnetic records inactive and producing the wrong ninth-bank total.

Apply flag-driven rules first, keep the 24,464 completion award at the old
single multiplier, then activate permanent double scoring and records 153, 6,
and 154 before the final 1,500-point collision score. Subsequent completed
banks now immediately add 100,000 to the per-player secondary score. Update the
regression test and reconstruction notes with the observable order.

Test Plan:
- `cargo test --workspace --all-targets --all-features` -- 114 passed
- `cargo clippy --workspace --all-targets --all-features -- -D warnings` -- passed
- `bash original/tools/test_reconstructed_c.sh` -- passed
- `python3 original/tools/audit_reconstruction.py --require-complete` -- passed
- `rumdl check --flavor commonmark RECONSTRUCTION.md CHANGELOG.md` -- passed
- `git diff --cached --check` -- passed
- `cargo +nightly fmt -- --check` -- reports pre-existing whole-project diffs
This commit is contained in:
2026-08-23 19:52:28 +02:00
parent 6f8c55657b
commit 4a55150fc0
3 changed files with 44 additions and 20 deletions
+39 -17
View File
@@ -1260,14 +1260,6 @@ impl Game {
.iter()
.find(|wall| wall.id == object_id)
.expect("a wall collision id must reference a recovered wall");
if wall.score != 0 {
self.add_score(wall.score, events);
events.push(if object_id == 121 {
Event::Wheel
} else {
Event::Target
});
}
if wall.flags & 0x0002 != 0 {
let group = usize::from(wall.contact_group);
@@ -1314,6 +1306,12 @@ impl Game {
u32::from(segments + 1) * 10_000
};
self.add_score(award, events);
if segments == 8 {
self.players[self.current_player].double_score = true;
for object_id in [153, 6, 154] {
self.object_active[object_id] = true;
}
}
events.push(Event::Sound(2017));
} else {
let player = &mut self.players[self.current_player];
@@ -1353,6 +1351,24 @@ impl Game {
}
self.object_active[usize::from(EFFECT_SENSOR.id)] = true;
}
// 1000:91eb dispatches b476's flag-driven rules before it adds the
// selected record's static score. This is observable when the ninth
// diamond enables permanent double scoring: that collision's 1,500
// points are already doubled, while the preceding 24,464 award is not.
self.apply_static_wall_score(object_id, wall.score, events);
}
fn apply_static_wall_score(&mut self, object_id: u8, score: u32, events: &mut Vec<Event>) {
if score == 0 {
return;
}
self.add_score(score, events);
events.push(if object_id == 121 {
Event::Wheel
} else {
Event::Target
});
}
#[cfg(test)]
@@ -2471,25 +2487,31 @@ mod tests {
game.players[0].diamond_segments = 8;
let mut events = Vec::new();
assert!(!game.player().double_score);
assert!(!game.magnetic_field_active(153));
assert!(!game.magnetic_field_active(6));
assert!(!game.magnetic_field_active(154));
for object_id in [109, 112, 115, 118] {
game.apply_wall_rule(object_id, &mut events);
}
assert_eq!(game.player().diamond_segments, 9);
assert_eq!(game.player().score, 4 * 1_500 + 24_464);
assert!(!game.player().double_score);
for object_id in [109, 112, 115, 118] {
game.apply_wall_rule(object_id, &mut events);
}
assert_eq!(game.player().score, 3 * 1_500 + 24_464 + 2 * 1_500);
assert!(game.player().double_score);
let score = game.player().score;
game.add_score(1_000, &mut events);
assert_eq!(game.player().score, score + 2_000);
assert!(game.magnetic_field_active(153));
assert!(game.magnetic_field_active(6));
assert!(game.magnetic_field_active(154));
for object_id in [109, 112, 115, 118] {
game.apply_wall_rule(object_id, &mut events);
}
assert_eq!(game.player().score, 3 * 1_500 + 24_464 + 10 * 1_500);
assert_eq!(game.player().secondary_score, 100_000);
for object_id in [109, 112, 115, 118] {
game.apply_wall_rule(object_id, &mut events);
}
assert_eq!(game.player().secondary_score, 200_000);
}
#[test]