/**
 * 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;
}
/**
 * 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;
	}
}
Ejemplo n.º 3
0
int ship_weapon_check_collision(object *ship_objp, object *weapon_objp, float time_limit = 0.0f, int *next_hit = NULL)
{
	mc_info mc, mc_shield, mc_hull;
	ship	*shipp;
	ship_info *sip;
	weapon	*wp;
	weapon_info	*wip;

	Assert( ship_objp != NULL );
	Assert( ship_objp->type == OBJ_SHIP );
	Assert( ship_objp->instance >= 0 );

	shipp = &Ships[ship_objp->instance];
	sip = &Ship_info[shipp->ship_info_index];

	Assert( weapon_objp != NULL );
	Assert( weapon_objp->type == OBJ_WEAPON );
	Assert( weapon_objp->instance >= 0 );

	wp = &Weapons[weapon_objp->instance];
	wip = &Weapon_info[wp->weapon_info_index];


	Assert( shipp->objnum == OBJ_INDEX(ship_objp));

	// Make ships that are warping in not get collision detection done
	if ( shipp->flags & SF_ARRIVING ) return 0;

	// if one object is a capital, only check player and player weapons with
	// the capital -- too slow for now otherwise.
//	if ( Polygon_models[Ships[num].modelnum].use_grid && !( (other_objp == Player_obj) || (&Objects[other_objp->parent] == Player_obj)) )
//		return 0;

	//	If either of these objects doesn't get collision checks, abort.
	if (Ship_info[shipp->ship_info_index].flags & SIF_NO_COLLIDE)
		return 0;

	//	Return information for AI to detect incoming fire.
	//	Could perhaps be done elsewhere at lower cost --MK, 11/7/97
	float	dist = vm_vec_dist_quick(&ship_objp->pos, &weapon_objp->pos);
	if (dist < weapon_objp->phys_info.speed) {
		update_danger_weapon(ship_objp, weapon_objp);
	}
	
	ship_model_start(ship_objp);

	int	valid_hit_occurred = 0;				// If this is set, then hitpos is set
	int	quadrant_num = -1;
	polymodel *pm = model_get(sip->model_num);

	//	total time is flFrametime + time_limit (time_limit used to predict collisions into the future)
	vec3d weapon_end_pos;
	vm_vec_scale_add( &weapon_end_pos, &weapon_objp->pos, &weapon_objp->phys_info.vel, time_limit );


	// Goober5000 - I tried to make collision code here much saner... here begin the (major) changes

	// set up collision structs
	mc.model_num = sip->model_num;
	mc.submodel_num = -1;
	mc.orient = &ship_objp->orient;
	mc.pos = &ship_objp->pos;
	mc.p0 = &weapon_objp->last_pos;
	mc.p1 = &weapon_end_pos;
	memcpy(&mc_shield, &mc, sizeof(mc_info));
	memcpy(&mc_hull, &mc, sizeof(mc_info));

	// (btw, these are leftover comments from below...)
	//
	//	Note: This code is obviously stupid. We want to add the shield point if there is shield to hit, but:
	//		1. We want the size/color of the hit effect to indicate shield damage done.  (i.e., for already-weak shield, smaller effect)
	//		2. Currently (8/9/97), apply_damage_to_shield() passes lefer damage to hull, which might not make sense.  If
	//			wouldn't have collided with hull, shouldn't do damage.  Once this is fixed, the code below needs to cast the
	//			vector through to the hull if there is leftover damage.
	//
	// WIF2_PIERCE_SHIELDS pierces shields
	// AL 1-14-97: "Puncture" doesn't mean penetrate shield anymore, it means that it punctures
	//					hull to inflict maximum subsystem damage
	//
	// _argv[-1], 16 Jan 2005: Surface shields.
	// Surface shields allow for shields on a ship without a shield mesh.  Good for putting real shields
	// on the Lucifer.  This also fixes the strange bug where shots will occasionally go through the
	// shield mesh when they shouldn't.  I don't know what causes this, but this fixes that -- shields
	// will absorb it when it hits the hull instead.  This has no fancy graphical effect, though.
	// Someone should make one.

	// set flags
	mc_shield.flags = MC_CHECK_SHIELD;
	mc_hull.flags = MC_CHECK_MODEL;

	// check both kinds of collisions
	int shield_collision = (pm->shield.ntris > 0) ? model_collide(&mc_shield) : 0;
	int hull_collision = model_collide(&mc_hull);

	// check shields for impact
	if (!(ship_objp->flags & OF_NO_SHIELDS))
	{
		// pick out the shield quadrant
		if (shield_collision)
			quadrant_num = get_quadrant(&mc_shield.hit_point);
		else if (hull_collision && (sip->flags2 & SIF2_SURFACE_SHIELDS))
			quadrant_num = get_quadrant(&mc_hull.hit_point);

		// make sure that the shield is active in that quadrant
		if ((quadrant_num >= 0) && ((shipp->flags & SF_DYING) || !ship_is_shield_up(ship_objp, quadrant_num)))
			quadrant_num = -1;

		// see if we hit the shield
		if (quadrant_num >= 0)
		{
			// do the hit effect
			if (shield_collision)
				add_shield_point(OBJ_INDEX(ship_objp), mc_shield.shield_hit_tri, &mc_shield.hit_point);
			else
				/* TODO */;

			// if this weapon pierces the shield, then do the hit effect, but act like a shield collision never occurred;
			// otherwise, we have a valid hit on this shield
			if (wip->wi_flags2 & WIF2_PIERCE_SHIELDS)
				quadrant_num = -1;
			else
				valid_hit_occurred = 1;
		}
	}

	// see which impact we use
	if (shield_collision && valid_hit_occurred)
	{
		memcpy(&mc, &mc_shield, sizeof(mc_info));
		Assert(quadrant_num >= 0);
	}
	else if (hull_collision)
	{
		memcpy(&mc, &mc_hull, sizeof(mc_info));
		valid_hit_occurred = 1;
	}


	//nprintf(("AI", "Frame %i, Hit tri = %i\n", Framecount, mc.shield_hit_tri));
	ship_model_stop(ship_objp);

	// deal with predictive collisions.  Find their actual hit time and see if they occured in current frame
	if (next_hit && valid_hit_occurred) {
		// find hit time
		*next_hit = (int) (1000.0f * (mc.hit_dist*(flFrametime + time_limit) - flFrametime) );
		if (*next_hit > 0)
			// if hit occurs outside of this frame, do not do damage 
			return 1;
	}

	if ( valid_hit_occurred )
	{
		Script_system.SetHookObjects(4, "Ship", ship_objp, "Weapon", weapon_objp, "Self",ship_objp, "Object", weapon_objp);
		bool ship_override = Script_system.IsConditionOverride(CHA_COLLIDEWEAPON, ship_objp);

		Script_system.SetHookObjects(2, "Self",weapon_objp, "Object", ship_objp);
		bool weapon_override = Script_system.IsConditionOverride(CHA_COLLIDESHIP, weapon_objp);

		if(!ship_override && !weapon_override) {
			ship_weapon_do_hit_stuff(ship_objp, weapon_objp, &mc.hit_point_world, &mc.hit_point, quadrant_num, mc.hit_submodel, mc.hit_normal);
		}

		Script_system.SetHookObjects(2, "Self",ship_objp, "Object", weapon_objp);
		if(!(weapon_override && !ship_override))
			Script_system.RunCondition(CHA_COLLIDEWEAPON, '\0', NULL, ship_objp);

		Script_system.SetHookObjects(2, "Self",weapon_objp, "Object", ship_objp);
		if((weapon_override && !ship_override) || (!weapon_override && !ship_override))
			Script_system.RunCondition(CHA_COLLIDESHIP, '\0', NULL, weapon_objp);

		Script_system.RemHookVars(4, "Ship", "Weapon", "Self","Object");
		/*
		if(!Script_system.IsOverride(wip->sc_collide_ship)) {
			ship_weapon_do_hit_stuff(ship_objp, weapon_objp, &mc.hit_point_world, &mc.hit_point, quadrant_num, mc.hit_submodel, mc.hit_normal);
		}

		if(wip->sc_collide_ship.IsValid()) {
			ade_odata lua_self_obj = l_Weapon.Set(object_h(weapon_objp));
			ade_odata lua_ship_obj = l_Ship.Set(object_h(ship_objp));
			
			Script_system.SetHookVar("Self", 'o', &lua_self_obj);
			Script_system.SetHookVar("Ship", 'o', &lua_ship_obj);

			Script_system.RunBytecode(wip->sc_collide_ship);

			Script_system.RemHookVar("Self");
			Script_system.RemHookVar("Ship");
		}*/
	}
	else if ((Missiontime - wp->creation_time > F1_0/2) && (wip->wi_flags & WIF_HOMING) && (wp->homing_object == ship_objp)) {
		if (dist < wip->shockwave.inner_rad) {
			vec3d	vec_to_ship;

			vm_vec_normalized_dir(&vec_to_ship, &ship_objp->pos, &weapon_objp->pos);

			if (vm_vec_dot(&vec_to_ship, &weapon_objp->orient.vec.fvec) < 0.0f) {
				// check if we're colliding against "invisible" ship
				if (!(shipp->flags2 & SF2_DONT_COLLIDE_INVIS)) {
					wp->lifeleft = 0.001f;
					if (ship_objp == Player_obj)
						nprintf(("Jim", "Frame %i: Weapon %i set to detonate, dist = %7.3f.\n", Framecount, OBJ_INDEX(weapon_objp), dist));
					valid_hit_occurred = 1;
				}
			}

		}
	}

	return valid_hit_occurred;
}
int ship_weapon_check_collision(object *ship_objp, object *weapon_objp, float time_limit = 0.0f, int *next_hit = NULL)
{
    mc_info mc, mc_shield, mc_hull;
    ship	*shipp;
    ship_info *sip;
    weapon	*wp;
    weapon_info	*wip;

    Assert( ship_objp != NULL );
    Assert( ship_objp->type == OBJ_SHIP );
    Assert( ship_objp->instance >= 0 );

    shipp = &Ships[ship_objp->instance];
    sip = &Ship_info[shipp->ship_info_index];

    Assert( weapon_objp != NULL );
    Assert( weapon_objp->type == OBJ_WEAPON );
    Assert( weapon_objp->instance >= 0 );

    wp = &Weapons[weapon_objp->instance];
    wip = &Weapon_info[wp->weapon_info_index];


    Assert( shipp->objnum == OBJ_INDEX(ship_objp));

    // Make ships that are warping in not get collision detection done
    if ( shipp->is_arriving() ) return 0;

    //	Return information for AI to detect incoming fire.
    //	Could perhaps be done elsewhere at lower cost --MK, 11/7/97
    float	dist = vm_vec_dist_quick(&ship_objp->pos, &weapon_objp->pos);
    if (dist < weapon_objp->phys_info.speed) {
        update_danger_weapon(ship_objp, weapon_objp);
    }

    int	valid_hit_occurred = 0;				// If this is set, then hitpos is set
    int	quadrant_num = -1;
    polymodel *pm = model_get(sip->model_num);

    //	total time is flFrametime + time_limit (time_limit used to predict collisions into the future)
    vec3d weapon_end_pos;
    vm_vec_scale_add( &weapon_end_pos, &weapon_objp->pos, &weapon_objp->phys_info.vel, time_limit );


    // Goober5000 - I tried to make collision code here much saner... here begin the (major) changes
    mc_info_init(&mc);

    // set up collision structs
    mc.model_instance_num = shipp->model_instance_num;
    mc.model_num = sip->model_num;
    mc.submodel_num = -1;
    mc.orient = &ship_objp->orient;
    mc.pos = &ship_objp->pos;
    mc.p0 = &weapon_objp->last_pos;
    mc.p1 = &weapon_end_pos;
    mc.lod = sip->collision_lod;
    memcpy(&mc_shield, &mc, sizeof(mc_info));
    memcpy(&mc_hull, &mc, sizeof(mc_info));

    // (btw, these are leftover comments from below...)
    //
    //	Note: This code is obviously stupid. We want to add the shield point if there is shield to hit, but:
    //		1. We want the size/color of the hit effect to indicate shield damage done.  (i.e., for already-weak shield, smaller effect)
    //		2. Currently (8/9/97), apply_damage_to_shield() passes lefer damage to hull, which might not make sense.  If
    //			wouldn't have collided with hull, shouldn't do damage.  Once this is fixed, the code below needs to cast the
    //			vector through to the hull if there is leftover damage.
    //
    // WIF2_PIERCE_SHIELDS pierces shields
    // AL 1-14-97: "Puncture" doesn't mean penetrate shield anymore, it means that it punctures
    //					hull to inflict maximum subsystem damage
    //
    // _argv[-1], 16 Jan 2005: Surface shields.
    // Surface shields allow for shields on a ship without a shield mesh.  Good for putting real shields
    // on the Lucifer.  This also fixes the strange bug where shots will occasionally go through the
    // shield mesh when they shouldn't.  I don't know what causes this, but this fixes that -- shields
    // will absorb it when it hits the hull instead.  This has no fancy graphical effect, though.
    // Someone should make one.

    // check both kinds of collisions
    int shield_collision = 0;
    int hull_collision = 0;

    // check shields for impact
    if (!(ship_objp->flags[Object::Object_Flags::No_shields])) {
        if (sip->flags[Ship::Info_Flags::Auto_spread_shields]) {
            // The weapon is not allowed to impact the shield before it reaches this point
            vec3d shield_ignored_until = weapon_objp->last_pos;

            float weapon_flown_for = vm_vec_dist(&wp->start_pos, &weapon_objp->last_pos);
            float min_weapon_span;

            if (sip->auto_shield_spread_min_span >= 0.0f) {
                min_weapon_span = sip->auto_shield_spread_min_span;
            } else {
                min_weapon_span = sip->auto_shield_spread;
            }

            // If weapon hasn't yet flown a distance greater than the maximum ignore
            // range, then some part of the currently checked range needs to be
            // ignored
            if (weapon_flown_for < min_weapon_span) {
                vm_vec_sub(&shield_ignored_until, &weapon_end_pos, &wp->start_pos);
                vm_vec_normalize(&shield_ignored_until);
                vm_vec_scale(&shield_ignored_until, min_weapon_span);
                vm_vec_add2(&shield_ignored_until, &wp->start_pos);
            }

            float this_range = vm_vec_dist(&weapon_objp->last_pos, &weapon_end_pos);

            // The range during which the weapon is not allowed to collide with the
            // shield, except if it actually hits the hull
            float ignored_range;

            // If the weapon has not yet surpassed the ignore range, calculate the
            // remaining ignore range
            if (vm_vec_dist(&wp->start_pos, &shield_ignored_until) > weapon_flown_for)
                ignored_range = vm_vec_dist(&weapon_objp->last_pos, &shield_ignored_until);
            else
                ignored_range = 0.0f;

            // The range during which the weapon may impact the shield
            float active_range = this_range - ignored_range;

            // During the ignored range, we only check for a ray collision with
            // the model
            if (ignored_range > 0.0f) {
                mc_shield.flags = MC_CHECK_MODEL;
                mc_shield.p1 = &shield_ignored_until;

                shield_collision = model_collide(&mc_shield);

                mc_shield.p1 = &weapon_end_pos;
                mc_shield.hit_dist = mc_shield.hit_dist * (ignored_range / this_range);
            }

            // If no collision with the model found in the ignore range, only
            // then do we check for sphereline collisions with the model during the
            // non-ignored range
            if (!shield_collision && weapon_flown_for + this_range > min_weapon_span) {
                mc_shield.p0 = &shield_ignored_until;

                mc_shield.p1 = &weapon_end_pos;

                mc_shield.radius = sip->auto_shield_spread;

                if (sip->auto_shield_spread_from_lod > -1) {
                    mc_shield.lod = sip->auto_shield_spread_from_lod;
                }

                mc_shield.flags = MC_CHECK_MODEL | MC_CHECK_SPHERELINE;

                shield_collision = model_collide(&mc_shield);

                mc_shield.lod = sip->collision_lod;
                mc_shield.submodel_num = -1;

                // Because we manipulated p0 and p1 above, hit_dist will be
                // relative to the values we used, not the values the rest of
                // the code expects; this fixes that
                mc_shield.p0 = &weapon_objp->last_pos;
                mc_shield.p1 = &weapon_end_pos;
                mc_shield.hit_dist = (ignored_range + (active_range * mc_shield.hit_dist)) / this_range;
            }

            if (shield_collision) {
                // If we used a sphereline check, then the collision point will lie
                // somewhere on the ship's hull; this re-positions it to lie on the
                // correct point along the weapon's path
                if (mc_shield.flags & MC_CHECK_SPHERELINE) {
                    vec3d tempv;
                    vm_vec_sub(&tempv, mc_shield.p1, mc_shield.p0);
                    vm_vec_scale(&tempv, mc_shield.hit_dist);
                    vm_vec_add2(&tempv, mc_shield.p0);
                    mc_shield.hit_point_world = tempv;
                }

                // Re-calculate hit_point because it's likely pointing to the wrong
                // place
                vec3d tempv;
                vm_vec_sub(&tempv, &mc_shield.hit_point_world, &ship_objp->pos);
                vm_vec_rotate(&mc_shield.hit_point, &tempv, &ship_objp->orient);
            }
        } else if (sip->flags[Ship::Info_Flags::Surface_shields]) {
            if (pm->shield.ntris > 0) {
                // If there is a shield mesh, we need to check that first
                mc_shield.flags = MC_CHECK_SHIELD;
                shield_collision = model_collide(&mc_shield);
            }

            if (!shield_collision) {
                // But if no shield mesh or it was missed, check for a hull collision
                mc_shield.flags = MC_CHECK_MODEL;
                shield_collision = model_collide(&mc_shield);

                // Because we used MC_CHECK_MODEL, the returned hit position might be
                // in a submodel's frame of reference, so we need to ensure we end up
                // in the ship's frame of reference
                vec3d local_pos;
                vm_vec_sub(&local_pos, &mc_shield.hit_point_world, &ship_objp->pos);
                vm_vec_rotate(&mc_shield.hit_point, &local_pos, &ship_objp->orient);
            }
        } else {
            // Normal collision check against a shield mesh
            mc_shield.flags = MC_CHECK_SHIELD;
            shield_collision = (pm->shield.ntris > 0) ? model_collide(&mc_shield) : 0;
        }
    }

    // If we found a shield collision but were only checking for a simple model
    // collision, we can re-use the same collision info for the hull as well
    if (shield_collision && mc_shield.flags == MC_CHECK_MODEL) {
        memcpy(&mc_hull, &mc_shield, sizeof(mc_info));
        hull_collision = shield_collision;

        // The weapon has impacted on the hull, so if it should therefore bypass
        // the shields altogether, we do it here
        if (sip->auto_shield_spread_bypass) {
            shield_collision = 0;
        }
    } else {
        mc_hull.flags = MC_CHECK_MODEL;
        hull_collision = model_collide(&mc_hull);
    }

    if (shield_collision) {
        // pick out the shield quadrant
        quadrant_num = get_quadrant(&mc_shield.hit_point, ship_objp);

        // make sure that the shield is active in that quadrant
        if (shipp->flags[Ship::Ship_Flags::Dying] || !ship_is_shield_up(ship_objp, quadrant_num))
            quadrant_num = -1;

        // see if we hit the shield
        if (quadrant_num >= 0) {
            // do the hit effect
            if ( mc_shield.shield_hit_tri != -1 && (mc_shield.hit_dist*(flFrametime + time_limit) - flFrametime) < 0.0f ) {
                add_shield_point(OBJ_INDEX(ship_objp), mc_shield.shield_hit_tri, &mc_shield.hit_point);
            }

            // if this weapon pierces the shield, then do the hit effect, but act like a shield collision never occurred;
            // otherwise, we have a valid hit on this shield
            if (wip->wi_flags[Weapon::Info_Flags::Pierce_shields])
                quadrant_num = -1;
            else
                valid_hit_occurred = 1;
        }
    }

    // see which impact we use
    if (shield_collision && valid_hit_occurred)
    {
        memcpy(&mc, &mc_shield, sizeof(mc_info));
        Assert(quadrant_num >= 0);
    }
    else if (hull_collision)
    {
        memcpy(&mc, &mc_hull, sizeof(mc_info));
        valid_hit_occurred = 1;
    }

    // check if the hit point is beyond the clip plane when warping out.
    if ((shipp->flags[Ship::Ship_Flags::Depart_warp]) &&
            (shipp->warpout_effect) &&
            (valid_hit_occurred))
    {
        vec3d warp_pnt, hit_direction;
        matrix warp_orient;

        shipp->warpout_effect->getWarpPosition(&warp_pnt);
        shipp->warpout_effect->getWarpOrientation(&warp_orient);

        vm_vec_sub(&hit_direction, &mc.hit_point_world, &warp_pnt);

        if (vm_vec_dot(&hit_direction, &warp_orient.vec.fvec) < 0.0f)
        {
            valid_hit_occurred = 0;
        }
    }

    // deal with predictive collisions.  Find their actual hit time and see if they occured in current frame
    if (next_hit && valid_hit_occurred) {
        // find hit time
        *next_hit = (int) (1000.0f * (mc.hit_dist*(flFrametime + time_limit) - flFrametime) );
        if (*next_hit > 0)
            // if hit occurs outside of this frame, do not do damage
            return 1;
    }

    if ( valid_hit_occurred )
    {
        wp->collisionInfo = new mc_info;	// The weapon will free this memory later
        memcpy(wp->collisionInfo, &mc, sizeof(mc_info));

        Script_system.SetHookObjects(4, "Ship", ship_objp, "Weapon", weapon_objp, "Self",ship_objp, "Object", weapon_objp);
        bool ship_override = Script_system.IsConditionOverride(CHA_COLLIDEWEAPON, ship_objp);

        Script_system.SetHookObjects(2, "Self",weapon_objp, "Object", ship_objp);
        bool weapon_override = Script_system.IsConditionOverride(CHA_COLLIDESHIP, weapon_objp);

        if(!ship_override && !weapon_override) {
            if (shield_collision && quadrant_num >= 0) {
                if ((sip->shield_impact_explosion_anim > -1) && (wip->shield_impact_explosion_radius > 0)) {
                    shield_impact_explosion(&mc.hit_point, ship_objp, wip->shield_impact_explosion_radius, sip->shield_impact_explosion_anim);
                }
            }
            ship_weapon_do_hit_stuff(ship_objp, weapon_objp, &mc.hit_point_world, &mc.hit_point, quadrant_num, mc.hit_submodel, mc.hit_normal);
        }

        Script_system.SetHookObjects(2, "Self",ship_objp, "Object", weapon_objp);
        if(!(weapon_override && !ship_override))
            Script_system.RunCondition(CHA_COLLIDEWEAPON, '\0', NULL, ship_objp, wp->weapon_info_index);

        Script_system.SetHookObjects(2, "Self",weapon_objp, "Object", ship_objp);
        if((weapon_override && !ship_override) || (!weapon_override && !ship_override))
            Script_system.RunCondition(CHA_COLLIDESHIP, '\0', NULL, weapon_objp);

        Script_system.RemHookVars(4, "Ship", "Weapon", "Self","Object");
    }
    else if ((Missiontime - wp->creation_time > F1_0/2) && (wip->is_homing()) && (wp->homing_object == ship_objp)) {
        if (dist < wip->shockwave.inner_rad) {
            vec3d	vec_to_ship;

            vm_vec_normalized_dir(&vec_to_ship, &ship_objp->pos, &weapon_objp->pos);

            if (vm_vec_dot(&vec_to_ship, &weapon_objp->orient.vec.fvec) < 0.0f) {
                // check if we're colliding against "invisible" ship
                if (!(shipp->flags[Ship::Ship_Flags::Dont_collide_invis])) {
                    wp->lifeleft = 0.001f;
                    if (ship_objp == Player_obj)
                        nprintf(("Jim", "Frame %i: Weapon %d set to detonate, dist = %7.3f.\n", Framecount, OBJ_INDEX(weapon_objp), dist));
                    valid_hit_occurred = 1;
                }
            }

        }
    }

    return valid_hit_occurred;
}
Ejemplo n.º 5
0
// Checks debris-ship collisions.  pair->a is debris and pair->b is ship.
// Returns 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 );

