/* Collision/rule-record construction from the 1615-byte Code2 initializer. */ #include "tdkpin_collision_records.h" enum { DGROUP_COLLISION_RECORDS = 0x095b, COLLISION_LAST_INDEX = 175, COORDINATE_SCALE = 1000, BOUNDS_PADDING_MILLI = 5000, }; typedef struct { int32_t minimum; int32_t maximum; } Bounds; static int32_t add_wrap(int32_t left, int32_t right) { return (int32_t)((uint32_t)left + (uint32_t)right); } static int32_t subtract_wrap(int32_t left, int32_t right) { return (int32_t)((uint32_t)left - (uint32_t)right); } static int32_t scale(int32_t value) { return borland_multiply_i32(value, COORDINATE_SCALE); } /* Exact signed-high/unsigned-low comparisons at 0208..03ee. */ static Bounds choose_bounds( int32_t first, int32_t second, int32_t radius) { if (second > 0 && second > first) { return (Bounds){first, second}; } if (second > 0 && second < first) { return (Bounds){second, first}; } return (Bounds){ subtract_wrap(first, radius), add_wrap(first, radius), }; } static void write_real48( Win16FarPtr destination, const BorlandReal48 *value) { for (uint16_t index = 0; index < 6; index++) { win16_write_u8( win16_far_add_offset(destination, index), value->bytes[index]); } } static void write_record( Win16FarPtr destination, const TdkpinCollisionRecord *record) { win16_write_u8(destination, record->type); win16_write_u8(win16_far_add_offset(destination, 1), record->subtype); win16_write_u32(destination, 0x02, (uint32_t)record->bounds_min_x_milli); win16_write_u32(destination, 0x06, (uint32_t)record->bounds_min_y_milli); win16_write_u32(destination, 0x0a, (uint32_t)record->bounds_max_x_milli); win16_write_u32(destination, 0x0e, (uint32_t)record->bounds_max_y_milli); win16_write_u32(destination, 0x12, (uint32_t)record->point1_x_milli); win16_write_u32(destination, 0x16, (uint32_t)record->point1_y_milli); win16_write_u32(destination, 0x1a, (uint32_t)record->point2_x_milli); win16_write_u32(destination, 0x1e, (uint32_t)record->point2_y_milli); write_real48(win16_far_add_offset(destination, 0x22), &record->radius_milli); write_real48( win16_far_add_offset(destination, 0x28), &record->response_normal); write_real48( win16_far_add_offset(destination, 0x2e), &record->response_tangent); win16_write_u8(win16_far_add_offset(destination, 0x34), record->active); write_real48( win16_far_add_offset(destination, 0x35), &record->response_auxiliary); write_real48( win16_far_add_offset(destination, 0x3b), &record->response_kick); win16_write_u16(destination, 0x41, record->flags); win16_write_u16(destination, 0x43, record->contact_state); win16_write_u16(destination, 0x45, record->score_low); win16_write_u16(destination, 0x47, record->score_high); win16_write_u16(destination, 0x49, record->layer_mask); win16_write_u16(destination, 0x4b, record->render_left); win16_write_u16(destination, 0x4d, record->render_top); win16_write_u16(destination, 0x4f, record->render_right); win16_write_u16(destination, 0x51, record->render_bottom); } /* * 1008:0138 -- build one of slots 0..175 in the packed 83-byte table. * * Radius zero selects segment geometry; nonzero radius selects a circle. In * relative mode, point1 is offset from the preceding path endpoint and point2 * is a delta from point1. Absolute mode retains X and subtracts the historical * 20-unit board Y origin from both points. All arithmetic wraps at 32 bits. */ void tdkpin_initialize_collision_record( const TdkpinCollisionInitArgs *arguments) { if (arguments->index > COLLISION_LAST_INDEX) { return; } int32_t point1_x = arguments->point1_x; int32_t point1_y = arguments->point1_y; int32_t point2_x; int32_t point2_y; if ((uint8_t)arguments->coordinate_mode == 1) { point1_x = add_wrap(g_collision_path_x, point1_x); point1_y = add_wrap(g_collision_path_y, point1_y); point2_x = add_wrap(point1_x, arguments->point2_x); point2_y = add_wrap(point1_y, arguments->point2_y); } else { point1_y = subtract_wrap(point1_y, 20); point2_x = arguments->point2_x; point2_y = subtract_wrap(arguments->point2_y, 20); } Bounds x_bounds = choose_bounds(point1_x, point2_x, arguments->radius); Bounds y_bounds = choose_bounds(point1_y, point2_y, arguments->radius); TdkpinCollisionRecord record = {0}; record.type = (uint8_t)arguments->type; record.subtype = (uint8_t)arguments->subtype; record.active = (uint8_t)arguments->active; record.bounds_min_x_milli = scale(x_bounds.minimum); record.bounds_min_y_milli = scale(y_bounds.minimum); record.bounds_max_x_milli = scale(x_bounds.maximum); record.bounds_max_y_milli = scale(y_bounds.maximum); if (arguments->radius != 0) { record.point1_x_milli = scale(point1_x); record.point1_y_milli = scale(point1_y); record.radius_milli = borland_i32_to_real48( scale(arguments->radius)); if (record.bounds_min_x_milli < record.point1_x_milli) { record.bounds_min_x_milli = subtract_wrap( record.bounds_min_x_milli, BOUNDS_PADDING_MILLI); } else { record.bounds_min_x_milli = add_wrap( record.bounds_min_x_milli, scale(arguments->fallback_x_adjust)); } if (record.bounds_min_y_milli < record.point1_y_milli) { record.bounds_min_y_milli = subtract_wrap( record.bounds_min_y_milli, BOUNDS_PADDING_MILLI); } else { record.bounds_min_y_milli = add_wrap( record.bounds_min_y_milli, scale(arguments->fallback_y_adjust)); } if (record.point1_x_milli < record.bounds_max_x_milli) { record.bounds_max_x_milli = add_wrap( record.bounds_max_x_milli, BOUNDS_PADDING_MILLI); } else { record.bounds_max_x_milli = add_wrap( record.bounds_max_x_milli, scale(arguments->fallback_x_adjust)); } if (record.point1_y_milli < record.bounds_max_y_milli) { record.bounds_max_y_milli = add_wrap( record.bounds_max_y_milli, BOUNDS_PADDING_MILLI); } else { record.bounds_max_y_milli = add_wrap( record.bounds_max_y_milli, scale(arguments->fallback_y_adjust)); } g_collision_path_x = point1_x; g_collision_path_y = point1_y; } else { record.bounds_min_x_milli = subtract_wrap( record.bounds_min_x_milli, BOUNDS_PADDING_MILLI); record.bounds_min_y_milli = subtract_wrap( record.bounds_min_y_milli, BOUNDS_PADDING_MILLI); record.bounds_max_x_milli = add_wrap( record.bounds_max_x_milli, BOUNDS_PADDING_MILLI); record.bounds_max_y_milli = add_wrap( record.bounds_max_y_milli, BOUNDS_PADDING_MILLI); record.point1_x_milli = scale(point1_x); record.point1_y_milli = scale(point1_y); record.point2_x_milli = scale(point2_x); record.point2_y_milli = scale(point2_y); g_collision_path_x = point2_x; g_collision_path_y = point2_y; } record.response_normal = arguments->response_normal; record.response_tangent = arguments->response_tangent; record.response_auxiliary = arguments->response_auxiliary; record.response_kick = arguments->response_kick; record.flags = arguments->flags; record.contact_state = arguments->contact_state; record.score_low = arguments->score_low; record.score_high = arguments->score_high; record.layer_mask = arguments->layer_mask; record.render_left = arguments->render_left; record.render_top = arguments->render_top; record.render_right = arguments->render_right; record.render_bottom = arguments->render_bottom; uint16_t offset = (uint16_t)( DGROUP_COLLISION_RECORDS + (uint16_t)(arguments->index * sizeof(TdkpinCollisionRecord))); write_record(win16_dgroup_pointer(offset), &record); }