diff --git a/code/asteroid/asteroid.cpp b/code/asteroid/asteroid.cpp index 9c59ee6940..fd36efc7cd 100644 --- a/code/asteroid/asteroid.cpp +++ b/code/asteroid/asteroid.cpp @@ -1693,7 +1693,7 @@ static void asteroid_do_area_effect(object *asteroid_objp) if ( weapon_area_calc_damage(ship_objp, &asteroid_objp->pos, asip->inner_rad, asip->outer_rad, asip->blast, asip->damage, &blast, &damage, asip->outer_rad) == -1 ) continue; - ship_apply_global_damage(ship_objp, asteroid_objp, &asteroid_objp->pos, damage, asip->damage_type_idx); + ship_apply_global_damage(ship_objp, asteroid_objp, &asteroid_objp->pos, damage, asip->damage_type_idx, DamageSource::Explosion); weapon_area_apply_blast(nullptr, ship_objp, &asteroid_objp->pos, blast, false); } // end for } diff --git a/code/object/collidedebrisship.cpp b/code/object/collidedebrisship.cpp index fa7c340410..11fea040bb 100644 --- a/code/object/collidedebrisship.cpp +++ b/code/object/collidedebrisship.cpp @@ -158,12 +158,12 @@ int collide_debris_ship( obj_pair * pair ) quadrant_num = -1; } if (apply_ship_damage) { - ship_apply_local_damage(debris_hit_info.heavy, debris_hit_info.light, &hitpos, ship_damage, Debris[debris_objp->instance].damage_type_idx, quadrant_num, CREATE_SPARKS, debris_hit_info.heavy_submodel_num); + ship_apply_local_damage(debris_hit_info.heavy, debris_hit_info.light, &hitpos, ship_damage, Debris[debris_objp->instance].damage_type_idx, quadrant_num, CREATE_SPARKS, debris_hit_info.heavy_submodel_num, nullptr, 1.f, nullptr, DamageSource::Collision); } } else { // don't draw sparks using sphere hit position if (apply_ship_damage) { - ship_apply_local_damage(debris_hit_info.light, debris_hit_info.heavy, &hitpos, ship_damage, Debris[debris_objp->instance].damage_type_idx, MISS_SHIELDS, NO_SPARKS); + ship_apply_local_damage(debris_hit_info.light, debris_hit_info.heavy, &hitpos, ship_damage, Debris[debris_objp->instance].damage_type_idx, MISS_SHIELDS, NO_SPARKS, -1, nullptr, 1.f, nullptr, DamageSource::Collision); } } @@ -348,10 +348,10 @@ int collide_asteroid_ship( obj_pair * pair ) (ship_objp->flags[Object::Object_Flags::No_shields]) || !ship_is_shield_up(ship_objp, quadrant_num) ) { quadrant_num = -1; } - ship_apply_local_damage(asteroid_hit_info.heavy, asteroid_hit_info.light, &hitpos, ship_damage, ast_damage_type, quadrant_num, CREATE_SPARKS, asteroid_hit_info.heavy_submodel_num); + ship_apply_local_damage(asteroid_hit_info.heavy, asteroid_hit_info.light, &hitpos, ship_damage, ast_damage_type, quadrant_num, CREATE_SPARKS, asteroid_hit_info.heavy_submodel_num, nullptr, 1.f, nullptr, DamageSource::Collision); } else { // don't draw sparks (using sphere hitpos) - ship_apply_local_damage(asteroid_hit_info.light, asteroid_hit_info.heavy, &hitpos, ship_damage, ast_damage_type, MISS_SHIELDS, NO_SPARKS); + ship_apply_local_damage(asteroid_hit_info.light, asteroid_hit_info.heavy, &hitpos, ship_damage, ast_damage_type, MISS_SHIELDS, NO_SPARKS, -1, nullptr, 1.f, nullptr, DamageSource::Collision); } // maybe print Collision on HUD diff --git a/code/object/collideshipship.cpp b/code/object/collideshipship.cpp index 4ad05b0eb0..ef642e8faf 100644 --- a/code/object/collideshipship.cpp +++ b/code/object/collideshipship.cpp @@ -1392,14 +1392,14 @@ void collide_ship_ship_process(obj_pair * pair, const std::any& collision_data) float damage_heavy = (100.0f * damage / heavy_obj->phys_info.mass); ship_apply_local_damage(ship_ship_hit_info.heavy, ship_ship_hit_info.light, &world_hit_pos, damage_heavy, light_shipp->collision_damage_type_idx, - quadrant_num, CREATE_SPARKS, ship_ship_hit_info.heavy_submodel_num, &ship_ship_hit_info.collision_normal); + quadrant_num, CREATE_SPARKS, ship_ship_hit_info.heavy_submodel_num, &ship_ship_hit_info.collision_normal, 1.f, nullptr, DamageSource::Collision); hud_shield_quadrant_hit(ship_ship_hit_info.heavy, quadrant_num); // don't draw sparks (using sphere hitpos) float damage_light = (100.0f * damage / light_obj->phys_info.mass); ship_apply_local_damage(ship_ship_hit_info.light, ship_ship_hit_info.heavy, &world_hit_pos, damage_light, heavy_shipp->collision_damage_type_idx, - MISS_SHIELDS, NO_SPARKS, -1, &ship_ship_hit_info.collision_normal); + MISS_SHIELDS, NO_SPARKS, -1, &ship_ship_hit_info.collision_normal, 1.f, nullptr, DamageSource::Collision); hud_shield_quadrant_hit(ship_ship_hit_info.light, -1); @@ -1811,7 +1811,7 @@ int collide_prop_ship(obj_pair* pair) // don't draw sparks (using sphere hitpos) damage = (100.0f * damage / ship_objp->phys_info.mass); - ship_apply_local_damage(ship_objp, prop_objp, &world_hit_pos, damage, -1, MISS_SHIELDS, CREATE_SPARKS, -1, &prop_ship_hit_info.collision_normal); + ship_apply_local_damage(ship_objp, prop_objp, &world_hit_pos, damage, -1, MISS_SHIELDS, CREATE_SPARKS, -1, &prop_ship_hit_info.collision_normal, 1.f, nullptr, DamageSource::Collision); hud_shield_quadrant_hit(ship_objp, -1); } diff --git a/code/scripting/global_hooks.cpp b/code/scripting/global_hooks.cpp index 2a6ee23bf9..dc3ccec91b 100644 --- a/code/scripting/global_hooks.cpp +++ b/code/scripting/global_hooks.cpp @@ -559,6 +559,22 @@ const std::shared_ptr> OnCameraSetUpHook = Hook<>::Factory("On Camera Set {"Camera", "camera", "The camera about to be used for rendering."}, }); +const std::shared_ptr> OnShipDamage = Hook::Factory( + "On Ship Damage", "Called after damage is applied to a ship; lethal hits run before the On Ship Death Started hook. " + "Only called where damage is applied: in single player and on a multiplayer server, not on multiplayer clients.", + { + {"Target", "ship", "The ship that took the damage."}, + {"Source", "object", "The weapon, beam, shockwave, or other object that caused the damage. Could be nil."}, + {"Attacker", "object", "The source ship or parent object, if available."}, + {"SourceKind", "string", "NON_BEAM_WEAPON, BEAM, SHOCKWAVE, COLLISION, EXPLOSION, WASH, SELF (the ship damaged itself, such as a self-destruct), or OTHER."}, + {"DamageType", "string", "The damage type, or UNKNOWN if none is defined."}, + {"ShieldDamage", "number", "Actual total shield strength removed by this damage application."}, + {"HullDamage", "number", "Actual hull strength removed, clamped to the target's remaining hull."}, + {"SubsystemDamage", "table", "Maps each subsystem's CanonicalName to the strength it lost in this damage application. Only subsystems that lost strength are listed, in no particular order."}, + {"Quadrant", "number", "Shield quadrant hit, or -1 when no single quadrant applies."}, + {"LethalHit", "boolean", "Whether this hit reduced positive hull strength to zero or below."}, + }); + // ========== FRED HOOKS ========== const std::shared_ptr> FredOnMissionLoad = Hook<>::Factory("FRED On Mission Load", diff --git a/code/scripting/global_hooks.h b/code/scripting/global_hooks.h index 27e3d94292..1c38571c2b 100644 --- a/code/scripting/global_hooks.h +++ b/code/scripting/global_hooks.h @@ -100,6 +100,8 @@ extern const std::shared_ptr> OnStateAboutToEndHook; extern const std::shared_ptr> OnStateEndHook; extern const std::shared_ptr> OnCameraSetUpHook; +extern const std::shared_ptr> OnShipDamage; + // FRED Hooks extern const std::shared_ptr> FredOnMissionLoad; extern const std::shared_ptr> FredOnMissionSpecsSave; diff --git a/code/scripting/hook_conditions.cpp b/code/scripting/hook_conditions.cpp index 9fb0c79d91..a579e377d8 100644 --- a/code/scripting/hook_conditions.cpp +++ b/code/scripting/hook_conditions.cpp @@ -232,6 +232,18 @@ HOOK_CONDITIONS_START(ShipDeathConditions) HOOK_CONDITION_SHIPP(ShipDeathConditions, "", "that died.", dying_shipp); HOOK_CONDITIONS_END +// Damage with no weapon has weapon_info_index -1, and so does a weapon name that does not exist, so a named weapon +// class must also be a real weapon to match. +static bool conditionDamageWeaponClassMatches(int damage_weapon_index, const int& weapon_index) +{ + return damage_weapon_index >= 0 && damage_weapon_index == weapon_index; +} + +HOOK_CONDITIONS_START(ShipDamageConditions) + HOOK_CONDITION_SHIPP(ShipDamageConditions, "Target ", "that took damage.", target_shipp); + HOOK_CONDITION(ShipDamageConditions, "Weapon class", "Specifies the class of the weapon that caused the damage. For a shockwave, this is the weapon that created it.", weapon_info_index, conditionParseWeaponClass, conditionDamageWeaponClassMatches); +HOOK_CONDITIONS_END + HOOK_CONDITIONS_START(SubsystemDeathConditions) HOOK_CONDITION_SHIPP(SubsystemDeathConditions, "", "whose subsystem got destroyed.", affected_shipp); HOOK_CONDITIONS_END diff --git a/code/scripting/hook_conditions.h b/code/scripting/hook_conditions.h index 28d49fe773..43f96c4e09 100644 --- a/code/scripting/hook_conditions.h +++ b/code/scripting/hook_conditions.h @@ -63,6 +63,12 @@ struct ShipDeathConditions { const ship* dying_shipp; }; +struct ShipDamageConditions { + HOOK_DEFINE_CONDITIONS; + const ship* target_shipp; + int weapon_info_index; // the weapon that caused the damage, or -1 if none +}; + struct SubsystemDeathConditions { HOOK_DEFINE_CONDITIONS; const ship* affected_shipp; diff --git a/code/ship/ship.cpp b/code/ship/ship.cpp index f0efa918ca..ade31a568c 100644 --- a/code/ship/ship.cpp +++ b/code/ship/ship.cpp @@ -9330,7 +9330,7 @@ static void ship_blow_up_area_apply_blast( object *exp_objp) switch ( objp->type ) { case OBJ_SHIP: - ship_apply_global_damage( objp, exp_objp, &exp_objp->pos, damage, sip->shockwave.damage_type_idx); + ship_apply_global_damage( objp, exp_objp, &exp_objp->pos, damage, sip->shockwave.damage_type_idx, DamageSource::Explosion); ship_apply_whack( &force, &exp_objp->pos, objp ); break; case OBJ_ASTEROID: @@ -9387,7 +9387,7 @@ static void do_dying_undock_physics(object *dying_objp, ship *dying_shipp) float docked_mass = dying_objp->phys_info.mass + docked_objp->phys_info.mass; // damage this docked object - ship_apply_global_damage(docked_objp, dying_objp, &dying_objp->pos, damage, -1); + ship_apply_global_damage(docked_objp, dying_objp, &dying_objp->pos, damage, -1, DamageSource::Explosion); // do physics vm_vec_sub(&impulse_norm, &docked_objp->pos, &dying_objp->pos); diff --git a/code/ship/shiphit.cpp b/code/ship/shiphit.cpp index 7052850417..556616522b 100755 --- a/code/ship/shiphit.cpp +++ b/code/ship/shiphit.cpp @@ -2393,6 +2393,112 @@ static int maybe_shockwave_damage_adjust(const object *ship_objp, const object * return 1; } +// The shield, hull and per-subsystem strength of a ship, recorded before a ship_do_damage() call. Only filled in while +// the On Ship Damage hook is active. +struct ship_damage_snapshot { + float hull_strength = 0.0f; + float shield_strength = 0.0f; + SCP_vector> subsystem_hits; +}; + +static void ship_take_damage_snapshot(object *ship_objp, ship_damage_snapshot &snapshot) +{ + ship *shipp = &Ships[ship_objp->instance]; + snapshot.hull_strength = ship_objp->hull_strength; + snapshot.shield_strength = shield_get_strength(ship_objp); + snapshot.subsystem_hits.clear(); + for (ship_subsys *subsys = GET_FIRST(&shipp->subsys_list); subsys != END_OF_LIST(&shipp->subsys_list); subsys = GET_NEXT(subsys)) { + snapshot.subsystem_hits.emplace_back(subsys, subsys->current_hits); + } +} + +// Runs the On Ship Damage hook for one ship_do_damage() call, reporting what the ship lost since the snapshot. +// Does nothing if the ship lost none of its shield, hull or subsystem strength. +static void ship_run_damage_hook(object *ship_objp, object *other_obj, int weapon_info_index, int damage_type_idx, int quadrant, DamageSource source, bool wash_damage, const ship_damage_snapshot &before) +{ + const float shield_damage = MAX(0.0f, before.shield_strength - shield_get_strength(ship_objp)); + const float hull_damage = MAX(0.0f, MAX(0.0f, before.hull_strength) - MAX(0.0f, ship_objp->hull_strength)); + + bool subsystem_hit = false; + for (const auto &entry : before.subsystem_hits) { + if (entry.first->current_hits < entry.second) { + subsystem_hit = true; + break; + } + } + if (shield_damage <= 0.0f && hull_damage <= 0.0f && !subsystem_hit) { + return; + } + const bool lethal_hit = before.hull_strength > 0.0f && ship_objp->hull_strength <= 0.0f; + + // the subsystems that lost strength, keyed by canonical subsystem name + luacpp::LuaTable subsystem_losses = luacpp::LuaTable::create(Script_system.GetLuaSession()); + for (const auto &entry : before.subsystem_hits) { + const float lost = entry.second - entry.first->current_hits; + if (lost > 0.0f) { + subsystem_losses.addValue(ship_subsys_get_canonical_name(entry.first), lost); + } + } + + const weapon_info *wip = Weapon_info.in_bounds(weapon_info_index) ? &Weapon_info[weapon_info_index] : nullptr; + if (!Damage_types.in_bounds(damage_type_idx) && wip != nullptr) { + damage_type_idx = wip->damage_type_idx; + } + const char *damage_type = Damage_types.in_bounds(damage_type_idx) ? Damage_types[damage_type_idx].name : "UNKNOWN"; + + const char *source_kind = "OTHER"; + if (source == DamageSource::Collision) { + source_kind = "COLLISION"; + } else if (source == DamageSource::Explosion) { + source_kind = "EXPLOSION"; + } else if (wash_damage) { + source_kind = "WASH"; + } else if (other_obj == ship_objp) { + source_kind = "SELF"; + } else if (other_obj != nullptr) { + switch (other_obj->type) { + case OBJ_WEAPON: + source_kind = "NON_BEAM_WEAPON"; + break; + case OBJ_BEAM: + source_kind = "BEAM"; + break; + case OBJ_SHOCKWAVE: + source_kind = "SHOCKWAVE"; + break; + default: + break; + } + } + + object *attacker_objp = nullptr; + if (other_obj != nullptr) { + if (other_obj->type == OBJ_BEAM) { + int parent_objnum = beam_get_parent(other_obj); + if (parent_objnum >= 0) { + attacker_objp = &Objects[parent_objnum]; + } + } else if (other_obj->type == OBJ_SHIP) { + attacker_objp = other_obj; + } else if (other_obj->parent >= 0 && other_obj->parent < MAX_OBJECTS && Objects[other_obj->parent].signature == other_obj->parent_sig) { + attacker_objp = &Objects[other_obj->parent]; + } + } + + scripting::hooks::OnShipDamage->run(scripting::hooks::ShipDamageConditions{ &Ships[ship_objp->instance], weapon_info_index }, + scripting::hook_param_list( + scripting::hook_param("Target", 'o', ship_objp), + scripting::hook_param("Source", 'o', other_obj, other_obj != nullptr), + scripting::hook_param("Attacker", 'o', attacker_objp, attacker_objp != nullptr), + scripting::hook_param("SourceKind", 's', source_kind), + scripting::hook_param("DamageType", 's', damage_type), + scripting::hook_param("ShieldDamage", 'f', shield_damage), + scripting::hook_param("HullDamage", 'f', hull_damage), + scripting::hook_param("SubsystemDamage", 't', subsystem_losses), + scripting::hook_param("Quadrant", 'i', quadrant), + scripting::hook_param("LethalHit", 'b', lethal_hit))); +} + // ------------------------------------------------------------------------ // ship_do_damage() // @@ -2411,7 +2517,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, const vec3d* hit_normal = nullptr, const vec3d* local_hitpos = nullptr) +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, DamageSource source = DamageSource::Inferred) { // mprintf(("doing damage\n")); @@ -2426,6 +2532,13 @@ static void ship_do_damage(object *ship_objp, object *other_obj, const vec3d *hi Assertion(ship_objp->instance >= 0 && ship_objp->type == OBJ_SHIP, "invalid ship target in ship_do_damage"); shipp = &Ships[ship_objp->instance]; + // snapshot for the On Ship Damage hook, which reports what the ship actually lost + const bool damage_hook_active = scripting::hooks::OnShipDamage->isActive(); + bool damage_hook_ran = false; + ship_damage_snapshot damage_before; + if (damage_hook_active) { + ship_take_damage_snapshot(ship_objp, damage_before); + } // maybe adjust damage done by shockwave for BIG|HUGE maybe_shockwave_damage_adjust(ship_objp, other_obj, &damage); @@ -2697,10 +2810,19 @@ static void ship_do_damage(object *ship_objp, object *other_obj, const vec3d *hi // diag_printf2("Simulated Hull for Ship %s hit, dropping from %.32f to %d.\n", shipp->ship_name, (int) ( ship_objp->sim_hull_strength * 100 ), (int) ( ( ship_objp->sim_hull_strength - damage ) * 100 ) ); ship_objp->sim_hull_strength -= damage; ship_objp->sim_hull_strength = MAX(0.0f, ship_objp->sim_hull_strength); + if (damage_hook_active) { + ship_run_damage_hook(ship_objp, other_obj, weapon_info_index, damage_type_idx, quadrant, source, wash_damage, damage_before); + } return; } } ship_objp->hull_strength -= damage; + + // a lethal hit runs the hook now, before the ship's death processing below + if (damage_hook_active && ship_objp->hull_strength <= 0.0f) { + ship_run_damage_hook(ship_objp, other_obj, weapon_info_index, damage_type_idx, quadrant, source, wash_damage, damage_before); + damage_hook_ran = true; + } } // let damage gauge know that player ship just took damage @@ -2800,6 +2922,10 @@ static void ship_do_damage(object *ship_objp, object *other_obj, const vec3d *hi } } + if (damage_hook_active && !damage_hook_ran) { + ship_run_damage_hook(ship_objp, other_obj, weapon_info_index, damage_type_idx, quadrant, source, wash_damage, damage_before); + } + if (!global_damage) { maybe_play_conditional_impacts(impact_data, other_obj, ship_objp, true, submodel_num, hitpos, local_hitpos, hit_normal); } @@ -3006,7 +3132,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, const vec3d* local_hitpos) +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, DamageSource source) { Assert(ship_objp); // Goober5000 Assert(other_obj); // Goober5000 @@ -3103,7 +3229,7 @@ void ship_apply_local_damage(object *ship_objp, object *other_obj, const vec3d * ship_do_healing(ship_objp, other_obj, hitpos, damage, quadrant, submodel_num, -1, hit_normal, local_hitpos); 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); + ship_do_damage(ship_objp, other_obj, hitpos, damage, quadrant, submodel_num, damage_type_idx, false, hit_dot, hit_normal, local_hitpos, source); } // DA 5/5/98: move ship_hit_create_sparks() after do_damage() since number of sparks depends on hull strength @@ -3150,7 +3276,7 @@ void ship_apply_local_damage(object *ship_objp, object *other_obj, const vec3d * // You can pass force_center==NULL if you the damage doesn't come from anywhere, // like for debug keys to damage an object or something. It will // assume damage is non-directional and will apply it correctly. -void ship_apply_global_damage(object *ship_objp, object *other_obj, const vec3d *force_center, float damage, int damage_type_idx) +void ship_apply_global_damage(object *ship_objp, object *other_obj, const vec3d *force_center, float damage, int damage_type_idx, DamageSource source) { Assert(ship_objp); // Goober5000 (but not other_obj in case of sexp) @@ -3187,7 +3313,7 @@ void ship_apply_global_damage(object *ship_objp, object *other_obj, const vec3d ship_do_healing(ship_objp, other_obj, &world_hitpos, damage, -1, damage_type_idx); else // Do damage on local point - ship_do_damage(ship_objp, other_obj, &world_hitpos, damage, shield_quad, -1 , damage_type_idx); + ship_do_damage(ship_objp, other_obj, &world_hitpos, damage, shield_quad, -1 , damage_type_idx, false, 1.f, nullptr, nullptr, source); } else { // Since an force_center wasn't specified, this is probably just a debug key // to kill an object. So pick a shield quadrant and a point on the @@ -3196,7 +3322,7 @@ void ship_apply_global_damage(object *ship_objp, object *other_obj, const vec3d int n_quadrants = static_cast(ship_objp->shield_quadrant.size()); for (int i=0; i, float> do_subobj_hit_stuff(object *ship_ // (it might be possible to make `target` const, but that would set off another const-cascade) extern void ship_apply_tag(ship *ship_p, int tag_level, float tag_time, object *target, const vec3d *start, int ssm_index, int ssm_team); +// Where a ship damage application came from, for the On Ship Damage hook. Inferred uses the damaging object type and +// the engine wash flag. Collision and Explosion are for callers whose damaging object cannot tell them apart. +enum class DamageSource { + Inferred, + Collision, + Explosion, +}; + // This gets called to apply damage when something hits a particular point on a ship. // This assumes that whoever called this knows if the shield got hit or not. // 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, const vec3d* local_hitpos = nullptr); +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, DamageSource source = DamageSource::Inferred); // 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 @@ -67,7 +75,7 @@ void ship_apply_local_damage(object *ship_obj, object *other_obj, const vec3d *h // You can pass force_center==NULL if you the damage doesn't come from anywhere, // like for debug keys to damage an object or something. It will // assume damage is non-directional and will apply it correctly. -void ship_apply_global_damage(object *ship_obj, object *other_obj, const vec3d *force_center, float damage, int damage_type_idx); +void ship_apply_global_damage(object *ship_obj, object *other_obj, const vec3d *force_center, float damage, int damage_type_idx, DamageSource source = DamageSource::Inferred); // like above, but does not apply damage to shields void ship_apply_wash_damage(object *ship_obj, object *other_obj, float damage);