/* Collision-record geometry mover at 1000:8b0d. */ #include "tdkpin_flippers.h" #include "tdkpin_arithmetic.h" #include "tdkpin_render.h" enum { COLLISION_RECORDS_BASE = 0x095b, COLLISION_RECORD_BYTES = 0x53, COLLISION_BOUNDS_MIN_X = 0x02, COLLISION_BOUNDS_MIN_Y = 0x06, COLLISION_BOUNDS_MAX_X = 0x0a, COLLISION_BOUNDS_MAX_Y = 0x0e, COLLISION_POINT1_X = 0x12, COLLISION_POINT1_Y = 0x16, COLLISION_POINT2_X = 0x1a, COLLISION_POINT2_Y = 0x1e, COLLISION_BOUNDS_PADDING = 5000, }; 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)( COLLISION_RECORDS_BASE + id * COLLISION_RECORD_BYTES)); } static int32_t read_record_i32(Win16FarPtr record, uint16_t offset) { return (int32_t)win16_read_u32(win16_far_add_offset(record, offset)); } static void write_record_i32( Win16FarPtr record, uint16_t offset, int32_t value) { win16_write_u32(record, offset, (uint32_t)value); } static int32_t add_wrap_i32(int32_t left, int32_t right) { return (int32_t)((uint32_t)left + (uint32_t)right); } static int32_t subtract_wrap_i32(int32_t left, int32_t right) { return (int32_t)((uint32_t)left - (uint32_t)right); } /* The binary spells several of these as Mul32 -> I32ToReal48 -> Round. * Every int32 is exact in Real48, so the observable result is the wrapped * low dword from the first operation. */ static int32_t scaled_delta(int32_t delta, int32_t scale) { return borland_multiply_i32(delta, scale); } static void add_scaled( Win16FarPtr record, uint16_t offset, int32_t delta, int32_t scale) { write_record_i32( record, offset, add_wrap_i32( read_record_i32(record, offset), scaled_delta(delta, scale))); } static void subtract_scaled( Win16FarPtr record, uint16_t offset, int32_t delta, int32_t scale) { write_record_i32( record, offset, subtract_wrap_i32( read_record_i32(record, offset), scaled_delta(delta, scale))); } static void recompute_segment_bounds(Win16FarPtr record) { int32_t point1_x = read_record_i32(record, COLLISION_POINT1_X); int32_t point1_y = read_record_i32(record, COLLISION_POINT1_Y); int32_t point2_x = read_record_i32(record, COLLISION_POINT2_X); int32_t point2_y = read_record_i32(record, COLLISION_POINT2_Y); int32_t minimum_x = point1_x < point2_x ? point1_x : point2_x; int32_t maximum_x = point1_x < point2_x ? point2_x : point1_x; int32_t minimum_y = point1_y < point2_y ? point1_y : point2_y; int32_t maximum_y = point1_y < point2_y ? point2_y : point1_y; write_record_i32( record, COLLISION_BOUNDS_MIN_X, subtract_wrap_i32(minimum_x, COLLISION_BOUNDS_PADDING)); write_record_i32( record, COLLISION_BOUNDS_MIN_Y, subtract_wrap_i32(minimum_y, COLLISION_BOUNDS_PADDING)); write_record_i32( record, COLLISION_BOUNDS_MAX_X, add_wrap_i32(maximum_x, COLLISION_BOUNDS_PADDING)); write_record_i32( record, COLLISION_BOUNDS_MAX_Y, add_wrap_i32(maximum_y, COLLISION_BOUNDS_PADDING)); } static void update_left_flipper_records(int32_t delta) { Win16FarPtr outer_segment = collision_record(66); add_scaled(outer_segment, COLLISION_POINT1_X, delta, 14000); add_scaled(outer_segment, COLLISION_POINT1_Y, delta, 4000); add_scaled(outer_segment, COLLISION_POINT2_X, delta, 10000); add_scaled(outer_segment, COLLISION_POINT2_Y, delta, 44000); recompute_segment_bounds(outer_segment); Win16FarPtr swept_edge = collision_record(67); add_scaled(swept_edge, COLLISION_POINT1_X, delta, 1000); add_scaled(swept_edge, COLLISION_POINT1_Y, delta, 42000); add_scaled(swept_edge, COLLISION_BOUNDS_MIN_X, delta, 2000); add_scaled(swept_edge, COLLISION_BOUNDS_MIN_Y, delta, 43000); subtract_scaled(swept_edge, COLLISION_BOUNDS_MAX_X, delta, 2000); subtract_scaled(swept_edge, COLLISION_BOUNDS_MAX_Y, delta, 43000); Win16FarPtr inner_segment = collision_record(68); subtract_scaled(inner_segment, COLLISION_POINT1_X, delta, 7000); add_scaled(inner_segment, COLLISION_POINT1_Y, delta, 43000); subtract_scaled(inner_segment, COLLISION_POINT2_X, delta, 19000); add_scaled(inner_segment, COLLISION_POINT2_Y, delta, 6000); recompute_segment_bounds(inner_segment); } static void update_right_flipper_records(int32_t delta) { Win16FarPtr outer_segment = collision_record(83); subtract_scaled(outer_segment, COLLISION_POINT2_X, delta, 16000); add_scaled(outer_segment, COLLISION_POINT2_Y, delta, 3000); subtract_scaled(outer_segment, COLLISION_POINT1_X, delta, 11000); add_scaled(outer_segment, COLLISION_POINT1_Y, delta, 45000); recompute_segment_bounds(outer_segment); Win16FarPtr swept_edge = collision_record(82); subtract_scaled(swept_edge, COLLISION_POINT1_X, delta, 2000); add_scaled(swept_edge, COLLISION_POINT1_Y, delta, 43000); subtract_scaled(swept_edge, COLLISION_BOUNDS_MIN_X, delta, 2000); add_scaled(swept_edge, COLLISION_BOUNDS_MIN_Y, delta, 43000); add_scaled(swept_edge, COLLISION_BOUNDS_MAX_X, delta, 2000); subtract_scaled(swept_edge, COLLISION_BOUNDS_MAX_Y, delta, 43000); Win16FarPtr inner_segment = collision_record(81); add_scaled(inner_segment, COLLISION_POINT2_X, delta, 6000); add_scaled(inner_segment, COLLISION_POINT2_Y, delta, 49000); add_scaled(inner_segment, COLLISION_POINT1_X, delta, 19000); add_scaled(inner_segment, COLLISION_POINT1_Y, delta, 6000); recompute_segment_bounds(inner_segment); } static void move_flipper_bitmap_geometry_with_dc( Win16FarPtr receiver, int32_t delta, uint16_t flipper, HDC16 dc) { if ((uint8_t)flipper == 1) { update_left_flipper_records(delta); if (win16_read_u16(win16_far_add_offset(receiver, 0x0bd4)) == 0) { tdkpin_restore_background_region_b(52, 47, 373, 93, dc); } else { tdkpin_restore_background_region_a(52, 47, 373, 93, dc); } tdkpin_present_background_region(52, 47, 373, 93, dc); } else if ((uint8_t)flipper == 2) { update_right_flipper_records(delta); if (win16_read_u16(win16_far_add_offset(receiver, 0x0bd6)) == 0) { tdkpin_restore_background_region_b(53, 45, 372, 175, dc); } else { tdkpin_restore_background_region_a(53, 45, 372, 175, dc); } tdkpin_present_background_region(53, 45, 372, 175, dc); } } void tdkpin_move_flipper_bitmap_geometry_for_receiver( Win16FarPtr receiver, int32_t delta, uint16_t flipper) { HWND16 handle = win16_read_u16(win16_far_add_offset(receiver, 4)); HDC16 dc = GetDC16(handle); move_flipper_bitmap_geometry_with_dc(receiver, delta, flipper, dc); (void)ReleaseDC16(handle, dc); } /* 1000:8b0d. parent_bp is the hidden static link to 1000:99f0. */ void tdkpin_move_flipper_bitmap_geometry( uint16_t parent_bp, int32_t delta, uint8_t flipper) { uint16_t caller_bp = win16_read_stack_u16((uint16_t)(parent_bp + 6)); Win16FarPtr receiver = caller_receiver(caller_bp); HWND16 handle = win16_read_u16(win16_far_add_offset(receiver, 4)); HDC16 dc = GetDC16(handle); win16_write_stack_u16((uint16_t)(parent_bp - 4), 0); win16_write_stack_u16((uint16_t)(parent_bp - 2), 0); uint32_t vertical_step = delta == 1 ? 43000u : (uint32_t)-43000; win16_write_stack_u16( (uint16_t)(parent_bp - 8), (uint16_t)vertical_step); win16_write_stack_u16( (uint16_t)(parent_bp - 6), (uint16_t)(vertical_step >> 16)); move_flipper_bitmap_geometry_with_dc(receiver, delta, flipper, dc); (void)ReleaseDC16(handle, dc); }