mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-07 08:15:59 -10:00
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:
1 parent
06eaf94cb3
commit
6921f550c1
11 files changed
+568
-224
No files matched your search
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user