fix(flippers): match original moving-hit geometry

The clone's moving-flipper gate used the cross-product operands in the
opposite order, mirroring and narrowing the hit wedge. Right-flipper release
also used record 81's first endpoint even though the original reads its second
endpoint. Correct both geometry paths, retain the recovered swept tip bounds,
and add live-binary boundary vectors plus a dense transition regression.

Test Plan:
- `cargo test --workspace --all-targets --all-features` -- passed (134 tests)
- `cargo clippy --workspace --all-targets --all-features -- -D warnings` -- passed
- `cargo build --profile production` -- passed
- `LSAN_OPTIONS=detect_leaks=0 ASAN_OPTIONS=detect_leaks=0 bash original/tools/test_reconstructed_c.sh` -- passed
- `python3 original/tools/audit_reconstruction.py --require-complete` -- passed
- `git diff --cached --check` -- passed
This commit is contained in:
2026-08-29 09:15:25 +02:00
parent faf66f1ac0
commit fa3f168467
5 changed files with 517 additions and 55 deletions
@@ -158,10 +158,10 @@ static FlipperCollisionGeometry load_flipper_geometry(
geometry.unused_positive_point_x = record_i32(
81, offsetof(TdkpinCollisionRecord, point1_x_milli));
geometry.positive_edge_x = subtract_wrap_i32(
record_i32(81, offsetof(TdkpinCollisionRecord, point1_x_milli)),
record_i32(81, offsetof(TdkpinCollisionRecord, point2_x_milli)),
20000);
geometry.positive_edge_y = record_i32(
81, offsetof(TdkpinCollisionRecord, point1_y_milli));
81, offsetof(TdkpinCollisionRecord, point2_y_milli));
geometry.response_record = delta == 1 ? 81 : 83;
} else {
geometry = (FlipperCollisionGeometry){
@@ -202,8 +202,8 @@ static int32_t cross_product_for_edge(
int32_t edge_from_pivot_x = divide_by_thousand(
subtract_wrap_i32(edge_x, pivot_x));
return subtract_wrap_i32(
borland_multiply_i32(edge_from_ball_y, edge_from_pivot_x),
borland_multiply_i32(edge_from_ball_x, edge_from_pivot_y));
borland_multiply_i32(edge_from_ball_x, edge_from_pivot_y),
borland_multiply_i32(edge_from_ball_y, edge_from_pivot_x));
}
static int32_t radial_distance_milli(
@@ -185,11 +185,25 @@ static void test_right_positive_contact(void)
win16_far_add_offset(g_receiver, 0x0baa)) == -737);
assert((int32_t)win16_read_u32(
win16_far_add_offset(g_receiver, 0x0bae)) == 2263);
assert(g_move_x == -8549 && g_move_y == 26250);
assert(g_move_x == -5578 && g_move_y == 17127);
assert(g_sound_count == 1 && g_sounds[0] == 21);
assert(g_move_calls == 1);
}
static void test_live_binary_cross_product_order(void)
{
prepare_fixture();
win16_write_u32(g_receiver, 0x56, 3800);
set_ball(100000, 370000, 1000, 2000);
tdkpin_move_flipper_collision_geometry(0x0300, -1, 1, 0);
assert((int32_t)win16_read_u32(
win16_far_add_offset(g_receiver, 0x0baa)) == -189);
assert((int32_t)win16_read_u32(
win16_far_add_offset(g_receiver, 0x0bae)) == 960);
assert(g_move_x == 0 && g_move_y == 0);
assert(g_move_calls == 1);
}
static void test_left_positive_contact(void)
{
prepare_fixture();
@@ -230,8 +244,12 @@ static void test_vertical_edge_fallbacks(void)
prepare_fixture();
write_i32(
81,
offsetof(TdkpinCollisionRecord, point1_x_milli),
offsetof(TdkpinCollisionRecord, point2_x_milli),
240000);
write_i32(
81,
offsetof(TdkpinCollisionRecord, point2_y_milli),
411000);
set_ball(197000, 455000, -1000, 2000);
tdkpin_move_flipper_collision_geometry(0x0300, 1, 2, 0);
assert(g_move_calls == 1 && g_move_y == 1000);
@@ -270,6 +288,7 @@ int main(void)
{
test_left_negative_contact();
test_right_positive_contact();
test_live_binary_cross_product_order();
test_left_positive_contact();
test_right_negative_contact();
test_vertical_edge_fallbacks();