Enhanced functionality for conditional impacts (#6785)

* add new conditions

* don't worry about it

* implement new conditions

* bugfixing

* handle pokethrough differently

* clang for the clang god

* appeasement

* to the last i grapple with thee

* from hell's heart etc

* bugfixing

* support hits while dying

* fix shield math, cleanup

* fix memory error
This commit is contained in:
Kestrellius authored and GitHub committed 2025-07-12 11:40:53 +02:00
1 parent 06eaf94cb3
commit 6921f550c1
11 files changed
+568 -224

No files matched your search

+2
View File
@@ -705,6 +705,8 @@ object *debris_create_only(int parent_objnum, int parent_ship_class, int alt_typ
}
}
db->max_hull = obj->hull_strength;
if (hull_flag) {
MONITOR_INC(NumHullDebris,1);
} else {
+1
View File
@@ -51,6 +51,7 @@ typedef struct debris {
int submodel_num; // What submodel this uses
TIMESTAMP arc_next_time; // When the next damage/emp arc will be created.
bool is_hull; // indicates whether this is a collideable, destructable piece of debris from the model, or just a generic debris fragment
float max_hull;
int species; // What species this is from. -1 if don't care.
TIMESTAMP arc_timeout; // timestamp that holds time for arcs to stop appearing
TIMESTAMP sound_delay; // timestamp to signal when sound should start
+2 -1
View File
@@ -6997,7 +6997,8 @@ void process_asteroid_info( ubyte *data, header *hinfo )
// if we know the other object is a weapon, then do a weapon hit to kill the weapon
if ( other_objp && (other_objp->type == OBJ_WEAPON) ){
weapon_hit( other_objp, objp, &hitpos );
bool armed = weapon_hit( other_objp, objp, &hitpos );
maybe_play_conditional_impacts({}, other_objp, objp, armed, -1, &hitpos);
}
break;
}
+24 -4
View File
@@ -68,8 +68,18 @@ int collide_debris_weapon( obj_pair * pair )
if(!weapon_override && !debris_override)
{
vec3d force = weapon_obj->phys_info.vel * Weapon_info[Weapons[weapon_obj->instance].weapon_info_index].mass;
weapon_hit( weapon_obj, pdebris, &hitpos, -1, &hitnormal );
debris_hit( pdebris, weapon_obj, &hitpos, Weapon_info[Weapons[weapon_obj->instance].weapon_info_index].damage , &force);
bool armed = weapon_hit( weapon_obj, pdebris, &hitpos, -1 );
float damage = Weapon_info[Weapons[weapon_obj->instance].weapon_info_index].damage;
std::array<std::optional<ConditionData>, NumHitTypes> impact_data = {};
impact_data[static_cast<std::underlying_type_t<HitType>>(HitType::HULL)] = ConditionData {
SpecialImpactCondition::DEBRIS,
HitType::HULL,
damage,
pdebris->hull_strength,
Debris[pdebris->instance].max_hull,
};
maybe_play_conditional_impacts(impact_data, weapon_obj, pdebris, armed, -1, &hitpos, nullptr, &hitnormal);
debris_hit( pdebris, weapon_obj, &hitpos, damage , &force);
}
if (scripting::hooks::OnDebrisCollision->isActive() && !(debris_override && !weapon_override))
@@ -147,8 +157,18 @@ int collide_asteroid_weapon( obj_pair * pair )
if(!weapon_override && !asteroid_override)
{
vec3d force = weapon_obj->phys_info.vel * Weapon_info[Weapons[weapon_obj->instance].weapon_info_index].mass;
weapon_hit( weapon_obj, pasteroid, &hitpos, -1, &hitnormal);
asteroid_hit( pasteroid, weapon_obj, &hitpos, Weapon_info[Weapons[weapon_obj->instance].weapon_info_index].damage, &force );
bool armed = weapon_hit( weapon_obj, pasteroid, &hitpos, -1);
float damage = Weapon_info[Weapons[weapon_obj->instance].weapon_info_index].damage;
std::array<std::optional<ConditionData>, NumHitTypes> impact_data = {};
impact_data[static_cast<std::underlying_type_t<HitType>>(HitType::HULL)] = ConditionData {
SpecialImpactCondition::DEBRIS,
HitType::HULL,
damage,
pasteroid->hull_strength,
Asteroid_info[Asteroids[pasteroid->instance].asteroid_type].initial_asteroid_strength,
};
maybe_play_conditional_impacts(impact_data, weapon_obj, pasteroid, armed, -1, &hitpos, nullptr, &hitnormal);
asteroid_hit( pasteroid, weapon_obj, &hitpos, damage, &force );
}
if (scripting::hooks::OnAsteroidCollision->isActive() && !(asteroid_override && !weapon_override))
+2 -2
View File
@@ -82,7 +82,7 @@ static void ship_weapon_do_hit_stuff(object *pship_obj, object *weapon_obj, cons
model_instance_local_to_global_dir(&worldNormal, hit_dir, pm, pmi, submodel_num, &pship_obj->orient);
// Apply hit & damage & stuff to weapon
weapon_hit(weapon_obj, pship_obj, world_hitpos, quadrant_num, &worldNormal, hitpos, submodel_num); //NOLINT(readability-suspicious-call-argument)
weapon_hit(weapon_obj, pship_obj, world_hitpos, quadrant_num); //NOLINT(readability-suspicious-call-argument)
if (wip->damage_time >= 0.0f && wp->lifeleft <= wip->damage_time) {
if (wip->atten_damage >= 0.0f) {
@@ -135,7 +135,7 @@ static void ship_weapon_do_hit_stuff(object *pship_obj, object *weapon_obj, cons
}
}
ship_apply_local_damage(pship_obj, weapon_obj, world_hitpos, damage, wip->damage_type_idx, quadrant_num, CREATE_SPARKS, submodel_num, nullptr, dot);
ship_apply_local_damage(pship_obj, weapon_obj, world_hitpos, damage, wip->damage_type_idx, quadrant_num, CREATE_SPARKS, submodel_num, &worldNormal, dot, hitpos); //NOLINT(readability-suspicious-call-argument)
// let the hud shield gauge know when Player or Player target is hit
hud_shield_quadrant_hit(pship_obj, quadrant_num);
+80 -8
View File
@@ -139,9 +139,27 @@ int collide_weapon_weapon( obj_pair * pair )
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);
weapon_hit(A, B, &A->pos, -1, nullptr);
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);
weapon_hit(B, A, &B->pos, -1, nullptr);
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;
@@ -157,29 +175,83 @@ int collide_weapon_weapon( obj_pair * pair )
if (A->hull_strength < 0.0f) {
wpA->weapon_flags.set(Weapon::Weapon_Flags::Destroyed_by_weapon);
weapon_hit(A, B, &A->pos, -1, nullptr);
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);
weapon_hit(B, A, &B->pos, -1, nullptr);
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);
weapon_hit(B, A, &B->pos, -1, nullptr);
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);
weapon_hit(A, B, &A->pos, -1, nullptr);
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);
weapon_hit(A, B, &A->pos, -1, nullptr);
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);
weapon_hit(B, A, &B->pos, -1, nullptr);
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);
}
}
+97 -15
View File
@@ -662,7 +662,7 @@ void do_subobj_heal_stuff(const object* ship_objp, const object* other_obj, cons
//
//WMC - hull_should_apply armor means that the initial subsystem had no armor, so the hull should apply armor instead.
float do_subobj_hit_stuff(object *ship_objp, const object *other_obj, const vec3d *hitpos, int submodel_num, float damage, bool *hull_should_apply_armor, float hit_dot)
std::pair<std::optional<ConditionData>, float> do_subobj_hit_stuff(object *ship_objp, const object *other_obj, const vec3d *hitpos, int submodel_num, float damage, bool *hull_should_apply_armor, float hit_dot)
{
vec3d g_subobj_pos;
float damage_left, damage_if_hull;
@@ -687,10 +687,12 @@ float do_subobj_hit_stuff(object *ship_objp, const object *other_obj, const vec3
ship_p = &Ships[ship_objp->instance];
std::optional<ConditionData> subsys_impact = std::nullopt;
// Don't damage player subsystems in a training mission.
if ( The_mission.game_type & MISSION_TYPE_TRAINING ) {
if (ship_objp == Player_obj){
return damage;
return std::make_pair(subsys_impact, damage);
}
}
@@ -700,7 +702,7 @@ float do_subobj_hit_stuff(object *ship_objp, const object *other_obj, const vec3
// MK, 9/2/99. Shockwaves do zero subsystem damage on small ships.
// Goober5000 - added back in via flag
if ((Ship_info[ship_p->ship_info_index].is_small_ship()) && !(The_mission.ai_profile->flags[AI::Profile_Flags::Shockwaves_damage_small_ship_subsystems]))
return damage;
return std::make_pair(subsys_impact, damage);
else {
damage_left = shockwave_get_damage(other_obj->instance) / 4.0f;
damage_if_hull = damage_left;
@@ -719,7 +721,7 @@ float do_subobj_hit_stuff(object *ship_objp, const object *other_obj, const vec3
weapon_info* wip = &Weapon_info[weapon_info_index];
if ( wip->wi_flags[Weapon::Info_Flags::Training] ) {
return damage_left;
return std::make_pair(subsys_impact, damage_left);
}
damage_left *= wip->subsystem_factor;
@@ -731,7 +733,7 @@ float do_subobj_hit_stuff(object *ship_objp, const object *other_obj, const vec3
if (Beams_use_damage_factors) {
if ( wip->wi_flags[Weapon::Info_Flags::Training] ) {
return damage_left;
return std::make_pair(subsys_impact, damage_left);
}
damage_left *= wip->subsystem_factor;
damage_if_hull *= wip->armor_factor;
@@ -919,6 +921,7 @@ float do_subobj_hit_stuff(object *ship_objp, const object *other_obj, const vec3
}
}
}
// HORRIBLE HACK!
// MK, 9/4/99
// When Helios bombs are dual fired against the Juggernaut in sm3-01 (FS2), they often
@@ -1000,6 +1003,16 @@ float do_subobj_hit_stuff(object *ship_objp, const object *other_obj, const vec3
damage_to_apply *= ss_dif_scale;
}
if (j == 0) {
subsys_impact = ConditionData {
ImpactCondition(subsystem->armor_type_idx),
HitType::SUBSYS,
damage_to_apply,
subsystem->current_hits,
subsystem->max_hits,
};
}
subsystem->current_hits -= damage_to_apply;
if (!(subsystem->flags[Ship::Subsystem_Flags::No_aggregate])) {
ship_p->subsys_info[subsystem->system_info->type].aggregate_current_hits -= damage_to_apply;
@@ -1037,7 +1050,7 @@ float do_subobj_hit_stuff(object *ship_objp, const object *other_obj, const vec3
// It had taken a few MX-50s to destory an Anubis (with 40% hull), then it took maybe ten.
// So, I left it alone. -- MK, 4/15/98
return damage;
return std::make_pair(subsys_impact, damage);
}
// Store who/what killed the player, so we can tell the player how he died
@@ -2292,7 +2305,7 @@ static int maybe_shockwave_damage_adjust(const object *ship_objp, const object *
// Goober5000 - sanity checked this whole function in the case that other_obj is NULL, which
// will happen with the explosion-effect sexp
void ai_update_lethality(const object *ship_objp, const object *weapon_obj, float damage);
static void ship_do_damage(object *ship_objp, object *other_obj, const vec3d *hitpos, float damage, int quadrant, int submodel_num, int damage_type_idx = -1, bool wash_damage = false, float hit_dot = 1.f)
static void ship_do_damage(object *ship_objp, object *other_obj, const vec3d *hitpos, float damage, int quadrant, int submodel_num, int damage_type_idx = -1, bool wash_damage = false, float hit_dot = 1.f, const vec3d* hit_normal = nullptr, const vec3d* local_hitpos = nullptr)
{
// mprintf(("doing damage\n"));
@@ -2397,6 +2410,7 @@ static void ship_do_damage(object *ship_objp, object *other_obj, const vec3d *hi
}
}
std::array<std::optional<ConditionData>, NumHitTypes> impact_data = {};
// If the ship is invulnerable, do nothing
if (ship_objp->flags[Object::Object_Flags::Invulnerable]) {
@@ -2405,6 +2419,26 @@ static void ship_do_damage(object *ship_objp, object *other_obj, const vec3d *hi
// if ship is already dying, shorten deathroll.
if (shipp->flags[Ship::Ship_Flags::Dying]) {
if (quadrant >= 0 && !(ship_objp->flags[Object::Object_Flags::No_shields])) {
impact_data[static_cast<std::underlying_type_t<HitType>>(HitType::SHIELD)] = ConditionData {
ImpactCondition(shipp->shield_armor_type_idx),
HitType::SHIELD,
0.0f,
// we have to do this annoying thing where we reduce the shield health a bit because it turns out the last ten percent of a shield doesn't matter
MAX(0.0f, ship_objp->shield_quadrant[quadrant] - MAX(2.0f, 0.1f * shield_get_max_quad(ship_objp))),
shield_get_max_quad(ship_objp) - MAX(2.0f, 0.1f * shield_get_max_quad(ship_objp)),
};
} else {
impact_data[static_cast<std::underlying_type_t<HitType>>(HitType::HULL)] = ConditionData {
ImpactCondition(shipp->armor_type_idx),
HitType::HULL,
0.0f,
ship_objp->hull_strength,
shipp->ship_max_hull_strength,
};
}
maybe_play_conditional_impacts(impact_data, other_obj, ship_objp, true, submodel_num, hitpos, local_hitpos, hit_normal);
shiphit_hit_after_death(ship_objp, (damage * difficulty_scale_factor));
return;
}
@@ -2417,6 +2451,15 @@ static void ship_do_damage(object *ship_objp, object *other_obj, const vec3d *hi
// mprintf(("applying damage ge to shield\n"));
float shield_damage = damage * damage_scale;
auto shield_impact = ConditionData {
ImpactCondition(shipp->shield_armor_type_idx),
HitType::SHIELD,
0.0f,
// we have to do this annoying thing where we reduce the shield health a bit because it turns out the last ten percent of a shield doesn't matter
MAX(0.0f, ship_objp->shield_quadrant[quadrant] - MAX(2.0f, 0.1f * shield_get_max_quad(ship_objp))),
shield_get_max_quad(ship_objp) - MAX(2.0f, 0.1f * shield_get_max_quad(ship_objp)),
};
if ( damage > 0.0f ) {
float piercing_pct = 0.0f;
@@ -2435,20 +2478,23 @@ static void ship_do_damage(object *ship_objp, object *other_obj, const vec3d *hi
float shield_factor = 1.0f;
if (weapon_info_index >= 0 && (!other_obj_is_beam || Beams_use_damage_factors))
shield_factor = Weapon_info[weapon_info_index].shield_factor;
shield_impact.damage = shield_damage * shield_factor;
// apply shield damage
float remaining_damage = shield_apply_damage(ship_objp, quadrant, shield_damage * shield_factor);
// remove the shield factor, since the overflow will no longer be thrown at shields
remaining_damage /= shield_factor;
// Unless the backwards compatible flag is on, remove difficulty scaling as well
// The hull/subsystem code below will re-add it where necessary
if (!The_mission.ai_profile->flags[AI::Profile_Flags::Carry_shield_difficulty_scaling_bug])
remaining_damage /= difficulty_scale_factor;
remaining_damage /= difficulty_scale_factor;
// the rest of the damage is what overflowed from the shield damage and pierced
damage = remaining_damage + (damage * piercing_pct);
}
impact_data[static_cast<std::underlying_type_t<HitType>>(HitType::SHIELD)] = shield_impact;
}
// Apply leftover damage to the ship's subsystem and hull.
@@ -2456,7 +2502,11 @@ static void ship_do_damage(object *ship_objp, object *other_obj, const vec3d *hi
bool apply_hull_armor = true;
// apply damage to subsystems, and get back any remaining damage that needs to go to the hull
damage = do_subobj_hit_stuff(ship_objp, other_obj, hitpos, submodel_num, damage, &apply_hull_armor, hit_dot);
auto damage_pair = do_subobj_hit_stuff(ship_objp, other_obj, hitpos, submodel_num, damage, &apply_hull_armor, hit_dot);
damage = damage_pair.second;
impact_data[static_cast<std::underlying_type_t<HitType>>(HitType::SUBSYS)] = damage_pair.first;
// damage scaling doesn't apply to subsystems, but it does to the hull
damage *= damage_scale;
@@ -2511,6 +2561,14 @@ static void ship_do_damage(object *ship_objp, object *other_obj, const vec3d *hi
}
}
impact_data[static_cast<std::underlying_type_t<HitType>>(HitType::HULL)] = ConditionData {
ImpactCondition(shipp->armor_type_idx),
HitType::HULL,
damage,
ship_objp->hull_strength,
shipp->ship_max_hull_strength,
};
// multiplayer clients don't do damage
if (((Game_mode & GM_MULTIPLAYER) && MULTIPLAYER_CLIENT)) {
} else {
@@ -2623,6 +2681,8 @@ static void ship_do_damage(object *ship_objp, object *other_obj, const vec3d *hi
}
}
maybe_play_conditional_impacts(impact_data, other_obj, ship_objp, true, submodel_num, hitpos, local_hitpos, hit_normal);
// handle weapon and afterburner leeching here
if(other_obj_is_weapon || other_obj_is_beam) {
Assert(weapon_info_index >= 0);
@@ -2788,7 +2848,7 @@ void ship_apply_tag(ship *shipp, int tag_level, float tag_time, object *target,
// hitpos is in world coordinates.
// if quadrant is not -1, then that part of the shield takes damage properly.
// (it might be possible to make `other_obj` const, but that would set off another const-cascade)
void ship_apply_local_damage(object *ship_objp, object *other_obj, const vec3d *hitpos, float damage, int damage_type_idx, int quadrant, bool create_spark, int submodel_num, const vec3d *hit_normal, float hit_dot)
void ship_apply_local_damage(object *ship_objp, object *other_obj, const vec3d *hitpos, float damage, int damage_type_idx, int quadrant, bool create_spark, int submodel_num, const vec3d *hit_normal, float hit_dot, const vec3d* local_hitpos)
{
Assert(ship_objp); // Goober5000
Assert(other_obj); // Goober5000
@@ -2805,6 +2865,28 @@ void ship_apply_local_damage(object *ship_objp, object *other_obj, const vec3d *
// Ie, player can always do damage. AI can only damage team if that ship is targeted.
if (wp->target_num != OBJ_INDEX(ship_objp)) {
if ((ship_p->team == wp->team) && !(Objects[other_obj->parent].flags[Object::Object_Flags::Player_ship]) ) {
// need to play the impact effect(s) for the weapon if we have one, since we won't get the chance to do it later
// we won't account for subsystems; that's a lot of extra logic for little benefit in this edge case
std::array<std::optional<ConditionData>, NumHitTypes> impact_data = {};
if (quadrant >= 0 && !(ship_objp->flags[Object::Object_Flags::No_shields])) {
impact_data[static_cast<std::underlying_type_t<HitType>>(HitType::SHIELD)] = ConditionData {
ImpactCondition(ship_p->shield_armor_type_idx),
HitType::SHIELD,
0.0f,
// we have to do this annoying thing where we reduce the shield health a bit because it turns out the last ten percent of a shield doesn't matter
MAX(0.0f, ship_objp->shield_quadrant[quadrant] - MAX(2.0f, 0.1f * shield_get_max_quad(ship_objp))),
shield_get_max_quad(ship_objp) - MAX(2.0f, 0.1f * shield_get_max_quad(ship_objp)),
};
} else {
impact_data[static_cast<std::underlying_type_t<HitType>>(HitType::HULL)] = ConditionData {
ImpactCondition(ship_p->armor_type_idx),
HitType::HULL,
0.0f,
ship_objp->hull_strength,
ship_p->ship_max_hull_strength,
};
}
maybe_play_conditional_impacts(impact_data, other_obj, ship_objp, true, submodel_num, hitpos, local_hitpos, hit_normal);
return;
}
}
@@ -2862,9 +2944,9 @@ void ship_apply_local_damage(object *ship_objp, object *other_obj, const vec3d *
if (wip_index >= 0 && Weapon_info[wip_index].wi_flags[Weapon::Info_Flags::Heals]) {
ship_do_healing(ship_objp, other_obj, hitpos, damage, submodel_num);
create_sparks = false;
} else {
ship_do_damage(ship_objp, other_obj, hitpos, damage, quadrant, submodel_num, damage_type_idx, false, hit_dot, hit_normal, local_hitpos);
}
else
ship_do_damage(ship_objp, other_obj, hitpos, damage, quadrant, submodel_num, damage_type_idx, false, hit_dot);
// DA 5/5/98: move ship_hit_create_sparks() after do_damage() since number of sparks depends on hull strength
// doesn't hit shield and we want sparks
+3 -2
View File
@@ -7,6 +7,7 @@
*
*/
#include "weapon/weapon.h"
#ifndef _SHIPHIT_H
@@ -34,7 +35,7 @@ constexpr float DEATHROLL_ROTVEL_CAP = 6.3f; // maximum added deathroll rotve
// function to destroy a subsystem. Called internally and from multiplayer messaging code
extern void do_subobj_destroyed_stuff( ship *ship_p, ship_subsys *subsys, const vec3d *hitpos, bool no_explosion = false );
float do_subobj_hit_stuff(object *ship_obj, const object *other_obj, const vec3d *hitpos, int submodel_num, float damage, bool *hull_should_apply_armor, float hit_dot = 1.f);
std::pair<std::optional<ConditionData>, float> do_subobj_hit_stuff(object *ship_obj, const object *other_obj, const vec3d *hitpos, int submodel_num, float damage, bool *hull_should_apply_armor, float hit_dot = 1.f);
// Goober5000
// (it might be possible to make `target` const, but that would set off another const-cascade)
@@ -45,7 +46,7 @@ extern void ship_apply_tag(ship *ship_p, int tag_level, float tag_time, object *
// hitpos is in world coordinates.
// if quadrant is not -1, then that part of the shield takes damage properly.
// (it might be possible to make `other_obj` const, but that would set off another const-cascade)
void ship_apply_local_damage(object *ship_obj, object *other_obj, const vec3d *hitpos, float damage, int damage_type_idx, int quadrant, bool create_spark=true, int submodel_num=-1, const vec3d *hit_normal=nullptr, float hit_dot = 1.f);
void ship_apply_local_damage(object *ship_obj, object *other_obj, const vec3d *hitpos, float damage, int damage_type_idx, int quadrant, bool create_spark=true, int submodel_num=-1, const vec3d *hit_normal=nullptr, float hit_dot = 1.f, const vec3d* local_hitpos = nullptr);
// This gets called to apply damage when a damaging force hits a ship, but at no
// point in particular. Like from a shockwave. This routine will see if the
+6 -3
View File
@@ -4072,12 +4072,14 @@ void beam_handle_collisions(beam *b)
if (trgt->hull_strength < 0) {
Weapons[trgt->instance].weapon_flags.set(Weapon::Weapon_Flags::Destroyed_by_weapon);
weapon_hit(trgt, NULL, &trgt->pos);
bool armed = weapon_hit(trgt, nullptr, &trgt->pos);
maybe_play_conditional_impacts({}, trgt, nullptr, armed, -1, &trgt->pos);
}
} else {
if (!(Game_mode & GM_MULTIPLAYER) || MULTIPLAYER_MASTER) {
Weapons[trgt->instance].weapon_flags.set(Weapon::Weapon_Flags::Destroyed_by_weapon);
weapon_hit(&Objects[target], NULL, &Objects[target].pos);
bool armed = weapon_hit(trgt, nullptr, &trgt->pos);
maybe_play_conditional_impacts({}, trgt, nullptr, armed, -1, &trgt->pos);
}
}
@@ -4089,7 +4091,8 @@ void beam_handle_collisions(beam *b)
if (!(Game_mode & GM_MULTIPLAYER) || MULTIPLAYER_MASTER) {
Weapons[Objects[target].instance].weapon_flags.set(Weapon::Weapon_Flags::Destroyed_by_weapon);
weapon_hit(&Objects[target], NULL, &Objects[target].pos);
bool armed = weapon_hit(&Objects[target], nullptr, &Objects[target].pos);
maybe_play_conditional_impacts({}, &Objects[target], nullptr, armed, -1, &Objects[target].pos);
}
}
break;
+40 -6
View File
@@ -80,6 +80,7 @@ constexpr int BANK_SWITCH_DELAY = 250; // after switching banks, 1/4 second dela
// range. Check the comment in weapon_set_tracking_info() for more details
#define LOCKED_HOMING_EXTENDED_LIFE_FACTOR 1.2f
struct homing_cache_info {
TIMESTAMP next_update;
vec3d expected_pos;
@@ -291,13 +292,45 @@ enum class HomingAcquisitionType {
RANDOM,
};
enum class HitType {
SHIELD,
SUBSYS,
HULL,
NONE,
};
constexpr size_t NumHitTypes = static_cast<std::underlying_type_t<HitType>>(HitType::NONE);
enum class SpecialImpactCondition {
DEBRIS,
ASTEROID,
EMPTY_SPACE,
};
using ImpactCondition = std::variant<int, SpecialImpactCondition>;
struct ConditionData {
ImpactCondition condition = SpecialImpactCondition::EMPTY_SPACE;
HitType hit_type = HitType::NONE;
float damage = 0.0f;
float health = 1.0f;
float max_health = 1.0f;
};
struct ConditionalImpact {
particle::ParticleEffectHandle effect;
float min_health_threshold; //factor, 0-1
float max_health_threshold; //factor, 0-1
float min_angle_threshold; //in degrees
float max_angle_threshold; //in degrees
std::optional<particle::ParticleEffectHandle> pokethrough_effect;
::util::ParsedRandomFloatRange min_health_threshold; // factor, 0-1
::util::ParsedRandomFloatRange max_health_threshold; // factor, 0-1
::util::ParsedRandomFloatRange min_damage_hits_ratio; // factor
::util::ParsedRandomFloatRange max_damage_hits_ratio; // factor
::util::ParsedRandomFloatRange min_angle_threshold; // in degrees
::util::ParsedRandomFloatRange max_angle_threshold; // in degrees
float laser_pokethrough_threshold; // factor, 0-1
bool dinky;
bool disable_if_player_parent;
bool disable_on_subsys_passthrough;
bool disable_main_on_pokethrough;
};
enum class FiringPattern {
@@ -513,7 +546,7 @@ struct weapon_info
particle::ParticleEffectHandle piercing_impact_effect;
particle::ParticleEffectHandle piercing_impact_secondary_effect;
SCP_map<int, SCP_vector<ConditionalImpact>> conditional_impacts;
SCP_map<ImpactCondition, SCP_vector<ConditionalImpact>> conditional_impacts;
particle::ParticleEffectHandle muzzle_effect;
@@ -962,7 +995,8 @@ void weapon_set_tracking_info(int weapon_objnum, int parent_objnum, int target_o
size_t* get_pointer_to_weapon_fire_pattern_index(int weapon_type, int ship_idx, ship_subsys* src_turret);
bool weapon_armed(weapon *wp, bool hit_target);
void weapon_hit( object* weapon_obj, object* impacted_obj, const vec3d* hitpos, int quadrant = -1, const vec3d* hitnormal = nullptr, const vec3d* local_hitpos = nullptr, int submodel = -1 );
void maybe_play_conditional_impacts(const std::array<std::optional<ConditionData>, NumHitTypes>& impact_data, const object* weapon_objp, const object* impacted_objp, bool armed_weapon, int submodel, const vec3d* hitpos, const vec3d* local_hitpos = nullptr, const vec3d* hit_normal = nullptr);
bool weapon_hit( object* weapon_obj, object* impacted_obj, const vec3d* hitpos, int quadrant = -1 );
void spawn_child_weapons( object *objp, int spawn_index_override = -1);
// call to detonate a weapon. essentially calls weapon_hit() with other_obj as NULL, and sends a packet in multiplayer
+311 -183
View File
@@ -2380,41 +2380,79 @@ int parse_weapon(int subtype, bool replace, const char *filename)
}
while (optional_string("$Conditional Impact:")) {
int armor_index;
ImpactCondition impact_condition;
ConditionalImpact ci;
ci.min_health_threshold = std::numeric_limits<float>::lowest();
ci.max_health_threshold = std::numeric_limits<float>::max();
ci.min_angle_threshold = 0.f;
ci.max_angle_threshold = 180.f;
ci.min_health_threshold = ::util::UniformFloatRange(std::numeric_limits<float>::lowest());
ci.max_health_threshold = ::util::UniformFloatRange(std::numeric_limits<float>::max());
ci.min_damage_hits_ratio = ::util::UniformFloatRange(std::numeric_limits<float>::lowest());
ci.max_damage_hits_ratio = ::util::UniformFloatRange(std::numeric_limits<float>::max());
ci.min_angle_threshold = ::util::UniformFloatRange(0.f);
ci.max_angle_threshold = ::util::UniformFloatRange(180.f);
ci.laser_pokethrough_threshold = 0.1f;
ci.dinky = false;
ci.disable_if_player_parent = false;
ci.disable_on_subsys_passthrough = false;
ci.disable_main_on_pokethrough = false;
bool invalid_armor = false;
required_string("+Armor Type:");
if (optional_string("+Armor Type:")) {
stuff_string(fname, F_NAME, NAME_LENGTH);
if (!stricmp(fname, "NO ARMOR")) {
armor_index = -1;
} else {
armor_index = armor_type_get_idx(fname);
if (armor_index < 0) {
Warning(LOCATION, "Armor type '%s' not found for conditional impact in weapon %s!", fname, wip->name);
invalid_armor = true;
}
};
parse_optional_float_into("+Min Health Threshold:", &ci.min_health_threshold);
parse_optional_float_into("+Max Health Threshold:", &ci.max_health_threshold);
parse_optional_float_into("+Min Angle Threshold:", &ci.min_angle_threshold);
parse_optional_float_into("+Max Angle Threshold:", &ci.max_angle_threshold);
if (!stricmp(fname, "NO ARMOR")) {
impact_condition = -1;
} else {
impact_condition = armor_type_get_idx(fname);
if (std::holds_alternative<int>(impact_condition) && std::get<int>(impact_condition) < 0) {
Warning(LOCATION, "Armor type '%s' not found for conditional impact in weapon %s!", fname, wip->name);
invalid_armor = true;
}
};
} else if (optional_string("+Asteroid")) {
impact_condition = SpecialImpactCondition::ASTEROID;
} else if (optional_string("+Debris")) {
impact_condition = SpecialImpactCondition::DEBRIS;
} else if (optional_string("+Empty Space")) {
impact_condition = SpecialImpactCondition::EMPTY_SPACE;
}
if (optional_string("+Min Health Threshold:")) {
ci.min_health_threshold = ::util::ParsedRandomFloatRange::parseRandomRange();
}
if (optional_string("+Max Health Threshold:")) {
ci.max_health_threshold = ::util::ParsedRandomFloatRange::parseRandomRange();
}
if (optional_string("+Min Damage/Hitpoints Ratio:")) {
ci.min_damage_hits_ratio = ::util::ParsedRandomFloatRange::parseRandomRange();
}
if (optional_string("+Max Damage/Hitpoints Ratio:")) {
ci.max_damage_hits_ratio = ::util::ParsedRandomFloatRange::parseRandomRange();
}
if (optional_string("+Min Angle Threshold:")) {
ci.min_angle_threshold = ::util::ParsedRandomFloatRange::parseRandomRange();
}
if (optional_string("+Max Angle Threshold:")) {
ci.max_angle_threshold = ::util::ParsedRandomFloatRange::parseRandomRange();
}
parse_optional_bool_into("+Dinky:", &ci.dinky);
parse_optional_bool_into("+Disable If Player Parent:", &ci.disable_if_player_parent);
parse_optional_bool_into("+Disable On Subsystem Passthrough:", &ci.disable_on_subsys_passthrough);
required_string("+Effect Name:");
ci.effect = particle::util::parseEffect(wip->name);
if (optional_string("+Laser Pokethrough Effect Name:")) {
ci.pokethrough_effect = particle::util::parseEffect(wip->name);
if (optional_string("+Laser Pokethrough Threshold:")) {
stuff_float(&ci.laser_pokethrough_threshold);
}
parse_optional_bool_into("+Disable Main On Pokethrough:", &ci.disable_main_on_pokethrough);
}
SCP_vector<ConditionalImpact> ci_vec;
if (wip->conditional_impacts.count(armor_index) == 1) {
SCP_vector<ConditionalImpact> existing_cis = wip->conditional_impacts[armor_index];
if (wip->conditional_impacts.count(impact_condition) == 1) {
SCP_vector<ConditionalImpact> existing_cis = wip->conditional_impacts[impact_condition];
ci_vec.insert(ci_vec.end(), existing_cis.begin(), existing_cis.end());
}
ci_vec.push_back(ci);
if (!invalid_armor) {
wip->conditional_impacts[armor_index] = ci_vec;
wip->conditional_impacts[impact_condition] = ci_vec;
}
}
@@ -7733,7 +7771,8 @@ bool weapon_armed(weapon *wp, bool hit_target)
static std::unique_ptr<EffectHost> weapon_hit_make_effect_host(const object* weapon_obj, const object* impacted_obj, int impacted_submodel, const vec3d* hitpos, const vec3d* local_hitpos) {
if (impacted_obj == nullptr || impacted_obj->type != OBJ_SHIP || local_hitpos == nullptr) {
//Fall back to Vector. Since we don't have a ship, it's quite likely whatever we're hitting will immediately die, so don't try to attach a particle source.
auto vector_host = std::make_unique<EffectHostVector>(*hitpos, weapon_obj->last_orient, weapon_obj->phys_info.vel);
vec3d vel = impacted_obj == nullptr ? weapon_obj->phys_info.vel : impacted_obj->phys_info.vel;
auto vector_host = std::make_unique<EffectHostVector>(*hitpos, weapon_obj->last_orient, vel);
vector_host->setRadius(impacted_obj == nullptr ? weapon_obj->radius : impacted_obj->radius);
return vector_host;
}
@@ -7755,19 +7794,256 @@ static std::unique_ptr<EffectHost> weapon_hit_make_effect_host(const object* wea
}
}
void process_conditional_impact(
const ConditionData& entry,
const object* weapon_objp,
const weapon_info* wip,
const object* impacted_objp,
bool armed_weapon,
bool subsys_hit,
int submodel,
const vec3d* hitpos,
const vec3d* local_hitpos,
const vec3d* hitnormal,
float hit_angle,
float radius_mult,
float laser_pokethrough_amount,
const vec3d* laser_head_pos,
bool* valid_conditional_impact
) {
auto conditional_impact_it = wip->conditional_impacts.find(entry.condition);
if (conditional_impact_it != wip->conditional_impacts.end()) {
float health_fraction = entry.health / entry.max_health;
float damage_hits_fraction = entry.damage / entry.health;
for (const auto& ci : conditional_impact_it->second) {
if (((!armed_weapon) == ci.dinky)
&& (!ci.disable_if_player_parent || (&Objects[weapon_objp->parent] != Player_obj))
&& ((entry.hit_type != HitType::HULL || !subsys_hit) || !ci.disable_on_subsys_passthrough)
&& health_fraction >= ci.min_health_threshold.next()
&& health_fraction <= ci.max_health_threshold.next()
&& damage_hits_fraction >= ci.min_damage_hits_ratio.next()
&& damage_hits_fraction <= ci.max_damage_hits_ratio.next()
&& hit_angle >= fl_radians(ci.min_angle_threshold.next())
&& hit_angle <= fl_radians(ci.max_angle_threshold.next())
) {
bool pokethrough = (laser_pokethrough_amount >= ci.laser_pokethrough_threshold && ci.pokethrough_effect && wip->render_type == WRT_LASER);
if (!(pokethrough && ci.disable_main_on_pokethrough)) {
auto particleSource = particle::ParticleManager::get()->createSource(ci.effect);
particleSource->setHost(weapon_hit_make_effect_host(weapon_objp, impacted_objp, submodel, hitpos, local_hitpos));
particleSource->setTriggerRadius(weapon_objp->radius * radius_mult);
particleSource->setTriggerVelocity(vm_vec_mag_quick(&weapon_objp->phys_info.vel));
if (hitnormal) {
particleSource->setNormal(*hitnormal);
}
particleSource->finishCreation();
}
if (pokethrough) {
auto pokethroughParticleSource = particle::ParticleManager::get()->createSource(ci.pokethrough_effect.value());
pokethroughParticleSource->setHost(weapon_hit_make_effect_host(weapon_objp, nullptr, submodel, laser_head_pos, nullptr));
pokethroughParticleSource->setTriggerRadius(weapon_objp->radius * radius_mult);
pokethroughParticleSource->setTriggerVelocity(vm_vec_mag_quick(&weapon_objp->phys_info.vel));
if (hitnormal) {
pokethroughParticleSource->setNormal(*hitnormal);
}
pokethroughParticleSource->finishCreation();
}
*valid_conditional_impact = true;
}
}
}
}
void maybe_play_conditional_impacts(const std::array<std::optional<ConditionData>, NumHitTypes>& impact_data, const object* weapon_objp, const object* impacted_objp, bool armed_weapon, int submodel, const vec3d* hitpos, const vec3d* local_hitpos, const vec3d* hitnormal) {
if (weapon_objp == nullptr || weapon_objp->type != OBJ_WEAPON) {
return;
}
auto wp = &Weapons[weapon_objp->instance];
auto wip = &Weapon_info[wp->weapon_info_index];
ship* shipp = nullptr;
if (impacted_objp != nullptr && impacted_objp->type == OBJ_SHIP) {
shipp = &Ships[impacted_objp->instance];
}
float hit_angle = 0.0f;
vec3d reverse_incoming = weapon_objp->orient.vec.fvec;
vm_vec_negate(&reverse_incoming);
if (hitnormal) {
hit_angle = vm_vec_delta_ang(hitnormal, &reverse_incoming, nullptr);
}
float radius_mult = 1.0f;
if (wip->render_type == WRT_LASER) {
radius_mult = wip->weapon_curves.get_output(weapon_info::WeaponCurveOutputs::LASER_RADIUS_MULT, *wp, &wp->modular_curves_instance);
}
float laser_pokethrough_amount = 0.0f;
vec3d laser_head_pos = vmd_zero_vector;
if (wip->render_type == WRT_LASER) {
float length_mult = wip->weapon_curves.get_output(weapon_info::WeaponCurveOutputs::LASER_LENGTH_MULT, *wp, &wp->modular_curves_instance);
if (wip->laser_length_by_frametime) {
length_mult *= flFrametime;
}
float laser_length = wip->laser_length * length_mult;
vm_vec_scale_add(&laser_head_pos, &weapon_objp->last_pos, &weapon_objp->orient.vec.fvec, laser_length);
laser_pokethrough_amount = vm_vec_dist_quick(&laser_head_pos, hitpos) / laser_length;
}
bool subsys_hit = impact_data[static_cast<std::underlying_type_t<HitType>>(HitType::SUBSYS)].has_value();
bool valid_conditional_impact = false;
for (const auto& entry : impact_data) {
if (!entry.has_value()) {
continue;
}
process_conditional_impact(
*entry,
weapon_objp,
wip,
impacted_objp,
armed_weapon,
subsys_hit,
submodel,
hitpos,
local_hitpos,
hitnormal,
hit_angle,
radius_mult,
laser_pokethrough_amount,
&laser_head_pos,
&valid_conditional_impact
);
}
// check for empty space impacts
if (impacted_objp == nullptr) {
auto space_entry = ConditionData {
SpecialImpactCondition::EMPTY_SPACE,
HitType::NONE,
0.0f,
1.0f,
1.0f,
};
process_conditional_impact(
space_entry,
weapon_objp,
wip,
impacted_objp,
armed_weapon,
subsys_hit,
submodel,
hitpos,
local_hitpos,
hitnormal,
hit_angle,
radius_mult,
laser_pokethrough_amount,
&laser_head_pos,
&valid_conditional_impact
);
}
if (!valid_conditional_impact && wip->impact_weapon_expl_effect.isValid() && armed_weapon) {
auto particleSource = particle::ParticleManager::get()->createSource(wip->impact_weapon_expl_effect);
particleSource->setHost(weapon_hit_make_effect_host(weapon_objp, impacted_objp, submodel, hitpos, local_hitpos));
particleSource->setTriggerRadius(weapon_objp->radius * radius_mult);
particleSource->setTriggerVelocity(vm_vec_mag_quick(&weapon_objp->phys_info.vel));
if (hitnormal)
{
particleSource->setNormal(*hitnormal);
}
particleSource->finishCreation();
} else if (!valid_conditional_impact && wip->dinky_impact_weapon_expl_effect.isValid() && !armed_weapon) {
auto particleSource = particle::ParticleManager::get()->createSource(wip->dinky_impact_weapon_expl_effect);
particleSource->setHost(weapon_hit_make_effect_host(weapon_objp, impacted_objp, submodel, hitpos, local_hitpos));
particleSource->setTriggerRadius(weapon_objp->radius * radius_mult);
particleSource->setTriggerVelocity(vm_vec_mag_quick(&weapon_objp->phys_info.vel));
if (hitnormal)
{
particleSource->setNormal(*hitnormal);
}
particleSource->finishCreation();
}
if (impacted_objp != nullptr && impact_data[static_cast<std::underlying_type_t<HitType>>(HitType::SHIELD)].has_value() && (!valid_conditional_impact && wip->piercing_impact_effect.isValid() && armed_weapon)) {
if ((impacted_objp->type == OBJ_SHIP) || (impacted_objp->type == OBJ_DEBRIS)) {
int ok_to_draw = 1;
if (impacted_objp->type == OBJ_SHIP) {
float draw_limit, hull_pct;
int dmg_type_idx, piercing_type;
hull_pct = impacted_objp->hull_strength / shipp->ship_max_hull_strength;
dmg_type_idx = wip->damage_type_idx;
draw_limit = Ship_info[shipp->ship_info_index].piercing_damage_draw_limit;
if (shipp->armor_type_idx != -1) {
piercing_type = Armor_types[shipp->armor_type_idx].GetPiercingType(dmg_type_idx);
if (piercing_type == SADTF_PIERCING_DEFAULT) {
draw_limit = Armor_types[shipp->armor_type_idx].GetPiercingLimit(dmg_type_idx);
} else if ((piercing_type == SADTF_PIERCING_NONE) || (piercing_type == SADTF_PIERCING_RETAIL)) {
ok_to_draw = 0;
}
}
if (hull_pct > draw_limit)
ok_to_draw = 0;
}
if (ok_to_draw) {
using namespace particle;
auto primarySource = ParticleManager::get()->createSource(wip->piercing_impact_effect);
primarySource->setHost(weapon_hit_make_effect_host(weapon_objp, impacted_objp, submodel, hitpos, local_hitpos));
primarySource->setTriggerRadius(weapon_objp->radius * radius_mult);
primarySource->setTriggerVelocity(vm_vec_mag_quick(&weapon_objp->phys_info.vel));
if (hitnormal)
{
primarySource->setNormal(*hitnormal);
}
primarySource->finishCreation();
if (wip->piercing_impact_secondary_effect.isValid()) {
auto secondarySource = ParticleManager::get()->createSource(wip->piercing_impact_secondary_effect);
secondarySource->setHost(weapon_hit_make_effect_host(weapon_objp, impacted_objp, submodel, hitpos, local_hitpos));
secondarySource->setTriggerRadius(weapon_objp->radius * radius_mult);
secondarySource->setTriggerVelocity(vm_vec_mag_quick(&weapon_objp->phys_info.vel));
if (hitnormal)
{
secondarySource->setNormal(*hitnormal);
}
secondarySource->finishCreation();
}
}
}
}
}
/**
* Called when a weapon hits something (or, in the case of
* missiles explodes for any particular reason)
* Returns true if weapon is armed, false otherwise
*/
void weapon_hit( object* weapon_obj, object* impacted_obj, const vec3d* hitpos, int quadrant, const vec3d* hitnormal, const vec3d* local_hitpos, int submodel)
bool weapon_hit( object* weapon_obj, object* impacted_obj, const vec3d* hitpos, int quadrant)
{
Assert(weapon_obj != NULL);
if(weapon_obj == NULL){
return;
Assert(weapon_obj != nullptr);
if(weapon_obj == nullptr){
return false;
}
Assert((weapon_obj->type == OBJ_WEAPON) && (weapon_obj->instance >= 0) && (weapon_obj->instance < MAX_WEAPONS));
if((weapon_obj->type != OBJ_WEAPON) || (weapon_obj->instance < 0) || (weapon_obj->instance >= MAX_WEAPONS)){
return;
return false;
}
int num = weapon_obj->instance;
@@ -7777,13 +8053,11 @@ void weapon_hit( object* weapon_obj, object* impacted_obj, const vec3d* hitpos,
bool hit_target = false;
ship *shipp;
weapon *target_wp;
weapon_info *target_wip;
int objnum;
Assert((weapon_type >= 0) && (weapon_type < weapon_info_size()));
if ((weapon_type < 0) || (weapon_type >= weapon_info_size())) {
return;
return false;
}
wp = &Weapons[weapon_obj->instance];
wip = &Weapon_info[weapon_type];
@@ -7810,155 +8084,6 @@ void weapon_hit( object* weapon_obj, object* impacted_obj, const vec3d* hitpos,
weapon_hit_do_sound(impacted_obj, wip, hitpos, armed_weapon, quadrant);
}
bool valid_conditional_impact = false;
int relevant_armor_idx = -1;
float relevant_fraction = 1.0f;
float hit_angle = 0.0f;
vec3d reverse_incoming = weapon_obj->orient.vec.fvec;
vm_vec_negate(&reverse_incoming);
float radius_mult = 1.f;
if (wip->render_type == WRT_LASER) {
radius_mult = wip->weapon_curves.get_output(weapon_info::WeaponCurveOutputs::LASER_RADIUS_MULT, *wp, &wp->modular_curves_instance);
}
if (hitnormal) {
hit_angle = vm_vec_delta_ang(hitnormal, &reverse_incoming, nullptr);
}
if (!wip->conditional_impacts.empty() && impacted_obj != nullptr) {
switch (impacted_obj->type) {
case OBJ_SHIP:
shipp = &Ships[impacted_obj->instance];
if (quadrant == -1) {
relevant_armor_idx = shipp->armor_type_idx;
relevant_fraction = impacted_obj->hull_strength / i2fl(shipp->ship_max_hull_strength);
} else {
relevant_armor_idx = shipp->shield_armor_type_idx;
relevant_fraction = ship_quadrant_shield_strength(impacted_obj, quadrant);
}
break;
case OBJ_WEAPON:
target_wp = &Weapons[impacted_obj->instance];
target_wip = &Weapon_info[target_wp->weapon_info_index];
relevant_armor_idx = target_wip->armor_type_idx;
relevant_fraction = impacted_obj->hull_strength / i2fl(target_wip->weapon_hitpoints);
break;
default:
break;
}
if (wip->conditional_impacts.count(relevant_armor_idx) == 1) {
for (const auto& ci : wip->conditional_impacts[relevant_armor_idx]) {
if (((!armed_weapon) == ci.dinky)
&& relevant_fraction >= ci.min_health_threshold
&& relevant_fraction <= ci.max_health_threshold
&& hit_angle >= fl_radians(ci.min_angle_threshold)
&& hit_angle <= fl_radians(ci.max_angle_threshold)
) {
auto particleSource = particle::ParticleManager::get()->createSource(ci.effect);
particleSource->setHost(weapon_hit_make_effect_host(weapon_obj, impacted_obj, submodel, hitpos, local_hitpos));
particleSource->setTriggerRadius(weapon_obj->radius * radius_mult);
particleSource->setTriggerVelocity(vm_vec_mag_quick(&weapon_obj->phys_info.vel));
if (hitnormal)
{
particleSource->setNormal(*hitnormal);
}
particleSource->finishCreation();
valid_conditional_impact = true;
}
}
}
}
if (!valid_conditional_impact && wip->impact_weapon_expl_effect.isValid() && armed_weapon) {
auto particleSource = particle::ParticleManager::get()->createSource(wip->impact_weapon_expl_effect);
particleSource->setHost(weapon_hit_make_effect_host(weapon_obj, impacted_obj, submodel, hitpos, local_hitpos));
particleSource->setTriggerRadius(weapon_obj->radius * radius_mult);
particleSource->setTriggerVelocity(vm_vec_mag_quick(&weapon_obj->phys_info.vel));
if (hitnormal)
{
particleSource->setNormal(*hitnormal);
}
particleSource->finishCreation();
} else if (!valid_conditional_impact && wip->dinky_impact_weapon_expl_effect.isValid() && !armed_weapon) {
auto particleSource = particle::ParticleManager::get()->createSource(wip->dinky_impact_weapon_expl_effect);
particleSource->setHost(weapon_hit_make_effect_host(weapon_obj, impacted_obj, submodel, hitpos, local_hitpos));
particleSource->setTriggerRadius(weapon_obj->radius * radius_mult);
particleSource->setTriggerVelocity(vm_vec_mag_quick(&weapon_obj->phys_info.vel));
if (hitnormal)
{
particleSource->setNormal(*hitnormal);
}
particleSource->finishCreation();
}
if ((impacted_obj != nullptr) && (quadrant == -1) && (!valid_conditional_impact && wip->piercing_impact_effect.isValid() && armed_weapon)) {
if ((impacted_obj->type == OBJ_SHIP) || (impacted_obj->type == OBJ_DEBRIS)) {
int ok_to_draw = 1;
if (impacted_obj->type == OBJ_SHIP) {
float draw_limit, hull_pct;
int dmg_type_idx, piercing_type;
shipp = &Ships[impacted_obj->instance];
hull_pct = impacted_obj->hull_strength / shipp->ship_max_hull_strength;
dmg_type_idx = wip->damage_type_idx;
draw_limit = Ship_info[shipp->ship_info_index].piercing_damage_draw_limit;
if (shipp->armor_type_idx != -1) {
piercing_type = Armor_types[shipp->armor_type_idx].GetPiercingType(dmg_type_idx);
if (piercing_type == SADTF_PIERCING_DEFAULT) {
draw_limit = Armor_types[shipp->armor_type_idx].GetPiercingLimit(dmg_type_idx);
} else if ((piercing_type == SADTF_PIERCING_NONE) || (piercing_type == SADTF_PIERCING_RETAIL)) {
ok_to_draw = 0;
}
}
if (hull_pct > draw_limit)
ok_to_draw = 0;
}
if (ok_to_draw) {
using namespace particle;
auto primarySource = ParticleManager::get()->createSource(wip->piercing_impact_effect);
primarySource->setHost(weapon_hit_make_effect_host(weapon_obj, impacted_obj, submodel, hitpos, local_hitpos));
primarySource->setTriggerRadius(weapon_obj->radius * radius_mult);
primarySource->setTriggerVelocity(vm_vec_mag_quick(&weapon_obj->phys_info.vel));
if (hitnormal)
{
primarySource->setNormal(*hitnormal);
}
primarySource->finishCreation();
if (wip->piercing_impact_secondary_effect.isValid()) {
auto secondarySource = ParticleManager::get()->createSource(wip->piercing_impact_secondary_effect);
secondarySource->setHost(weapon_hit_make_effect_host(weapon_obj, impacted_obj, submodel, hitpos, local_hitpos));
secondarySource->setTriggerRadius(weapon_obj->radius * radius_mult);
secondarySource->setTriggerVelocity(vm_vec_mag_quick(&weapon_obj->phys_info.vel));
if (hitnormal)
{
secondarySource->setNormal(*hitnormal);
}
secondarySource->finishCreation();
}
}
}
}
//Set shockwaves flag
int sw_flag = SW_WEAPON;
@@ -8006,7 +8131,7 @@ void weapon_hit( object* weapon_obj, object* impacted_obj, const vec3d* hitpos,
//No impacted_obj means this weapon detonates
if (wip->pierce_objects && impacted_obj && impacted_obj->type != OBJ_WEAPON)
return;
return armed_weapon;
// For all objects that had this weapon as a target, wipe it out, forcing find of a new enemy
for ( auto so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
@@ -8057,6 +8182,7 @@ void weapon_hit( object* weapon_obj, object* impacted_obj, const vec3d* hitpos,
if ( parent->type == OBJ_SHIP && parent->signature == weapon_obj->parent_sig)
Ships[Objects[weapon_obj->parent].instance].weapons.remote_detonaters_active--;
}
return armed_weapon;
}
void weapon_detonate(object *objp)
@@ -8078,9 +8204,11 @@ void weapon_detonate(object *objp)
// call weapon hit
// Wanderer - use last frame pos for the corkscrew missiles
if ( (Weapon_info[Weapons[objp->instance].weapon_info_index].wi_flags[Weapon::Info_Flags::Corkscrew]) ) {
weapon_hit(objp, NULL, &objp->last_pos);
bool armed = weapon_hit(objp, nullptr, &objp->last_pos);
maybe_play_conditional_impacts({}, objp, nullptr, armed, -1, &objp->pos);
} else {
weapon_hit(objp, NULL, &objp->pos);
bool armed = weapon_hit(objp, nullptr, &objp->pos);
maybe_play_conditional_impacts({}, objp, nullptr, armed, -1, &objp->pos);
}
}