Files
fs2open.github.com/code/object/collidedebrisship.cpp
T
Boutros Tawaifi 49464c56f6 Add an "On Ship Damage" script hook (#7894)
* Add an "On Ship Damage" script hook

Runs after resolved damage has been applied to a ship (lethal hits run before
the destruction callback). Hook variables: Target, Source, Attacker,
WeaponClass, WeaponSubtype, SourceKind (PROJECTILE, BEAM, SHOCKWAVE, COLLISION,
OTHER), DamageType, ShieldDamage, HullDamage, Quadrant, Ballistic, BeamWeapon
and LethalHit. Conditions can filter on the target ship. Nothing is computed
unless a script has registered the hook.

* Move the On Ship Damage hook call out of a lambda

ship_do_damage() now calls a file-level static function, ship_run_damage_hook(), that takes the values it needs as arguments instead of capturing the function state. Hook behaviour is unchanged.

* Report subsystem damage in the On Ship Damage hook

Adds SubsystemDamage, the total subsystem strength a damage application removed, so a hit absorbed by subsystems no longer looks like a small hull hit. The hook description now says it only runs where damage is applied, which excludes multiplayer clients.

* Address On Ship Damage review comments

- Remove WeaponClass, WeaponSubtype, Ballistic and BeamWeapon from the
  hook. Scripts read these from Source when it is a weapon.
- Add a "Weapon class" condition for the On Ship Damage hook. It matches
  only when the damage source is a weapon of that class; a weapon name
  that does not exist never matches.
- SubsystemDamage is now a table keyed by subsystem handle that lists only
  the subsystems that lost strength. The total is removed.
- Rename PROJECTILE to NON_BEAM_WEAPON.
- Classify SourceKind by damage source: COLLISION for ship, debris and
  asteroid collisions; EXPLOSION for ship-death and docked-ship blasts and
  asteroid blasts; WASH for engine wash; SELF for kills where the ship
  damages itself; OTHER for other ship-sourced damage.
- Reword the hook description.

* Key On Ship Damage SubsystemDamage by canonical name

Lua tables cannot use subsystem handles as keys in a usable way, so key
the SubsystemDamage table by each subsystem's CanonicalName instead.

* Tag ship-vs-prop damage as a collision and use in_bounds

The ship-vs-prop collision call didn't pass DamageSource::Collision, so
the On Ship Damage hook reported those hits as OTHER. Every other
collision path was already tagged.

Also use SCP_vector::in_bounds() for the Weapon_info and Damage_types
index checks in ship_run_damage_hook().
2026-11-06 18:41:29 -05:00

627 lines
24 KiB
C++

/*
* Copyright (C) Volition, Inc. 1999. All rights reserved.
*
* All source code herein is the property of Volition, Inc. You may not sell
* or otherwise commercially exploit the source or things you created based on the
* source.
*
*/
#include "asteroid/asteroid.h"
#include "debris/debris.h"
#include "hud/hud.h"
#include "io/timer.h"
#include "object/objcollide.h"
#include "object/object.h"
#include "scripting/global_hooks.h"
#include "scripting/scripting.h"
#include "scripting/api/objs/model.h"
#include "scripting/api/objs/vecmath.h"
#include "playerman/player.h"
#include "prop/prop.h"
#include "ship/ship.h"
#include "ship/shiphit.h"
void calculate_ship_ship_collision_physics(collision_info_struct *ship_ship_hit_info);
/**
* 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 *debris_objp = pair->a;
object *ship_objp = pair->b;
// Don't check collisions for warping out player
if ( Player->control_mode != PCM_NORMAL ) {
if ( ship_objp == Player_obj )
return 0;
}
Assert( debris_objp->type == OBJ_DEBRIS );
Assert( ship_objp->type == OBJ_SHIP );
if (reject_due_collision_groups(debris_objp, ship_objp))
return 0;
ship* shipp = &Ships[ship_objp->instance];
// don't check collision if it's our own debris and we are dying
if ( (debris_objp->parent == OBJ_INDEX(ship_objp)) && (shipp->flags[Ship::Ship_Flags::Dying]) )
return 0;
dist = vm_vec_dist( &debris_objp->pos, &ship_objp->pos );
if ( dist < debris_objp->radius + ship_objp->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 ( debris_objp->phys_info.mass > ship_objp->phys_info.mass ) {
debris_hit_info.heavy = debris_objp;
debris_hit_info.light = ship_objp;
} else {
debris_hit_info.heavy = ship_objp;
debris_hit_info.light = debris_objp;
}
hit = debris_check_collision(debris_objp, ship_objp, &hitpos, &debris_hit_info );
if ( hit )
{
bool ship_override = false, debris_override = false;
// get submodel handle if scripting needs it
bool has_submodel = (debris_hit_info.heavy_submodel_num >= 0);
scripting::api::submodel_h smh(debris_hit_info.heavy_model_num, debris_hit_info.heavy_submodel_num);
if (scripting::hooks::OnDebrisCollision->isActive()) {
ship_override = scripting::hooks::OnDebrisCollision->isOverride(scripting::hooks::CollisionConditions{ {ship_objp, debris_objp} },
scripting::hook_param_list(scripting::hook_param("Self", 'o', ship_objp),
scripting::hook_param("Object", 'o', debris_objp),
scripting::hook_param("Ship", 'o', ship_objp),
scripting::hook_param("Debris", 'o', debris_objp),
scripting::hook_param("Hitpos", 'o', hitpos)));
}
if (scripting::hooks::OnShipCollision->isActive()) {
debris_override = scripting::hooks::OnShipCollision->isOverride(scripting::hooks::CollisionConditions{ {ship_objp, debris_objp} },
scripting::hook_param_list(scripting::hook_param("Self", 'o', debris_objp),
scripting::hook_param("Object", 'o', ship_objp),
scripting::hook_param("Ship", 'o', ship_objp),
scripting::hook_param("Debris", 'o', debris_objp),
scripting::hook_param("Hitpos", 'o', hitpos),
scripting::hook_param("ShipSubmodel", 'o', scripting::api::l_Submodel.Set(smh), has_submodel && (debris_hit_info.heavy == ship_objp))));
}
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/debris_objp->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[debris_objp->instance].damage_mult;
// supercaps cap damage at 10-20% max hull ship damage
if (Ship_info[shipp->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 * shipp->ship_max_hull_strength);
}
if (Ship_info[shipp->ship_info_index].flags[Ship::Info_Flags::Big_damage] &&
The_mission.ai_profile->flags[AI::Profile_Flags::Debris_respects_big_damage]) {
// scale based on hull
float hull_pct = ship_objp->hull_strength / shipp->ship_max_hull_strength;
if (hull_pct > 0.1f) {
ship_damage *= hull_pct;
} else {
ship_damage = 0.0f;
}
}
// apply damage to debris
// no need for force, already handled in calculate_ship_ship_collision_physics
debris_hit( debris_objp, ship_objp, &hitpos, debris_damage, nullptr); // speed => damage
int apply_ship_damage;
// apply damage to ship unless 1) debris is from ship
apply_ship_damage = (ship_objp->signature != debris_objp->parent_sig);
if ( debris_hit_info.heavy == ship_objp) {
int quadrant_num = get_ship_quadrant_from_global(&hitpos, ship_objp);
if (The_mission.ai_profile->flags[AI::Profile_Flags::No_shield_damage_from_ship_collisions] ||
(ship_objp->flags[Object::Object_Flags::No_shields]) || !ship_is_shield_up(ship_objp, quadrant_num) ) {
quadrant_num = -1;
}
if (apply_ship_damage) {
ship_apply_local_damage(debris_hit_info.heavy, debris_hit_info.light, &hitpos, ship_damage, Debris[debris_objp->instance].damage_type_idx, quadrant_num, CREATE_SPARKS, debris_hit_info.heavy_submodel_num, nullptr, 1.f, nullptr, DamageSource::Collision);
}
} 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, Debris[debris_objp->instance].damage_type_idx, MISS_SHIELDS, NO_SPARKS, -1, nullptr, 1.f, nullptr, DamageSource::Collision);
}
}
// maybe print Collision on HUD
if ( ship_objp == Player_obj ) {
hud_start_text_flash(XSTR("Collision", 1431), 2000);
}
collide_ship_ship_do_sound(&hitpos, ship_objp, debris_objp, ship_objp==Player_obj);
}
if (scripting::hooks::OnDebrisCollision->isActive() && !(debris_override && !ship_override)) {
scripting::hooks::OnDebrisCollision->run(scripting::hooks::CollisionConditions{ {ship_objp, debris_objp} },
scripting::hook_param_list(scripting::hook_param("Self", 'o', ship_objp),
scripting::hook_param("Object", 'o', debris_objp),
scripting::hook_param("Ship", 'o', ship_objp),
scripting::hook_param("Debris", 'o', debris_objp),
scripting::hook_param("Hitpos", 'o', hitpos)));
}
if (scripting::hooks::OnShipCollision->isActive() && ((debris_override && !ship_override) || (!debris_override && !ship_override)))
{
scripting::hooks::OnShipCollision->run(scripting::hooks::CollisionConditions{ {ship_objp, debris_objp} },
scripting::hook_param_list(scripting::hook_param("Self", 'o', debris_objp),
scripting::hook_param("Object", 'o', ship_objp),
scripting::hook_param("Ship", 'o', ship_objp),
scripting::hook_param("Debris", 'o', debris_objp),
scripting::hook_param("Hitpos", 'o', hitpos),
scripting::hook_param("ShipSubmodel", 'o', scripting::api::l_Submodel.Set(smh), has_submodel && (debris_hit_info.heavy == ship_objp))));
}
return 0;
}
} else { // Bounding spheres don't intersect, set timestamp for next collision check.
float ship_max_speed, debris_speed;
float time;
if (ship_is_beginning_warpout_speedup(ship_objp)) {
ship_max_speed = MAX(ship_get_max_speed(shipp), ship_get_warpout_speed(ship_objp));
} 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, ship_objp->phys_info.vel.xyz.z);
debris_speed = debris_objp->phys_info.speed;
time = 1000.0f * (dist - ship_objp->radius - debris_objp->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 *asteroid_objp = pair->a;
object *ship_objp = pair->b;
// Don't check collisions for warping out player
if ( Player->control_mode != PCM_NORMAL ) {
if ( ship_objp == Player_obj ) return 0;
}
if (asteroid_objp->hull_strength < 0.0f)
return 0;
Assert( asteroid_objp->type == OBJ_ASTEROID );
Assert( ship_objp->type == OBJ_SHIP );
dist = vm_vec_dist( &asteroid_objp->pos, &ship_objp->pos );
ship* shipp = &Ships[ship_objp->instance];
if ( dist < asteroid_objp->radius + ship_objp->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 ( asteroid_objp->phys_info.mass > ship_objp->phys_info.mass ) {
asteroid_hit_info.heavy = asteroid_objp;
asteroid_hit_info.light = ship_objp;
} else {
asteroid_hit_info.heavy = ship_objp;
asteroid_hit_info.light = asteroid_objp;
}
hit = asteroid_check_collision(asteroid_objp, ship_objp, &hitpos, &asteroid_hit_info );
if ( hit )
{
bool ship_override = false, asteroid_override = false;
// get submodel handle if scripting needs it
bool has_submodel = (asteroid_hit_info.heavy_submodel_num >= 0);
scripting::api::submodel_h smh(asteroid_hit_info.heavy_model_num, asteroid_hit_info.heavy_submodel_num);
//Scripting support (WMC)
if (scripting::hooks::OnAsteroidCollision->isActive()) {
ship_override = scripting::hooks::OnAsteroidCollision->isOverride(scripting::hooks::CollisionConditions{ {ship_objp, asteroid_objp} },
scripting::hook_param_list(scripting::hook_param("Self", 'o', ship_objp),
scripting::hook_param("Object", 'o', asteroid_objp),
scripting::hook_param("Ship", 'o', ship_objp),
scripting::hook_param("Asteroid", 'o', asteroid_objp),
scripting::hook_param("Hitpos", 'o', hitpos)));
}
if (scripting::hooks::OnShipCollision->isActive()) {
asteroid_override = scripting::hooks::OnShipCollision->isOverride(scripting::hooks::CollisionConditions{ {ship_objp, asteroid_objp} },
scripting::hook_param_list(scripting::hook_param("Self", 'o', asteroid_objp),
scripting::hook_param("Object", 'o', ship_objp),
scripting::hook_param("Ship", 'o', ship_objp),
scripting::hook_param("Asteroid", 'o', asteroid_objp),
scripting::hook_param("Hitpos", 'o', hitpos),
scripting::hook_param("ShipSubmodel", 'o', scripting::api::l_Submodel.Set(smh), has_submodel && (asteroid_hit_info.heavy == ship_objp))));
}
if(!ship_override && !asteroid_override)
{
float ship_damage;
float asteroid_damage;
vec3d asteroid_vel = asteroid_objp->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*ship_objp->phys_info.max_vel.xyz.z+vm_vec_mag_quick(&asteroid_vel)) *
(ship_objp->phys_info.mass*asteroid_objp->phys_info.mass) / (ship_objp->phys_info.mass + asteroid_objp->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 > shipp->ship_max_hull_strength/8.0f)) {
ship_damage = shipp->ship_max_hull_strength/8.0f;
nprintf(("AI", "Pinning damage to %s from asteroid at %7.3f (%7.3f percent)\n", shipp->ship_name, ship_damage, 100.0f * ship_damage/ shipp->ship_max_hull_strength));
}
// Decrease damage during warp out because it's annoying when your escoree dies during warp out.
if (Ai_info[shipp->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/asteroid_objp->phys_info.mass; // ie, delta velocity of asteroid
asteroid_damage = (asteroid_damage > ship_damage) ? asteroid_damage : ship_damage;
// apply damage to asteroid
asteroid_hit( asteroid_objp, ship_objp, &hitpos, asteroid_damage, nullptr); // speed => damage
int ast_damage_type = Asteroid_info[Asteroids[asteroid_objp->instance].asteroid_type].damage_type_idx;
if ( asteroid_hit_info.heavy == ship_objp) {
int quadrant_num = get_ship_quadrant_from_global(&hitpos, ship_objp);
if (The_mission.ai_profile->flags[AI::Profile_Flags::No_shield_damage_from_ship_collisions] ||
(ship_objp->flags[Object::Object_Flags::No_shields]) || !ship_is_shield_up(ship_objp, quadrant_num) ) {
quadrant_num = -1;
}
ship_apply_local_damage(asteroid_hit_info.heavy, asteroid_hit_info.light, &hitpos, ship_damage, ast_damage_type, quadrant_num, CREATE_SPARKS, asteroid_hit_info.heavy_submodel_num, nullptr, 1.f, nullptr, DamageSource::Collision);
} else {
// don't draw sparks (using sphere hitpos)
ship_apply_local_damage(asteroid_hit_info.light, asteroid_hit_info.heavy, &hitpos, ship_damage, ast_damage_type, MISS_SHIELDS, NO_SPARKS, -1, nullptr, 1.f, nullptr, DamageSource::Collision);
}
// maybe print Collision on HUD
if ( ship_objp == Player_obj ) {
hud_start_text_flash(XSTR("Collision", 1431), 2000);
}
collide_ship_ship_do_sound(&hitpos, ship_objp, asteroid_objp, ship_objp==Player_obj);
}
if (scripting::hooks::OnAsteroidCollision->isActive() && !(asteroid_override && !ship_override)) {
scripting::hooks::OnAsteroidCollision->run(scripting::hooks::CollisionConditions{ {ship_objp, asteroid_objp} },
scripting::hook_param_list(scripting::hook_param("Self", 'o', ship_objp),
scripting::hook_param("Object", 'o', asteroid_objp),
scripting::hook_param("Ship", 'o', ship_objp),
scripting::hook_param("Asteroid", 'o', asteroid_objp),
scripting::hook_param("Hitpos", 'o', hitpos)));
}
if (scripting::hooks::OnShipCollision->isActive() && ((asteroid_override && !ship_override) || (!asteroid_override && !ship_override)))
{
scripting::hooks::OnShipCollision->run(scripting::hooks::CollisionConditions{ {ship_objp, asteroid_objp} },
scripting::hook_param_list(scripting::hook_param("Self", 'o', asteroid_objp),
scripting::hook_param("Object", 'o', ship_objp),
scripting::hook_param("Ship", 'o', ship_objp),
scripting::hook_param("Asteroid", 'o', asteroid_objp),
scripting::hook_param("Hitpos", 'o', hitpos),
scripting::hook_param("ShipSubmodel", 'o', scripting::api::l_Submodel.Set(smh), has_submodel && (asteroid_hit_info.heavy == ship_objp))));
}
return 0;
}
return 0;
} else {
// estimate earliest time at which pair can hit
float asteroid_max_speed, ship_max_speed, time;
asteroid_max_speed = vm_vec_mag(&asteroid_objp->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(ship_objp)) {
ship_max_speed = MAX(ship_get_max_speed(shipp), ship_get_warpout_speed(ship_objp));
} 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, ship_objp->phys_info.vel.xyz.z);
time = 1000.0f * (dist - ship_objp->radius - asteroid_objp->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;
}
}
/**
* Checks debris-prop collisions.
* @param pair obj_pair pointer to the two objects. pair->a is debris and pair->b is prop.
* @return 1 if all future collisions between these can be ignored
*/
int collide_debris_prop(obj_pair* pair)
{
float dist;
object* debris_objp = pair->a;
object* prop_objp = pair->b;
Assert(debris_objp->type == OBJ_DEBRIS);
Assert(prop_objp->type == OBJ_PROP);
if (reject_due_collision_groups(debris_objp, prop_objp))
return 0;
dist = vm_vec_dist(&debris_objp->pos, &prop_objp->pos);
if (dist < debris_objp->radius + prop_objp->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 (debris_objp->radius > prop_objp->radius) {
debris_hit_info.heavy = debris_objp;
debris_hit_info.light = prop_objp;
}
else {
debris_hit_info.heavy = prop_objp;
debris_hit_info.light = debris_objp;
}
hit = prop_check_collision(prop_objp, debris_objp, &hitpos, &debris_hit_info);
if (hit)
{
bool ship_override = false, debris_override = false;
// get submodel handle if scripting needs it
bool has_submodel = (debris_hit_info.heavy_submodel_num >= 0);
scripting::api::submodel_h smh(debris_hit_info.heavy_model_num, debris_hit_info.heavy_submodel_num);
if (scripting::hooks::OnDebrisCollision->isActive()) {
ship_override = scripting::hooks::OnDebrisCollision->isOverride(scripting::hooks::CollisionConditions{ {prop_objp, debris_objp} },
scripting::hook_param_list(scripting::hook_param("Self", 'o', prop_objp),
scripting::hook_param("Object", 'o', debris_objp),
scripting::hook_param("Prop", 'o', prop_objp),
scripting::hook_param("Debris", 'o', debris_objp),
scripting::hook_param("Hitpos", 'o', hitpos)));
}
if (scripting::hooks::OnPropCollision->isActive()) {
debris_override = scripting::hooks::OnPropCollision->isOverride(scripting::hooks::CollisionConditions{ {prop_objp, debris_objp} },
scripting::hook_param_list(scripting::hook_param("Self", 'o', debris_objp),
scripting::hook_param("Object", 'o', prop_objp),
scripting::hook_param("Prop", 'o', prop_objp),
scripting::hook_param("Debris", 'o', debris_objp),
scripting::hook_param("Hitpos", 'o', hitpos),
scripting::hook_param("PropSubmodel", 'o', scripting::api::l_Submodel.Set(smh), has_submodel && (debris_hit_info.heavy == prop_objp))));
}
if (!ship_override && !debris_override)
{
// do collision physics
calculate_ship_ship_collision_physics(&debris_hit_info);
if (debris_hit_info.impulse < 0.5f)
return 0;
float debris_damage = debris_hit_info.impulse / debris_objp->phys_info.mass; // ie, delta velocity of debris
// apply damage to debris
// no need for force, already handled in calculate_ship_ship_collision_physics
debris_hit(debris_objp, prop_objp, &hitpos, debris_damage, nullptr); // speed => damage
collide_ship_ship_do_sound(&hitpos, prop_objp, debris_objp,false );
}
return 0;
}
}
else { // Bounding spheres don't intersect, set timestamp for next collision check.
float debris_speed;
float time;
debris_speed = debris_objp->phys_info.speed;
time = 1000.0f * (dist - prop_objp->radius - debris_objp->radius - 10.0f) / (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-prop collisions.
* @param pair obj_pair pointer to the two objects. pair->a is asteroid and pair->b is prop.
* @return 1 if all future collisions between these can be ignored
*/
int collide_asteroid_prop(obj_pair* pair)
{
if (!Asteroids_enabled)
return 0;
float dist;
object* asteroid_objp = pair->a;
object* prop_objp = pair->b;
if (asteroid_objp->hull_strength < 0.0f)
return 0;
Assert(asteroid_objp->type == OBJ_ASTEROID);
Assert(prop_objp->type == OBJ_PROP);
dist = vm_vec_dist(&asteroid_objp->pos, &prop_objp->pos);
if (dist < asteroid_objp->radius + prop_objp->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 (asteroid_objp->radius > prop_objp->radius) {
asteroid_hit_info.heavy = asteroid_objp;
asteroid_hit_info.light = prop_objp;
}
else {
asteroid_hit_info.heavy = prop_objp;
asteroid_hit_info.light = asteroid_objp;
}
hit = prop_check_collision(prop_objp, asteroid_objp, &hitpos, &asteroid_hit_info);
if (hit)
{
bool ship_override = false, asteroid_override = false;
// get submodel handle if scripting needs it
bool has_submodel = (asteroid_hit_info.heavy_submodel_num >= 0);
scripting::api::submodel_h smh(asteroid_hit_info.heavy_model_num, asteroid_hit_info.heavy_submodel_num);
//Scripting support
if (scripting::hooks::OnAsteroidCollision->isActive()) {
ship_override = scripting::hooks::OnAsteroidCollision->isOverride(scripting::hooks::CollisionConditions{ {prop_objp, asteroid_objp} },
scripting::hook_param_list(scripting::hook_param("Self", 'o', prop_objp),
scripting::hook_param("Object", 'o', asteroid_objp),
scripting::hook_param("Prop", 'o', prop_objp),
scripting::hook_param("Asteroid", 'o', asteroid_objp),
scripting::hook_param("Hitpos", 'o', hitpos)));
}
if (scripting::hooks::OnPropCollision->isActive()) {
asteroid_override = scripting::hooks::OnPropCollision->isOverride(scripting::hooks::CollisionConditions{ {prop_objp, asteroid_objp} },
scripting::hook_param_list(scripting::hook_param("Self", 'o', asteroid_objp),
scripting::hook_param("Object", 'o', prop_objp),
scripting::hook_param("Prop", 'o', prop_objp),
scripting::hook_param("Asteroid", 'o', asteroid_objp),
scripting::hook_param("Hitpos", 'o', hitpos),
scripting::hook_param("PropSubmodel", 'o', scripting::api::l_Submodel.Set(smh), has_submodel && (asteroid_hit_info.heavy == prop_objp))));
}
if (!ship_override && !asteroid_override)
{
float asteroid_damage;
//vec3d asteroid_vel = asteroid_objp->phys_info.vel;
// do collision physics
calculate_ship_ship_collision_physics(&asteroid_hit_info);
if (asteroid_hit_info.impulse < 0.5f)
return 0;
asteroid_damage = asteroid_hit_info.impulse / asteroid_objp->phys_info.mass; // ie, delta velocity of asteroid
// apply damage to asteroid
asteroid_hit(asteroid_objp, prop_objp, &hitpos, asteroid_damage, nullptr); // speed => damage
collide_ship_ship_do_sound(&hitpos, prop_objp, asteroid_objp, false);
}
return 0;
}
return 0;
}
else {
// estimate earliest time at which pair can hit
float asteroid_max_speed, time;
asteroid_max_speed = vm_vec_mag(&asteroid_objp->phys_info.vel); // Asteroid... vel gets reset, not max vel.z
asteroid_max_speed = MAX(asteroid_max_speed, 10.0f);
time = 1000.0f * (dist - prop_objp->radius - asteroid_objp->radius - 10.0f) / (asteroid_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;
}
}