double move_outside_object(int object, double angle, double max_dist, bool solid_only) { enigma::object_collisions* const inst1 = ((enigma::object_collisions*)enigma::instance_event_iterator->inst); if (inst1->sprite_index == -1 && (inst1->mask_index == -1)) { return -4; } double x = inst1->x, y = inst1->y; if (collide_inst_inst(object, solid_only, true, x, y) == NULL) { return 0; } const double DMIN = 1, DMAX = 1000; // Arbitrary max for non-positive values, 1000 fits with other implementations. const double contact_distance = DMIN; if (max_dist <= 0) { // Use the arbitrary max for non-positive values. max_dist = DMAX; } const double radang = (fmod(fmod(angle, 360) + 360, 360))*(M_PI/180.0); double current_dist; for (current_dist = DMIN; current_dist <= max_dist; current_dist++) { const double next_x = x + current_dist*cos(radang); const double next_y = y - current_dist*sin(radang); inst1->x = next_x; inst1->y = next_y; if (collide_inst_inst(object, solid_only, true, next_x, next_y) == NULL) { break; } } return current_dist; }
object_basic *place_meeting_inst(double x, double y, int object) { return collide_inst_inst(object,false,true,x,y); }
int instance_place(double x, double y, int object) { enigma::object_collisions* const r = collide_inst_inst(object,false,true,x,y); return r == NULL ? noone : r->id; }
bool place_empty(double x,double y) { return collide_inst_inst(all,false,true,x,y) == NULL; }
bool place_free(double x,double y) { return collide_inst_inst(all,true,true,x,y) == NULL; }
bool move_bounce_object(int object, bool adv, bool solid_only) { enigma::object_collisions* const inst1 = ((enigma::object_collisions*)enigma::instance_event_iterator->inst); if (inst1->sprite_index == -1 && (inst1->mask_index == -1)) return -4; if (collide_inst_inst(object, solid_only, true, inst1->x, inst1->y) == NULL && collide_inst_inst(object, solid_only, true, inst1->x + inst1->hspeed, inst1->y + inst1->vspeed) == NULL) { return false; } if (collide_inst_inst(object, solid_only, true, inst1->x, inst1->y) != NULL) { // Return the instance to its previous position. inst1->x = inst1->xprevious; inst1->y = inst1->yprevious; } const double angle = inst1->direction, radang = angle*(M_PI/180.0), DBL_EPSILON = 0.00001; double sin_angle = sin(radang), cos_angle = cos(radang), pc_corner, pc_dist, max_dist = 1000000; int side_type = 0; const int quad = int(2*radang/M_PI); const bbox_rect_t &box = inst1->$bbox_relative(); const double x1 = inst1->x, y1 = inst1->y, xscale1 = inst1->image_xscale, yscale1 = inst1->image_yscale, ia1 = inst1->image_angle; int left1, top1, right1, bottom1; get_border(&left1, &right1, &top1, &bottom1, box.left, box.top, box.right, box.bottom, x1, y1, xscale1, yscale1, ia1); for (enigma::iterator it = enigma::fetch_inst_iter_by_int(object); it; ++it) { const enigma::object_collisions* inst2 = (enigma::object_collisions*)*it; if (inst2->id == inst1->id || (solid_only && !inst2->solid)) continue; if (inst2->sprite_index == -1 && (inst2->mask_index == -1)) continue; const bbox_rect_t &box2 = inst2->$bbox_relative(); const double x2 = inst2->x, y2 = inst2->y, xscale2 = inst2->image_xscale, yscale2 = inst2->image_yscale, ia2 = inst2->image_angle; int left2, top2, right2, bottom2; get_border(&left2, &right2, &top2, &bottom2, box2.left, box2.top, box2.right, box2.bottom, x2, y2, xscale2, yscale2, ia2); if (right2 >= left1 && bottom2 >= top1 && left2 <= right1 && top2 <= bottom1) return false; switch (quad) { case 0: if ((left2 > right1 || top1 > bottom2) && direction_difference(point_direction(right1, bottom1, left2, top2),angle) >= 0 && direction_difference(point_direction(left1, top1, right2, bottom2),angle) <= 0) { pc_corner = direction_difference(point_direction(right1, top1, left2, bottom2),angle); if (fabs(pc_corner) < DBL_EPSILON) { pc_dist = (left2 - right1)/cos_angle; if (pc_dist < max_dist) { max_dist = pc_dist; side_type = 4; } } else if (pc_corner > 0) { pc_dist = (top1 - bottom2)/sin_angle; if (fabs(pc_dist - max_dist) < DBL_EPSILON) { if (side_type == 2) side_type = 3; else if (side_type != 3) side_type = 1; } else if (pc_dist < max_dist) { max_dist = pc_dist; side_type = 1; } } else { pc_dist = (left2 - right1)/cos_angle; if (fabs(pc_dist - max_dist) < DBL_EPSILON) { if (side_type == 1) side_type = 3; else if (side_type != 3) side_type = 2; } else if (pc_dist < max_dist) { max_dist = pc_dist; side_type = 2; } } } break; case 1: if ((left1 > right2 || top1 > bottom2) && direction_difference(point_direction(left1, bottom1, right2, top2),angle) <= 0 && direction_difference(point_direction(right1, top1, left2, bottom2),angle) >= 0) { pc_corner = direction_difference(point_direction(left1, top1, right2, bottom2),angle); if (fabs(pc_corner) < DBL_EPSILON) { pc_dist = (left2 - right1)/cos_angle; if (pc_dist < max_dist) { max_dist = pc_dist; side_type = 4; } } else if (pc_corner > 0) { pc_dist = (right2 - left1)/cos_angle; if (fabs(pc_dist - max_dist) < DBL_EPSILON) { if (side_type == 1) side_type = 3; else if (side_type != 3) side_type = 2; } else if (pc_dist < max_dist) { max_dist = pc_dist; side_type = 2; } } else { pc_dist = (top1 - bottom2)/sin_angle; if (fabs(pc_dist - max_dist) < DBL_EPSILON) { if (side_type == 2) side_type = 3; else if (side_type != 3) side_type = 1; } else if (pc_dist < max_dist) { max_dist = pc_dist; side_type = 1; } } } break; case 2: if ((left1 > right2 || top2 > bottom1) && direction_difference(point_direction(right1, bottom1, left2, top2),angle) <= 0 && direction_difference(point_direction(left1, top1, right2, bottom2),angle) >= 0) { pc_corner = direction_difference(point_direction(left1, bottom1, right2, top2),angle); if (fabs(pc_corner) < DBL_EPSILON) { pc_dist = (right2 - left1)/cos_angle; if (pc_dist < max_dist) { max_dist = pc_dist; side_type = 4; } } else if (pc_corner > 0) { pc_dist = (bottom1 - top2)/sin_angle; if (fabs(pc_dist - max_dist) < DBL_EPSILON) { if (side_type == 2) side_type = 3; else if (side_type != 3) side_type = 1; } else if (pc_dist < max_dist) { max_dist = pc_dist; side_type = 1; } } else { pc_dist = (right2 - left1)/cos_angle; if (fabs(pc_dist - max_dist) < DBL_EPSILON) { if (side_type == 1) side_type = 3; else if (side_type != 3) side_type = 2; } else if (pc_dist < max_dist) { max_dist = pc_dist; side_type = 2; } } } break; case 3: if ((left2 > right1 || top2 > bottom1) && direction_difference(point_direction(right1, top1, left2, bottom2),angle) <= 0 && direction_difference(point_direction(left1, bottom1, right2, top2),angle) >= 0) { pc_corner = direction_difference(point_direction(right1, bottom1, left2, top2),angle); if (fabs(pc_corner) < DBL_EPSILON) { pc_dist = (bottom1 - top2)/sin_angle; if (pc_dist < max_dist) { max_dist = pc_dist; side_type = 4; } } else if (pc_corner > 0) { pc_dist = (left2 - right1)/cos_angle; if (fabs(pc_dist - max_dist) < DBL_EPSILON) { if (side_type == 1) side_type = 3; else if (side_type != 3) side_type = 2; } else if (pc_dist < max_dist) { max_dist = pc_dist; side_type = 2; } } else { pc_dist = (bottom1 - top2)/sin_angle; if (fabs(pc_dist - max_dist) < DBL_EPSILON) { if (side_type == 2) side_type = 3; else if (side_type != 3) side_type = 1; } else if (pc_dist < max_dist) { max_dist = pc_dist; side_type = 1; } } } break; } } switch (side_type) { case 0: //no side hit return false; case 1: //horizontal side hit inst1->vspeed *= -1; break; case 2: //vertical side hit inst1->hspeed *= -1; break; case 3: case 4: //corner or both horizontal and vertical side hit inst1->hspeed *= -1; inst1->vspeed *= -1; break; } return true; }
double move_outside_object(int object, double angle, double max_dist, bool solid_only) { enigma::object_collisions* const inst1 = ((enigma::object_collisions*)enigma::instance_event_iterator->inst); if (inst1->sprite_index == -1 && (inst1->mask_index == -1)) return -4; const double DMIN = 0.000001, DMAX = 1000000; const double contact_distance = DMIN; if (max_dist <= 0) { max_dist = DMAX; } double dist = 0; angle = fmod(angle, 360.0); double radang = angle*(M_PI/180.0); const double sin_angle = sin(radang), cos_angle = cos(radang); const int quad = int(angle/90.0); const double xscale1 = inst1->image_xscale, yscale1 = inst1->image_yscale, ia1 = inst1->image_angle; const double x_start = inst1->x, y_start = inst1->y; bool had_collision = true; while (had_collision) // If there was no collision in the last iteration, we have moved outside the object. { had_collision = false; for (enigma::iterator it = enigma::fetch_inst_iter_by_int(object); it; ++it) { const bbox_rect_t &box = inst1->$bbox_relative(); const double x1 = inst1->x, y1 = inst1->y; int left1, top1, right1, bottom1; get_border(&left1, &right1, &top1, &bottom1, box.left, box.top, box.right, box.bottom, x1, y1, xscale1, yscale1, ia1); const enigma::object_collisions* inst2 = (enigma::object_collisions*)*it; if (inst2->id == inst1->id || (solid_only && !inst2->solid)) continue; if (inst2->sprite_index == -1 && (inst2->mask_index == -1)) continue; const bbox_rect_t &box2 = inst2->$bbox_relative(); const double x2 = inst2->x, y2 = inst2->y, xscale2 = inst2->image_xscale, yscale2 = inst2->image_yscale, ia2 = inst2->image_angle; int left2, top2, right2, bottom2; get_border(&left2, &right2, &top2, &bottom2, box2.left, box2.top, box2.right, box2.bottom, x2, y2, xscale2, yscale2, ia2); if (!(right2 >= left1 && bottom2 >= top1 && left2 <= right1 && top2 <= bottom1)) continue; had_collision = true; // Move at least one step every time there is a collision. const double min_dist = dist + 1; switch (quad) { case 0: if (direction_difference(point_direction(left1, bottom1, right2, top2),angle) < 0) { dist += ((bottom1) - (top2))/sin_angle; } else { dist += ((right2) - (left1))/cos_angle; } break; case 1: if (direction_difference(point_direction(right1, bottom1, left2, top2),angle) < 0) { dist += ((left2) - (right1))/cos_angle; } else { dist += ((bottom1) - (top2))/sin_angle; } break; case 2: if (direction_difference(point_direction(right1, top1, left2, bottom2),angle) < 0) { dist += ((top1) - (bottom2))/sin_angle; } else { dist += ((left2) - (right1))/cos_angle; } break; case 3: if (direction_difference(point_direction(left1, top1, right2, bottom2),angle) < 0) { dist += ((right2) - (left1))/cos_angle; } else { dist += ((top1) - (bottom2))/sin_angle; } break; } dist = max(min(dist, max_dist), min_dist); inst1->x = x_start + cos_angle*dist; inst1->y = y_start - sin_angle*dist; for (int i = 0; i < 1; i++) // Move a single step on if the moving was not precise. { if (collide_inst_inst(inst2->id, solid_only, true, inst1->x, inst1->y) == NULL) { break; } dist += 1; inst1->x = x_start + cos_angle*dist; inst1->y = y_start - sin_angle*dist; } } } return dist; }
bool move_bounce_object(int object, bool adv, bool solid_only) { enigma::object_collisions* const inst1 = ((enigma::object_collisions*)enigma::instance_event_iterator->inst); if (inst1->sprite_index == -1 && (inst1->mask_index == -1)) return false; if (collide_inst_inst(object, solid_only, true, inst1->x, inst1->y) == NULL && collide_inst_inst(object, solid_only, true, inst1->x + inst1->hspeed, inst1->y + inst1->vspeed) == NULL) { return false; } if (collide_inst_inst(object, solid_only, true, inst1->x, inst1->y) != NULL) { // Return the instance to its previous position. inst1->x = inst1->xprevious; inst1->y = inst1->yprevious; } const double x_start = inst1->x, y_start = inst1->y; const double x = inst1->x + inst1->hspeed, y = inst1->y + inst1->vspeed; if (adv) { const double angle_radial = 10.0; // The angle increment for the radials. const int radial_max = max(int(180/angle_radial), 1); // The maximum number of radials in one direction. const double effective_direction = inst1->speed >= 0 ? inst1->direction : fmod(inst1->direction+180.0, 360.0); const double flipped_direction = fmod(effective_direction + 180.0, 360.0); const double speed = abs(inst1->speed + 1);//max(1, abs(inst1->speed)); // Find the normal direction of the collision by doing radial collisions based on the speed and flipped direction. int d1, d2; for (d1 = 0; d1 < radial_max; d1++) // Positive direction. { const double rad = (flipped_direction + d1*angle_radial)*M_PI/180.0; const double x1 = x + speed*cos(rad); const double y1 = y - speed*sin(rad); if (collide_inst_inst(object, solid_only, true, x1, y1) != NULL) { break; } } for (d2 = 0; d2 > -radial_max; d2--) // Negative direction. { const double rad = (flipped_direction + d2*angle_radial)*M_PI/180.0; const double x1 = x + speed*cos(rad); const double y1 = y - speed*sin(rad); if (collide_inst_inst(object, solid_only, true, x1, y1) != NULL) { break; } } const int d = int(round((d1 + d2)/2.0)); const double normal_direction = fmod(flipped_direction + d*angle_radial + 360.0, 360.0); // Flip and then mirror the effective direction unit vector along the direction of the normal. const double normal_rad = normal_direction * M_PI / 180.0; const double normal_x = cos(normal_rad), normal_y = -sin(normal_rad); const double flipped_rad = flipped_direction * M_PI / 180.0; const double flipped_x = cos(flipped_rad), flipped_y = -sin(flipped_rad); const double dot_product = normal_x*flipped_x + normal_y*flipped_y; const double flipped_on_normal_x = dot_product*normal_x, flipped_on_normal_y = dot_product*normal_y; const double crossed_x = flipped_x - flipped_on_normal_x, crossed_y = flipped_y - flipped_on_normal_y; const double mirror_x = flipped_x - 2*crossed_x, mirror_y = flipped_y - 2*crossed_y; // Set final direction from flipped, mirrored direction unit vector. const double mirror_direction = fmod(atan2(-mirror_y, mirror_x) * 180.0 / M_PI + 360.0, 360.0); inst1->direction = inst1->speed >= 0 ? mirror_direction : fmod(mirror_direction + 180.0, 360.0); return true; } else { const double direction = inst1->speed >= 0 ? inst1->direction : fmod(inst1->direction+180.0, 360.0); const bool free_horizontal = collide_inst_inst(object, solid_only, true, x, y_start) == NULL; const bool free_vertical = collide_inst_inst(object, solid_only, true, x_start, y) == NULL; double new_direction; if (!free_horizontal && free_vertical) { new_direction = direction <= 180.0 ? fmod(180.0 - direction, 360.0) : fmod((360.0 - direction) + 180.0, 360.0); } else if (free_horizontal && !free_vertical) { new_direction = fmod(360.0 - direction, 360.0); } else { new_direction = fmod(direction + 180.0, 360.0); } inst1->direction = inst1->speed >= 0 ? new_direction : fmod(new_direction + 180.0, 360.0); return true; } }
double move_contact_object(int object, double angle, double max_dist, bool solid_only) { enigma::object_collisions* const inst1 = ((enigma::object_collisions*)enigma::instance_event_iterator->inst); if (inst1->sprite_index == -1 && (inst1->mask_index == -1)) { return -4; } const double x = inst1->x, y = inst1->y; if (collide_inst_inst(object, solid_only, true, x, y) != NULL) { return 0; } const double DMIN = 1, DMAX = 1000; // Arbitrary max for non-positive values, 1000 fits with other implementations. const double contact_distance = DMIN; double mid_dist = max_dist; // mid_dist is used for the subtraction part. if (max_dist <= 0) { // Use the arbitrary max for non-positive values. max_dist = DMAX; } mid_dist = max_dist; const double radang = (fmod(fmod(angle, 360) + 360, 360))*(M_PI/180.0); const double sin_angle = sin(radang), cos_angle = cos(radang); // Subtraction. const int quad = int(angle/90.0); const bbox_rect_t &box = inst1->$bbox_relative(); const double x1 = inst1->x, y1 = inst1->y, xscale1 = inst1->image_xscale, yscale1 = inst1->image_yscale, ia1 = inst1->image_angle; int left1, top1, right1, bottom1; get_border(&left1, &right1, &top1, &bottom1, box.left, box.top, box.right, box.bottom, x1, y1, xscale1, yscale1, ia1); for (enigma::iterator it = enigma::fetch_inst_iter_by_int(object); it; ++it) { const enigma::object_collisions* inst2 = (enigma::object_collisions*)*it; if (inst2->sprite_index == -1 && (inst2->mask_index == -1)) continue; if (inst2->id == inst1->id || (solid_only && !inst2->solid)) continue; const bbox_rect_t &box2 = inst2->$bbox_relative(); const double x2 = inst2->x, y2 = inst2->y, xscale2 = inst2->image_xscale, yscale2 = inst2->image_yscale, ia2 = inst2->image_angle; int left2, top2, right2, bottom2; get_border(&left2, &right2, &top2, &bottom2, box2.left, box2.top, box2.right, box2.bottom, x2, y2, xscale2, yscale2, ia2); if (right2 >= left1 && bottom2 >= top1 && left2 <= right1 && top2 <= bottom1) { mid_dist = DMIN; break; } switch (quad) { case 0: if ((left2 > right1 || top1 > bottom2) && direction_difference(point_direction(right1, bottom1, left2, top2),angle) >= 0 && direction_difference(point_direction(left1, top1, right2, bottom2),angle) <= 0) { if (direction_difference(point_direction(right1, top1, left2, bottom2),angle) > 0) { mid_dist = min(mid_dist, (top1 - bottom2 - contact_distance)/sin_angle); } else { mid_dist = min(mid_dist, (left2 - right1 - contact_distance)/cos_angle); } } break; case 1: if ((left1 > right2 || top1 > bottom2) && direction_difference(point_direction(left1, bottom1, right2, top2),angle) <= 0 && direction_difference(point_direction(right1, top1, left2, bottom2),angle) >= 0) { if (direction_difference(point_direction(left1, top1, right2, bottom2),angle) > 0) { mid_dist = min(mid_dist, (right2 - left1 + contact_distance)/cos_angle); } else { mid_dist = min(mid_dist, (top1 - bottom2 - contact_distance)/sin_angle); } } break; case 2: if ((left1 > right2 || top2 > bottom1) && direction_difference(point_direction(right1, bottom1, left2, top2),angle) <= 0 && direction_difference(point_direction(left1, top1, right2, bottom2),angle) >= 0) { if (direction_difference(point_direction(left1, bottom1, right2, top2),angle) > 0) { mid_dist = min(mid_dist, (bottom1 - top2 + contact_distance)/sin_angle); } else { mid_dist = min(mid_dist, (right2 - left1 + contact_distance)/cos_angle); } } break; case 3: if ((left2 > right1 || top2 > bottom1) && direction_difference(point_direction(right1, top1, left2, bottom2),angle) <= 0 && direction_difference(point_direction(left1, bottom1, right2, top2),angle) >= 0) { if (direction_difference(point_direction(right1, bottom1, left2, top2),angle) > 0) { mid_dist = min(mid_dist, (left2 - right1 - contact_distance)/cos_angle); } else { mid_dist = min(mid_dist, (bottom1 - top2 + contact_distance)/sin_angle); } } break; } } double current_dist = DMIN; { // Avoid moving to position which isn't free. mid_dist is not guaranteed to indicate a free position. double next_x = x + mid_dist*cos_angle; double next_y = y - mid_dist*sin_angle; if (collide_inst_inst(object, solid_only, true, next_x, next_y) == NULL) { inst1->x = next_x; inst1->y = next_y; current_dist = mid_dist; } } // Subtraction end. for (; current_dist <= max_dist; current_dist++) { const double next_x = x + current_dist*cos_angle; const double next_y = y - current_dist*sin_angle; if (collide_inst_inst(object, solid_only, true, next_x, next_y) != NULL) { current_dist--; break; } else { inst1->x = next_x; inst1->y = next_y; } } return current_dist; }
object_basic *place_meeting_inst(cs_scalar x, cs_scalar y, int object) { return collide_inst_inst(object,false,true,x,y); }
bool place_empty(cs_scalar x, cs_scalar y) { return collide_inst_inst(all,false,true,x,y) == NULL; }
bool place_free(cs_scalar x, cs_scalar y) { return collide_inst_inst(all,true,true,x,y) == NULL; }