//	See if two lines intersect by doing recursive subdivision.
//	Bails out if larger distance traveled is less than sum of radii + 1.0f.
int collide_subdivide(vec3d *p0, vec3d *p1, float prad, vec3d *q0, vec3d *q1, float qrad)
{
	float	a_dist, b_dist, ab_dist;

	a_dist = vm_vec_dist(p0, p1);
	b_dist = vm_vec_dist(q0, q1);

	ab_dist = vm_vec_dist(p1, q1);

	//	See if their spheres intersect
	if (ab_dist < a_dist + b_dist + prad + qrad) {
		if (ab_dist  < prad + qrad)
			return 1;
		else if (vm_vec_dist(p0, q0) < prad + qrad)
			return 1;
		else if (MAX(a_dist, b_dist) < prad + qrad + 1.0f)
			return 0;
		else {
			int	r1, r2 = 0;
			vec3d	pa, qa;

			vm_vec_avg(&pa, p0, p1);
			vm_vec_avg(&qa, q0, q1);
			r1 = collide_subdivide(p0, &pa, prad, q0, &qa, qrad);
			if (!r1)
				r2 = collide_subdivide(&pa, p1, prad, &qa, q1, qrad);

			return r1 | r2;
		}
	} else
		return 0;
}
Example #2
0
//	-----------------------------------------------------------
int DoTexSlideLeft(int value)
{
	side	*sidep;
	uvl	duvl03;
	fix	dist;
	sbyte	*vp;
	int	v;

	vp = Side_to_verts[Curside];
	sidep = &Cursegp->sides[Curside];

	dist = vm_vec_dist(&Vertices[Cursegp->verts[vp[3]]], &Vertices[Cursegp->verts[vp[0]]]);
	dist *= value;
	if (dist < F1_0/(64*value))
		dist = F1_0/(64*value);

	duvl03.u = fixdiv(sidep->uvls[3].u - sidep->uvls[0].u,dist);
	duvl03.v = fixdiv(sidep->uvls[3].v - sidep->uvls[0].v,dist);

	for (v=0; v<4; v++) {
		sidep->uvls[v].u -= duvl03.u;
		sidep->uvls[v].v -= duvl03.v;
	}

	Update_flags |= UF_WORLD_CHANGED;

	return	1;
}
void model_collide_sortnorm(ubyte * p)
{
	int frontlist = w(p+36);
	int backlist = w(p+40);
	int prelist = w(p+44);
	int postlist = w(p+48);
	int onlist = w(p+52);
	vec3d hitpos;

	if ( Mc_pm->version >= 2000 )	{
		if ( mc_ray_boundingbox( vp(p+56), vp(p+68), &Mc_p0, &Mc_direction, &hitpos) )	{
			if ( !(Mc->flags & MC_CHECK_RAY) && (vm_vec_dist(&hitpos, &Mc_p0) > Mc_mag) ) {
				return;
			}
		} else {
			return;
		}
	}

	if (prelist) model_collide_sub(p+prelist);
	if (backlist) model_collide_sub(p+backlist);
	if (onlist) model_collide_sub(p+onlist);
	if (frontlist) model_collide_sub(p+frontlist);
	if (postlist) model_collide_sub(p+postlist);
}
Example #4
0
void HudGaugeRadarOrb::drawContactHtl(vec3d *pnt, int rad)
{
    vec3d p;

    p=*pnt;

    vm_vec_normalize(&p);

    float size = fl_sqrt(vm_vec_dist(&Orb_eye_position, pnt) * 8.0f);
    if (size < i2fl(rad))	size = i2fl(rad);

    if (rad == Radar_blip_radius_target)
    {
        if (radar_target_id_flags & RTIF_PULSATE) {
            // use mask to make the darn thing work faster
            size *= 1.3f + (sinf(10 * f2fl(Missiontime)) * 0.3f);
        }
        if (radar_target_id_flags & RTIF_BLINK) {
            if (Missiontime & 8192)
                return;
        }
        g3_render_sphere(pnt,size/100.0f);
    }
    else
    {
        g3_render_sphere(pnt,size/300.0f);
    }

    //g3_draw_htl_line(&p,pnt);
    g3_render_line_3d(false, &p, pnt);
}
//	return quadrant containing hit_pnt.
//	\  1  /.
//	3 \ / 0
//	  / \.
//	/  2  \.
//	Note: This is in the object's local reference frame.  Do _not_ pass a vector in the world frame.
int get_quadrant(vec3d *hit_pnt, object *shipobjp)
{
	if (shipobjp != NULL && Ship_info[Ships[shipobjp->instance].ship_info_index].flags2 & SIF2_MODEL_POINT_SHIELDS) {
		int closest = -1;
		float closest_dist = FLT_MAX;

		for (unsigned int i=0; i<Ships[shipobjp->instance].shield_points.size(); i++) {
			float dist = vm_vec_dist(hit_pnt, &Ships[shipobjp->instance].shield_points.at(i));

			if (dist < closest_dist) {
				closest = i;
				closest_dist = dist;
			}
		}

		return closest;
	} else {
		int	result = 0;

		if (hit_pnt->xyz.x < hit_pnt->xyz.z)
			result |= 1;

		if (hit_pnt->xyz.x < -hit_pnt->xyz.z)
			result |= 2;

		return result;
	}
}
Example #6
0
void HudGaugeRadarOrb::drawContact(vec3d *pnt, int rad)
{
    vertex verts[2];
    vec3d p;

    p=*pnt;
    vm_vec_normalize(&p);

    g3_rotate_vertex(&verts[0], &p);
    g3_project_vertex(&verts[0]);

    g3_rotate_vertex(&verts[1], pnt);
    g3_project_vertex(&verts[1]);

    float size = fl_sqrt(vm_vec_dist(&Orb_eye_position, pnt) * 8.0f);
    if (size < i2fl(rad))	size = i2fl(rad);

    if (rad == Radar_blip_radius_target)
    {
        g3_draw_sphere(&verts[1],size/100.0f);
    }
    else
    {
        g3_draw_sphere(&verts[1],size/300.0f);
    }

    g3_draw_line(&verts[0],&verts[1]);
}
Example #7
0
//	-----------------------------------------------------------
static int DoTexSlideLeft(int value)
{
	side	*sidep;
	uvl	duvl03;
	fix	dist;
	auto &vp = Side_to_verts[Curside];
	sidep = &Cursegp->sides[Curside];

	dist = vm_vec_dist(Vertices[Cursegp->verts[vp[3]]], Vertices[Cursegp->verts[vp[0]]]);
	dist *= value;
	if (dist < F1_0/(64*value))
		dist = F1_0/(64*value);

	duvl03.u = fixdiv(sidep->uvls[3].u - sidep->uvls[0].u,dist);
	duvl03.v = fixdiv(sidep->uvls[3].v - sidep->uvls[0].v,dist);

	range_for (auto &v, sidep->uvls)
	{
		v.u -= duvl03.u;
		v.v -= duvl03.v;
	}

	Update_flags |= UF_WORLD_CHANGED;

	return	1;
}
Example #8
0
void move_object_to_vector(vms_vector *vec_through_screen, fix delta_distance)
{
	vms_vector	result;

	vm_vec_scale_add(&result, &Viewer->pos, vec_through_screen, vm_vec_dist(&Viewer->pos,&Objects[Cur_object_index].pos)+delta_distance);

	move_object_to_position(Cur_object_index, &result);

}
Example #9
0
//	Project the viewer's position onto the grid plane.  If more than threshold distance
//	from grid center, move grid center.
void maybe_create_new_grid(grid* gridp, vector *pos, matrix *orient, int force)
{
	int roundoff;
	plane	tplane;
	vector	gpos, tmp, c;
	float	dist_to_plane;
	float	square_size, ux, uy, uz;

	ux = tplane.A = gridp->gmatrix.v.uvec.xyz.x;
	uy = tplane.B = gridp->gmatrix.v.uvec.xyz.y;
	uz = tplane.C = gridp->gmatrix.v.uvec.xyz.z;
	tplane.D = gridp->planeD;

	compute_point_on_plane(&c, &tplane, pos);
	dist_to_plane = fl_abs(vm_dist_to_plane(pos, &gridp->gmatrix.v.uvec, &c));
	square_size = 1.0f;
	while (dist_to_plane >= 25.0f)
	{
		square_size *= 10.0f;
		dist_to_plane /= 10.0f;
	}
	
	if (fvi_ray_plane(&gpos, &gridp->center, &gridp->gmatrix.v.uvec, pos, &orient->v.fvec, 0.0f)<0.0f)	{
		vector p;
		vm_vec_scale_add(&p,pos,&orient->v.fvec, 100.0f );
		compute_point_on_plane(&gpos, &tplane, &p );
	}

	if (vm_vec_dist(&gpos, &c) > 50.0f * square_size)
	{
		vm_vec_sub(&tmp, &gpos, &c);
		vm_vec_normalize(&tmp);
		vm_vec_scale_add(&gpos, &c, &tmp, 50.0f * square_size);
	}

	roundoff = (int) square_size * 10;
	if (!ux)
		gpos.xyz.x = fl_roundoff(gpos.xyz.x, roundoff);
	if (!uy)
		gpos.xyz.y = fl_roundoff(gpos.xyz.y, roundoff);
	if (!uz)
		gpos.xyz.z = fl_roundoff(gpos.xyz.z, roundoff);

	if ((square_size != gridp->square_size) ||
		(gpos.xyz.x != gridp->center.xyz.x) ||
		(gpos.xyz.y != gridp->center.xyz.y) ||
		(gpos.xyz.z != gridp->center.xyz.z) || force)
	{
		gridp->square_size = square_size;
		gridp->center = gpos;
		modify_grid(gridp);
	}
}
Example #10
0
// Textured Poly
// +0      int         id
// +4      int         size 
// +8      vec3d      normal
// +20     vec3d      center
// +32     float      radius
// +36     int         nverts
// +40     int         tmap_num
// +44     nverts*(model_tmap_vert) vertlist (n,u,v)
void moff_tmappoly(ubyte* p, polymodel* pm, model_octant* oct, int just_count)
{
	int i, nv;
	model_tmap_vert* verts;

	nv = w(p + 36);
	if (nv < 0)
		return;

	verts = (model_tmap_vert*)(p + 44);

	if ((pm->version < 2003) && !just_count)
	{
		// Set the "normal_point" part of field to be the center of the polygon
		vec3d center_point;
		vm_vec_zero(&center_point);

		Assert(Interp_verts != NULL);

		for (i = 0; i < nv; i++)
		{
			vm_vec_add2(&center_point, Interp_verts[verts[i].vertnum]);
		}

		center_point.xyz.x /= nv;
		center_point.xyz.y /= nv;
		center_point.xyz.z /= nv;

		*vp(p + 20) = center_point;

		float rad = 0.0f;

		for (i = 0; i < nv; i++)
		{
			float dist = vm_vec_dist(&center_point, Interp_verts[verts[i].vertnum]);
			if (dist > rad)
			{
				rad = dist;
			}
		}
		fl(p + 32) = rad;
	}

	// Put each face into a particular octant
	if (point_in_octant(pm, oct, vp(p + 20)))
	{
		if (just_count)
			oct->nverts++;
		else
			oct->verts[oct->nverts++] = vp(p + 20);
		return;
	}
}
void HudGaugeRadarDradis::drawContact(vec3d *pnt, int idx, int clr_idx, float  /*dist*/, float alpha, float scale_factor)
{
	vec3d  p;
	int h, w;
	vertex vert;
	float aspect_mp;

	if ((sub_y_clip && (pnt->xyz.y > 0)) || ((!sub_y_clip) && (pnt->xyz.y <= 0)))
		return;

    memset(&vert, 0, sizeof(vert));
    
	vm_vec_rotate(&p, pnt,  &vmd_identity_matrix); 
	g3_transfer_vertex(&vert, &p);
	
	float sizef = fl_sqrt(vm_vec_dist(&Orb_eye_position, pnt) * 8.0f) * scale_factor;

    if ( clr_idx >= 0 ) {
        bm_get_info(clr_idx, &w, &h);
        
        if (h == w) {
            aspect_mp = 1.0f;
        } else {
            aspect_mp = (((float) h) / ((float) w));
        }
        
        //gr_set_bitmap(clr_idx, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, alpha);
        //g3_draw_polygon(&p, &vmd_identity_matrix, sizef/35.0f, aspect_mp*sizef/35.0f, TMAP_FLAG_TEXTURED | TMAP_HTL_3D_UNLIT);
		material mat_params;
		material_set_unlit_color(&mat_params, clr_idx, &Color_bright_white, alpha, true, false);
		g3_render_rect_oriented(&mat_params, &p, &vmd_identity_matrix, sizef/35.0f, aspect_mp*sizef/35.0f);
    }
    
    if ( idx >= 0 ) {
        bm_get_info(idx, &w, &h);
        
        if (h == w) {
            aspect_mp = 1.0f;
        } else {
            aspect_mp = (((float) h) / ((float) w));
        }
        
        //gr_set_bitmap(idx, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, alpha);
        //g3_draw_polygon(&p, &vmd_identity_matrix, sizef/35.0f, aspect_mp*sizef/35.0f, TMAP_FLAG_TEXTURED | TMAP_FLAG_BW_TEXTURE | TMAP_HTL_3D_UNLIT);
		material mat_params;
		material_set_unlit_color(&mat_params, idx, &gr_screen.current_color, alpha, true, false);
		g3_render_rect_oriented(&mat_params, &p, &vmd_identity_matrix, sizef/35.0f, aspect_mp*sizef/35.0f);
    }
}
Example #12
0
fix curve_dist(vms_equation *coeffs, int degree, fix t0, vms_vector *p0, fix dist) {
	 vms_vector coord;
    fix t, diff;

    if (degree!=3) printf("ERROR: for Hermite Curves degree must be 3\n");

    for (t=t0;t<1*F1_0;t+=0.001*F1_0) {
        coord = evaluate_curve(coeffs, 3, t);
        diff = dist - vm_vec_dist(&coord, p0);
        if (diff<ACCURACY)   //&&(diff>-ACCURACY))
            return t;
    }
    return -1*F1_0;

}
Example #13
0
fix curve_dist(vms_equation *coeffs, int degree, fix t0, const vms_vector &p0, fix dist)
{
	 vms_vector coord;
    fix t, diff;

    if (degree!=3) con_printf(CON_CRITICAL," for Hermite Curves degree must be 3");

    for (t=t0;t<1*F1_0;t+=0.001*F1_0) {
        coord = evaluate_curve(coeffs, 3, t);
        diff = dist - vm_vec_dist(coord, p0);
        if (diff<ACCURACY)   //&&(diff>-ACCURACY))
            return t;
    }
    return -1*F1_0;

}
//calls the object interpreter to render an object.  The object renderer
//is really a seperate pipeline. returns true if drew
int model_collide_sub(void *model_ptr )
{
    ubyte *p = (ubyte *)model_ptr;
    int chunk_type, chunk_size;
    vec3d hitpos;

    chunk_type = w(p);
    chunk_size = w(p+4);

    while (chunk_type != OP_EOF)	{

//		mprintf(( "Processing chunk type %d, len=%d\n", chunk_type, chunk_size ));

        switch (chunk_type) {
        case OP_DEFPOINTS:
            model_collide_defpoints(p);
            break;
        case OP_FLATPOLY:
            model_collide_flatpoly(p);
            break;
        case OP_TMAPPOLY:
            model_collide_tmappoly(p);
            break;
        case OP_SORTNORM:
            model_collide_sortnorm(p);
            break;
        case OP_BOUNDBOX:
            if ( mc_ray_boundingbox( vp(p+8), vp(p+20), &Mc_p0, &Mc_direction, &hitpos ) )	{
                if ( !(Mc->flags & MC_CHECK_RAY) && (vm_vec_dist(&hitpos, &Mc_p0) > Mc_mag) ) {
                    // The ray isn't long enough to intersect the bounding box
                    return 1;
                }
            } else {
                return 1;
            }
            break;
        default:
            mprintf(( "Bad chunk type %d, len=%d in model_collide_sub\n", chunk_type, chunk_size ));
            Int3();		// Bad chunk type!
            return 0;
        }
        p += chunk_size;
        chunk_type = w(p);
        chunk_size = w(p+4);
    }
    return 1;
}
Example #15
0
//	-----------------------------------------------------------------------------------------------------------------
//	Return true if side is planar, else return false.
int side_is_planar_p(segment *sp, int side)
{
	sbyte			*vp;
	vms_vector	*v0,*v1,*v2,*v3;
	vms_vector	va,vb;

	vp = Side_to_verts[side];
	v0 = &Vertices[sp->verts[vp[0]]];
	v1 = &Vertices[sp->verts[vp[1]]];
	v2 = &Vertices[sp->verts[vp[2]]];
	v3 = &Vertices[sp->verts[vp[3]]];

	vm_vec_normalize(vm_vec_normal(&va,v0,v1,v2));
	vm_vec_normalize(vm_vec_normal(&vb,v0,v2,v3));
	
	// If the two vectors are very close to being the same, then generate one quad, else generate two triangles.
	return (vm_vec_dist(&va,&vb) < F1_0/1000);
}
/**
 * Given an object, returns which jump node it's inside (if any)
 *
 * @param objp Object
 * @return Jump node object or NULL if not in one
 */
