// What we're doing here is projecting each object extent onto the directrix, calculating the distance between the
// projected point and the origin, and then taking the maximum distance as the semilatus rectum.  We're actually
// maintaining the square of the distance rather than the actual distance, as it's faster to calculate and it gives
// the same result in a greater-than or less-than comparison.  When we're done calculating everything for all
// objects (i.e. when we return to the parent function) we take the square root of the final value.
void dock_calc_max_semilatus_rectum_squared_parallel_to_directrix_helper(object *objp, dock_function_info *infop)
{
	vec3d world_point, local_point[6], nearest;
	polymodel *pm;
	int i;
	float temp, dist_squared;

	// line parameters
	vec3d *line_start = infop->parameter_variables.vecp_value;
	vec3d *line_end = infop->parameter_variables.vecp_value2;

	// We must find world coordinates for each of the six endpoints on the three axes of the object.  I looked up
	// which axis is front/back, left/right, and up/down, as well as which endpoint is which.  It doesn't really
	// matter, though, as all we need are the distances.

	// grab our model
	Assert(objp->type == OBJ_SHIP);
	pm = model_get(Ship_info[Ships[objp->instance].ship_info_index].model_num);

	// set up the points we want to check
	memset(local_point, 0, sizeof(vec3d) * 6);
	local_point[0].xyz.x = pm->maxs.xyz.x;	// right point (max x)
	local_point[1].xyz.x = pm->mins.xyz.x;	// left point (min x)
	local_point[2].xyz.y = pm->maxs.xyz.y;	// top point (max y)
	local_point[3].xyz.y = pm->mins.xyz.y;	// bottom point (min y)
	local_point[4].xyz.z = pm->maxs.xyz.z;	// front point (max z)
	local_point[5].xyz.z = pm->mins.xyz.z;	// rear point (min z)

	// check points
	for (i = 0; i < 6; i++)
	{
		// calculate position of point
		vm_vec_rotate(&world_point, &local_point[i], &objp->orient);
		vm_vec_add2(&world_point, &objp->pos);

		// find the nearest point along the line
		vm_vec_dist_squared_to_line(&world_point, line_start, line_end, &nearest, &temp);

		// find the distance squared between the origin of the line and the point on the line
		dist_squared = vm_vec_dist_squared(line_start, &nearest);
	
		// update with farthest distance squared
		if (dist_squared > infop->maintained_variables.float_value)
			infop->maintained_variables.float_value = dist_squared;
	}
}
/**
 * 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;
}
// Adds the pair to the pair list
void obj_add_pair( object *A, object *B, int check_time, int add_to_end )
{
	uint ctype;
	int (*check_collision)( obj_pair *pair );
	int swapped = 0;	
	
	check_collision = NULL;

	if ( Num_pairs_allocated == 0 ) return;		// don't have anything to add the pair too

	if ( A==B ) return;		// Don't check collisions with yourself

	if ( !(A->flags&OF_COLLIDES) ) return;		// This object doesn't collide with anything
	if ( !(B->flags&OF_COLLIDES) ) return;		// This object doesn't collide with anything
	
	// Make sure you're not checking a parent with it's kid or vicy-versy
//	if ( A->parent_sig == B->signature && !(A->type == OBJ_SHIP && B->type == OBJ_DEBRIS) ) return;
//	if ( B->parent_sig == A->signature && !(A->type == OBJ_DEBRIS && B->type == OBJ_SHIP) ) return;
	if ( reject_obj_pair_on_parent(A,B) ) {
		return;
	}

	Assert( A->type < 127 );
	Assert( B->type < 127 );

	ctype = COLLISION_OF(A->type,B->type);
	switch( ctype )	{
	case COLLISION_OF(OBJ_WEAPON,OBJ_SHIP):
		swapped = 1;
		check_collision = collide_ship_weapon;
		break;
	case COLLISION_OF(OBJ_SHIP, OBJ_WEAPON):
		check_collision = collide_ship_weapon;
		break;
	case COLLISION_OF(OBJ_DEBRIS, OBJ_WEAPON):
		check_collision = collide_debris_weapon;
		break;
	case COLLISION_OF(OBJ_WEAPON, OBJ_DEBRIS):
		swapped = 1;
		check_collision = collide_debris_weapon;
		break;
	case COLLISION_OF(OBJ_DEBRIS, OBJ_SHIP):
		check_collision = collide_debris_ship;		
		break;
	case COLLISION_OF(OBJ_SHIP, OBJ_DEBRIS):
		check_collision = collide_debris_ship;
		swapped = 1;
		break;
	case COLLISION_OF(OBJ_ASTEROID, OBJ_WEAPON):
		// Only check collision's with player weapons
//		if ( Objects[B->parent].flags & OF_PLAYER_SHIP ) {
			check_collision = collide_asteroid_weapon;
//		}
		break;
	case COLLISION_OF(OBJ_WEAPON, OBJ_ASTEROID):
		swapped = 1;
		// Only check collision's with player weapons
//		if ( Objects[A->parent].flags & OF_PLAYER_SHIP ) {
			check_collision = collide_asteroid_weapon;
//		}
		break;
	case COLLISION_OF(OBJ_ASTEROID, OBJ_SHIP):
		// Only check collisions with player ships
//		if ( B->flags & OF_PLAYER_SHIP )	{
			check_collision = collide_asteroid_ship;
//		}
		break;
	case COLLISION_OF(OBJ_SHIP, OBJ_ASTEROID):
		// Only check collisions with player ships
//		if ( A->flags & OF_PLAYER_SHIP )	{
			check_collision = collide_asteroid_ship;
//		}
		swapped = 1;
		break;
	case COLLISION_OF(OBJ_SHIP,OBJ_SHIP):
		check_collision = collide_ship_ship;
		break;	
	
	case COLLISION_OF(OBJ_BEAM, OBJ_SHIP):
		if(beam_collide_early_out(A, B)){
			return;
		}
		check_collision = beam_collide_ship;
		break;

	case COLLISION_OF(OBJ_BEAM, OBJ_ASTEROID):
		if(beam_collide_early_out(A, B)){
			return;
		}
		check_collision = beam_collide_asteroid;
		break;

	case COLLISION_OF(OBJ_BEAM, OBJ_DEBRIS):
		if(beam_collide_early_out(A, B)){
			return;
		}
		check_collision = beam_collide_debris;
		break;

	case COLLISION_OF(OBJ_BEAM, OBJ_WEAPON):
		if(beam_collide_early_out(A, B)){
			return;
		}		
		check_collision = beam_collide_missile;
		break;

	case COLLISION_OF(OBJ_WEAPON, OBJ_WEAPON): {
		weapon_info *awip, *bwip;
		awip = &Weapon_info[Weapons[A->instance].weapon_info_index];
		bwip = &Weapon_info[Weapons[B->instance].weapon_info_index];

		if ((awip->weapon_hitpoints > 0) || (bwip->weapon_hitpoints > 0)) {
			if (bwip->weapon_hitpoints == 0) {
				check_collision = collide_weapon_weapon;
				swapped=1;
			} else {
				check_collision = collide_weapon_weapon;
			}
		}
/*

		if (awip->subtype != WP_LASER || bwip->subtype != WP_LASER) {
			if (awip->subtype == WP_LASER) {
				if ( bwip->wi_flags & WIF_BOMB ) {
					check_collision = collide_weapon_weapon;
				}
			} else if (bwip->subtype == WP_LASER) {
				if ( awip->wi_flags & WIF_BOMB ) {
					check_collision = collide_weapon_weapon;
					swapped=1;			
				}
			} else {
				if ( (awip->wi_flags&WIF_BOMB) || (bwip->wi_flags&WIF_BOMB) ) {
					check_collision = collide_weapon_weapon;
				}
			}
		}
*/
/*
		int	atype, btype;

		atype = Weapon_info[Weapons[A->instance].weapon_info_index].subtype;
		btype = Weapon_info[Weapons[B->instance].weapon_info_index].subtype;

		if ((atype == WP_LASER) && (btype == WP_MISSILE))
			check_collision = collide_weapon_weapon;
		else if ((atype == WP_MISSILE) && (btype == WP_LASER)) {
			check_collision = collide_weapon_weapon;
			swapped = 1;
		} else if ((atype == WP_MISSILE) && (btype == WP_MISSILE))
			check_collision = collide_weapon_weapon;
*/

		break;
	}

	default:
		return;
	}

	// Swap them if needed
	if ( swapped )	{
		object *tmp = A;
		A = B;
		B = tmp;
	}

	// if there are any more obj_pair checks
	// we should then add function int maybe_not_add_obj_pair()
	// MWA -- 4/1/98 -- I'd do it, but I don't want to bust anything, so I'm doing my stuff here instead :-)
	//if ( MULTIPLAYER_CLIENT && !(Netgame.debug_flags & NETD_FLAG_CLIENT_NODAMAGE)){
		// multiplayer clients will only do ship/ship collisions, and their own ship to boot
	//	if ( check_collision != collide_ship_ship ){
	//		return;
	//	}

	//	if ( (A != Player_obj) && (B != Player_obj) ){
	//		return;
	//	}
	//}	

	// only check debris:weapon collisions for player
	if (check_collision == collide_debris_weapon) {
		// weapon is B
		if ( !(Weapon_info[Weapons[B->instance].weapon_info_index].wi_flags & WIF_TURNS) ) {
		// check for dumbfire weapon
			// check if debris is behind laser
			float vdot;
			if (Weapon_info[Weapons[B->instance].weapon_info_index].subtype == WP_LASER) {
				vec3d velocity_rel_weapon;
				vm_vec_sub(&velocity_rel_weapon, &B->phys_info.vel, &A->phys_info.vel);
				vdot = -vm_vec_dot(&velocity_rel_weapon, &B->orient.vec.fvec);
			} else {
				vdot = vm_vec_dot( &A->phys_info.vel, &B->phys_info.vel);
			}
			if ( vdot <= 0.0f )	{
				// They're heading in opposite directions...
				// check their positions
				vec3d weapon2other;
				vm_vec_sub( &weapon2other, &A->pos, &B->pos );
				float pdot = vm_vec_dot( &B->orient.vec.fvec, &weapon2other );
				if ( pdot <= -A->radius )	{
					// The other object is behind the weapon by more than
					// its radius, so it will never hit...
					return;
				}
			}

			// check dist vs. dist moved during weapon lifetime
			vec3d delta_v;
			vm_vec_sub(&delta_v, &B->phys_info.vel, &A->phys_info.vel);
			if (vm_vec_dist_squared(&A->pos, &B->pos) > (vm_vec_mag_squared(&delta_v)*Weapons[B->instance].lifeleft*Weapons[B->instance].lifeleft)) {
				return;
			}

			// for nonplayer ships, only create collision pair if close enough
			if ( (B->parent >= 0) && !(Objects[B->parent].flags & OF_PLAYER_SHIP) && (vm_vec_dist(&B->pos, &A->pos) < (4.0f*A->radius + 200.0f)) )
				return;
		}
	}

	// don't check same team laser:ship collisions on small ships if not player
	if (check_collision == collide_ship_weapon) {
		// weapon is B
		if ( (B->parent >= 0)
			&& !(Objects[B->parent].flags & OF_PLAYER_SHIP)
			&& (Ships[Objects[B->parent].instance].team == Ships[A->instance].team) 
			&& (Ship_info[Ships[A->instance].ship_info_index].flags & SIF_SMALL_SHIP) 
			&& (Weapon_info[Weapons[B->instance].weapon_info_index].subtype == WP_LASER) ) {
			pairs_not_created++;
			return;
		}
	}

	if ( !check_collision ) return;
	Pairs_created++;

	// At this point, we have determined that collisions between
	// these two should be checked, so add the pair to the
	// collision pair list.

	if ( pair_free_list.next == NULL )	{
		nprintf(( "collision", "Out of object pairs!! Not all collisions will work!\n" ));
		return;
	}

	Num_pairs++;
