fix(attract): use remainder-driven item phases
Raw 1000:061c-063b divides the idle tick by 144, copies the remainder from CX:BX into AX:DX, and only then divides by 8. Readable C and Rust instead used an unbounded quotient equivalent to tick/1152, making DAT801-809 advance far too slowly and suggesting a nonexistent negative long-idle index. Use `(tick % 144) / 8` to repeat item states 0,1..9,8..1 every 144 callbacks. Remove the disproven long-idle status crops and restore the ten-minute simulator ceiling. Extend the C and Rust phase tests across the forward, reverse, and wrap boundaries. Test Plan: - raw instruction review at `1000:061c-063b` -- remainder transfer confirmed - `cargo test --workspace --all-targets --all-features` -- 125 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 with zero incomplete or unclassified units - `rumdl check --flavor commonmark RECONSTRUCTION.md CHANGELOG.md` -- passed - `git diff --cached --check` -- passed
This commit is contained in:
@@ -94,6 +94,11 @@ Wine record-57 transition `(75530,354765)/(3000,15)` to
|
|||||||
`(77369,356597)/(1839,1832)` with exact Real48 spin `-5.28`, plus record 72
|
`(77369,356597)/(1839,1832)` with exact Real48 spin `-5.28`, plus record 72
|
||||||
`(235348,356825)/(-3000,15)` to `(233253,358494)/(-2095,1669)` with spin
|
`(235348,356825)/(-3000,15)` to `(233253,358494)/(-2095,1669)` with spin
|
||||||
`+5.46`, seal both paths and both relative slingshot chains.
|
`+5.46`, seal both paths and both relative slingshot chains.
|
||||||
|
Raw `1000:061c-063b` further distinguishes the DOS divide outputs in the idle
|
||||||
|
item animation: it copies the remainder of `tick / 144` from `CX:BX` and then
|
||||||
|
divides that value by 8. The readable C and focused timer harness therefore use
|
||||||
|
`(tick % 144) / 8`, producing the repeating `0,1..9,8..1` sequence without the
|
||||||
|
previously inferred unbounded quotient.
|
||||||
|
|
||||||
## Reproducible gates
|
## Reproducible gates
|
||||||
|
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ address raw_name segment body_bytes status reconstructed_name module evidence no
|
|||||||
1000:c426 FUN_1000_c426 Code1 187 verified tdkpin_save_ball_slot reconstructed/tdkpin_gameplay_helpers.c disassembly 1000:c426-c4e0; SS caller-frame receiver; reconstructed/tests/test_gameplay_helpers.c Exact inverse unchecked uint16 slot times 4/6 transfer of global position, receiver velocity/auxiliary dwords and six-byte Real48 field into packed DGROUP slot arrays, overlapping out-of-range slot behavior, and six-byte cleanup are represented and tested.
|
1000:c426 FUN_1000_c426 Code1 187 verified tdkpin_save_ball_slot reconstructed/tdkpin_gameplay_helpers.c disassembly 1000:c426-c4e0; SS caller-frame receiver; reconstructed/tests/test_gameplay_helpers.c Exact inverse unchecked uint16 slot times 4/6 transfer of global position, receiver velocity/auxiliary dwords and six-byte Real48 field into packed DGROUP slot arrays, overlapping out-of-range slot behavior, and six-byte cleanup are represented and tested.
|
||||||
1000:c4e1 FUN_1000_c4e1 Code1 699 verified tdkpin_apply_player_sprite_effect reconstructed/tdkpin_player_state.c disassembly 1000:c4e1-c79b; verified multiply/score dependencies; exact player tables and record 399a access; reconstructed/tests/test_player_state.c Exact sprite-index dispatch 1..7, wrapping secondary-score additions 10000/20000/50000/100000/200000, unsigned-byte multiplier redraw, effect-6 multiplied transfer/reset/zero redraw, effect-7 nonzero record gate, signed-negative or unequal argument condition, receiver +be0 and global 0874 publications, and default no-op behavior are represented and branch-tested.
|
1000:c4e1 FUN_1000_c4e1 Code1 699 verified tdkpin_apply_player_sprite_effect reconstructed/tdkpin_player_state.c disassembly 1000:c4e1-c79b; verified multiply/score dependencies; exact player tables and record 399a access; reconstructed/tests/test_player_state.c Exact sprite-index dispatch 1..7, wrapping secondary-score additions 10000/20000/50000/100000/200000, unsigned-byte multiplier redraw, effect-6 multiplied transfer/reset/zero redraw, effect-7 nonzero record gate, signed-negative or unequal argument condition, receiver +be0 and global 0874 publications, and default no-op behavior are represented and branch-tested.
|
||||||
1000:c79c FUN_1000_c79c Code1 8981 verified tdkpin_simulate_ball_and_dispatch_collision reconstructed/tdkpin_physics.c disassembly 1000:c79c-eab0 and rz-ghidra stack-offset view; packed 83-byte records and exact 52-byte candidate layout; all integer/Real48/gameplay/GDI dependencies verified; reconstructed/tests/test_physics.c Exact RETF-2 caller-frame ABI, simulation-call counter, snapshotted ball count and substep word, multiball load/save, gravity and two ordered speed clamps, previous/predicted coordinates, board-loss/layer gates, 175-record broadphase, types 1 circle, 2 segment, 3 capture/pocket, and 4 stateful trigger, subtype normal reversal, signed cross/end-point tests, contact-state sentinels, claw/target-set/sprite/score special paths, trigger 0x4000 lifecycle and random bounce, packed improving-distance copies with fixed first-slot response, event/score dispatch, Real48 normal/tangent/spin/response/kick transform with standard dot-product tangent `tv=(vx*nx+vy*ny)/length`, opposing signed `abs(normal_velocity)*response_tangent` response (negative at zero), spin delta `tv*0.02*response_tangent`, retained-spin contribution, and deliberately zeroed intermediate, capture-age-gated ball-to-ball transfer, record 25/2 terminal/special-respawn continuation paths, ordered velocity/position publication, dynamic-record update, swept rendering, 32-bit wrap, and Borland divide/error behavior are represented. Free integration, exact 3-4-5 clamp, live line/circle tangent transitions, four trigger/capture modes, two-candidate first-slot behavior, distinct response/event/score paths, out-of-bounds, and zero-substep multiball are tested.
|
1000:c79c FUN_1000_c79c Code1 8981 verified tdkpin_simulate_ball_and_dispatch_collision reconstructed/tdkpin_physics.c disassembly 1000:c79c-eab0 and rz-ghidra stack-offset view; packed 83-byte records and exact 52-byte candidate layout; all integer/Real48/gameplay/GDI dependencies verified; reconstructed/tests/test_physics.c Exact RETF-2 caller-frame ABI, simulation-call counter, snapshotted ball count and substep word, multiball load/save, gravity and two ordered speed clamps, previous/predicted coordinates, board-loss/layer gates, 175-record broadphase, types 1 circle, 2 segment, 3 capture/pocket, and 4 stateful trigger, subtype normal reversal, signed cross/end-point tests, contact-state sentinels, claw/target-set/sprite/score special paths, trigger 0x4000 lifecycle and random bounce, packed improving-distance copies with fixed first-slot response, event/score dispatch, Real48 normal/tangent/spin/response/kick transform with standard dot-product tangent `tv=(vx*nx+vy*ny)/length`, opposing signed `abs(normal_velocity)*response_tangent` response (negative at zero), spin delta `tv*0.02*response_tangent`, retained-spin contribution, and deliberately zeroed intermediate, capture-age-gated ball-to-ball transfer, record 25/2 terminal/special-respawn continuation paths, ordered velocity/position publication, dynamic-record update, swept rendering, 32-bit wrap, and Borland divide/error behavior are represented. Free integration, exact 3-4-5 clamp, live line/circle tangent transitions, four trigger/capture modes, two-candidate first-slot behavior, distinct response/event/score paths, out-of-bounds, and zero-substep multiball are tested.
|
||||||
1000:eab1 exported_ordinal_10 Code1 4820 verified tdkpin_timer_tick reconstructed/tdkpin_timer_tick.c disassembly 1000:eab1-fd84 including RETF-8 and all annotated calls; exact Real48 claw-release table; complete verified dependency closure including 1000:c79c; reconstructed/tests/test_timer_tick.c Exact two-DWORD callback ABI with unused second argument, all nine guard conditions, busy flag, 175-record refresh, two dword counters, claw movement/release/record activation, ten exact spawn states, 175 countdowns, target rotation, multiball collapse/promotion/reset/spawn, physics gate, quotient-versus-remainder intro schedules, record/item/chase/status animations, complete 30x284 score-marquee GDI lifetime, panel frames 1..281, game initialization, flipper/message epilogue, busy clear, redundant tilt read, and tilt-counter decrement are represented and branch-/sequence-tested.
|
1000:eab1 exported_ordinal_10 Code1 4820 verified tdkpin_timer_tick reconstructed/tdkpin_timer_tick.c disassembly 1000:eab1-fd84 including RETF-8 and all annotated calls; exact Real48 claw-release table; complete verified dependency closure including 1000:c79c; reconstructed/tests/test_timer_tick.c Exact two-DWORD callback ABI with unused second argument, all nine guard conditions, busy flag, 175-record refresh, two dword counters, claw movement/release/record activation, ten exact spawn states, 175 countdowns, target rotation, multiball collapse/promotion/reset/spawn, physics gate, quotient-versus-remainder intro schedules including item phase `(tick%144)/8`, record/item/chase/status animations, complete 30x284 score-marquee GDI lifetime, panel frames 1..281, game initialization, flipper/message epilogue, busy clear, redundant tilt read, and tilt-counter decrement are represented and branch-/sequence-tested.
|
||||||
1000:fd85 ne_program_entry Code1 181 verified tdkpin_ne_program_entry reconstructed/tdkpin_program_entry.c disassembly 1000:fd85-fe39; relocations 1669-1679; reconstructed/tests/test_program_entry.c Exact InitTask register handoff, eight-unit initialization order, full DX:AX GetVersion store, Windows 3.10 threshold, indirect MessageBox error path, fixed application object construction, VMT +1c Run and +08 destructor calls, dispose mode zero, and unconditional Halt(0) are represented and branch-tested.
|
1000:fd85 ne_program_entry Code1 181 verified tdkpin_ne_program_entry reconstructed/tdkpin_program_entry.c disassembly 1000:fd85-fe39; relocations 1669-1679; reconstructed/tests/test_program_entry.c Exact InitTask register handoff, eight-unit initialization order, full DX:AX GetVersion store, Windows 3.10 threshold, indirect MessageBox error path, fixed application object construction, VMT +1c Run and +08 destructor calls, dispose mode zero, and unconditional Halt(0) are represented and branch-tested.
|
||||||
1008:0002 FUN_1008_0002 Code2 33 verified tdkpin_play_sound_if_not_tilted reconstructed/tdkpin_sound.c disassembly 1008:0002-0022; verified 1008:1131 dependency; DGROUP game-tilted byte 07c6 and sound-enabled byte 43f2; reconstructed/tests/test_sound.c Exact Tilted==0 then Enabled!=0 short-circuit order, unchanged sound-number argument, ignored player result, two-byte Pascal cleanup, and all gate combinations are branch-tested.
|
1008:0002 FUN_1008_0002 Code2 33 verified tdkpin_play_sound_if_not_tilted reconstructed/tdkpin_sound.c disassembly 1008:0002-0022; verified 1008:1131 dependency; DGROUP game-tilted byte 07c6 and sound-enabled byte 43f2; reconstructed/tests/test_sound.c Exact Tilted==0 then Enabled!=0 short-circuit order, unchanged sound-number argument, ignored player result, two-byte Pascal cleanup, and all gate combinations are branch-tested.
|
||||||
1008:0023 FUN_1008_0023 Code2 191 verified tdkpin_realize_nocollapse_palette reconstructed/tdkpin_palette.c disassembly 1008:0023-00e1; exact LOGPALETTE256 layout; verified Borland allocation/free; GDI palette/DC APIs; reconstructed/tests/test_palette_gdi.c Exact null-allocation early return, 1028-byte palette, version 0300/count 256, 256 zero RGB entries with flag 4, GetDC/Create/Select(false)/Realize/Select(previous,true)/Delete/Release ordering, ignored API returns, free, and RETF 2 are branch-tested.
|
1008:0023 FUN_1008_0023 Code2 191 verified tdkpin_realize_nocollapse_palette reconstructed/tdkpin_palette.c disassembly 1008:0023-00e1; exact LOGPALETTE256 layout; verified Borland allocation/free; GDI palette/DC APIs; reconstructed/tests/test_palette_gdi.c Exact null-allocation early return, 1028-byte palette, version 0300/count 256, 256 zero RGB entries with flag 4, GetDC/Create/Select(false)/Realize/Select(previous,true)/Delete/Release ordering, ignored API returns, free, and RETF 2 are branch-tested.
|
||||||
|
|||||||
|
@@ -469,7 +469,7 @@ static void update_intro_item_status(Win16FarPtr window, int32_t tick)
|
|||||||
if (remainder_i32(tick, 8) != 0) {
|
if (remainder_i32(tick, 8) != 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int32_t phase = quotient_i32(quotient_i32(tick, 144), 8);
|
int32_t phase = quotient_i32(remainder_i32(tick, 144), 8);
|
||||||
int16_t item = (int16_t)(phase < 10 ? phase : 18 - phase);
|
int16_t item = (int16_t)(phase < 10 ? phase : 18 - phase);
|
||||||
HWND16 handle = win16_read_u16(win16_far_add_offset(window, 4));
|
HWND16 handle = win16_read_u16(win16_far_add_offset(window, 4));
|
||||||
HDC16 dc = GetDC16(handle);
|
HDC16 dc = GetDC16(handle);
|
||||||
|
|||||||
@@ -466,7 +466,7 @@ static void test_intro_tick_32_and_marquee(void)
|
|||||||
assert(g_restore_b_id_count == 1 && g_restore_b_ids[0] == 1002);
|
assert(g_restore_b_id_count == 1 && g_restore_b_ids[0] == 1002);
|
||||||
assert(g_region_a_calls == 2 && g_region_b_calls == 1);
|
assert(g_region_a_calls == 2 && g_region_b_calls == 1);
|
||||||
assert(g_present_calls == 3);
|
assert(g_present_calls == 3);
|
||||||
assert(g_item_calls == 1 && g_last_item == 0);
|
assert(g_item_calls == 1 && g_last_item == 4);
|
||||||
|
|
||||||
prepare_fixture();
|
prepare_fixture();
|
||||||
win16_write_u8(win16_dgroup_pointer(0x07c9), 1);
|
win16_write_u8(win16_dgroup_pointer(0x07c9), 1);
|
||||||
@@ -485,6 +485,29 @@ static void test_intro_tick_32_and_marquee(void)
|
|||||||
g_blits[3].x == 233 && g_blits[3].y == 372);
|
g_blits[3].x == 233 && g_blits[3].y == 372);
|
||||||
assert(g_select_calls == 5 && g_delete_object_calls == 1);
|
assert(g_select_calls == 5 && g_delete_object_calls == 1);
|
||||||
assert(g_delete_dc_calls == 2);
|
assert(g_delete_dc_calls == 2);
|
||||||
|
|
||||||
|
static const struct {
|
||||||
|
uint32_t before_tick;
|
||||||
|
int16_t expected_item;
|
||||||
|
} item_cases[] = {
|
||||||
|
{7, 1},
|
||||||
|
{71, 9},
|
||||||
|
{79, 8},
|
||||||
|
{135, 1},
|
||||||
|
{143, 0},
|
||||||
|
};
|
||||||
|
for (size_t index = 0;
|
||||||
|
index < sizeof(item_cases) / sizeof(item_cases[0]);
|
||||||
|
index++) {
|
||||||
|
prepare_fixture();
|
||||||
|
win16_write_u8(win16_dgroup_pointer(0x07c9), 1);
|
||||||
|
win16_write_u32(
|
||||||
|
win16_dgroup_pointer(0x0876), 0,
|
||||||
|
item_cases[index].before_tick);
|
||||||
|
tdkpin_timer_tick_with_frame(0x0300, g_window);
|
||||||
|
assert(g_item_calls == 1);
|
||||||
|
assert(g_last_item == item_cases[index].expected_item);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_panel_and_game_initialization(void)
|
static void test_panel_and_game_initialization(void)
|
||||||
|
|||||||
@@ -53,11 +53,9 @@ and this project adheres to
|
|||||||
previously impossible Pascal-order C interpretation of a 328x202 blit.
|
previously impossible Pascal-order C interpretation of a 328x202 blit.
|
||||||
- Restore the original idle/attract timer animation: target and word-quad
|
- Restore the original idle/attract timer animation: target and word-quad
|
||||||
chases, magnetic flashes, five-record strip, four-point chase, DAT801-809
|
chases, magnetic flashes, five-record strip, four-point chase, DAT801-809
|
||||||
item progression, and the circular `WELCOME TO THE MACHINE` BITMAP500
|
item progression on exact `(tick % 144) / 8` phases, and the circular
|
||||||
marquee. Move gameplay item frames to their exact `(123,300)` destination.
|
`WELCOME TO THE MACHINE` BITMAP500 marquee. Move gameplay item frames to
|
||||||
- Preserve the long-idle signed item-index wrap: phases 19-21 read the adjacent
|
their exact `(123,300)` destination.
|
||||||
DAT703/DAT702/DAT701 handles and copy their upper 68x61 regions before later
|
|
||||||
phases return to the inactive item panel.
|
|
||||||
- Match central score/event side effects: every stored collision candidate
|
- Match central score/event side effects: every stored collision candidate
|
||||||
clears its ball slot's capture age, Tilt suppresses all score and rule-record
|
clears its ball slot's capture age, Tilt suppresses all score and rule-record
|
||||||
mutations, and active multiball doubles points before permanent Double can
|
mutations, and active multiball doubles points before permanent Double can
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ the same evidence rather than against this Rust implementation.
|
|||||||
| Numeric scoring | Recovered gameplay values | Static scores come from the initialized 175-object ledger. Dynamic bumper progression, target-bank completion, diamond awards, 10k-160k lock bonuses, 310k transfer, six effect values, multiball mode, and all four media thresholds are transcribed from `1000:b476`, `1000:c4e1`, `1000:bc36`, and live state probes. The central helper suppresses all mutation during Tilt, applies a 2x active-multiball factor, and then independently applies the active ball's 2x factor, allowing the original 4x stack. Lock and effect awards share the original per-player secondary score and display multiplier; the fifth hole transfers and clears it, increments the multiplier, and grants the recovered ball award. Score mutation uses the original 32-bit wrapping behavior; media markers use the binary's signed-high/unsigned-low comparison, and each add operation can advance at most one threshold. Type-4 WAVE 2004, bumper WAVE 2006, and bank-completion WAVE 2017 dispatch before their associated score mutations, leaving a crossed marker's WAVE 2007 as the audible asynchronous sample. Rendering separately follows `1008:0996`'s eight-place 130x40 fields/right edge X=606 and `1008:0b9e`'s strict signed status thresholds; configured-player rows and 5x5 remaining-ball markers retain their exact dimensions and current/noncurrent counts. |
|
| Numeric scoring | Recovered gameplay values | Static scores come from the initialized 175-object ledger. Dynamic bumper progression, target-bank completion, diamond awards, 10k-160k lock bonuses, 310k transfer, six effect values, multiball mode, and all four media thresholds are transcribed from `1000:b476`, `1000:c4e1`, `1000:bc36`, and live state probes. The central helper suppresses all mutation during Tilt, applies a 2x active-multiball factor, and then independently applies the active ball's 2x factor, allowing the original 4x stack. Lock and effect awards share the original per-player secondary score and display multiplier; the fifth hole transfers and clears it, increments the multiplier, and grants the recovered ball award. Score mutation uses the original 32-bit wrapping behavior; media markers use the binary's signed-high/unsigned-low comparison, and each add operation can advance at most one threshold. Type-4 WAVE 2004, bumper WAVE 2006, and bank-completion WAVE 2017 dispatch before their associated score mutations, leaving a crossed marker's WAVE 2007 as the audible asynchronous sample. Rendering separately follows `1008:0996`'s eight-place 130x40 fields/right edge X=606 and `1008:0b9e`'s strict signed status thresholds; configured-player rows and 5x5 remaining-ball markers retain their exact dimensions and current/noncurrent counts. |
|
||||||
| High scores | Recovered visible flow; portable storage | The original 276-byte table is decoded as ten `IWIK`-XOR-obfuscated little-endian scores plus ten 22-byte names. Each player is checked immediately when their own last ball is lost; qualifying scores use the original signed-high/unsigned-low comparison and a `TDK Pinball Player`-prefilled DIALOG 32514 flow with the exact caption, prompt, 21-character limit, OK/Cancel semantics, and allowance for an empty accepted name. The `HighScore` child uses exact DAT993 at 360x300, heading `(180,27)`, name/rank X=30, right-aligned score X=320, and ten 20-pixel rows before play resumes. Persisted updates use portable JSON rather than rewriting the Win16 file. |
|
| High scores | Recovered visible flow; portable storage | The original 276-byte table is decoded as ten `IWIK`-XOR-obfuscated little-endian scores plus ten 22-byte names. Each player is checked immediately when their own last ball is lost; qualifying scores use the original signed-high/unsigned-low comparison and a `TDK Pinball Player`-prefilled DIALOG 32514 flow with the exact caption, prompt, 21-character limit, OK/Cancel semantics, and allowance for an empty accepted name. The `HighScore` child uses exact DAT993 at 360x300, heading `(180,27)`, name/rank X=30, right-aligned score X=320, and ten 20-pixel rows before play resumes. Persisted updates use portable JSON rather than rewriting the Win16 file. |
|
||||||
| Configuration | Recovered behavior; portable follow-up storage | Before portable JSON exists, Rust reads the original case-insensitive `[Settings]` keys `Language`, `Speed`, and singular `Sound` from the executable-adjacent INI, falling back to the embedded distributed TDKPIN.INI. Values retain the Win16 defaults/clamps; the shipped file therefore selects German, speed 3, and sound-on because its plural `Sounds` line does not override singular `Sound`. F12 toggles only runtime playback without UI or persistence. Later explicit F10 changes use the platform user-data directory. |
|
| Configuration | Recovered behavior; portable follow-up storage | Before portable JSON exists, Rust reads the original case-insensitive `[Settings]` keys `Language`, `Speed`, and singular `Sound` from the executable-adjacent INI, falling back to the embedded distributed TDKPIN.INI. Values retain the Win16 defaults/clamps; the shipped file therefore selects German, speed 3, and sound-on because its plural `Sounds` line does not override singular `Sound`. F12 toggles only runtime playback without UI or persistence. Later explicit F10 changes use the platform user-data directory. |
|
||||||
| Windows UI shell | Original gameplay surface with native host shell | Win16 window ownership and GDI calls are replaced by a fixed native window, but the visible game, help resources, automatic per-player high-score flow, original keys, and 640x460 logical pixels are retained. Idle mode advances on the selected detail callback and reproduces `idle_transition_tick`: target/word-quad/record-strip chases, magnetic flashes, four-point chase, DAT801-809 item progression, its signed long-idle wrap through the adjacent DAT703/DAT702/DAT701 handles, and the circular BITMAP500 `WELCOME TO THE MACHINE` marquee composited over exact DAT997 regions. Add-player, nudge, and F12 actions dispatch on Key-Up through the recovered release handler. Both key handlers honor the four-child auxiliary-window gate, so F1/F12 cannot control the main game through Help, HighScore, or name-entry UI. The low-byte scan aliases are preserved: both Ctrl keys map left, both Enter keys map right, and keypad plus or the physical main-keyboard `0x1b` plus/right-bracket position adds players. F2/F3 remain unassigned as in normal original operation; optional portable settings and table viewers use F10/F9 and do not replace gameplay input. |
|
| Windows UI shell | Original gameplay surface with native host shell | Win16 window ownership and GDI calls are replaced by a fixed native window, but the visible game, help resources, automatic per-player high-score flow, original keys, and 640x460 logical pixels are retained. Idle mode advances on the selected detail callback and reproduces `idle_transition_tick`: target/word-quad/record-strip chases, magnetic flashes, four-point chase, DAT801-809 item progression on `(tick % 144) / 8`, and the circular BITMAP500 `WELCOME TO THE MACHINE` marquee composited over exact DAT997 regions. Add-player, nudge, and F12 actions dispatch on Key-Up through the recovered release handler. Both key handlers honor the four-child auxiliary-window gate, so F1/F12 cannot control the main game through Help, HighScore, or name-entry UI. The low-byte scan aliases are preserved: both Ctrl keys map left, both Enter keys map right, and keypad plus or the physical main-keyboard `0x1b` plus/right-bracket position adds players. F2/F3 remain unassigned as in normal original operation; optional portable settings and table viewers use F10/F9 and do not replace gameplay input. |
|
||||||
|
|
||||||
## Extracted asset inventory
|
## Extracted asset inventory
|
||||||
|
|
||||||
|
|||||||
+8
-30
@@ -150,24 +150,15 @@ impl AttractAnimation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn item_index(self) -> Option<usize> {
|
fn item_index(self) -> Option<usize> {
|
||||||
let phase = self.tick / 1_152;
|
let phase = (self.tick % 144) / 8;
|
||||||
let item = if phase < 10 {
|
let item = if phase < 10 {
|
||||||
phase
|
phase
|
||||||
} else {
|
} else {
|
||||||
18_u32.saturating_sub(phase)
|
18 - phase
|
||||||
};
|
};
|
||||||
(item != 0).then(|| usize::try_from(item - 1).unwrap_or(0))
|
(item != 0).then(|| usize::try_from(item - 1).unwrap_or(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wrapped_status_item_index(self) -> Option<usize> {
|
|
||||||
match self.tick / 1_152 {
|
|
||||||
19 => Some(2),
|
|
||||||
20 => Some(1),
|
|
||||||
21 => Some(0),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn marquee_source_x(self) -> Option<f32> {
|
fn marquee_source_x(self) -> Option<f32> {
|
||||||
(self.score_counter != 0).then(|| {
|
(self.score_counter != 0).then(|| {
|
||||||
let phase = self.score_counter % 49;
|
let phase = self.score_counter % 49;
|
||||||
@@ -620,16 +611,6 @@ impl App {
|
|||||||
300.0,
|
300.0,
|
||||||
WHITE,
|
WHITE,
|
||||||
);
|
);
|
||||||
} else if let Some(status) = self.attract.wrapped_status_item_index() {
|
|
||||||
draw_texture_region(
|
|
||||||
&self.assets.media[status],
|
|
||||||
123.0,
|
|
||||||
300.0,
|
|
||||||
68.0,
|
|
||||||
61.0,
|
|
||||||
0.0,
|
|
||||||
0.0,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
self.draw_intro_marquee();
|
self.draw_intro_marquee();
|
||||||
}
|
}
|
||||||
@@ -1419,15 +1400,12 @@ mod tests {
|
|||||||
assert!(!at(800).record_strip_active(0));
|
assert!(!at(800).record_strip_active(0));
|
||||||
assert_eq!(at(4).chase_index(), Some(0));
|
assert_eq!(at(4).chase_index(), Some(0));
|
||||||
assert_eq!(at(192).chase_index(), Some(3));
|
assert_eq!(at(192).chase_index(), Some(3));
|
||||||
assert_eq!(at(1_151).item_index(), None);
|
assert_eq!(at(7).item_index(), None);
|
||||||
assert_eq!(at(1_152).item_index(), Some(0));
|
assert_eq!(at(8).item_index(), Some(0));
|
||||||
assert_eq!(at(10_368).item_index(), Some(8));
|
assert_eq!(at(72).item_index(), Some(8));
|
||||||
assert_eq!(at(11_520).item_index(), Some(7));
|
assert_eq!(at(80).item_index(), Some(7));
|
||||||
assert_eq!(at(20_736).item_index(), None);
|
assert_eq!(at(136).item_index(), Some(0));
|
||||||
assert_eq!(at(21_888).wrapped_status_item_index(), Some(2));
|
assert_eq!(at(144).item_index(), None);
|
||||||
assert_eq!(at(23_040).wrapped_status_item_index(), Some(1));
|
|
||||||
assert_eq!(at(24_192).wrapped_status_item_index(), Some(0));
|
|
||||||
assert_eq!(at(25_344).wrapped_status_item_index(), None);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use std::{
|
|||||||
pub const SIMULATION_HZ: u32 = 120;
|
pub const SIMULATION_HZ: u32 = 120;
|
||||||
const SIMULATION_HZ_F64: f64 = 120.0;
|
const SIMULATION_HZ_F64: f64 = 120.0;
|
||||||
const SIMULATION_DT: f32 = 1.0 / 120.0;
|
const SIMULATION_DT: f32 = 1.0 / 120.0;
|
||||||
const MAX_SIMULATION_STEPS: u64 = 100_000;
|
const MAX_SIMULATION_STEPS: u64 = 72_000;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub enum Scenario {
|
pub enum Scenario {
|
||||||
@@ -287,7 +287,7 @@ impl Simulation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn record(&mut self, events: Vec<Event>) {
|
fn record(&mut self, events: Vec<Event>) {
|
||||||
let step = u32::try_from(self.step).expect("simulation step is limited to 100000");
|
let step = u32::try_from(self.step).expect("simulation step is limited to 72000");
|
||||||
let claw_bank = match self.game.claw.bank {
|
let claw_bank = match self.game.claw.bank {
|
||||||
ClawSpriteBank::Closing => "closing",
|
ClawSpriteBank::Closing => "closing",
|
||||||
ClawSpriteBank::Opening => "opening",
|
ClawSpriteBank::Opening => "opening",
|
||||||
|
|||||||
Reference in New Issue
Block a user