CJumpNode *jumpnode_get_which_in(object *objp)
{
	Assert(objp != NULL);
	SCP_list<CJumpNode>::iterator jnp;
	float radius, dist;

	for (jnp = Jump_nodes.begin(); jnp != Jump_nodes.end(); ++jnp) {
		if(jnp->GetModelNumber() < 0)
			continue;

		radius = model_get_radius( jnp->GetModelNumber() );
		dist = vm_vec_dist( &objp->pos, &jnp->GetSCPObject()->pos );
		if ( dist <= radius ) {
			return &(*jnp);
		}
	}

	return NULL;
}
void HudGaugeRadarDradis::drawContact(vec3d *pnt, int idx, int clr_idx, float dist, float alpha, float scale_factor)
{
	vec3d  p;
	int h, w;
	vertex vert;
	float aspect_mp;

	if ((sub_y_clip && (pnt->xyz.y > 0)) || ((!sub_y_clip) && (pnt->xyz.y <= 0)))
		return;

    memset(&vert, 0, sizeof(vert));
    
	vm_vec_rotate(&p, pnt,  &vmd_identity_matrix); 
	g3_transfer_vertex(&vert, &p);
	
	float sizef = fl_sqrt(vm_vec_dist(&Orb_eye_position, pnt) * 8.0f) * scale_factor;

    if ( clr_idx >= 0 ) {
        bm_get_info(clr_idx, &w, &h);
        
        if (h == w) {
            aspect_mp = 1.0f;
        } else {
            aspect_mp = (((float) h) / ((float) w));
        }
        
        gr_set_bitmap(clr_idx, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, alpha);
        g3_draw_polygon(&p, &vmd_identity_matrix, sizef/35.0f, aspect_mp*sizef/35.0f, TMAP_FLAG_TEXTURED | TMAP_HTL_3D_UNLIT);
    }
    
    if ( idx >= 0 ) {
        bm_get_info(idx, &w, &h);
        
        if (h == w) {
            aspect_mp = 1.0f;
        } else {
            aspect_mp = (((float) h) / ((float) w));
        }
        
        gr_set_bitmap(idx, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, alpha);
        g3_draw_polygon(&p, &vmd_identity_matrix, sizef/35.0f, aspect_mp*sizef/35.0f, TMAP_FLAG_TEXTURED | TMAP_FLAG_BW_TEXTURE | TMAP_HTL_3D_UNLIT);
    }
}
Example #18
0
void grid_render_elevation_line(vector *pos, grid* gridp)
{
	vector	gpos;	//	Location of point on grid.
	vector	tpos;
	float		dxz;
	plane		tplane;
	vector	*gv;
	
	tplane.A = gridp->gmatrix.v.uvec.xyz.x;
	tplane.B = gridp->gmatrix.v.uvec.xyz.y;
	tplane.C = gridp->gmatrix.v.uvec.xyz.z;
	tplane.D = gridp->planeD;

	compute_point_on_plane(&gpos, &tplane, pos);

	dxz = vm_vec_dist(pos, &gpos)/8.0f;

	gv = &gridp->gmatrix.v.uvec;
	if (gv->xyz.x * pos->xyz.x + gv->xyz.y * pos->xyz.y + gv->xyz.z * pos->xyz.z < -gridp->planeD)
		gr_set_color(127, 127, 127);
	else
		gr_set_color(255, 255, 255);   // white

	rpd_line(&gpos, pos);	//	Line from grid to object center.

	tpos = gpos;

	vm_vec_scale_add2(&gpos, &gridp->gmatrix.v.rvec, -dxz/2);
	vm_vec_scale_add2(&gpos, &gridp->gmatrix.v.fvec, -dxz/2);
	
	vm_vec_scale_add2(&tpos, &gridp->gmatrix.v.rvec, dxz/2);
	vm_vec_scale_add2(&tpos, &gridp->gmatrix.v.fvec, dxz/2);
	
	rpd_line(&gpos, &tpos);

	vm_vec_scale_add2(&gpos, &gridp->gmatrix.v.rvec, dxz);
	vm_vec_scale_add2(&tpos, &gridp->gmatrix.v.rvec, -dxz);

	rpd_line(&gpos, &tpos);
}
void model_collide_bsp(bsp_collision_tree *tree, int node_index)
{
	if ( tree->node_list == NULL || tree->n_verts <= 0) {
		return;
	}

	bsp_collision_node *node = &tree->node_list[node_index];
	vec3d hitpos;

	// check the bounding box of this node. if it passes, check left and right children
	if ( mc_ray_boundingbox( &node->min, &node->max, &Mc_p0, &Mc_direction, &hitpos ) ) {
		if ( !(Mc->flags & MC_CHECK_RAY) && (vm_vec_dist(&hitpos, &Mc_p0) > Mc_mag) ) {
			// The ray isn't long enough to intersect the bounding box
			return;
		}

		if ( node->leaf >= 0 ) {
			model_collide_bsp_poly(tree, node->leaf);
		} else {
			if ( node->back >= 0 ) model_collide_bsp(tree, node->back);
			if ( node->front >= 0 ) model_collide_bsp(tree, node->front);
		}
	}
}
// Returns TRUE if the weapon will never hit the other object.
// If it can it predicts how long until these two objects need
// to be checked and fills the time in in current_pair.
int weapon_will_never_hit( object *obj_weapon, object *other, obj_pair * current_pair )
{

	Assert( obj_weapon->type == OBJ_WEAPON );
	weapon *wp = &Weapons[obj_weapon->instance];
	weapon_info *wip = &Weapon_info[wp->weapon_info_index];

//	mprintf(( "Frame: %d,  Weapon=%d, Other=%d, pair=$%08x\n", G3_frame_count, OBJ_INDEX(weapon), OBJ_INDEX(other), current_pair ));
	

	// Do some checks for weapons that don't turn
	if ( !(wip->wi_flags & WIF_TURNS) )	{

		// This first check is to see if a weapon is behind an object, and they
		// are heading in opposite directions.   If so, we don't need to ever check	
		// them again.   This is only valid for weapons that don't turn. 

		float vdot;
		if (wip->subtype == WP_LASER) {
			vec3d velocity_rel_weapon;
			vm_vec_sub(&velocity_rel_weapon, &obj_weapon->phys_info.vel, &other->phys_info.vel);
			vdot = -vm_vec_dot(&velocity_rel_weapon, &obj_weapon->orient.vec.fvec);
		} else {
			vdot = vm_vec_dot( &other->phys_info.vel, &obj_weapon->phys_info.vel);
		}
		if ( vdot <= 0.0f )	{
			// They're heading in opposite directions...
			// check their positions
			vec3d weapon2other;
			vm_vec_sub( &weapon2other, &other->pos, &obj_weapon->pos );
			float pdot = vm_vec_dot( &obj_weapon->orient.vec.fvec, &weapon2other );
			if ( pdot <= -other->radius )	{
				// The other object is behind the weapon by more than
				// its radius, so it will never hit...
				return 1;
			}
		}

		// FUTURE ENHANCEMENT IDEAS 

		// Given a laser does it hit a slow or not moving object
		// in its life or the next n seconds?  We'd actually need to check the 
		// model for this.
				
	}


	// This check doesn't care about orient, only looks at the maximum speed
	// of the two objects, so it knows that in the next n seconds, they can't
	// go further than some distance, so don't bother checking collisions for 
	// that time.   This is very rough, but is so general that it works for
	// everything and immidiately gets rid of a lot of cases.
	
	if ( current_pair )	{
		// Find the time it will take before these get within each others distances.
		// tmp->next_check_time = timestamp(500);
		//vector	max_vel;			//maximum foward velocity in x,y,z

		float max_vel_weapon, max_vel_other;

		//SUSHI: Fix bug where additive weapon velocity screws up collisions
		//Assumes that weapons which don't home don't change speed, which is currently the case.
		if (!(wip->wi_flags & WIF_TURNS))
			max_vel_weapon = obj_weapon->phys_info.speed;
		else if (wp->lssm_stage==5)
			max_vel_weapon = wip->lssm_stage5_vel;
		else
			max_vel_weapon = wp->weapon_max_vel;

		max_vel_other = other->phys_info.max_vel.xyz.z;
		if (max_vel_other < 10.0f) {
			if ( vm_vec_mag_squared( &other->phys_info.vel ) > 100 ) {
				// bump up velocity from collision
				max_vel_other = vm_vec_mag( &other->phys_info.vel ) + 10.0f;
			} else {
				max_vel_other = 10.0f;		// object may move from collision
			}
		}

		// check weapon that does not turn against sphere expanding at ship maxvel
		// compare (weeapon) ray with expanding sphere (ship) to find earliest possible collision time
		// look for two time solutions to Xw = Xs, where Xw = Xw0 + Vwt*t  Xs = Xs + Vs*(t+dt), where Vs*dt = radius of ship 
		// Since direction of Vs is unknown, solve for (Vs*t) and find norm of both sides
		if ( !(wip->wi_flags & WIF_TURNS) ) {
			vec3d delta_x, laser_vel;
			float a,b,c, delta_x_dot_vl, delta_t;
			float root1, root2, root, earliest_time;

			if (max_vel_weapon == max_vel_other) {
				// this will give us NAN using the below formula, so check every frame
				current_pair->next_check_time = timestamp(0);
				return 0;
			}

			vm_vec_sub( &delta_x, &obj_weapon->pos, &other->pos );
			laser_vel = obj_weapon->phys_info.vel;
			// vm_vec_copy_scale( &laser_vel, &weapon->orient.vec.fvec, max_vel_weapon );
			delta_t = (other->radius + 10.0f) / max_vel_other;		// time to get from center to radius of other obj
			delta_x_dot_vl = vm_vec_dotprod( &delta_x, &laser_vel );

			a = max_vel_weapon*max_vel_weapon - max_vel_other*max_vel_other;
			b = 2.0f * (delta_x_dot_vl - max_vel_other*max_vel_other*delta_t);
			c = vm_vec_mag_squared( &delta_x ) - max_vel_other*max_vel_other*delta_t*delta_t;

			float discriminant = b*b - 4.0f*a*c;
			if ( discriminant < 0) {
				// never hit
				return 1;
			} else {
				root = fl_sqrt( discriminant );
				root1 = (-b + root) / (2.0f * a) * 1000.0f;	// get time in ms
				root2 = (-b - root) / (2.0f * a) * 1000.0f;	// get time in ms
			}

			// standard algorithm
			if (max_vel_weapon > max_vel_other) {
				// find earliest positive time
				if ( root1 > root2 ) {
					float temp = root1;
					root1 = root2;
					root2 = temp;
				}

				if (root1 > 0) {
					earliest_time = root1;
				} else if (root2 > 0) {
					// root1 < 0 and root2 > 0, so we're inside sphere and next check should be next frame
					current_pair->next_check_time = timestamp(0);	// check next time
					return 0;
				} else {
					// both times negative, so never collides
					return 1;
				}
			}
			// need to modify it for weapons that are slower than ships
			else {
				if (root2 > 0) {
					earliest_time = root2;
				} else {
					current_pair->next_check_time = timestamp(0);
					return 0;
				}
			}



			// check if possible collision occurs after weapon expires
			if ( earliest_time > 1000*wp->lifeleft )
				return 1;

			// Allow one worst case frametime to elapse (~5 fps)
			earliest_time -= 200.0f;

			if (earliest_time > 100) {
				current_pair->next_check_time = timestamp( fl2i(earliest_time) );
				return 0;
			} else {
				current_pair->next_check_time = timestamp(0);	// check next time
				return 0;
			}

		} else {

			float dist, max_vel, time;

			max_vel = max_vel_weapon + max_vel_other;

			// suggest that fudge factor for other radius be changed to other_radius + const (~10)
			dist = vm_vec_dist( &other->pos, &obj_weapon->pos ) - (other->radius + 10.0f);
			if ( dist > 0.0f )	{
				time = (dist*1000.0f) / max_vel;
				int time_ms = fl2i(time);

				// check if possible collision occurs after weapon expires
				if ( time_ms > 1000*wp->lifeleft )
					return 1;

				time_ms -= 200;	// Allow at least one worst case frametime to elapse (~5 fps)
						
				if ( time_ms > 100 )	{		// If it takes longer than 1/10th of a second, then delay it
					current_pair->next_check_time = timestamp(time_ms);
					//mprintf(( "Delaying %d ms\n", time_ms ));
					return 0;
				}
			}
			current_pair->next_check_time = timestamp(0);	// check next time

		}
	}

	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;
	}
}
Example #23
0
void radar_plot_object( object *objp )	
{
	vector	pos, tempv;
	float		dist, rscale, zdist, max_radar_dist;
	int		xpos, ypos, color=0;
	vector	*world_pos = &objp->pos;	
	float		awacs_level;

	// don't process anything here.  Somehow, a jumpnode object caused this function
	// to get entered on server side.
	if( Game_mode & GM_STANDALONE_SERVER ){
		return;
	}

	// multiplayer clients ingame joining should skip this function
	if ( MULTIPLAYER_CLIENT && (Net_player->flags & NETINFO_FLAG_INGAME_JOIN) ){
		return;
	}

	// get team-wide awacs level for the object if not ship
	int ship_is_visible = 0;
	if (objp->type == OBJ_SHIP) {
		if (Player_ship != NULL) {
			if (ship_is_visible_by_team(objp->instance, Player_ship->team)) {
				ship_is_visible = 1;
			}
		}
	}

	// only check awacs level if ship is not visible by team
	awacs_level = 1.5f;
	if (Player_ship != NULL && !ship_is_visible) {
		awacs_level = awacs_get_level(objp, Player_ship);
	}

	// if the awacs level is unviewable - bail
	if(awacs_level < 0.0f && !See_all){
		return;
	}

	// Apply object type filters	
	switch ( objp->type ) {
	case OBJ_SHIP:
		// Place to cull ships, such as NavBuoys		
		break;
		
	case OBJ_JUMP_NODE:
		// filter jump nodes here if required
		break;

	case OBJ_WEAPON: {
		// if not a bomb, return
		if ( !(Weapon_info[Weapons[objp->instance].weapon_info_index].wi_flags & WIF_BOMB) ) {
			return;
		}

		// if bomb is on same team as player, return
		if ( (obj_team(objp) == Player_ship->team) && (Player_ship->team != TEAM_TRAITOR) ) {
			return;
		}
		break;
	}

	default:
		return;			// if any other kind of object, don't want to show on radar
		break;
	} // end switch

	
	// JAS -- new way of getting the rotated point that doesn't require this to be
	// in a g3_start_frame/end_frame block.
	vm_vec_sub(&tempv,world_pos,&Player_obj->pos);
	vm_vec_rotate( &pos, &tempv, &Player_obj->orient );

	// Apply range filter
	dist = vm_vec_dist(world_pos, &Player_obj->pos);
	max_radar_dist = Radar_ranges[HUD_config.rp_dist];
	if ( dist > max_radar_dist ){
		return;
	}

	if ( dist < pos.xyz.z ) {
		rscale = 0.0f;
	} else {
		rscale = (float) acos( pos.xyz.z/dist ) / 3.14159f;		//2.0f;
	}

	zdist = fl_sqrt( (pos.xyz.x*pos.xyz.x)+(pos.xyz.y*pos.xyz.y) );

	float new_x_dist, clipped_x_dist;
	float new_y_dist, clipped_y_dist;

	if (zdist < 0.01f ) {
		new_x_dist = 0.0f;
		new_y_dist = 0.0f;
	}
	else {
		new_x_dist = (pos.xyz.x/zdist) * rscale * radx;
		new_y_dist = (pos.xyz.y/zdist) * rscale * rady;

		// force new_x_dist and new_y_dist to be inside the radar

		float hypotenuse;
		float max_radius;

		hypotenuse = (float)_hypot(new_x_dist, new_y_dist);
		max_radius = i2fl(Radar_radius[gr_screen.res][0] - 5);

		if (hypotenuse >= (max_radius) ) {
			clipped_x_dist = max_radius * (new_x_dist / hypotenuse);
			clipped_y_dist = max_radius * (new_y_dist / hypotenuse);
			new_x_dist = clipped_x_dist;
			new_y_dist = clipped_y_dist;
		}
	}

	xpos = fl2i( Radar_center[gr_screen.res][0] + new_x_dist );
	ypos = fl2i( Radar_center[gr_screen.res][1] - new_y_dist );

	color = radar_blip_color(objp);

	// Determine the distance at which we will dim the radar blip
	if ( timestamp_elapsed(Radar_calc_dim_dist_timer) ) {
		Radar_calc_dim_dist_timer=timestamp(1000);
		Radar_dim_range = player_farthest_weapon_range();
		if ( Radar_dim_range <= 0 ) {
			Radar_dim_range=1500.0f;
		}
	}

	blip	*b;
	int blip_dim=0;

	if ( dist > Radar_dim_range ) {
		blip_dim=1;
	}

	if ( N_blips >= MAX_BLIPS ) {
		// out of blips, don't plot
		Int3();
		return;
	}

	b = &Blips[N_blips];
	b->flags=0;

	// flag the blip as a current target if it is
	if (OBJ_INDEX(objp) == Player_ai->target_objnum)	{
		b->flags |= BLIP_CURRENT_TARGET;
		blip_dim = 0;
	}

	if ( blip_dim ) {
		list_append( &Blip_dim_list[color], b );
	} else {
		list_append( &Blip_bright_list[color], b );
	}

	b->x = xpos;
	b->y = ypos;

	// see if blip should be drawn distorted
	if (objp->type == OBJ_SHIP) {
		// ships specifically hidden from sensors
		if ( Ships[objp->instance].flags & SF_HIDDEN_FROM_SENSORS ) {
			b->flags |= BLIP_DRAW_DISTORTED;
		}

		// determine if its AWACS distorted
		if ( awacs_level < 1.0f ){
			b->flags |= BLIP_DRAW_DISTORTED;
		}
	}				

	N_blips++;
}
Example #24
0
/**
 * @brief Return if the specified object is visible on the radar
 *
 * @param objp The object which should be checked
 * @return A RadarVisibility enum specifying the visibility of the specified object
 */
