Files
fs2open.github.com/code/object/collideweaponweapon.cpp

197 lines
6.9 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))
{
Script_system.SetHookObjects(4, "Self", A, "Object", B, "Weapon", A, "WeaponB", B);
Script_system.SetHookVar("Hitpos", 'o', scripting::api::l_Vector.Set(B->pos));
bool 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));
bool b_override = Script_system.IsConditionOverride(CHA_COLLIDEWEAPON, B);
Script_system.RemHookVars({ "Self", "Object", "Weapon", "WeaponB", "Hitpos" });
if(!a_override && !b_override)
{
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.01f;
wpB->lifeleft = 0.01f;
wpA->weapon_flags.set(Weapon::Weapon_Flags::Destroyed_by_weapon);
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.01f;
wpA->weapon_flags.set(Weapon::Weapon_Flags::Destroyed_by_weapon);
}
if (B->hull_strength < 0.0f) {
wpB->lifeleft = 0.01f;
wpB->weapon_flags.set(Weapon::Weapon_Flags::Destroyed_by_weapon);
}
}
} else {
A->hull_strength -= bDamage;
wpB->lifeleft = 0.01f;
wpB->weapon_flags.set(Weapon::Weapon_Flags::Destroyed_by_weapon);
if (A->hull_strength < 0.0f) {
wpA->lifeleft = 0.01f;
wpA->weapon_flags.set(Weapon::Weapon_Flags::Destroyed_by_weapon);
}
}
} else if (wipB->weapon_hitpoints > 0) {
B->hull_strength -= aDamage;
wpA->lifeleft = 0.01f;
wpA->weapon_flags.set(Weapon::Weapon_Flags::Destroyed_by_weapon);
if (B->hull_strength < 0.0f) {
wpB->lifeleft = 0.01f;
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(!(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;
}