/* Bitmask-driven collision event dispatcher from TDKPIN Code1. */ #include "tdkpin_collision_events.h" #include "tdkpin_arithmetic.h" #include "tdkpin_random.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 record(uint16_t id) { return win16_dgroup_pointer((uint16_t)(0x095b + id * 0x53u)); } static uint8_t record_active(uint16_t id) { return win16_read_u8(win16_far_add_offset(record(id), 0x34)); } static void set_record_active(uint16_t id, uint8_t active) { win16_write_u8(win16_far_add_offset(record(id), 0x34), active); } static uint16_t record_link(uint16_t id) { return win16_read_u16(win16_far_add_offset(record(id), 0x43)); } static uint16_t player_row_offset(uint16_t item) { return (uint16_t)( 0x0467 + (uint16_t)g_current_player * 0x14u + item); } static void activate_record_groups( uint16_t caller_bp, uint16_t first, uint16_t groups) { for (uint16_t group = 0; group < groups; group++) { uint16_t base = (uint16_t)(first + group * 3u); set_record_active(base, 1); set_record_active((uint16_t)(base + 1u), 1); set_record_active((uint16_t)(base + 2u), 1); tdkpin_refresh_collision_record(caller_bp, base); } } static bool group_heads_all_inactive(uint16_t first, uint16_t groups) { for (uint16_t group = 0; group < groups; group++) { if (record_active((uint16_t)(first + group * 3u)) != 0) { return false; } } return true; } static void restore_record_bounds_on_dc( uint16_t id, HDC16 dc) { Win16FarPtr bounds = win16_far_add_offset(record(id), 0x4b); INT16 left = (INT16)win16_read_u16(bounds); INT16 top = (INT16)win16_read_u16(win16_far_add_offset(bounds, 2)); INT16 right = (INT16)win16_read_u16(win16_far_add_offset(bounds, 4)); INT16 bottom = (INT16)win16_read_u16(win16_far_add_offset(bounds, 6)); INT16 width = (INT16)(uint16_t)( (uint16_t)right - (uint16_t)left + 1u); INT16 height = (INT16)(uint16_t)( (uint16_t)bottom - (uint16_t)top + 1u); tdkpin_restore_background_region_a(height, width, top, left, dc); tdkpin_present_background_region(height, width, top, left, dc); } static void publish_three_record_completion( uint16_t caller_bp, Win16FarPtr receiver) { win16_write_u8(win16_far_add_offset(receiver, 0x0bdc), 1); static const uint16_t ids[3] = {153, 6, 154}; for (uint16_t index = 0; index < 3; index++) { set_record_active(ids[index], 1); } HWND16 window = win16_read_u16(win16_far_add_offset(receiver, 4)); HDC16 dc = GetDC16(window); for (uint16_t index = 0; index < 3; index++) { restore_record_bounds_on_dc(ids[index], dc); } (void)ReleaseDC16(window, dc); (void)caller_bp; } static void handle_sprite_award( uint16_t caller_bp, Win16FarPtr receiver) { tdkpin_play_sound_if_not_tilted(12); set_record_active(149, 1); win16_write_u8(win16_far_add_offset(receiver, (uint16_t)( 0x00f6 + (uint16_t)g_current_player * 0x00afu)), 1); tdkpin_restore_record_or_item_a(caller_bp, 149); Win16FarPtr sprite = win16_dgroup_pointer((uint16_t)( 0x09a4 + (uint16_t)g_current_player * 2u)); if (win16_read_u16(win16_dgroup_pointer(0x399a)) != 0) { win16_write_u16(sprite, 0, 7); } else { uint16_t previous = win16_read_u16(sprite); uint16_t selected; do { selected = (uint16_t)(borland_random_below(6) + 1u); } while (selected == previous); win16_write_u16(sprite, 0, selected); } HWND16 window = win16_read_u16(win16_far_add_offset(receiver, 4)); HDC16 dc = GetDC16(window); tdkpin_composite_player_sprite(win16_read_u16(sprite), dc); tdkpin_present_background_region(123, 42, 79, 55, dc); (void)ReleaseDC16(window, dc); } static void handle_ready_upgrade(uint16_t caller_bp) { tdkpin_play_sound_if_not_tilted(12); if (!group_heads_all_inactive(90, 5)) { return; } tdkpin_play_sound_if_not_tilted(17); Win16FarPtr ready = win16_dgroup_pointer((uint16_t)( 0x0816 + (uint16_t)g_current_player)); uint8_t value = win16_read_u8(ready); if (value == 6) { tdkpin_add_score_from_caller_frame(caller_bp, 50000); } else { value = value == 1 ? 2 : (uint8_t)(value + 1u); win16_write_u8(ready, value); tdkpin_restore_record_or_item_a( caller_bp, (uint16_t)(value + 999u)); } activate_record_groups(caller_bp, 90, 5); } static void handle_item_bank( uint16_t caller_bp, Win16FarPtr receiver) { tdkpin_play_sound_if_not_tilted(12); if (!group_heads_all_inactive(109, 4)) { return; } uint16_t slot = 1; for (uint16_t item = 1; item <= 9; item++) { if (win16_read_u8(win16_far_add_offset( receiver, player_row_offset((uint16_t)(item + 5u)))) != 0) { slot++; } } if (slot < 9) { tdkpin_play_sound_if_not_tilted(17); win16_write_u8( win16_far_add_offset( receiver, player_row_offset((uint16_t)(slot + 5u))), 1); tdkpin_restore_record_or_item_a( caller_bp, (uint16_t)(slot + 1005u)); tdkpin_add_score_from_caller_frame( caller_bp, (int32_t)slot * 10000); } else if (slot == 9) { tdkpin_play_sound_if_not_tilted(17); win16_write_u8( win16_far_add_offset(receiver, player_row_offset(14)), 1); tdkpin_restore_record_or_item_a(caller_bp, 1014); tdkpin_add_score_from_caller_frame(caller_bp, 24464); publish_three_record_completion(caller_bp, receiver); } else { tdkpin_play_sound_if_not_tilted(7); if (win16_read_u8(win16_far_add_offset(receiver, 0x0bdc)) == 0) { win16_write_u8(win16_far_add_offset(receiver, 0x0bdc), 1); } else { Win16FarPtr secondary = win16_dgroup_pointer((uint16_t)( 0x098e + (uint16_t)g_current_player * 4u)); win16_write_u32( secondary, 0, win16_read_u32(secondary) + 100000u); uint8_t multiplier = win16_read_u8(win16_dgroup_pointer((uint16_t)( 0x09a1 + (uint16_t)g_current_player))); int32_t shown = borland_multiply_i32( (int32_t)win16_read_u32(secondary), multiplier); tdkpin_draw_score_from_caller_frame( caller_bp, (uint16_t)shown, (uint16_t)((uint32_t)shown >> 16)); } } activate_record_groups(caller_bp, 109, 4); } static void handle_three_record_switch( uint16_t caller_bp, Win16FarPtr receiver, uint16_t record_id) { if (record_id == 150) { win16_write_u8(win16_far_add_offset(receiver, 0x00f7), 10); tdkpin_restore_record_or_item_a(caller_bp, 150); set_record_active(153, 1); tdkpin_restore_record_or_item_a(caller_bp, 153); } else if (record_id == 151) { win16_write_u8(win16_far_add_offset(receiver, 0x00f8), 10); tdkpin_restore_record_or_item_a(caller_bp, 151); set_record_active(6, 1); tdkpin_restore_record_or_item_a(caller_bp, 6); } else if (record_id == 152) { win16_write_u8(win16_far_add_offset(receiver, 0x00f9), 10); tdkpin_restore_record_or_item_a(caller_bp, 152); set_record_active(154, 1); tdkpin_restore_record_or_item_a(caller_bp, 154); } } static void handle_timed_record( uint16_t caller_bp, Win16FarPtr receiver, uint16_t record_id) { if (record_id >= 51 && record_id <= 53) { tdkpin_play_sound_if_not_tilted(6); win16_write_u8( win16_far_add_offset(receiver, (uint16_t)(record_id + 0x61u)), 5); tdkpin_restore_record_or_item_a(caller_bp, record_id); uint8_t ready = win16_read_u8(win16_dgroup_pointer((uint16_t)( 0x0816 + (uint16_t)g_current_player))); tdkpin_add_score_from_caller_frame( caller_bp, (int32_t)((int16_t)ready - 1) * 1000); } else if (record_id >= 140 && record_id <= 147) { win16_write_u8( win16_far_add_offset(receiver, (uint16_t)(record_id + 0x61u)), 20); tdkpin_restore_record_or_item_a(caller_bp, record_id); } } /* 1000:b476 -- dispatch all collision event bits in their binary order. */ void tdkpin_handle_collision_events( uint16_t caller_bp, uint16_t event_flags, uint16_t record_id) { if (win16_read_u8(win16_dgroup_pointer(0x07c6)) != 0) { return; } Win16FarPtr receiver = caller_receiver(caller_bp); if ((event_flags & 0x0001u) != 0 && record_id >= 129 && record_id <= 133) { uint16_t item = (uint16_t)(record_id - 0x72u); win16_write_u8(win16_far_add_offset( receiver, player_row_offset(item)), 1); tdkpin_restore_record_or_item_a( caller_bp, (uint16_t)(item + 1000u)); } if ((event_flags & 0x0002u) != 0) { uint16_t linked = record_link(record_id); set_record_active(linked, 0); tdkpin_refresh_collision_record(caller_bp, linked); if ((event_flags & 0x2000u) != 0) { set_record_active((uint16_t)(linked + 1u), 0); set_record_active((uint16_t)(linked + 2u), 0); } } if ((event_flags & 0x0400u) != 0 && win16_read_u8(win16_dgroup_pointer(0x07c7)) == 0 && win16_read_u8(win16_dgroup_pointer(0x07c8)) == 0) { win16_write_u32(win16_dgroup_pointer(0x0855), 0, 0); win16_write_u8(win16_dgroup_pointer(0x07c7), 1); } if ((event_flags & 0x0004u) != 0) { handle_sprite_award(caller_bp, receiver); } if ((event_flags & 0x0008u) != 0) { handle_ready_upgrade(caller_bp); } if ((event_flags & 0x0010u) != 0) { handle_item_bank(caller_bp, receiver); } if ((event_flags & 0x1000u) != 0) { handle_three_record_switch(caller_bp, receiver, record_id); } if ((event_flags & 0x0040u) != 0) { handle_timed_record(caller_bp, receiver, record_id); } }