RadarVisibility radar_is_visible( object *objp )
{
	Assert( objp != NULL );

	if (objp->flags & OF_SHOULD_BE_DEAD)
	{
		return NOT_VISIBLE;
	}

	vec3d pos, tempv;
	float awacs_level, dist, max_radar_dist;
	vec3d world_pos = objp->pos;
	SCP_list<CJumpNode>::iterator jnp;

	// get team-wide awacs level for the object if not ship
	int ship_is_visible = 0;
	if (objp->type == OBJ_SHIP) {
		if (Player_ship != NULL) {
			if (ship_is_visible_by_team(objp, Player_ship)) {
				ship_is_visible = 1;
			}
		}
	}

	// only check awacs level if ship is not visible by team
	awacs_level = 1.5f;
	if (Player_ship != NULL && !ship_is_visible) {
		awacs_level = awacs_get_level(objp, Player_ship);
	}

	// if the awacs level is unviewable - bail
	if(awacs_level < 0.0f && !See_all){
		return NOT_VISIBLE;
	}

	// Apply object type filters	
	switch (objp->type)
	{
		case OBJ_SHIP:
			if (Ships[objp->instance].flags & SIF_STEALTH)
				return NOT_VISIBLE;

			// Ships that are warp in in are not visible on the radar
			if (Ships[objp->instance].flags & SF_ARRIVING_STAGE_1)
				return NOT_VISIBLE;

			break;
		
		case OBJ_JUMP_NODE:
		{
			for (jnp = Jump_nodes.begin(); jnp != Jump_nodes.end(); ++jnp) {
				if(jnp->GetSCPObject() == objp)
					break;
			}
			
			// don't plot hidden jump nodes
			if ( jnp->IsHidden() )
				return NOT_VISIBLE;

			// filter jump nodes here if required
			break;
		}

		case OBJ_WEAPON:
		{
			// if not a bomb, return
			if ( !(Weapon_info[Weapons[objp->instance].weapon_info_index].wi_flags2 & WIF2_SHOWN_ON_RADAR) )
				if ( !(Weapon_info[Weapons[objp->instance].weapon_info_index].wi_flags & WIF_BOMB) )
					return NOT_VISIBLE;

			// if explicitly hidden, return
			if (Weapon_info[Weapons[objp->instance].weapon_info_index].wi_flags2 & WIF2_DONT_SHOW_ON_RADAR)
				return NOT_VISIBLE;

			// if we don't attack the bomb, return
			if ( (!(Weapon_info[Weapons[objp->instance].weapon_info_index].wi_flags2 & WIF2_SHOW_FRIENDLY)) && (!iff_x_attacks_y(Player_ship->team, obj_team(objp))))
				return NOT_VISIBLE;

			// if a local ssm is in subspace, return
			if (Weapons[objp->instance].lssm_stage == 3)
				return NOT_VISIBLE;

			break;
		}

		// if any other kind of object, don't show it on radar
		default:
			return NOT_VISIBLE;
	}
	
	vm_vec_sub(&tempv, &world_pos, &Player_obj->pos);
	vm_vec_rotate(&pos, &tempv, &Player_obj->orient);

	// Apply range filter
	dist = vm_vec_dist(&world_pos, &Player_obj->pos);
	max_radar_dist = Radar_ranges[HUD_config.rp_dist];
	if (dist > max_radar_dist) {
		return NOT_VISIBLE;
	}
	
	if (objp->type == OBJ_SHIP)
	{
		// ships specifically hidden from sensors
		if (Ships[objp->instance].flags & SF_HIDDEN_FROM_SENSORS)
			return DISTORTED;

		// determine if its AWACS distorted
		if (awacs_level < 1.0f)
			return DISTORTED;
	}
	
	return VISIBLE;
}
Example #25
0
void radar_plot_object( object *objp )
{
	vec3d pos, tempv;
	float awacs_level, dist, max_radar_dist;
	vec3d world_pos = objp->pos;
	SCP_list<CJumpNode>::iterator jnp;

	// don't process anything here.  Somehow, a jumpnode object caused this function
	// to get entered on server side.
	if( Game_mode & GM_STANDALONE_SERVER ){
		return;
	}

	// multiplayer clients ingame joining should skip this function
	if ( MULTIPLAYER_CLIENT && (Net_player->flags & NETINFO_FLAG_INGAME_JOIN) ){
		return;
	}

	// get team-wide awacs level for the object if not ship
	int ship_is_visible = 0;
	if (objp->type == OBJ_SHIP) {
		if (Player_ship != NULL) {
			if (ship_is_visible_by_team(objp, Player_ship)) {
				ship_is_visible = 1;
			}
		}
	}

	// only check awacs level if ship is not visible by team
	awacs_level = 1.5f;
	if (Player_ship != NULL && !ship_is_visible) {
		awacs_level = awacs_get_level(objp, Player_ship);
	}

	// if the awacs level is unviewable - bail
	if(awacs_level < 0.0f && !See_all){
		return;
	}

	// Apply object type filters	
	switch (objp->type)
	{
		case OBJ_SHIP:
			// Place to cull ships, such as NavBuoys		
			break;
		
		case OBJ_JUMP_NODE:
		{
			for (jnp = Jump_nodes.begin(); jnp != Jump_nodes.end(); ++jnp) {
				if(jnp->GetSCPObject() == objp)
					break;
			}
			
			// don't plot hidden jump nodes
			if ( jnp->IsHidden() )
				return;

			// filter jump nodes here if required
			break;
		}

		case OBJ_WEAPON:
		{
			// if not a bomb, return
			if ( !(Weapon_info[Weapons[objp->instance].weapon_info_index].wi_flags2 & WIF2_SHOWN_ON_RADAR) )
				if ( !(Weapon_info[Weapons[objp->instance].weapon_info_index].wi_flags & WIF_BOMB) )
					return;

			// if explicitly hidden, return
			if (Weapon_info[Weapons[objp->instance].weapon_info_index].wi_flags2 & WIF2_DONT_SHOW_ON_RADAR)
				return;

			// if we don't attack the bomb, return
			if ( (!(Weapon_info[Weapons[objp->instance].weapon_info_index].wi_flags2 & WIF2_SHOW_FRIENDLY)) && (!iff_x_attacks_y(Player_ship->team, obj_team(objp))))
				return;

			// if a local ssm is in subspace, return
			if (Weapons[objp->instance].lssm_stage == 3)
				return;

			// if corkscrew missile use last frame pos for pos
			if ( (Weapon_info[Weapons[objp->instance].weapon_info_index].wi_flags & WIF_CORKSCREW) )
				world_pos = objp->last_pos;

			break;
		}

		// if any other kind of object, don't show it on radar
		default:
			return;
	}

	// Retrieve the eye orientation so we can position the blips relative to it
	matrix eye_orient;

	if (Player_obj->type == OBJ_SHIP) 
		ship_get_eye(&tempv, &eye_orient, Player_obj, false , false);
	else
		eye_orient = Player_obj->orient;

	// JAS -- new way of getting the rotated point that doesn't require this to be
	// in a g3_start_frame/end_frame block.
	vm_vec_sub(&tempv, &world_pos, &Player_obj->pos);
	vm_vec_rotate(&pos, &tempv, &eye_orient);

	// Apply range filter
	dist = vm_vec_dist(&world_pos, &Player_obj->pos);
	max_radar_dist = Radar_ranges[HUD_config.rp_dist];
	if (dist > max_radar_dist) {
		return;
	}

	// determine the range within which the radar blip is bright
	if (timestamp_elapsed(Radar_calc_bright_dist_timer))
	{
		Radar_calc_bright_dist_timer = timestamp(1000);
		Radar_bright_range = player_farthest_weapon_range();
		if (Radar_bright_range <= 0)
			Radar_bright_range = 1500.0f;
	}

	blip *b;
	int blip_bright = 0;
	int blip_type = 0;

	if (N_blips >= MAX_BLIPS)
	{
		return;
	}

	b = &Blips[N_blips];
	b->flags = 0;

	// bright if within range
	blip_bright = (dist <= Radar_bright_range);

	// flag the blip as a current target if it is
	if (OBJ_INDEX(objp) == Player_ai->target_objnum)
	{
		b->flags |= BLIP_CURRENT_TARGET;
		blip_bright = 1;
	}

	radar_stuff_blip_info(objp, blip_bright, &b->blip_color, &blip_type);

	if (blip_bright)
		list_append(&Blip_bright_list[blip_type], b);
	else
		list_append(&Blip_dim_list[blip_type], b);

	b->position = pos;
	b->dist = dist;
	b->objp = objp;
	b->radar_image_2d = -1;
	b->radar_color_image_2d = -1;
	b->radar_image_size = -1;
	b->radar_projection_size = 1.0f;

	// see if blip should be drawn distorted
	// also determine if alternate image was defined for this ship
	if (objp->type == OBJ_SHIP)
	{
		// ships specifically hidden from sensors
		if (Ships[objp->instance].flags & SF_HIDDEN_FROM_SENSORS)
			b->flags |= BLIP_DRAW_DISTORTED;

		// determine if its AWACS distorted
		if (awacs_level < 1.0f)
			b->flags |= BLIP_DRAW_DISTORTED;

		ship_info *Iff_ship_info = &Ship_info[Ships[objp->instance].ship_info_index];

		if (Iff_ship_info->radar_image_2d_idx >= 0 || Iff_ship_info->radar_color_image_2d_idx >= 0)
		{
			b->radar_image_2d = Iff_ship_info->radar_image_2d_idx;
			b->radar_color_image_2d = Iff_ship_info->radar_color_image_2d_idx;
			b->radar_image_size = Iff_ship_info->radar_image_size;
			b->radar_projection_size = Iff_ship_info->radar_projection_size_mult;
		}
	}

	// don't distort the sensor blips if the player has primitive sensors and the nebula effect
	// is not active
	if (Player_ship->flags2 & SF2_PRIMITIVE_SENSORS)
	{
		if (!(The_mission.flags & MISSION_FLAG_FULLNEB))
			b->flags &= ~BLIP_DRAW_DISTORTED;
	}

	N_blips++;
}
Example #26
0
do_endlevel_flythrough(int n)
{
	object *obj;
	segment *pseg;
	int old_player_seg;

	flydata = &fly_objects[n];
	obj = flydata->obj;
	
	old_player_seg = obj->segnum;

	//move the player for this frame

	if (!flydata->first_time) {

		vm_vec_scale_add2(&obj->pos,&flydata->step,FrameTime);
		angvec_add2_scale(&flydata->angles,&flydata->angstep,FrameTime);

		vm_angles_2_matrix(&obj->orient,&flydata->angles);
	}

	//check new player seg

	update_object_seg(obj);
	pseg = &Segments[obj->segnum];

	if (flydata->first_time || obj->segnum != old_player_seg) {		//moved into new seg
		vms_vector curcenter,nextcenter;
		fix step_size,seg_time;
		short entry_side,exit_side;	//what sides we entry and leave through
		vms_vector dest_point;		//where we are heading (center of exit_side)
		vms_angvec dest_angles;		//where we want to be pointing
		vms_matrix dest_orient;
		int up_side;

		//find new exit side

		if (!flydata->first_time) {

			entry_side = matt_find_connect_side(obj->segnum,old_player_seg);
			exit_side = Side_opposite[entry_side];
		}

		if (flydata->first_time || entry_side==-1 || pseg->children[exit_side]==-1)
			exit_side = find_exit_side(obj);

		{										//find closest side to align to
			fix d,largest_d=-f1_0;
			int i;

			for (i=0;i<6;i++) {
				#ifdef COMPACT_SEGS
				vms_vector v1;
				get_side_normal(pseg, i, 0, &v1 );
				d = vm_vec_dot(&v1,&flydata->obj->orient.uvec);
				#else
				d = vm_vec_dot(&pseg->sides[i].normals[0],&flydata->obj->orient.uvec);
				#endif
				if (d > largest_d) {largest_d = d; up_side=i;}
			}

		}

		//update target point & angles

		compute_center_point_on_side(&dest_point,pseg,exit_side);

		//update target point and movement points

		//offset object sideways
		if (flydata->offset_frac) {
			int s0=-1,s1,i;
			vms_vector s0p,s1p;
			fix dist;

			for (i=0;i<6;i++)
				if (i!=entry_side && i!=exit_side && i!=up_side && i!=Side_opposite[up_side])
					if (s0==-1)
						s0 = i;
					else
						s1 = i;

			compute_center_point_on_side(&s0p,pseg,s0);
			compute_center_point_on_side(&s1p,pseg,s1);
			dist = fixmul(vm_vec_dist(&s0p,&s1p),flydata->offset_frac);

			if (dist-flydata->offset_dist > MAX_SLIDE_PER_SEGMENT)
				dist = flydata->offset_dist + MAX_SLIDE_PER_SEGMENT;

			flydata->offset_dist = dist;

			vm_vec_scale_add2(&dest_point,&obj->orient.rvec,dist);

		}

		vm_vec_sub(&flydata->step,&dest_point,&obj->pos);
		step_size = vm_vec_normalize_quick(&flydata->step);
		vm_vec_scale(&flydata->step,flydata->speed);

		compute_segment_center(&curcenter,pseg);
		compute_segment_center(&nextcenter,&Segments[pseg->children[exit_side]]);
		vm_vec_sub(&flydata->headvec,&nextcenter,&curcenter);

		#ifdef COMPACT_SEGS	
		{
			vms_vector _v1;
			get_side_normal(pseg, up_side, 0, &_v1 );
			vm_vector_2_matrix(&dest_orient,&flydata->headvec,&_v1,NULL);
		}
		#else
		vm_vector_2_matrix(&dest_orient,&flydata->headvec,&pseg->sides[up_side].normals[0],NULL);
		#endif
		vm_extract_angles_matrix(&dest_angles,&dest_orient);

		if (flydata->first_time)
			vm_extract_angles_matrix(&flydata->angles,&obj->orient);

		seg_time = fixdiv(step_size,flydata->speed);	//how long through seg

		if (seg_time) {
			flydata->angstep.x = max(-MAX_ANGSTEP,min(MAX_ANGSTEP,fixdiv(delta_ang(flydata->angles.p,dest_angles.p),seg_time)));
			flydata->angstep.z = max(-MAX_ANGSTEP,min(MAX_ANGSTEP,fixdiv(delta_ang(flydata->angles.b,dest_angles.b),seg_time)));
			flydata->angstep.y = max(-MAX_ANGSTEP,min(MAX_ANGSTEP,fixdiv(delta_ang(flydata->angles.h,dest_angles.h),seg_time)));

		}
		else {
			flydata->angles = dest_angles;
			flydata->angstep.x = flydata->angstep.y = flydata->angstep.z = 0;
		}
	}

	flydata->first_time=0;
}
Example #27
0
static void move_object_to_vector(const vms_vector &vec_through_screen, fix delta_distance)
{
	const auto &&objp = vobjptridx(Cur_object_index);
	const auto result = vm_vec_scale_add(Viewer->pos, vec_through_screen, vm_vec_dist(Viewer->pos, objp->pos) + delta_distance);
	move_object_to_position(objp, result);
}
/**
 * Checks asteroid-ship collisions.  
 * @param pair obj_pair pointer to the two objects. pair->a is asteroid and pair->b is ship.
 * @return 1 if all future collisions between these can be ignored
 */
