mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-07 16:18:19 -10:00
#6248 added an extra subtraction line to calculating when a bomb can be shot down `extra_buggy_time`, but that value should only be calculated if `Aspect_invulnerability_fix` is off. Specifically, the `else if` checks on lines 79 and 97 of `collideweaponweapon.cpp` were getting hit even when `Aspect_invulnerability_fix` was enabled, so ensure that if that flag is on that it correctly keeps the `extra_buggy_time` at 0. Fix works as expected, happy to edit or tune however.
308 lines
12 KiB
C++
308 lines
12 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 "math/curve.h"
|
|
#include "network/multi.h"
|
|
#include "object/objcollide.h"
|
|
#include "object/object.h"
|
|
#include "scripting/global_hooks.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;
|
|
|
|
float dot = vm_vec_dot(&A->orient.vec.fvec, &B->orient.vec.fvec);
|
|
|
|
// Only shoot down teammate's missile if not traveling in nearly same direction.
|
|
if (Weapons[A->instance].team == Weapons[B->instance].team)
|
|
if (dot > 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;
|
|
|
|
float A_time_alive = f2fl(Missiontime - wpA->creation_time);
|
|
float B_time_alive = f2fl(Missiontime - wpB->creation_time);
|
|
|
|
if (wipA->weapon_hitpoints > 0) {
|
|
if (!(wipA->wi_flags[Weapon::Info_Flags::No_radius_doubling])) {
|
|
A_radius *= 2; // Makes bombs easier to hit
|
|
}
|
|
|
|
// the erroneous extra time a bomb stays invulnerable without the fix
|
|
float extra_buggy_time = 0.0f;
|
|
if (!(The_mission.ai_profile->flags[AI::Profile_Flags::Aspect_invulnerability_fix]) && wipA->is_locked_homing())
|
|
extra_buggy_time = (wipA->lifetime * LOCKED_HOMING_EXTENDED_LIFE_FACTOR) - wipA->lifetime;
|
|
|
|
if ((The_mission.ai_profile->flags[AI::Profile_Flags::Aspect_invulnerability_fix]) && (wipA->is_locked_homing()) && (wpA->homing_object != &obj_used_list)) {
|
|
if (A_time_alive < The_mission.ai_profile->delay_bomb_arm_timer[Game_skill_level] )
|
|
return 0;
|
|
}
|
|
else if (A_time_alive - extra_buggy_time < 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
|
|
}
|
|
|
|
// the erroneous extra time a bomb stays invulnerable without the fix
|
|
float extra_buggy_time = 0.0f;
|
|
if (!(The_mission.ai_profile->flags[AI::Profile_Flags::Aspect_invulnerability_fix]) && wipB->is_locked_homing())
|
|
extra_buggy_time = (wipB->lifetime * LOCKED_HOMING_EXTENDED_LIFE_FACTOR) - wipB->lifetime;
|
|
|
|
if ((The_mission.ai_profile->flags[AI::Profile_Flags::Aspect_invulnerability_fix]) && (wipB->is_locked_homing()) && (wpB->homing_object != &obj_used_list)) {
|
|
if (B_time_alive < The_mission.ai_profile->delay_bomb_arm_timer[Game_skill_level] )
|
|
return 0;
|
|
}
|
|
else if (B_time_alive - extra_buggy_time < 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 (scripting::hooks::OnWeaponCollision->isActive()) {
|
|
a_override = scripting::hooks::OnWeaponCollision->isOverride(scripting::hooks::CollisionConditions{ {A, B} },
|
|
scripting::hook_param_list(scripting::hook_param("Self", 'o', A),
|
|
scripting::hook_param("Object", 'o', B),
|
|
scripting::hook_param("Weapon", 'o', A),
|
|
scripting::hook_param("WeaponB", 'o', B),
|
|
scripting::hook_param("Hitpos", 'o', B->pos)));
|
|
//Yes, this should be reversed
|
|
b_override = scripting::hooks::OnWeaponCollision->isOverride(scripting::hooks::CollisionConditions{ {A, B} },
|
|
scripting::hook_param_list(scripting::hook_param("Self", 'o', B),
|
|
scripting::hook_param("Object", 'o', A),
|
|
scripting::hook_param("Weapon", 'o', B),
|
|
scripting::hook_param("WeaponB", 'o', A),
|
|
scripting::hook_param("Hitpos", 'o', A->pos)));
|
|
}
|
|
|
|
// 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 dot_curve = -dot;
|
|
float aDamage = wipA->damage;
|
|
aDamage *= wipA->weapon_hit_curves.get_output(weapon_info::WeaponHitCurveOutputs::DAMAGE_MULT, std::forward_as_tuple(*wpA, *B, dot_curve), &wpA->modular_curves_instance);
|
|
aDamage *= wipA->weapon_hit_curves.get_output(weapon_info::WeaponHitCurveOutputs::HULL_DAMAGE_MULT, std::forward_as_tuple(*wpA, *B, dot_curve), &wpA->modular_curves_instance);
|
|
if (wipB->armor_type_idx >= 0)
|
|
aDamage = Armor_types[wipB->armor_type_idx].GetDamage(aDamage, wipA->damage_type_idx, 1.0f, false);
|
|
|
|
float bDamage = wipB->damage;
|
|
bDamage *= wipB->weapon_hit_curves.get_output(weapon_info::WeaponHitCurveOutputs::DAMAGE_MULT, std::forward_as_tuple(*wpB, *A, dot_curve), &wpB->modular_curves_instance);
|
|
bDamage *= wipB->weapon_hit_curves.get_output(weapon_info::WeaponHitCurveOutputs::HULL_DAMAGE_MULT, std::forward_as_tuple(*wpB, *A, dot_curve), &wpB->modular_curves_instance);
|
|
if (wipA->armor_type_idx >= 0)
|
|
bDamage = Armor_types[wipA->armor_type_idx].GetDamage(bDamage, wipB->damage_type_idx, 1.0f, false);
|
|
|
|
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->weapon_flags.set(Weapon::Weapon_Flags::Destroyed_by_weapon);
|
|
std::array<std::optional<ConditionData>, NumHitTypes> impact_data_b = {};
|
|
impact_data_b[static_cast<std::underlying_type_t<HitType>>(HitType::HULL)] = ConditionData {
|
|
ImpactCondition(wipB->armor_type_idx),
|
|
HitType::HULL,
|
|
aDamage,
|
|
B->hull_strength,
|
|
i2fl(wipB->weapon_hitpoints),
|
|
};
|
|
bool a_armed = weapon_hit(A, B, &A->pos, -1);
|
|
maybe_play_conditional_impacts(impact_data_b, A, B, a_armed, -1, &A->pos);
|
|
wpB->weapon_flags.set(Weapon::Weapon_Flags::Destroyed_by_weapon);
|
|
std::array<std::optional<ConditionData>, NumHitTypes> impact_data_a = {};
|
|
impact_data_a[static_cast<std::underlying_type_t<HitType>>(HitType::HULL)] = ConditionData {
|
|
ImpactCondition(wipA->armor_type_idx),
|
|
HitType::HULL,
|
|
bDamage,
|
|
A->hull_strength,
|
|
i2fl(wipA->weapon_hitpoints),
|
|
};
|
|
bool b_armed = weapon_hit(B, A, &B->pos, -1);
|
|
maybe_play_conditional_impacts(impact_data_a, B, A, b_armed, -1, &B->pos);
|
|
} 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->weapon_flags.set(Weapon::Weapon_Flags::Destroyed_by_weapon);
|
|
std::array<std::optional<ConditionData>, NumHitTypes> impact_data_b = {};
|
|
impact_data_b[static_cast<std::underlying_type_t<HitType>>(HitType::HULL)] = ConditionData {
|
|
ImpactCondition(wipB->armor_type_idx),
|
|
HitType::HULL,
|
|
aDamage,
|
|
B->hull_strength,
|
|
i2fl(wipB->weapon_hitpoints),
|
|
};
|
|
bool a_armed = weapon_hit(A, B, &A->pos, -1);
|
|
maybe_play_conditional_impacts(impact_data_b, A, B, a_armed, -1, &A->pos);
|
|
}
|
|
if (B->hull_strength < 0.0f) {
|
|
wpB->weapon_flags.set(Weapon::Weapon_Flags::Destroyed_by_weapon);
|
|
std::array<std::optional<ConditionData>, NumHitTypes> impact_data_a = {};
|
|
impact_data_a[static_cast<std::underlying_type_t<HitType>>(HitType::HULL)] = ConditionData {
|
|
ImpactCondition(wipA->armor_type_idx),
|
|
HitType::HULL,
|
|
bDamage,
|
|
A->hull_strength,
|
|
i2fl(wipA->weapon_hitpoints),
|
|
};
|
|
bool b_armed = weapon_hit(B, A, &B->pos, -1);
|
|
maybe_play_conditional_impacts(impact_data_a, B, A, b_armed, -1, &B->pos);
|
|
}
|
|
}
|
|
} else {
|
|
A->hull_strength -= bDamage;
|
|
wpB->weapon_flags.set(Weapon::Weapon_Flags::Destroyed_by_weapon);
|
|
std::array<std::optional<ConditionData>, NumHitTypes> impact_data_a = {};
|
|
impact_data_a[static_cast<std::underlying_type_t<HitType>>(HitType::HULL)] = ConditionData {
|
|
ImpactCondition(wipA->armor_type_idx),
|
|
HitType::HULL,
|
|
bDamage,
|
|
A->hull_strength,
|
|
i2fl(wipA->weapon_hitpoints),
|
|
};
|
|
bool b_armed = weapon_hit(B, A, &B->pos, -1);
|
|
maybe_play_conditional_impacts(impact_data_a, B, A, b_armed, -1, &B->pos);
|
|
if (A->hull_strength < 0.0f) {
|
|
wpA->weapon_flags.set(Weapon::Weapon_Flags::Destroyed_by_weapon);
|
|
std::array<std::optional<ConditionData>, NumHitTypes> impact_data_b = {};
|
|
impact_data_b[static_cast<std::underlying_type_t<HitType>>(HitType::HULL)] = ConditionData {
|
|
ImpactCondition(wipB->armor_type_idx),
|
|
HitType::HULL,
|
|
aDamage,
|
|
B->hull_strength,
|
|
i2fl(wipB->weapon_hitpoints),
|
|
};
|
|
bool a_armed = weapon_hit(A, B, &A->pos, -1);
|
|
maybe_play_conditional_impacts(impact_data_b, A, B, a_armed, -1, &A->pos);
|
|
}
|
|
}
|
|
} else if (wipB->weapon_hitpoints > 0) {
|
|
B->hull_strength -= aDamage;
|
|
wpA->weapon_flags.set(Weapon::Weapon_Flags::Destroyed_by_weapon);
|
|
std::array<std::optional<ConditionData>, NumHitTypes> impact_data_b = {};
|
|
impact_data_b[0] = ConditionData {
|
|
ImpactCondition(wipB->armor_type_idx),
|
|
HitType::HULL,
|
|
aDamage,
|
|
B->hull_strength,
|
|
i2fl(wipB->weapon_hitpoints),
|
|
};
|
|
bool a_armed = weapon_hit(A, B, &A->pos, -1);
|
|
maybe_play_conditional_impacts(impact_data_b, A, B, a_armed, -1, &A->pos);
|
|
if (B->hull_strength < 0.0f) {
|
|
wpB->weapon_flags.set(Weapon::Weapon_Flags::Destroyed_by_weapon);
|
|
std::array<std::optional<ConditionData>, NumHitTypes> impact_data_a = {};
|
|
impact_data_a[0] = ConditionData {
|
|
ImpactCondition(wipA->armor_type_idx),
|
|
HitType::HULL,
|
|
bDamage,
|
|
A->hull_strength,
|
|
i2fl(wipA->weapon_hitpoints),
|
|
};
|
|
bool b_armed = weapon_hit(B, A, &B->pos, -1);
|
|
maybe_play_conditional_impacts(impact_data_a, B, A, b_armed, -1, &B->pos);
|
|
}
|
|
}
|
|
|
|
// 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 (!scripting::hooks::OnWeaponCollision->isActive()) {
|
|
return 1;
|
|
}
|
|
|
|
if(!(b_override && !a_override))
|
|
{
|
|
scripting::hooks::OnWeaponCollision->run(scripting::hooks::CollisionConditions{ {A, B} },
|
|
scripting::hook_param_list(scripting::hook_param("Self", 'o', A),
|
|
scripting::hook_param("Object", 'o', B),
|
|
scripting::hook_param("Weapon", 'o', A),
|
|
scripting::hook_param("WeaponB", 'o', B),
|
|
scripting::hook_param("Hitpos", 'o', B->pos)));
|
|
}
|
|
else
|
|
{
|
|
// Yes, this should be reversed.
|
|
scripting::hooks::OnWeaponCollision->run(scripting::hooks::CollisionConditions{ {A, B} },
|
|
scripting::hook_param_list(scripting::hook_param("Self", 'o', B),
|
|
scripting::hook_param("Object", 'o', A),
|
|
scripting::hook_param("Weapon", 'o', B),
|
|
scripting::hook_param("WeaponB", 'o', A),
|
|
scripting::hook_param("Hitpos", 'o', A->pos)));
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|