/*	Debris_ship_count++;
	if (Debris_ship_count % 100 == 0)
		nprintf(("AI", "Done %i debris:ship checks in %i frames = %.2f checks/frame\n", Debris_ship_count, Framecount, (float) Debris_ship_count/Framecount));
*/
	dist = vm_vec_dist( &pdebris->pos, &pship->pos );
	if ( dist < pdebris->radius + pship->radius )	{
		int hit;
		vector	hitpos;
		// create and initialize ship_ship_hit_info struct
		collision_info_struct debris_hit_info;
		memset( &debris_hit_info, -1, sizeof(collision_info_struct) );

		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 ) {
			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;

			// supercaps cap damage at 10-20% max hull ship damage
			if (Ship_info[Ships[pship->instance].ship_info_index].flags & SIF_SUPERCAP) {
				float cap_percent_damage = frand_range(0.1f, 0.2f);
				ship_damage = min(ship_damage, cap_percent_damage * Ship_info[Ships[pship->instance].ship_info_index].initial_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) && ship_is_beginning_warpout_speedup(pship));
			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 & OF_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);

			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_warp_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) {
			//nprintf(("AI", "Ship %s debris #%i delay time = %.1f seconds\n", Ships[pship->instance].ship_name, pdebris-Objects, time/1000.0f));
			pair->next_check_time = timestamp( fl2i(time) );
		} else {
			pair->next_check_time = timestamp(0);	// check next time
		}
	}

	return 0;
}