int collide_asteroid_ship( obj_pair * pair )
{
	if (!Asteroids_enabled)
		return 0;

	float		dist;
	object	*pasteroid = pair->a;
	object	*pship = pair->b;

	// Don't check collisions for warping out player
	if ( Player->control_mode != PCM_NORMAL )	{
		if ( pship == Player_obj ) return 0;
	}

	if (pasteroid->hull_strength < 0.0f)
		return 0;

	Assert( pasteroid->type == OBJ_ASTEROID );
	Assert( pship->type == OBJ_SHIP );

	dist = vm_vec_dist( &pasteroid->pos, &pship->pos );

	if ( dist < pasteroid->radius + pship->radius )	{
		int hit;
		vec3d	hitpos;
		// create and initialize ship_ship_hit_info struct
		collision_info_struct asteroid_hit_info;
		init_collision_info_struct(&asteroid_hit_info);

		if ( pasteroid->phys_info.mass > pship->phys_info.mass ) {
			asteroid_hit_info.heavy = pasteroid;
			asteroid_hit_info.light = pship;
		} else {
			asteroid_hit_info.heavy = pship;
			asteroid_hit_info.light = pasteroid;
		}

		hit = asteroid_check_collision(pasteroid, pship, &hitpos, &asteroid_hit_info );
		if ( hit )
		{
			//Scripting support (WMC)
			Script_system.SetHookObjects(4, "Ship", pship, "Asteroid", pasteroid, "Self",pship, "Object", pasteroid);
			bool ship_override = Script_system.IsConditionOverride(CHA_COLLIDEASTEROID, pship);

			Script_system.SetHookObjects(2, "Self",pasteroid, "Object", pship);
			bool asteroid_override = Script_system.IsConditionOverride(CHA_COLLIDESHIP, pasteroid);

			if(!ship_override && !asteroid_override)
			{
				float		ship_damage;	
				float		asteroid_damage;

				vec3d asteroid_vel = pasteroid->phys_info.vel;

				// do collision physics
				calculate_ship_ship_collision_physics( &asteroid_hit_info );

				if ( asteroid_hit_info.impulse < 0.5f )
					return 0;

				// limit damage from impulse by making max impulse (for damage) 2*m*v_max_relative
				float max_ship_impulse = (2.0f*pship->phys_info.max_vel.xyz.z+vm_vec_mag_quick(&asteroid_vel)) * 
					(pship->phys_info.mass*pasteroid->phys_info.mass) / (pship->phys_info.mass + pasteroid->phys_info.mass);

				if (asteroid_hit_info.impulse > max_ship_impulse) {
					ship_damage = 0.001f * max_ship_impulse;
				} else {
					ship_damage = 0.001f * asteroid_hit_info.impulse;	//	Cut collision-based damage in half.
				}

				//	Decrease heavy damage by 2x.
				if (ship_damage > 5.0f)
					ship_damage = 5.0f + (ship_damage - 5.0f)/2.0f;

				if ((ship_damage > 500.0f) && (ship_damage > Ships[pship->instance].ship_max_hull_strength/8.0f)) {
					ship_damage = Ships[pship->instance].ship_max_hull_strength/8.0f;
					nprintf(("AI", "Pinning damage to %s from asteroid at %7.3f (%7.3f percent)\n", Ships[pship->instance].ship_name, ship_damage, 100.0f * ship_damage/Ships[pship->instance].ship_max_hull_strength));
				}

				//	Decrease damage during warp out because it's annoying when your escoree dies during warp out.
				if (Ai_info[Ships[pship->instance].ai_index].mode == AIM_WARP_OUT)
					ship_damage /= 3.0f;

				// calculate asteroid damage and set asteroid damage to greater or asteroid and ship
				// asteroid damage is needed since we can really whack some small asteroid with afterburner and not do
				// significant damage to ship but the asteroid goes off faster than afterburner speed.
				asteroid_damage = asteroid_hit_info.impulse/pasteroid->phys_info.mass;	// ie, delta velocity of asteroid
				asteroid_damage = (asteroid_damage > ship_damage) ? asteroid_damage : ship_damage;

				// apply damage to asteroid
				asteroid_hit( pasteroid, pship, &hitpos, asteroid_damage);		// speed => damage

				//extern fix Missiontime;

				int quadrant_num;
				if ( asteroid_hit_info.heavy == pship ) {
					quadrant_num = get_ship_quadrant_from_global(&hitpos, pship);
					if ((pship->flags[Object::Object_Flags::No_shields]) || !ship_is_shield_up(pship, quadrant_num) ) {
						quadrant_num = -1;
					}
					ship_apply_local_damage(asteroid_hit_info.heavy, asteroid_hit_info.light, &hitpos, ship_damage, quadrant_num, CREATE_SPARKS, asteroid_hit_info.submodel_num);
				} else {
					// don't draw sparks (using sphere hitpos)
					ship_apply_local_damage(asteroid_hit_info.light, asteroid_hit_info.heavy, &hitpos, ship_damage, MISS_SHIELDS, NO_SPARKS);
				}

				// maybe print Collision on HUD
				if ( pship == Player_obj ) {					
					hud_start_text_flash(XSTR("Collision", 1431), 2000);
				}

				collide_ship_ship_do_sound(&hitpos, pship, pasteroid, pship==Player_obj);
			}

			Script_system.SetHookObjects(2, "Self",pship, "Object", pasteroid);
			if(!(asteroid_override && !ship_override))
				Script_system.RunCondition(CHA_COLLIDEASTEROID, '\0', NULL, pship);

			Script_system.SetHookObjects(2, "Self",pasteroid, "Object", pship);
			if((asteroid_override && !ship_override) || (!asteroid_override && !ship_override))
				Script_system.RunCondition(CHA_COLLIDESHIP, '\0', NULL, pasteroid);

			Script_system.RemHookVars(4, "Ship", "Asteroid", "Self", "Object");

			return 0;
		}

		return 0;
	} else {
		// estimate earliest time at which pair can hit
		float asteroid_max_speed, ship_max_speed, time;
		ship *shipp = &Ships[pship->instance];

		asteroid_max_speed = vm_vec_mag(&pasteroid->phys_info.vel);		// Asteroid... vel gets reset, not max vel.z
		asteroid_max_speed = MAX(asteroid_max_speed, 10.0f);

		if (ship_is_beginning_warpout_speedup(pship)) {
			ship_max_speed = MAX(ship_get_max_speed(shipp), ship_get_warpout_speed(pship));
		} else {
			ship_max_speed = ship_get_max_speed(shipp);
		}
		ship_max_speed = MAX(ship_max_speed, 10.0f);
		ship_max_speed = MAX(ship_max_speed, pship->phys_info.vel.xyz.z);


		time = 1000.0f * (dist - pship->radius - pasteroid->radius - 10.0f) / (asteroid_max_speed + ship_max_speed);		// 10.0f is a safety factor
		time -= 200.0f;		// allow one frame slow frame at ~5 fps

		if (time > 100) {
			pair->next_check_time = timestamp( fl2i(time) );
		} else {
			pair->next_check_time = timestamp(0);	// check next time
		}
		return 0;
	}
}
Example #29
0
do_endlevel_frame()
{
	static fix timer;
	vms_vector save_last_pos;
	static fix explosion_wait1=0;
	static fix explosion_wait2=0;
	static fix bank_rate;
	static fix ext_expl_halflife;

	save_last_pos = ConsoleObject->last_pos;	//don't let move code change this
	object_move_all();
	ConsoleObject->last_pos = save_last_pos;

	if (ext_expl_playing) {

		external_explosion.lifeleft -= FrameTime;
		do_explosion_sequence(&external_explosion);

		if (external_explosion.lifeleft < ext_expl_halflife)
			mine_destroyed = 1;

		if (external_explosion.flags & OF_SHOULD_BE_DEAD)
			ext_expl_playing = 0;
	}

	if (cur_fly_speed != desired_fly_speed) {
		fix delta = desired_fly_speed - cur_fly_speed;
		fix frame_accel = fixmul(FrameTime,FLY_ACCEL);

		if (abs(delta) < frame_accel)
			cur_fly_speed = desired_fly_speed;
		else
			if (delta > 0)
				cur_fly_speed += frame_accel;
			else
				cur_fly_speed -= frame_accel;
	}

	//do big explosions
	if (!outside_mine) {

		if (Endlevel_sequence==EL_OUTSIDE) {
			vms_vector tvec;

			vm_vec_sub(&tvec,&ConsoleObject->pos,&mine_side_exit_point);

			if (vm_vec_dot(&tvec,&mine_exit_orient.fvec) > 0) {
				object *tobj;

				outside_mine = 1;

				tobj = object_create_explosion(exit_segnum,&mine_side_exit_point,i2f(50),VCLIP_BIG_PLAYER_EXPLOSION);

				if (tobj) {
					external_explosion = *tobj;

					tobj->flags |= OF_SHOULD_BE_DEAD;

					flash_scale = 0;	//kill lights in mine

					ext_expl_halflife = tobj->lifeleft;

					ext_expl_playing = 1;
				}
	
				digi_link_sound_to_pos( SOUND_BIG_ENDLEVEL_EXPLOSION, exit_segnum, 0, &mine_side_exit_point, 0, i2f(3)/4 );
			}
		}

		//do explosions chasing player
		if ((explosion_wait1-=FrameTime) < 0) {
			vms_vector tpnt;
			int segnum;
			object *expl;
			static int sound_count;

			vm_vec_scale_add(&tpnt,&ConsoleObject->pos,&ConsoleObject->orient.fvec,-ConsoleObject->size*5);
			vm_vec_scale_add2(&tpnt,&ConsoleObject->orient.rvec,(rand()-RAND_MAX/2)*15);
			vm_vec_scale_add2(&tpnt,&ConsoleObject->orient.uvec,(rand()-RAND_MAX/2)*15);

			segnum = find_point_seg(&tpnt,ConsoleObject->segnum);

			if (segnum != -1) {
				expl = object_create_explosion(segnum,&tpnt,i2f(20),VCLIP_BIG_PLAYER_EXPLOSION);
				if (rand()<10000 || ++sound_count==7) {		//pseudo-random
					digi_link_sound_to_pos( SOUND_TUNNEL_EXPLOSION, segnum, 0, &tpnt, 0, F1_0 );
					sound_count=0;
				}
			}

			explosion_wait1 = 0x2000 + rand()/4;

		}
	}

	//do little explosions on walls
	if (Endlevel_sequence >= EL_FLYTHROUGH && Endlevel_sequence < EL_OUTSIDE)
		if ((explosion_wait2-=FrameTime) < 0) {
			vms_vector tpnt;
			fvi_query fq;
			fvi_info hit_data;

			//create little explosion on wall

			vm_vec_copy_scale(&tpnt,&ConsoleObject->orient.rvec,(rand()-RAND_MAX/2)*100);
			vm_vec_scale_add2(&tpnt,&ConsoleObject->orient.uvec,(rand()-RAND_MAX/2)*100);
			vm_vec_add2(&tpnt,&ConsoleObject->pos);

			if (Endlevel_sequence == EL_FLYTHROUGH)
				vm_vec_scale_add2(&tpnt,&ConsoleObject->orient.fvec,rand()*200);
			else
				vm_vec_scale_add2(&tpnt,&ConsoleObject->orient.fvec,rand()*60);

			//find hit point on wall

			fq.p0 = &ConsoleObject->pos;
			fq.p1 = &tpnt;
			fq.startseg = ConsoleObject->segnum;
			fq.rad = 0;
			fq.thisobjnum = 0;
			fq.ignore_obj_list = NULL;
			fq.flags = 0;

			find_vector_intersection(&fq,&hit_data);

			if (hit_data.hit_type==HIT_WALL && hit_data.hit_seg!=-1)
				object_create_explosion(hit_data.hit_seg,&hit_data.hit_pnt,i2f(3)+rand()*6,VCLIP_SMALL_EXPLOSION);

			explosion_wait2 = (0xa00 + rand()/8)/2;
		}

	switch (Endlevel_sequence) {

		case EL_OFF: return;

		case EL_FLYTHROUGH: {

			do_endlevel_flythrough(0);

			if (ConsoleObject->segnum == transition_segnum) {
				int objnum;

				Endlevel_sequence = EL_LOOKBACK;

				objnum = obj_create(OBJ_CAMERA, 0, 
					ConsoleObject->segnum,&ConsoleObject->pos,&ConsoleObject->orient,0,
					CT_NONE,MT_NONE,RT_NONE);

				if (objnum == -1) {				//can't get object, so abort
					mprintf((1, "Can't get object for endlevel sequence.  Aborting endlevel sequence.\n"));
					stop_endlevel_sequence();
					return;
				}

				Viewer = endlevel_camera = &Objects[objnum];

				select_cockpit(CM_LETTERBOX);

				fly_objects[1] = fly_objects[0];
				fly_objects[1].obj = endlevel_camera;
				fly_objects[1].speed = (5*cur_fly_speed)/4;
				fly_objects[1].offset_frac = 0x4000;

				vm_vec_scale_add2(&endlevel_camera->pos,&endlevel_camera->orient.fvec,i2f(7));

				timer=0x20000;
			}

			break;
		}


		case EL_LOOKBACK: {

			do_endlevel_flythrough(0);
			do_endlevel_flythrough(1);

			if (timer>0) {

				timer -= FrameTime;

				if (timer < 0)		//reduce speed
					fly_objects[1].speed = fly_objects[0].speed;
			}

			if (endlevel_camera->segnum == exit_segnum) {
				vms_angvec cam_angles,exit_seg_angles;

				Endlevel_sequence = EL_OUTSIDE;

				timer = i2f(2);

				vm_vec_negate(&endlevel_camera->orient.fvec);
				vm_vec_negate(&endlevel_camera->orient.rvec);

				vm_extract_angles_matrix(&cam_angles,&endlevel_camera->orient);
				vm_extract_angles_matrix(&exit_seg_angles,&mine_exit_orient);
				bank_rate = (-exit_seg_angles.b - cam_angles.b)/2;

				ConsoleObject->control_type = endlevel_camera->control_type = CT_NONE;

				//_MARK_("Starting outside");//Commented out by KRB

#ifdef SLEW_ON
 slew_obj = endlevel_camera;
#endif
			}
				
			break;
		}

		case EL_OUTSIDE: {
			#ifndef SLEW_ON
			vms_angvec cam_angles;
			#endif

			vm_vec_scale_add2(&ConsoleObject->pos,&ConsoleObject->orient.fvec,fixmul(FrameTime,cur_fly_speed));
#ifndef SLEW_ON
			vm_vec_scale_add2(&endlevel_camera->pos,&endlevel_camera->orient.fvec,fixmul(FrameTime,-2*cur_fly_speed));
			vm_vec_scale_add2(&endlevel_camera->pos,&endlevel_camera->orient.uvec,fixmul(FrameTime,-cur_fly_speed/10));

			vm_extract_angles_matrix(&cam_angles,&endlevel_camera->orient);
			cam_angles.b += fixmul(bank_rate,FrameTime);
			vm_angles_2_matrix(&endlevel_camera->orient,&cam_angles);
#endif

			timer -= FrameTime;

			if (timer < 0) {

				Endlevel_sequence = EL_STOPPED;

				vm_extract_angles_matrix(&player_angles,&ConsoleObject->orient);

				timer = i2f(3);

			}

			break;
		}

		case EL_STOPPED: {

			get_angs_to_object(&player_dest_angles,&station_pos,&ConsoleObject->pos);
			chase_angles(&player_angles,&player_dest_angles);
			vm_angles_2_matrix(&ConsoleObject->orient,&player_angles);

			vm_vec_scale_add2(&ConsoleObject->pos,&ConsoleObject->orient.fvec,fixmul(FrameTime,cur_fly_speed));

			timer -= FrameTime;

			if (timer < 0) {

				#ifdef SLEW_ON
				slew_obj = endlevel_camera;
				_do_slew_movement(endlevel_camera,1,1);
				timer += FrameTime;		//make time stop
				break;
				#else

				#ifdef SHORT_SEQUENCE

				stop_endlevel_sequence();

				#else
				Endlevel_sequence = EL_PANNING;

				vm_extract_angles_matrix(&camera_cur_angles,&endlevel_camera->orient);


				timer = i2f(3);

				if (Game_mode & GM_MULTI) { // try to skip part of the seq if multiplayer
					stop_endlevel_sequence();
					return;
				}

				//mprintf((0,"Switching to pan...\n"));
				#endif		//SHORT_SEQUENCE
				#endif		//SLEW_ON

			}
			break;
		}

		#ifndef SHORT_SEQUENCE
		case EL_PANNING: {
			#ifndef SLEW_ON
			int mask;
			#endif

			get_angs_to_object(&player_dest_angles,&station_pos,&ConsoleObject->pos);
			chase_angles(&player_angles,&player_dest_angles);
			vm_angles_2_matrix(&ConsoleObject->orient,&player_angles);
			vm_vec_scale_add2(&ConsoleObject->pos,&ConsoleObject->orient.fvec,fixmul(FrameTime,cur_fly_speed));

			#ifdef SLEW_ON
			_do_slew_movement(endlevel_camera,1,1);
			#else

			get_angs_to_object(&camera_desired_angles,&ConsoleObject->pos,&endlevel_camera->pos);
			mask = chase_angles(&camera_cur_angles,&camera_desired_angles);
			vm_angles_2_matrix(&endlevel_camera->orient,&camera_cur_angles);

			if ((mask&5) == 5) {

				vms_vector tvec;

				Endlevel_sequence = EL_CHASING;

				//_MARK_("Done outside");//Commented out -KRB

				vm_vec_normalized_dir_quick(&tvec,&station_pos,&ConsoleObject->pos);
				vm_vector_2_matrix(&ConsoleObject->orient,&tvec,&surface_orient.uvec,NULL);

				desired_fly_speed *= 2;

				//mprintf((0,"Switching to chase...\n"));

			}
			#endif

			break;
		}

		case EL_CHASING: {
			fix d,speed_scale;

			#ifdef SLEW_ON
			_do_slew_movement(endlevel_camera,1,1);
			#endif

			get_angs_to_object(&camera_desired_angles,&ConsoleObject->pos,&endlevel_camera->pos);
			chase_angles(&camera_cur_angles,&camera_desired_angles);

			#ifndef SLEW_ON
			vm_angles_2_matrix(&endlevel_camera->orient,&camera_cur_angles);
			#endif

			d = vm_vec_dist_quick(&ConsoleObject->pos,&endlevel_camera->pos);

			speed_scale = fixdiv(d,i2f(0x20));
			if (d<f1_0) d=f1_0;

			get_angs_to_object(&player_dest_angles,&station_pos,&ConsoleObject->pos);
			chase_angles(&player_angles,&player_dest_angles);
			vm_angles_2_matrix(&ConsoleObject->orient,&player_angles);

			vm_vec_scale_add2(&ConsoleObject->pos,&ConsoleObject->orient.fvec,fixmul(FrameTime,cur_fly_speed));
			#ifndef SLEW_ON
			vm_vec_scale_add2(&endlevel_camera->pos,&endlevel_camera->orient.fvec,fixmul(FrameTime,fixmul(speed_scale,cur_fly_speed)));

			if (vm_vec_dist(&ConsoleObject->pos,&station_pos) < i2f(10))
				stop_endlevel_sequence();
			#endif

			break;

		}
		#endif		//ifdef SHORT_SEQUENCE
	}
}
/**
 * Checks debris-ship collisions.  
 * @param pair obj_pair pointer to the two objects. pair->a is debris and pair->b is ship.
 * @return 1 if all future collisions between these can be ignored
 */