/*	if (Num_pairs > Num_pairs_hwm) {
		Num_pairs_hwm = Num_pairs;
		//nprintf(("AI", "Num_pairs high water mark = %i\n", Num_pairs_hwm));
	}
*/

	if ( Num_pairs >= (Num_pairs_allocated - 20) ) {
		int i;

		Assert( Obj_pairs != NULL );

		int old_pair_count = Num_pairs_allocated;
		obj_pair *old_pairs_ptr = Obj_pairs;

		// determine where we need to update the "previous" ptrs to
		int prev_free_mark = (pair_free_list.next - old_pairs_ptr);
		int prev_used_mark = (pair_used_list.next - old_pairs_ptr);

		Obj_pairs = (obj_pair*) vm_realloc_q( Obj_pairs, sizeof(obj_pair) * (Num_pairs_allocated + PAIRS_BUMP) );

		// allow us to fail here and only if we don't do we setup the new pairs

		if (Obj_pairs == NULL) {
			// failed, just go back to the way we were and use only the pairs we have already
			Obj_pairs = old_pairs_ptr;
		} else {
			Num_pairs_allocated += PAIRS_BUMP;

			Assert( Obj_pairs != NULL );

			// have to reset all of the "next" ptrs for the old set and handle the new set
			for (i = 0; i < Num_pairs_allocated; i++) {
				if (i >= old_pair_count) {
					memset( &Obj_pairs[i], 0, sizeof(obj_pair) );
					Obj_pairs[i].next = &Obj_pairs[i+1];
				} else {
					if (Obj_pairs[i].next != NULL) {
						// the "next" ptr will end up going backwards for used pairs so we have
						// to allow for that with this craziness...
						int next_mark = (Obj_pairs[i].next - old_pairs_ptr);
						Obj_pairs[i].next = &Obj_pairs[next_mark];
					}

					// catch that last NULL from the previously allocated set
					if ( i == (old_pair_count-1) ) {
						Obj_pairs[i].next = &Obj_pairs[i+1];
					}
				}
			}

			Obj_pairs[Num_pairs_allocated-1].next = NULL;

			// reset the "previous" ptrs
			pair_free_list.next = &Obj_pairs[prev_free_mark];
			pair_used_list.next = &Obj_pairs[prev_used_mark];
		}
	}

	// get a new obj_pair from the free list
	obj_pair * new_pair = pair_free_list.next;
	pair_free_list.next = new_pair->next;

	if ( add_to_end ) {
		obj_pair *last, *tmp;

		last = tmp = pair_used_list.next;
		while( tmp != NULL )	{
			if ( tmp->next == NULL )
				last = tmp;

			tmp = tmp->next;
		}

		if ( last == NULL )
			last = &pair_used_list;
			
		last->next = new_pair;
		Assert(new_pair != NULL);
		new_pair->next = NULL;
	}
	else {
		new_pair->next = pair_used_list.next;
		pair_used_list.next = new_pair;
	}

	A->num_pairs++;
	B->num_pairs++;
	
	new_pair->a = A;
	new_pair->b = B;
	new_pair->check_collision = check_collision;

	if ( check_time == -1 ){
		new_pair->next_check_time = timestamp(0);	// 0 means instantly time out
	} else {
		new_pair->next_check_time = check_time;
	}

}
void obj_collide_pair(object *A, object *B)
{
	uint ctype;
	int (*check_collision)( obj_pair *pair );
	int swapped = 0;	
	
	check_collision = NULL;

	if ( A==B ) return;		// Don't check collisions with yourself

	if ( !(A->flags&OF_COLLIDES) ) return;		// This object doesn't collide with anything
	if ( !(B->flags&OF_COLLIDES) ) return;		// This object doesn't collide with anything
	
	// Make sure you're not checking a parent with it's kid or vicy-versy
//	if ( A->parent_sig == B->signature && !(A->type == OBJ_SHIP && B->type == OBJ_DEBRIS) ) return;
//	if ( B->parent_sig == A->signature && !(A->type == OBJ_DEBRIS && B->type == OBJ_SHIP) ) return;
	if ( reject_obj_pair_on_parent(A,B) ) {
		return;
	}

	Assert( A->type < 127 );
	Assert( B->type < 127 );

	ctype = COLLISION_OF(A->type,B->type);
	switch( ctype )	{
	case COLLISION_OF(OBJ_WEAPON,OBJ_SHIP):
		swapped = 1;
		check_collision = collide_ship_weapon;
		break;
	case COLLISION_OF(OBJ_SHIP, OBJ_WEAPON):
		check_collision = collide_ship_weapon;
		break;
	case COLLISION_OF(OBJ_DEBRIS, OBJ_WEAPON):
		check_collision = collide_debris_weapon;
		break;
	case COLLISION_OF(OBJ_WEAPON, OBJ_DEBRIS):
		swapped = 1;
		check_collision = collide_debris_weapon;
		break;
	case COLLISION_OF(OBJ_DEBRIS, OBJ_SHIP):
		check_collision = collide_debris_ship;		
		break;
	case COLLISION_OF(OBJ_SHIP, OBJ_DEBRIS):
		check_collision = collide_debris_ship;
		swapped = 1;
		break;
	case COLLISION_OF(OBJ_ASTEROID, OBJ_WEAPON):
		// Only check collision's with player weapons
//		if ( Objects[B->parent].flags & OF_PLAYER_SHIP ) {
			check_collision = collide_asteroid_weapon;
//		}
		break;
	case COLLISION_OF(OBJ_WEAPON, OBJ_ASTEROID):
		swapped = 1;
		// Only check collision's with player weapons
//		if ( Objects[A->parent].flags & OF_PLAYER_SHIP ) {
			check_collision = collide_asteroid_weapon;
//		}
		break;
	case COLLISION_OF(OBJ_ASTEROID, OBJ_SHIP):
		// Only check collisions with player ships
//		if ( B->flags & OF_PLAYER_SHIP )	{
			check_collision = collide_asteroid_ship;
//		}
		break;
	case COLLISION_OF(OBJ_SHIP, OBJ_ASTEROID):
		// Only check collisions with player ships
//		if ( A->flags & OF_PLAYER_SHIP )	{
			check_collision = collide_asteroid_ship;
//		}
		swapped = 1;
		break;
	case COLLISION_OF(OBJ_SHIP,OBJ_SHIP):
		check_collision = collide_ship_ship;
		break;	
	
	case COLLISION_OF(OBJ_SHIP, OBJ_BEAM):
		if(beam_collide_early_out(B, A)){
			return;
		}
		swapped = 1;
		check_collision = beam_collide_ship;
		break;

	case COLLISION_OF(OBJ_BEAM, OBJ_SHIP):
		if(beam_collide_early_out(A, B)){
			return;
		}
		check_collision = beam_collide_ship;
		break;

	case COLLISION_OF(OBJ_ASTEROID, OBJ_BEAM):
		if(beam_collide_early_out(B, A)) {
			return;
		}
		swapped = 1;
		check_collision = beam_collide_asteroid;
		break;

	case COLLISION_OF(OBJ_BEAM, OBJ_ASTEROID):
		if(beam_collide_early_out(A, B)){
			return;
		}
		check_collision = beam_collide_asteroid;
		break;
	case COLLISION_OF(OBJ_DEBRIS, OBJ_BEAM):
		if(beam_collide_early_out(B, A)) {
			return;
		}
		swapped = 1;
		check_collision = beam_collide_debris;
		break;
	case COLLISION_OF(OBJ_BEAM, OBJ_DEBRIS):
		if(beam_collide_early_out(A, B)){
			return;
		}
		check_collision = beam_collide_debris;
		break;
	case COLLISION_OF(OBJ_WEAPON, OBJ_BEAM):
		if(beam_collide_early_out(B, A)) {
			return;
		}
		swapped = 1;
		check_collision = beam_collide_missile;
		break;

	case COLLISION_OF(OBJ_BEAM, OBJ_WEAPON):
		if(beam_collide_early_out(A, B)){
			return;
		}		
		check_collision = beam_collide_missile;
		break;

	case COLLISION_OF(OBJ_WEAPON, OBJ_WEAPON): {
		weapon_info *awip, *bwip;
		awip = &Weapon_info[Weapons[A->instance].weapon_info_index];
		bwip = &Weapon_info[Weapons[B->instance].weapon_info_index];

		if ((awip->weapon_hitpoints > 0) || (bwip->weapon_hitpoints > 0)) {
			if (bwip->weapon_hitpoints == 0) {
				check_collision = collide_weapon_weapon;
				swapped=1;
			} else {
				check_collision = collide_weapon_weapon;
			}
		}

		break;
	}

	default:
		return;
	}

	if ( !check_collision ) return;

	// Swap them if needed
	if ( swapped )	{
		object *tmp = A;
		A = B;
		B = tmp;
	}

	collider_pair *collision_info = NULL;
	bool valid = false;
	uint key = (OBJ_INDEX(A) << 12) + OBJ_INDEX(B);

	collision_info = &Collision_cached_pairs[key];

	if ( collision_info->initialized ) {
		// make sure we're referring to the correct objects in case the original pair was deleted
		if ( collision_info->signature_a == collision_info->a->signature && 
			collision_info->signature_b == collision_info->b->signature ) {
			valid = true;
		} else {
			collision_info->a = A;
			collision_info->b = B;
			collision_info->signature_a = A->signature;
			collision_info->signature_b = B->signature;
			collision_info->next_check_time = timestamp(0);
		}
	} else {
		collision_info->a = A;
		collision_info->b = B;
		collision_info->signature_a = A->signature;
		collision_info->signature_b = B->signature;
		collision_info->initialized = true;
		collision_info->next_check_time = timestamp(0);
	}

	if ( valid &&  A->type != OBJ_BEAM ) {
		// if this signature is valid, make the necessary checks to see if we need to collide check
		if ( collision_info->next_check_time == -1 ) {
			return;
		} else {
			if ( !timestamp_elapsed(collision_info->next_check_time) ) {
				return;
			}
		}
	} else {
		//if ( A->type == OBJ_BEAM ) {
			//if(beam_collide_early_out(A, B)){
				//collision_info->next_check_time = -1;
				//return;
			//}
		//}

		// only check debris:weapon collisions for player
		if (check_collision == collide_debris_weapon) {
			// weapon is B
			if ( !(Weapon_info[Weapons[B->instance].weapon_info_index].wi_flags & WIF_TURNS) ) {
				// check for dumbfire weapon
				// check if debris is behind laser
				float vdot;
				if (Weapon_info[Weapons[B->instance].weapon_info_index].subtype == WP_LASER) {
					vec3d velocity_rel_weapon;
					vm_vec_sub(&velocity_rel_weapon, &B->phys_info.vel, &A->phys_info.vel);
					vdot = -vm_vec_dot(&velocity_rel_weapon, &B->orient.vec.fvec);
				} else {
					vdot = vm_vec_dot( &A->phys_info.vel, &B->phys_info.vel);
				}
				if ( vdot <= 0.0f )	{
					// They're heading in opposite directions...
					// check their positions
					vec3d weapon2other;
					vm_vec_sub( &weapon2other, &A->pos, &B->pos );
					float pdot = vm_vec_dot( &B->orient.vec.fvec, &weapon2other );
					if ( pdot <= -A->radius )	{
						// The other object is behind the weapon by more than
						// its radius, so it will never hit...
						collision_info->next_check_time = -1;
						return;
					}
				}

				// check dist vs. dist moved during weapon lifetime
				vec3d delta_v;
				vm_vec_sub(&delta_v, &B->phys_info.vel, &A->phys_info.vel);
				if (vm_vec_dist_squared(&A->pos, &B->pos) > (vm_vec_mag_squared(&delta_v)*Weapons[B->instance].lifeleft*Weapons[B->instance].lifeleft)) {
					collision_info->next_check_time = -1;
					return;
				}

				// for nonplayer ships, only create collision pair if close enough
				if ( (B->parent >= 0) && !(Objects[B->parent].flags & OF_PLAYER_SHIP) && (vm_vec_dist(&B->pos, &A->pos) < (4.0f*A->radius + 200.0f)) ) {
					collision_info->next_check_time = -1;
					return;
				}
			}
		}

		// don't check same team laser:ship collisions on small ships if not player
		if (check_collision == collide_ship_weapon) {
			// weapon is B
			if ( (B->parent >= 0)
				&& !(Objects[B->parent].flags & OF_PLAYER_SHIP)
				&& (Ships[Objects[B->parent].instance].team == Ships[A->instance].team) 
				&& (Ship_info[Ships[A->instance].ship_info_index].flags & SIF_SMALL_SHIP) 
				&& (Weapon_info[Weapons[B->instance].weapon_info_index].subtype == WP_LASER) ) {
				collision_info->next_check_time = -1;
				return;
			}
		}
	}

	obj_pair new_pair;	

	new_pair.a = A;
	new_pair.b = B;
	new_pair.check_collision = check_collision;
	new_pair.next_check_time = collision_info->next_check_time;

	if ( check_collision(&new_pair) ) {
		// don't have to check ever again
		collision_info->next_check_time = -1;
	} else {
		collision_info->next_check_time = new_pair.next_check_time;
	}
}