mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-10 17:46:53 -10:00
There is a bit of code that detonates a missile if it misses impacting on a subsystem. On Nyctaeus's Arachnas model, missiles could reach the center point of the main beam turret submodel without actually hitting any part of the submodel. The fail-safe code would set the missile to detonate in that case, but on low-FPS systems the weapon would be able to actually hit the turret before it detonated. On high-FPS systems, there is a clean miss. This reworks the code to delay the missile detonation until it reaches the other side of the submodel, so it has time to hit some other part of it. It also reduces the detonation time from 1/100 of a second (which is more than one frame on 120 FPS systems) to 1/1000 of a second (which is always less than one frame).
214 lines
7.7 KiB
C++
214 lines
7.7 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 "freespace.h"
|
|
#include "network/multi.h"
|
|
#include "object/objcollide.h"
|
|
#include "object/object.h"
|
|
#include "scripting/scripting.h"
|
|
#include "scripting/api/objs/vecmath.h"
|
|
#include "ship/ship.h"
|
|
#include "stats/scoring.h"
|
|
#include "weapon/weapon.h"
|
|
|
|
|
|
/**
|
|
* Checks weapon-weapon collisions.
|
|
* @param pair obj_pair pointer to the two objects. pair->a and pair->b are weapons.
|
|
* @return 1 if all future collisions between these can be ignored
|
|
*/
|
|
int collide_weapon_weapon( obj_pair * pair )
|
|
{
|
|
float A_radius, B_radius;
|
|
object *A = pair->a;
|
|
object *B = pair->b;
|
|
|
|
Assert( A->type == OBJ_WEAPON );
|
|
Assert( B->type == OBJ_WEAPON );
|
|
|
|
// Don't allow ship to shoot down its own missile.
|
|
if (A->parent_sig == B->parent_sig)
|
|
return 1;
|
|
|
|
// Only shoot down teammate's missile if not traveling in nearly same direction.
|
|
if (Weapons[A->instance].team == Weapons[B->instance].team)
|
|
if (vm_vec_dot(&A->orient.vec.fvec, &B->orient.vec.fvec) > 0.7f)
|
|
return 1;
|
|
|
|
// Ignore collisions involving a bomb if the bomb is not yet armed.
|
|
weapon *wpA, *wpB;
|
|
weapon_info *wipA, *wipB;
|
|
|
|
wpA = &Weapons[A->instance];
|
|
wpB = &Weapons[B->instance];
|
|
wipA = &Weapon_info[wpA->weapon_info_index];
|
|
wipB = &Weapon_info[wpB->weapon_info_index];
|
|
|
|
A_radius = A->radius;
|
|
B_radius = B->radius;
|
|
|
|
if (wipA->weapon_hitpoints > 0) {
|
|
if (!(wipA->wi_flags[Weapon::Info_Flags::No_radius_doubling])) {
|
|
A_radius *= 2; // Makes bombs easier to hit
|
|
}
|
|
|
|
if ((The_mission.ai_profile->flags[AI::Profile_Flags::Aspect_invulnerability_fix]) && (wipA->is_locked_homing()) && (wpA->homing_object != &obj_used_list)) {
|
|
if ( (wipA->max_lifetime - wpA->lifeleft) < The_mission.ai_profile->delay_bomb_arm_timer[Game_skill_level] )
|
|
return 0;
|
|
}
|
|
else if ( (wipA->lifetime - wpA->lifeleft) < The_mission.ai_profile->delay_bomb_arm_timer[Game_skill_level] )
|
|
return 0;
|
|
}
|
|
|
|
if (wipB->weapon_hitpoints > 0) {
|
|
if (!(wipB->wi_flags[Weapon::Info_Flags::No_radius_doubling])) {
|
|
B_radius *= 2; // Makes bombs easier to hit
|
|
}
|
|
if ((The_mission.ai_profile->flags[AI::Profile_Flags::Aspect_invulnerability_fix]) && (wipB->is_locked_homing()) && (wpB->homing_object != &obj_used_list)) {
|
|
if ( (wipB->max_lifetime - wpB->lifeleft) < The_mission.ai_profile->delay_bomb_arm_timer[Game_skill_level] )
|
|
return 0;
|
|
}
|
|
else if ( (wipB->lifetime - wpB->lifeleft) < The_mission.ai_profile->delay_bomb_arm_timer[Game_skill_level] )
|
|
return 0;
|
|
}
|
|
|
|
// Rats, do collision detection.
|
|
if (collide_subdivide(&A->last_pos, &A->pos, A_radius, &B->last_pos, &B->pos, B_radius))
|
|
{
|
|
bool a_override = false, b_override = false;
|
|
|
|
if (Script_system.IsActiveAction(CHA_COLLIDEWEAPON)) {
|
|
Script_system.SetHookObjects(4, "Self", A, "Object", B, "Weapon", A, "WeaponB", B);
|
|
Script_system.SetHookVar("Hitpos", 'o', scripting::api::l_Vector.Set(B->pos));
|
|
a_override = Script_system.IsConditionOverride(CHA_COLLIDEWEAPON, A);
|
|
Script_system.RemHookVars({"Self", "Object", "Weapon", "WeaponB", "Hitpos" });
|
|
|
|
// Yes, this should be reversed.
|
|
Script_system.SetHookObjects(4, "Self", B, "Object", A, "Weapon", B, "WeaponB", A);
|
|
Script_system.SetHookVar("Hitpos", 'o', scripting::api::l_Vector.Set(A->pos));
|
|
b_override = Script_system.IsConditionOverride(CHA_COLLIDEWEAPON, B);
|
|
Script_system.RemHookVars({ "Self", "Object", "Weapon", "WeaponB", "Hitpos" });
|
|
}
|
|
|
|
// damage calculation should not be done on clients, the server will tell the client version of the bomb when to die
|
|
if(!a_override && !b_override && !MULTIPLAYER_CLIENT)
|
|
{
|
|
float aDamage = wipA->damage;
|
|
if (wipB->armor_type_idx >= 0)
|
|
aDamage = Armor_types[wipB->armor_type_idx].GetDamage(aDamage, wipA->damage_type_idx, 1.0f);
|
|
|
|
float bDamage = wipB->damage;
|
|
if (wipA->armor_type_idx >= 0)
|
|
bDamage = Armor_types[wipA->armor_type_idx].GetDamage(bDamage, wipB->damage_type_idx, 1.0f);
|
|
|
|
if (wipA->weapon_hitpoints > 0) {
|
|
if (wipB->weapon_hitpoints > 0) { // Two bombs collide, detonate both.
|
|
if ((wipA->wi_flags[Weapon::Info_Flags::Bomb]) && (wipB->wi_flags[Weapon::Info_Flags::Bomb])) {
|
|
wpA->lifeleft = 0.001f;
|
|
wpA->weapon_flags.set(Weapon::Weapon_Flags::Begun_detonation);
|
|
wpA->weapon_flags.set(Weapon::Weapon_Flags::Destroyed_by_weapon);
|
|
wpB->lifeleft = 0.001f;
|
|
wpB->weapon_flags.set(Weapon::Weapon_Flags::Begun_detonation);
|
|
wpB->weapon_flags.set(Weapon::Weapon_Flags::Destroyed_by_weapon);
|
|
} else {
|
|
A->hull_strength -= bDamage;
|
|
B->hull_strength -= aDamage;
|
|
|
|
// safety to make sure either of the weapons die - allow 'bulkier' to keep going
|
|
if ((A->hull_strength > 0.0f) && (B->hull_strength > 0.0f)) {
|
|
if (wipA->weapon_hitpoints > wipB->weapon_hitpoints) {
|
|
B->hull_strength = -1.0f;
|
|
} else {
|
|
A->hull_strength = -1.0f;
|
|
}
|
|
}
|
|
|
|
if (A->hull_strength < 0.0f) {
|
|
wpA->lifeleft = 0.001f;
|
|
wpA->weapon_flags.set(Weapon::Weapon_Flags::Begun_detonation);
|
|
wpA->weapon_flags.set(Weapon::Weapon_Flags::Destroyed_by_weapon);
|
|
}
|
|
if (B->hull_strength < 0.0f) {
|
|
wpB->lifeleft = 0.001f;
|
|
wpB->weapon_flags.set(Weapon::Weapon_Flags::Begun_detonation);
|
|
wpB->weapon_flags.set(Weapon::Weapon_Flags::Destroyed_by_weapon);
|
|
}
|
|
}
|
|
} else {
|
|
A->hull_strength -= bDamage;
|
|
wpB->lifeleft = 0.001f;
|
|
wpB->weapon_flags.set(Weapon::Weapon_Flags::Begun_detonation);
|
|
wpB->weapon_flags.set(Weapon::Weapon_Flags::Destroyed_by_weapon);
|
|
if (A->hull_strength < 0.0f) {
|
|
wpA->lifeleft = 0.001f;
|
|
wpA->weapon_flags.set(Weapon::Weapon_Flags::Begun_detonation);
|
|
wpA->weapon_flags.set(Weapon::Weapon_Flags::Destroyed_by_weapon);
|
|
}
|
|
}
|
|
} else if (wipB->weapon_hitpoints > 0) {
|
|
B->hull_strength -= aDamage;
|
|
wpA->lifeleft = 0.001f;
|
|
wpA->weapon_flags.set(Weapon::Weapon_Flags::Begun_detonation);
|
|
wpA->weapon_flags.set(Weapon::Weapon_Flags::Destroyed_by_weapon);
|
|
if (B->hull_strength < 0.0f) {
|
|
wpB->lifeleft = 0.001f;
|
|
wpB->weapon_flags.set(Weapon::Weapon_Flags::Begun_detonation);
|
|
wpB->weapon_flags.set(Weapon::Weapon_Flags::Destroyed_by_weapon);
|
|
}
|
|
}
|
|
|
|
// single player and multiplayer masters evaluate the scoring and kill stuff
|
|
if (!MULTIPLAYER_CLIENT) {
|
|
|
|
// If bomb was destroyed, do scoring
|
|
if (wipA->wi_flags[Weapon::Info_Flags::Bomb]) {
|
|
//Update stats. -Halleck
|
|
scoring_eval_hit(A, B, 0);
|
|
if (wpA->weapon_flags[Weapon::Weapon_Flags::Destroyed_by_weapon]) {
|
|
scoring_eval_kill_on_weapon(A, B);
|
|
}
|
|
}
|
|
if (wipB->wi_flags[Weapon::Info_Flags::Bomb]) {
|
|
//Update stats. -Halleck
|
|
scoring_eval_hit(B, A, 0);
|
|
if (wpB->weapon_flags[Weapon::Weapon_Flags::Destroyed_by_weapon]) {
|
|
scoring_eval_kill_on_weapon(B, A);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!Script_system.IsActiveAction(CHA_COLLIDEWEAPON)) {
|
|
return 1;
|
|
}
|
|
|
|
if(!(b_override && !a_override))
|
|
{
|
|
Script_system.SetHookObjects(4, "Self", A, "Object", B, "Weapon", A, "WeaponB", B);
|
|
Script_system.SetHookVar("Hitpos", 'o', scripting::api::l_Vector.Set(B->pos));
|
|
Script_system.RunCondition(CHA_COLLIDEWEAPON, A, wpA->weapon_info_index);
|
|
Script_system.RemHookVars({ "Self", "Object", "Weapon", "WeaponB", "Hitpos" });
|
|
}
|
|
else
|
|
{
|
|
// Yes, this should be reversed.
|
|
Script_system.SetHookObjects(4, "Self", B, "Object", A, "Weapon", B, "WeaponB", A);
|
|
Script_system.SetHookVar("Hitpos", 'o', scripting::api::l_Vector.Set(A->pos));
|
|
Script_system.RunCondition(CHA_COLLIDEWEAPON, B, wpB->weapon_info_index);
|
|
Script_system.RemHookVars({ "Self", "Object", "Weapon", "WeaponB", "Hitpos" });
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|