int collide_debris_ship( obj_pair * pair )
{
	float dist;
	object *pdebris = pair->a;
	object *pship = pair->b;

	// Don't check collisions for warping out player
	if ( Player->control_mode != PCM_NORMAL )	{
		if ( pship == Player_obj )
			return 0;
	}

	Assert( pdebris->type == OBJ_DEBRIS );
	Assert( pship->type == OBJ_SHIP );

	// don't check collision if it's our own debris and we are dying
	if ( (pdebris->parent == OBJ_INDEX(pship)) && (Ships[pship->instance].flags[Ship::Ship_Flags::Dying]) )
		return 0;

	dist = vm_vec_dist( &pdebris->pos, &pship->pos );
	if ( dist < pdebris->radius + pship->radius )	{
		int hit;
		vec3d	hitpos;
		// create and initialize ship_ship_hit_info struct
		collision_info_struct debris_hit_info;
		init_collision_info_struct(&debris_hit_info);

		if ( pdebris->phys_info.mass > pship->phys_info.mass ) {
			debris_hit_info.heavy = pdebris;
			debris_hit_info.light = pship;
		} else {
			debris_hit_info.heavy = pship;
			debris_hit_info.light = pdebris;
		}

		hit = debris_check_collision(pdebris, pship, &hitpos, &debris_hit_info );
		if ( hit )
		{
			Script_system.SetHookObjects(4, "Ship", pship, "Debris", pdebris, "Self", pship, "Object", pdebris);
			bool ship_override = Script_system.IsConditionOverride(CHA_COLLIDEDEBRIS, pship);

			Script_system.SetHookObjects(2, "Self",pdebris, "Object", pship);
			bool debris_override = Script_system.IsConditionOverride(CHA_COLLIDESHIP, pdebris);
			if(!ship_override && !debris_override)
			{
				float		ship_damage;	
				float		debris_damage;

				// do collision physics
				calculate_ship_ship_collision_physics( &debris_hit_info );

				if ( debris_hit_info.impulse < 0.5f )
					return 0;

				// calculate ship damage
				ship_damage = 0.005f * debris_hit_info.impulse;	//	Cut collision-based damage in half.
				//	Decrease heavy damage by 2x.
				if (ship_damage > 5.0f)
					ship_damage = 5.0f + (ship_damage - 5.0f)/2.0f;

				// calculate debris damage and set debris damage to greater or debris and ship
				// debris damage is needed since we can really whack some small debris with afterburner and not do
				// significant damage to ship but the debris goes off faster than afterburner speed.
				debris_damage = debris_hit_info.impulse/pdebris->phys_info.mass;	// ie, delta velocity of debris
				debris_damage = (debris_damage > ship_damage) ? debris_damage : ship_damage;

				// modify ship damage by debris damage multiplier
				ship_damage *= Debris[pdebris->instance].damage_mult;

				// supercaps cap damage at 10-20% max hull ship damage
				if (Ship_info[Ships[pship->instance].ship_info_index].flags[Ship::Info_Flags::Supercap]) {
					float cap_percent_damage = frand_range(0.1f, 0.2f);
					ship_damage = MIN(ship_damage, cap_percent_damage * Ships[pship->instance].ship_max_hull_strength);
				}

				// apply damage to debris
				debris_hit( pdebris, pship, &hitpos, debris_damage);		// speed => damage
				int quadrant_num, apply_ship_damage;

				// apply damage to ship unless 1) debris is from ship
				apply_ship_damage = !(pship->signature == pdebris->parent_sig);

				if ( debris_hit_info.heavy == pship ) {
					quadrant_num = get_ship_quadrant_from_global(&hitpos, pship);
					if ((pship->flags[Object::Object_Flags::No_shields]) || !ship_is_shield_up(pship, quadrant_num) ) {
						quadrant_num = -1;
					}
					if (apply_ship_damage) {
						ship_apply_local_damage(debris_hit_info.heavy, debris_hit_info.light, &hitpos, ship_damage, quadrant_num, CREATE_SPARKS, debris_hit_info.submodel_num);
					}
				} else {
					// don't draw sparks using sphere hit position
					if (apply_ship_damage) {
						ship_apply_local_damage(debris_hit_info.light, debris_hit_info.heavy, &hitpos, ship_damage, MISS_SHIELDS, NO_SPARKS);
					}
				}

				// maybe print Collision on HUD
				if ( pship == Player_obj ) {					
					hud_start_text_flash(XSTR("Collision", 1431), 2000);
				}

				collide_ship_ship_do_sound(&hitpos, pship, pdebris, pship==Player_obj);
			}

			Script_system.SetHookObjects(2, "Self",pship, "Object", pdebris);
			if(!(debris_override && !ship_override))
				Script_system.RunCondition(CHA_COLLIDEDEBRIS, '\0', NULL, pship);

			Script_system.SetHookObjects(2, "Self",pdebris, "Object", pship);
			if((debris_override && !ship_override) || (!debris_override && !ship_override))
				Script_system.RunCondition(CHA_COLLIDESHIP, '\0', NULL, pdebris);

			Script_system.RemHookVars(4, "Ship", "Debris", "Self", "Object");

			return 0;
		}
	} else {	//	Bounding spheres don't intersect, set timestamp for next collision check.
		float	ship_max_speed, debris_speed;
		float	time;
		ship *shipp;

		shipp = &Ships[pship->instance];

		if (ship_is_beginning_warpout_speedup(pship)) {
			ship_max_speed = MAX(ship_get_max_speed(shipp), ship_get_warpout_speed(pship));
		} else {
			ship_max_speed = ship_get_max_speed(shipp);
		}
		ship_max_speed = MAX(ship_max_speed, 10.0f);
		ship_max_speed = MAX(ship_max_speed, pship->phys_info.vel.xyz.z);

		debris_speed = pdebris->phys_info.speed;

		time = 1000.0f * (dist - pship->radius - pdebris->radius - 10.0f) / (ship_max_speed + debris_speed);		// 10.0f is a safety factor
		time -= 200.0f;		// allow one frame slow frame at ~5 fps

		if (time > 100) {
			pair->next_check_time = timestamp( fl2i(time) );
		} else {
			pair->next_check_time = timestamp(0);	// check next time
		}
	}

	return 0;
}