* 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().
Ship's implicit move operations compiled but were semantically broken in
two ways: moving copied the subsys_list sentinel's links verbatim,
leaving the bookend nodes of the intrusive subsystem list pointing at
the old sentinel address; and the raw owning warpin_effect /
warpout_effect pointers were copied without nulling the source, so a
vacated Ships[] slot aliased live WarpEffects that ship::clear() (on
slot reuse) or ship_close() (which walks every slot) would delete out
from under the destination ship.
Fix both at the member level so the compiler-generated moves are
correct by composition:
- Add ship_subsys_sentinel, derived from ship_subsys, whose move
operations re-link the bookend nodes (handling the null-linked,
self-referential-empty, and populated source states) and leave the
moved-from sentinel as an initialized empty list. Copying is
deleted. Public inheritance keeps &ship::subsys_list convertible to
ship_subsys*, so list_init, GET_FIRST, and every other call site is
unchanged.
- Convert warpin_effect and warpout_effect to std::unique_ptr. The
delete-and-null blocks in ship::clear(), ship_close(), and
ship_set_warp_effects() collapse into reset(); call sites that borrow
the pointer into a raw local now use get().
With the members handling themselves, declare ship's special member
functions: defaulted default constructor and moves, deleted copies
(previously implicit via the unique_ptr members, now stated intent).
The moves are deliberately not declared noexcept because members like
SCP_map and std::deque do not have standard-guaranteed nothrow moves;
the computed exception specification is used instead.
Co-Authored-By: Claude Fable 5 <[email protected]>
Set model instances to -1 when objects are deleted, and guard against null model instances in `modelinstance_replace_active_texture`. This may fix a crash in Event Horizon.
* use std::move() for local variables that are copied but not reused
Addresses ~140 Coverity "Variable copied when it could be moved"
defects across 74 files. Every std::move() targets a local variable
whose value is consumed by push_back, emplace, assignment, lambda
capture, or a function call and is never read again afterward.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* clang: use std::make_shared
* clang: use std::make_unique
* address feedback
---------
Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
Three out-of-bounds/UB fixes for issue #6793:
- asteroid_create_all: skip field_debris_type[idx] == -1 entries when
building the DG_DEBRIS odds table and loading models; iterate only
over valid table slots during spawn. Matches the guard already present
in asteroid_frame.
- missionparse: bounds-check subtype before indexing colors[] in the
obsolete +Field Debris Type: parsing path.
- verify_asteroid_list: guard the asteroid_list[0] fallback access
against an empty list.
- asteroid_init: emit an mprintf when no asteroids are defined instead
of returning silently.
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
* rename misleading variable check_again to never_check_again
The first element of collision_result means "never check again" per the
typedef comment, but was destructured as check_again (opposite meaning).
Rename to never_check_again to match the actual semantics and prevent
future confusion.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* fix data races in ship-weapon collision threading
Two thread-safety bugs in ship_weapon_check_collision, which runs on
worker threads:
1. update_danger_weapon() wrote to Ai_info[] (danger_weapon_objnum,
danger_weapon_signature) without synchronization. Multiple weapons
targeting the same ship would race on the same Ai_info entry.
2. Homing missile detonation logic wrote wp->lifeleft and
wp->weapon_flags directly on the Weapons[] global array.
Fix: defer both writes to the main-thread post-processing phase via
new flags in ship_weapon_collision_data. Also convert the collision
data from a 6-element tuple to a named struct for readability.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* fix data race in ship-ship collision submodel checking
ship_ship_check_collision() toggled pmi->submodel[].collision_checked
flags on the heavy ship's polymodel_instance to selectively enable/
disable submodel collision checks. When two collision pairs shared the
same heavy ship, worker threads would race on the same pmi entries.
Fix: add a collision_checked_override pointer to mc_info that lets
callers provide a thread-local array of collision_checked values.
model_collide() uses the override when set, falling back to the
pmi->submodel[] field otherwise. ship_ship_check_collision() now
builds a local SCP_vector<char> and passes it via the override,
completely avoiding writes to shared polymodel_instance state.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* Ensure threads have spun down before continuing non-threaded execution
* address review feedback for ship-weapon collision threading
- postproc flag now accumulates incrementally instead of being OR'd
at the final return, ensuring all return paths respect it
- early return for predictive collisions now preserves the
should_update_danger_weapon flag in the collision data
- moved nprintf detonation debug print to the main-thread process
pass for thread safety
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* move collision_checked from pmi into mc_info for thread safety
collision_checked flags were transient per-collision-pass state that
didn't belong in the shared polymodel_instance. Now mc_info owns an
SCP_vector<char> that model_collide auto-initializes from pmi when
empty. All collision callers (ship-ship, asteroid, debris, prop)
updated to manipulate mc.collision_checked instead of pmi->submodel[].
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* Convert to CV and spinup counter
* Update comment
---------
Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
Co-authored-by: BMagnu <[email protected]>
Addresses a behavior idiosyncrasy encountered by Solaris. If this flag is enabled, explosions with a visual radius of 0 do not have fireballs created, and do not have the fireball lifetime added to the explosion lifetime.
For type safety, use enum class rather than int.
Also make a few miscellaneous tweaks such as adding a new casting macro and making detail level settings a bit cleaner.
* Add parent / host input curves
* reorder some includes to allow particles access to ship.h
* Allow modular_curves to deal with functions returning optionals
* Add convenience function to return an optional from an objp and an instance type
* Add a bunch of particle curve inputs
* Fix internal linkage warning
* Add hitpoint and shield fraction (albeit limited to ships)
* begin asteroid dialog rewrite
* activate fields
* clean up connections
* clean up and rearrange files for consistency
* use constructor to zero out the field when needed
* fix checks errors
* remove duplicate lines
* add missing z
* Make scripting use the full-blown particle system
* Add reverse option
* Add animation reversal as an option to modern particles
* Start implementation of beam particles
* Add multi-emission particle source API for legacy particles
* Attach beam muzzle particles to the correct host
* Reorder code in order to facilitate apparent visual size as modular curve input for particles
* fix lua api
* Typo
* Fix scripting and beam muzzle particles
* Remove muzzleflash code, prepare to replace with particles
* Begin mflash effect conversion
* Pass final parameters to mflashes and fix curve
* Remove deprecated particle function
* Add debris flame particles
* remove weapon_explosion table
* Convert shield explosion particles
* Fix modular curve full inputs and convert particle curve inputs accordingly
* Allow particle volume offset and rotation with curve-based rot around fvec
* Add Ring and Point Volumes
* Convert PSPEW to new system
* Make particle curve timing aware of interp
* Update todos
* get rid of performance waste
* Add last parameter for converted pspews
* Fix pspew bugs
* Fix direction for default pspew
* Fix order for voluem rotation
* particle vel interp
* particle vel interp 2
* Make beam muzzle stay parented to the firing ship, as it's likely the more commonly intended variamnt
* fix scripting particle lifetime
* Allow beam muzzle particle override
* Skip invalid pspews
* remove pspew dcf code
* Fix sparkler stretch
* Correct sparkler bia
* General sparkler weirdness
* Tune distribution of sparklers
* Convert Asteroid particles
* Fix conversion warning
* Fix conversion warning again
* Fix conversion warning again again
* Really clang?
* Fix asteroids spawning outside active fields
Found an interesting edge case bug with active asteroid fields that has been present since retail: if a large ship is just outside an active asteroid field, that field will end up spawning asteroids outside the field bounds. This is because the game thinks that the large ship is within the asteroid field when it is actually not. Specifically, the check `asteroid_is_ship_inside_field` does not properly account for the ship the addition of the ship's diameter to the bounding box check.
This PR adds a flag to fix that bounding box check and fix the bug of spawing asteroids outside the asteroid field bounds. Note, `asteroid_is_ship_inside_field` is used twice in the code, once within `set_asteroid_throw_objnum` and once following that call within `maybe_throw_asteroid`. Also note, `asteroid_in_inner_bound` uses a delta of 0 in all cases except when being called within `asteroid_is_ship_inside_field`.
Tested and this fixes the bug while still having asteroid fields work as expected. (Also took this chance to rename a local variable for newly create asteroids to more descriptive name).
* enable by default for 25.0
* initial version of default_settings.tbl
* Add a way to change player detail level defaults and implement a couple more options to detail_settings.tbl
* support the rest of 2d.cpp options
* Allow enforcing options to a specific value
* modernize detail defaults, levels, and values
* Fix parsing issue and remove lambda capture by reference
* fix print
* clang
* More efficient +Enforce parsing
* cleanup
* typo
* don't negate this
* error_display
* clarify language between presets, levels, and values
* add default case
* small fix
* use a map to minimize string lookups
* Set ver to C++17
* remove deprecated shuffle
* Fix std::iterator being deprecated
* Work around codecvt deprecation
* Fix std::result_of deprecation
* Another random_shuffle
* another text conversion replacement
* upgrade asteroid sexps to match new asteroid field features
* clang is a needy boy
* unify the sexp operators into one since there are minimal changes
* cleanup for readability
* address feedback
* coverity - using `std::move` instead of copying, and change some parameters from value to reference
* coverity - fix or mark potential copy-paste mistakes
* coverity - various checking of parameter and return values
* coverity - miscellaneous fixes, and change some 'intentional' markers to 'false positive' markers
* One more follow-up to #6021
Update assertion message and mission saving check since older builds are not going to be able to read fields created with newer builds
* update to use empty
The Unlimited Asteroid/Debris Field Types & Subtypes PR (#6021) updated to using vectors to allow for unlimited asteroid and debris types. That update removed the count field, `num_used_field _debris_types`, which counted both asteroids and debris pieces (even though the name only has debris in it). The counter variable was removed in favor of `.size()` but now both asteroid and debris field sizes needed to be checked, so this PR updates the code to account for that. Allows asteroid fields to properly pass the count check and properly again spawn in mission.
Tested on asteroid fields passive and active along with debris fields (both created from sexps and the asteroid mission editor) and everything now works as expected. Also tested on missions with no fields and those also still work as expected.
* base working version of unlimited debris types
* Proper FRED support
* make qtFRED handle asteroids properly
* fix qtFRED assertion
* further clean out MAX_RETAIL_DEBRIS_TYPES
* These should be sizes not POFs
* Move this to it's own method
* convert asteroid subtypes to a vector
* unlimit asteroid subtypes
* fix some global shadowing
* signed/unsigned mismatch
* Address feedback
* Move replacement textures from ship to modelinstance
* Actually set pmi
* Fix texture replace in fred as well
* Fix bad indent
* incorporate feedback
As requested by Event Horizon, this removes the limit on fireball types. The fixed array has been replaced with a vector and the relevant code updated accordingly. There is also a new version of `stuff_int_list()` to parse ints into a vector.
This also adds bounds checking for explosion animations that are parsed for ship classes, ship types, and asteroids, since previously there was no guarantee that the indexes would be in range.
There were a few instances of `Random::next()` being called without ensuring its arguments couldn't trip an assertion. This should hopefully catch them all.
* Allow Modders to Set Rotational Scaling Multiplier for Asteroids/Debris
Previously, there was no way to limit the velocity at which asteroids/debris randomly rotated. There was a way to set maximum speed, thus this PR follows that convention and adds a way to cap the rotational velocity scaling. This PR allows modders to set caps per type, including setting the value to 0 for no rotation.
A single scaling value was used b/c it was the most straightforward value to se via table and it was the employed the least code changes. Asteroids pick a random normalized vector for their rotation when spawning, and then that value is scaled by a random multiplier.
* use full multiplier instead
Add `const` to the `mc_info` struct so its values aren't inadvertently overwritten. Tweak some of the collision code to prevent overwrites.
Also remove the `flak_muzzle_flash` declaration since that function no longer exists.
* refactor how asteroid wrapping is done and setup for allowing or disallowing range check
* allow fred to toggle enhanced spawn checking
* field naming adjustment
* some minor tweaks
* Add sexp compatibility
* We don't need to save the boolean, just the string.
* full qtFRED support
Chucked asteroids are always large. Therefore use their speed, not the
speed of the 1st parsed/small asteroid. Should not affect retail because
it uses the same speed for all three asteroids
This initializes `mc_info` to the correct values when it is constructed, removing the need for `mc_info_init`.
This PR also fixes a group of incorrectly inverted assignments in beam.cpp.