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.º 2
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;
}