/* Player initialization and multi-player redraw paths from TDKPIN Code1. */ #include "tdkpin_player_state.h" #include "tdkpin_arithmetic.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); } /* * 1000:a72c -- initialize collision mirrors and all configured player state. * The collision field at record +43 is copied into four receiver rows even * when fewer than four players are configured, exactly as the binary does. */ void tdkpin_initialize_player_state(uint16_t caller_bp) { Win16FarPtr receiver = caller_receiver(caller_bp); for (uint16_t record_id = 1; record_id <= 175; record_id++) { tdkpin_refresh_collision_record(caller_bp, record_id); uint16_t mirrored = win16_read_u16(win16_dgroup_pointer((uint16_t)( 0x095b + record_id * 0x53u + 0x43u))); for (uint16_t player = 1; player <= 4; player++) { win16_write_u16( receiver, (uint16_t)(0x036c + player * 0x015eu + record_id * 2u), mirrored); } } for (uint16_t item = 1; item <= 5; item++) { tdkpin_restore_record_b(caller_bp, (uint16_t)(1000 + item)); } tdkpin_draw_score_from_caller_frame(caller_bp, 0, 0); win16_write_u8(win16_dgroup_pointer(0x0874), 0); for (uint8_t player = 1; player <= 4; player++) { if (player <= g_player_count) { g_current_player = player; win16_write_u32( win16_dgroup_pointer((uint16_t)(0x097e + player * 4u)), 0, 0); win16_write_u32( win16_dgroup_pointer((uint16_t)(0x098e + player * 4u)), 0, 0); win16_write_u8( win16_dgroup_pointer((uint16_t)(0x09a1 + player)), 1); win16_write_u16( win16_dgroup_pointer((uint16_t)(0x09a4 + player * 2u)), 0, 0); tdkpin_add_score_from_caller_frame(caller_bp, 0); win16_write_u8( win16_dgroup_pointer((uint16_t)(0x0816 + player)), 1); win16_write_u16( win16_dgroup_pointer((uint16_t)(0x081f + player * 2u)), 0, player < 2 ? 2 : 3); tdkpin_refresh_player_progress_markers(caller_bp); win16_write_u32( win16_dgroup_pointer((uint16_t)(0x0835 + player * 4u)), 0, 1); win16_write_u8( win16_dgroup_pointer((uint16_t)(0x081a + player)), 1); for (uint16_t item = 1; item <= 20; item++) { win16_write_u8( win16_far_add_offset(receiver, (uint16_t)( 0x0467 + player * 0x14u + item)), 0); } } } g_current_player = 1; win16_write_u8(win16_dgroup_pointer(0x07d2), 0); for (uint16_t item = 1; item <= 20; item++) { tdkpin_refresh_player_item(caller_bp, item); } } /* 1000:acc2 -- redraw each configured player and emphasize the active row. */ void tdkpin_redraw_all_players(uint16_t caller_bp) { Win16FarPtr receiver = caller_receiver(caller_bp); HWND16 window = win16_read_u16(win16_far_add_offset(receiver, 4)); HDC16 dc = GetDC16(window); uint8_t saved_player = g_current_player; uint8_t count = g_player_count; for (uint8_t player = 1; player <= count; player++) { INT16 row_y = (INT16)((uint16_t)(player - 1u) * 44u + 276u); tdkpin_restore_background_region_a(31, 139, row_y, 472, dc); tdkpin_present_background_region(31, 139, row_y, 472, dc); g_current_player = player; INT16 marker_y = (INT16)((uint16_t)(player - 1u) * 44u + 280u); if (player == saved_player) { tdkpin_add_score_from_caller_frame(caller_bp, 0); uint32_t secondary = win16_read_u32( win16_dgroup_pointer((uint16_t)(0x098e + player * 4u))); uint8_t multiplier = win16_read_u8( win16_dgroup_pointer((uint16_t)(0x09a1 + player))); int32_t shown = borland_multiply_i32( (int32_t)secondary, multiplier); tdkpin_draw_score_from_caller_frame( caller_bp, (uint16_t)shown, (uint16_t)((uint32_t)shown >> 16)); uint16_t sprite = win16_read_u16( win16_dgroup_pointer((uint16_t)(0x09a4 + player * 2u))); tdkpin_composite_player_sprite(sprite, dc); tdkpin_present_background_region(123, 42, 79, 55, dc); tdkpin_restore_background_region_a(11, 11, marker_y, 372, dc); } else { tdkpin_add_score_from_caller_frame(caller_bp, -1); tdkpin_restore_background_region_b(11, 11, marker_y, 372, dc); } tdkpin_present_background_region(11, 11, marker_y, 372, dc); } g_current_player = saved_player; (void)ReleaseDC16(window, dc); } static Win16FarPtr active_secondary_score(void) { return win16_dgroup_pointer((uint16_t)( 0x098e + (uint16_t)g_current_player * 4u)); } static uint8_t active_score_multiplier(void) { return win16_read_u8(win16_dgroup_pointer((uint16_t)( 0x09a1 + (uint16_t)g_current_player))); } static void draw_active_secondary_score(uint16_t caller_bp) { int32_t shown = borland_multiply_i32( (int32_t)win16_read_u32(active_secondary_score()), active_score_multiplier()); tdkpin_draw_score_from_caller_frame( caller_bp, (uint16_t)shown, (uint16_t)((uint32_t)shown >> 16)); } /* 1000:c4e1 -- apply the effect selected by the active player's sprite index. */ void tdkpin_apply_player_sprite_effect( uint16_t caller_bp, int16_t argument) { uint16_t effect = win16_read_u16(win16_dgroup_pointer((uint16_t)( 0x09a4 + (uint16_t)g_current_player * 2u))); static const uint32_t additions[5] = { 10000u, 20000u, 50000u, 100000u, 200000u}; if (effect >= 1 && effect <= 5) { Win16FarPtr secondary = active_secondary_score(); win16_write_u32( secondary, 0, win16_read_u32(secondary) + additions[effect - 1u]); draw_active_secondary_score(caller_bp); } else if (effect == 6) { int32_t transfer = borland_multiply_i32( (int32_t)win16_read_u32(active_secondary_score()), active_score_multiplier()); tdkpin_add_score_from_caller_frame(caller_bp, transfer); win16_write_u32(active_secondary_score(), 0, 0); tdkpin_draw_score_from_caller_frame(caller_bp, 0, 0); } else if (effect == 7) { uint16_t required = win16_read_u16( win16_dgroup_pointer(0x399a)); if (required != 0 && (argument < 0 || required != (uint16_t)argument)) { Win16FarPtr receiver = caller_receiver(caller_bp); win16_write_u8(win16_far_add_offset(receiver, 0x0be0), 1); win16_write_u8(win16_dgroup_pointer(0x0874), 1); } } }