/** * Checks ship-weapon collisions. * @param pair obj_pair pointer to the two objects. pair->a is ship and pair->b is weapon. * @return 1 if all future collisions between these can be ignored */ int collide_ship_weapon( obj_pair * pair ) { int did_hit; object *ship = pair->a; object *weapon_obj = pair->b; Assert( ship->type == OBJ_SHIP ); Assert( weapon_obj->type == OBJ_WEAPON ); ship_info *sip = &Ship_info[Ships[ship->instance].ship_info_index]; // Don't check collisions for player if past first warpout stage. if ( Player->control_mode > PCM_WARPOUT_STAGE1) { if ( ship == Player_obj ) return 0; } if (reject_due_collision_groups(ship, weapon_obj)) return 0; // Cull lasers within big ship spheres by casting a vector forward for (1) exit sphere or (2) lifetime of laser // If it does hit, don't check the pair until about 200 ms before collision. // If it does not hit and is within error tolerance, cull the pair. if ( (sip->is_big_or_huge()) && (Weapon_info[Weapons[weapon_obj->instance].weapon_info_index].subtype == WP_LASER) ) { // Check when within ~1.1 radii. // This allows good transition between sphere checking (leaving the laser about 200 ms from radius) and checking // within the sphere with little time between. There may be some time for "small" big ships // Note: culling ships with auto spread shields seems to waste more performance than it saves, // so we're not doing that here if ( !(sip->flags[Ship::Info_Flags::Auto_spread_shields]) && vm_vec_dist_squared(&ship->pos, &weapon_obj->pos) < (1.2f*ship->radius*ship->radius) ) { return check_inside_radius_for_big_ships( ship, weapon_obj, pair ); } } did_hit = ship_weapon_check_collision( ship, weapon_obj ); if ( !did_hit ) { // Since we didn't hit, check to see if we can disable all future collisions // between these two. return weapon_will_never_hit( weapon_obj, ship, pair ); } return 0; }
// Checks ship-weapon collisions. pair->a is ship and pair->b is weapon. // Returns 1 if all future collisions between these can be ignored int collide_ship_weapon( obj_pair * pair ) { int did_hit; object *ship = pair->a; object *weapon = pair->b; Assert( ship->type == OBJ_SHIP ); Assert( weapon->type == OBJ_WEAPON ); // Don't check collisions for player if past first warpout stage. if ( Player->control_mode > PCM_WARPOUT_STAGE1) { if ( ship == Player_obj ) return 0; } // Cull lasers within big ship spheres by casting a vector forward for (1) exit sphere or (2) lifetime of laser // If it does hit, don't check the pair until about 200 ms before collision. // If it does not hit and is within error tolerance, cull the pair. if ( (Ship_info[Ships[ship->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) && (Weapon_info[Weapons[weapon->instance].weapon_info_index].subtype == WP_LASER) ) { // if ( (ship->radius > 50) && (Weapon_info[Weapons[weapon->instance].weapon_info_index].subtype == WP_LASER) ) { // Check when within ~1.1 radii. // This allows good transition between sphere checking (leaving the laser about 200 ms from radius) and checking // within the sphere with little time between. There may be some time for "small" big ships if ( vm_vec_dist_squared(&ship->pos, &weapon->pos) < (1.2f*ship->radius*ship->radius) ) { return check_inside_radius_for_big_ships( ship, weapon, pair ); } } // demo_do_rand_test(); did_hit = ship_weapon_check_collision( ship, weapon ); // demo_do_rand_test(); if ( !did_hit ) { // Since we didn't hit, check to see if we can disable all future collisions // between these two. return weapon_will_never_hit( weapon, ship, pair ); } return 0; }
/** * When inside radius of big ship, check if we can cull collision pair determine the time when pair should next be checked * @return 1 if pair can be culled * @return 0 if pair can not be culled */ int check_inside_radius_for_big_ships( object *ship, object *weapon_obj, obj_pair *pair ) { vec3d error_vel; // vel perpendicular to laser float error_vel_mag; // magnitude of error_vel float time_to_max_error, time_to_exit_sphere; float ship_speed_at_exit_sphere, error_at_exit_sphere; float max_error = (float) ERROR_STD / 150.0f * ship->radius; if (max_error < 2) max_error = 2.0f; time_to_exit_sphere = (ship->radius + vm_vec_dist(&ship->pos, &weapon_obj->pos)) / (weapon_obj->phys_info.max_vel.xyz.z - ship->phys_info.max_vel.xyz.z); ship_speed_at_exit_sphere = estimate_ship_speed_upper_limit( ship, time_to_exit_sphere ); // update estimated time to exit sphere time_to_exit_sphere = (ship->radius + vm_vec_dist(&ship->pos, &weapon_obj->pos)) / (weapon_obj->phys_info.max_vel.xyz.z - ship_speed_at_exit_sphere); vm_vec_scale_add( &error_vel, &ship->phys_info.vel, &weapon_obj->orient.vec.fvec, -vm_vec_dot(&ship->phys_info.vel, &weapon_obj->orient.vec.fvec) ); error_vel_mag = vm_vec_mag_quick( &error_vel ); error_vel_mag += 0.5f * (ship->phys_info.max_vel.xyz.z - error_vel_mag)*(time_to_exit_sphere/ship->phys_info.forward_accel_time_const); // error_vel_mag is now average velocity over period error_at_exit_sphere = error_vel_mag * time_to_exit_sphere; time_to_max_error = max_error / error_at_exit_sphere * time_to_exit_sphere; // find the minimum time we can safely check into the future. // limited by (1) time to exit sphere (2) time to weapon expires // if ship_weapon_check_collision comes back with a hit_time > error limit, ok // if ship_weapon_check_collision comes finds no collision, next check time based on error time float limit_time; // furthest time to check (either lifetime or exit sphere) if ( time_to_exit_sphere < Weapons[weapon_obj->instance].lifeleft ) { limit_time = time_to_exit_sphere; } else { limit_time = Weapons[weapon_obj->instance].lifeleft; } // Note: when estimated hit time is less than 200 ms, look at every frame int hit_time; // estimated time of hit in ms // modify ship_weapon_check_collision to do damage if hit_time is negative (ie, hit occurs in this frame) if ( ship_weapon_check_collision( ship, weapon_obj, limit_time, &hit_time ) ) { // hit occured in while in sphere if (hit_time < 0) { // hit occured in the frame return 1; } else if (hit_time > 200) { pair->next_check_time = timestamp(hit_time - 200); return 0; // set next check time to time - 200 } else { // set next check time to next frame pair->next_check_time = 1; return 0; } } else { if (limit_time > time_to_max_error) { // no hit, but beyond error tolerance if (1000*time_to_max_error > 200) { pair->next_check_time = timestamp( (int)(1000*time_to_max_error) - 200 ); } else { pair->next_check_time = 1; } return 0; } else { // no hit and within error tolerance return 1; } } }