/* Ball-end, special-respawn, player-switch, and game-over state machine. */ #include "tdkpin_turn_state.h" #include "tdkpin_sound.h" static Win16FarPtr caller_receiver(uint16_t caller_bp) { uint16_t offset = win16_read_stack_u16((uint16_t)(caller_bp + 6)); uint16_t selector = win16_read_stack_u16((uint16_t)(caller_bp + 8)); return win16_make_far_pointer(selector, offset); } static Win16FarPtr collision_record(uint16_t id) { return win16_dgroup_pointer((uint16_t)(0x095b + id * 0x53u)); } static Win16FarPtr player_marker_count(uint8_t player) { return win16_dgroup_pointer((uint16_t)(0x081f + player * 2u)); } static void special_respawn(uint16_t caller_bp, Win16FarPtr receiver) { win16_write_u8(win16_dgroup_pointer(0x0874), 0); tdkpin_restore_previous_ball_region(caller_bp, 0); g_ball_x_milli = (int32_t)win16_read_u32(win16_dgroup_pointer(0x3969)); g_ball_y_milli = (int32_t)win16_read_u32(win16_dgroup_pointer(0x396d)); win16_write_u32(receiver, 0x0bc2, (uint32_t)g_ball_x_milli); win16_write_u32(receiver, 0x0bc6, (uint32_t)g_ball_y_milli); win16_write_u8( win16_far_add_offset(receiver, (uint16_t)( 0x047b + (uint16_t)g_current_player * 0x14u)), 0); tdkpin_restore_record_b(caller_bp, 1020); tdkpin_update_dynamic_ball_record(caller_bp, 1); win16_write_u32(receiver, 0x0baa, 0); static const BorlandReal48 factor_point_eight = {{0x80, 0xcd, 0xcc, 0xcc, 0xcc, 0x4c}}; int32_t source = (int32_t)win16_read_u32( win16_far_add_offset(receiver, 0x56)); int32_t scaled = borland_real48_round_to_i32( borland_real48_multiply( borland_i32_to_real48(source), factor_point_eight)); win16_write_u32(receiver, 0x0bae, (uint32_t)scaled); win16_write_u32(receiver, 0x0bca, 0); win16_write_u16(receiver, 0x0bce, 0); win16_write_u16(receiver, 0x0bd0, 0); win16_write_u16(receiver, 0x0bd2, 0); win16_write_u16(win16_dgroup_pointer(0x399a), 0, 0); win16_write_u8(win16_dgroup_pointer(0x0875), 1); win16_write_u8(win16_dgroup_pointer(0x41f9), 1); win16_write_u8(win16_far_add_offset(receiver, 0x61), 1); } static void save_current_collision_mirrors(Win16FarPtr receiver) { for (uint16_t id = 1; id <= 175; id++) { Win16FarPtr record = collision_record(id); win16_write_u8( win16_far_add_offset(receiver, (uint16_t)( 0x0061 + (uint16_t)g_current_player * 0x00afu + id)), win16_read_u8(win16_far_add_offset(record, 0x34))); win16_write_u16( receiver, (uint16_t)(0x036c + (uint16_t)g_current_player * 0x015eu + id * 2u), win16_read_u16(win16_far_add_offset(record, 0x43))); } } static void load_next_collision_mirrors( uint16_t caller_bp, Win16FarPtr receiver, uint8_t previous_player) { for (uint16_t id = 1; id <= 175; id++) { Win16FarPtr record = collision_record(id); win16_write_u8( win16_far_add_offset(record, 0x34), win16_read_u8(win16_far_add_offset(receiver, (uint16_t)( 0x0061 + (uint16_t)g_current_player * 0x00afu + id)))); win16_write_u16( record, 0x43, win16_read_u16(win16_far_add_offset(receiver, (uint16_t)( 0x036c + (uint16_t)g_current_player * 0x015eu + id * 2u)))); if (g_current_player != previous_player) { tdkpin_refresh_collision_record(caller_bp, id); } } } static void refresh_changed_player_ui( Win16FarPtr receiver, uint8_t previous_player) { if (g_current_player == previous_player) { return; } uint16_t active_items = 0; for (uint16_t item = 1; item <= 9; item++) { if (win16_read_u8(win16_far_add_offset(receiver, (uint16_t)( 0x046c + (uint16_t)g_current_player * 0x14u + item))) != 0) { active_items++; } } HWND16 window = win16_read_u16(win16_far_add_offset(receiver, 4)); HDC16 dc = GetDC16(window); tdkpin_draw_item_status((int16_t)active_items, dc); tdkpin_update_status_display(g_current_player, dc); (void)ReleaseDC16(window, dc); } static void normal_ball_end(uint16_t caller_bp, Win16FarPtr receiver) { win16_write_u8(win16_dgroup_pointer(0x0875), 0); tdkpin_play_sound_if_not_tilted(8); tdkpin_restore_previous_ball_region(caller_bp, 0); if (win16_read_u8(win16_dgroup_pointer(0x0874)) == 0) { win16_write_u8(win16_far_add_offset(receiver, 0x0bdd), 0); } win16_write_u8(win16_far_add_offset(receiver, 0x0bdc), 0); if ((int16_t)win16_read_u16(player_marker_count(g_current_player)) == 0) { win16_write_u8(win16_far_add_offset(receiver, 0x0bda), 0); win16_write_u8(win16_far_add_offset(receiver, 0x0bdb), 0); tdkpin_load_highscores(caller_bp); tdkpin_insert_current_score(caller_bp); } win16_write_u16(receiver, 0x0bce, 0); win16_write_u16(receiver, 0x0bd0, 0); win16_write_u16(receiver, 0x0bd2, 0); save_current_collision_mirrors(receiver); uint8_t previous_player = g_current_player; if (g_player_count == 0) { win16_integer_divide_fault(); } do { g_current_player = (uint8_t)( (uint16_t)g_current_player % g_player_count + 1u); if ((int16_t)win16_read_u16( player_marker_count(g_current_player)) > 0) { break; } } while (g_current_player != previous_player); if ((int16_t)win16_read_u16( player_marker_count(g_current_player)) < 1) { g_current_player = 0; tdkpin_set_gameplay_transition_flags(caller_bp); if (win16_read_u8(win16_dgroup_pointer(0x07d2)) == 0) { win16_write_u8(win16_far_add_offset(receiver, 0x0bda), 0); win16_write_u8(win16_far_add_offset(receiver, 0x0bdb), 0); tdkpin_show_highscore_windows(caller_bp); } return; } load_next_collision_mirrors(caller_bp, receiver, previous_player); for (uint16_t item = 1; item <= 20; item++) { if (g_current_player != previous_player) { tdkpin_refresh_player_item(caller_bp, item); } } uint8_t ready = win16_read_u8(win16_dgroup_pointer((uint16_t)( 0x0816 + (uint16_t)g_current_player))); for (uint16_t item = 2; item <= 6; item++) { if (item <= ready) { tdkpin_restore_record_or_item_a( caller_bp, (uint16_t)(item + 999u)); } } refresh_changed_player_ui(receiver, previous_player); tdkpin_redraw_all_players(caller_bp); Win16FarPtr markers = player_marker_count(g_current_player); win16_write_u16( markers, 0, (uint16_t)(win16_read_u16(markers) - 1u)); tdkpin_reset_ball_state(caller_bp); HWND16 window = win16_read_u16(win16_far_add_offset(receiver, 4)); HDC16 dc = GetDC16(window); tdkpin_restore_background_region_b(20, 148, 243, 84, dc); tdkpin_present_background_region(20, 148, 243, 84, dc); (void)ReleaseDC16(window, dc); tdkpin_refresh_player_progress_markers(caller_bp); } /* 1000:ae6e -- complete ball-end and player-advance state machine. */ void tdkpin_finish_ball_or_advance_player(uint16_t caller_bp) { Win16FarPtr receiver = caller_receiver(caller_bp); if (win16_read_u8(win16_dgroup_pointer(0x0874)) == 0 && win16_read_u16(win16_dgroup_pointer(0x399a)) != 0) { special_respawn(caller_bp, receiver); } else { normal_ball_end(caller_bp, receiver); } }