Files
Boutros Tawaifi 49464c56f6 Add an "On Ship Damage" script hook (#7894)
* Add an "On Ship Damage" script hook

Runs after resolved damage has been applied to a ship (lethal hits run before
the destruction callback). Hook variables: Target, Source, Attacker,
WeaponClass, WeaponSubtype, SourceKind (PROJECTILE, BEAM, SHOCKWAVE, COLLISION,
OTHER), DamageType, ShieldDamage, HullDamage, Quadrant, Ballistic, BeamWeapon
and LethalHit. Conditions can filter on the target ship. Nothing is computed
unless a script has registered the hook.

* Move the On Ship Damage hook call out of a lambda

ship_do_damage() now calls a file-level static function, ship_run_damage_hook(), that takes the values it needs as arguments instead of capturing the function state. Hook behaviour is unchanged.

* Report subsystem damage in the On Ship Damage hook

Adds SubsystemDamage, the total subsystem strength a damage application removed, so a hit absorbed by subsystems no longer looks like a small hull hit. The hook description now says it only runs where damage is applied, which excludes multiplayer clients.

* Address On Ship Damage review comments

- Remove WeaponClass, WeaponSubtype, Ballistic and BeamWeapon from the
  hook. Scripts read these from Source when it is a weapon.
- Add a "Weapon class" condition for the On Ship Damage hook. It matches
  only when the damage source is a weapon of that class; a weapon name
  that does not exist never matches.
- SubsystemDamage is now a table keyed by subsystem handle that lists only
  the subsystems that lost strength. The total is removed.
- Rename PROJECTILE to NON_BEAM_WEAPON.
- Classify SourceKind by damage source: COLLISION for ship, debris and
  asteroid collisions; EXPLOSION for ship-death and docked-ship blasts and
  asteroid blasts; WASH for engine wash; SELF for kills where the ship
  damages itself; OTHER for other ship-sourced damage.
- Reword the hook description.

* Key On Ship Damage SubsystemDamage by canonical name

Lua tables cannot use subsystem handles as keys in a usable way, so key
the SubsystemDamage table by each subsystem's CanonicalName instead.

* Tag ship-vs-prop damage as a collision and use in_bounds

The ship-vs-prop collision call didn't pass DamageSource::Collision, so
the On Ship Damage hook reported those hits as OTHER. Every other
collision path was already tagged.

Also use SCP_vector::in_bounds() for the Weapon_info and Damage_types
index checks in ship_run_damage_hook().
2026-11-06 18:41:29 -05:00

161 lines
3.4 KiB
C++

#pragma once
#include <any>
class object;
class ship;
struct weapon;
class ship_subsys;
struct object_ship_wing_point_team;
enum class ArrivalLocation;
#define HOOK_DEFINE_CONDITIONS static const SCP_unordered_map<SCP_string, const std::unique_ptr<const ParseableCondition>> conditions
namespace scripting {
class EvaluatableCondition {
public:
virtual bool evaluate(const std::any& /*conditionContext*/) const {
return false;
};
virtual ~EvaluatableCondition() = default;
};
class ParseableCondition {
public:
SCP_string documentation;
virtual std::unique_ptr<EvaluatableCondition> parse(const SCP_string& /*input*/) const {
return std::make_unique<EvaluatableCondition>();
};
ParseableCondition() : documentation("Invalid Condition. Will never evaluate.") { }
virtual ~ParseableCondition() = default;
protected:
ParseableCondition(SCP_string documentation_) : documentation(std::move(documentation_)) { }
};
namespace hooks {
// ---- Hook Condition System Conditions ----
struct ControlActionConditions {
HOOK_DEFINE_CONDITIONS;
int action_index;
};
struct ShipSourceConditions {
HOOK_DEFINE_CONDITIONS;
const ship* source_shipp;
};
struct CollisionConditions {
HOOK_DEFINE_CONDITIONS;
struct ParticipatingObjects {
const object *objp_a, *objp_b;
} participating_objects;
};
struct ShipDeathConditions {
HOOK_DEFINE_CONDITIONS;
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;
const ship_subsys* destroyed_subsys; // As of yet unused
};
struct ShipDepartConditions {
HOOK_DEFINE_CONDITIONS;
const ship* leaving_shipp;
};
struct WeaponDeathConditions {
HOOK_DEFINE_CONDITIONS;
const weapon* dying_wep;
};
struct WeaponProximityTriggeredConditions {
HOOK_DEFINE_CONDITIONS;
const weapon* triggered_wep;
const ship* trigger_shipp;
};
struct ObjectDeathConditions {
HOOK_DEFINE_CONDITIONS;
const object* dying_objp;
};
struct ShipArriveConditions {
HOOK_DEFINE_CONDITIONS;
const ship* spawned_shipp;
ArrivalLocation arrival_location; // As of yet unused
const object* spawn_anchor_objp; // As of yet unused
};
struct WeaponCreatedConditions {
HOOK_DEFINE_CONDITIONS;
const weapon* spawned_wep;
const object* parent_objp;
};
struct WeaponEquippedConditions {
HOOK_DEFINE_CONDITIONS;
const ship* user_shipp;
const object* target_objp; // As of yet unused
};
struct WeaponUsedConditions {
HOOK_DEFINE_CONDITIONS;
const ship* user_shipp;
const object* target; // As of yet unused
SCP_vector<int> weaponclasses;
bool isPrimary; // As of yet unused
};
struct WeaponSelectedConditions {
HOOK_DEFINE_CONDITIONS;
const ship* user_shipp;
int weaponclass, weaponclass_prev;
bool isPrimary; // As of yet unused
};
struct WeaponDeselectedConditions {
HOOK_DEFINE_CONDITIONS;
const ship* user_shipp;
int weaponclass, weaponclass_prev;
bool isPrimary; // As of yet unused
};
struct ObjectDrawConditions {
HOOK_DEFINE_CONDITIONS;
const object* drawn_from_objp;
};
struct KeyPressConditions {
HOOK_DEFINE_CONDITIONS;
int keycode;
};
struct CommOrderConditions {
HOOK_DEFINE_CONDITIONS;
const ship* source;
const object* target;
const object_ship_wing_point_team* reciever; // As of yet unused
};
} // namespace hooks
}
#undef HOOK_DEFINE_CONDITIONS