Two subsystem handles are equal when both are valid and refer to the same
subsystem on the same ship. Invalid handles compare as not equal, matching
the other handle types.
When a destroyed subsystem was repaired, its submodels were only restored if the repair was done through a SEXP. A subsystem repaired by a support ship, by automatic repair, by a script, or through the debug console regained its hitpoints but not its submodels, so a ship could have a working turret with no model. Any -destroyed form also remained visible.
All of these now call restore_subsystem_submodels, which brings back the subsystem's submodels and hides the -destroyed submodel linked to a special-point subsystem. The -destroyed form of an ordinary submodel is hidden when the submodels are next replicated. The SEXP code uses the same function, with no change in its behavior.
Destruction outside do_subobj_destroyed_stuff now goes through a new blow_off_subsystem_submodels function, which do_subobj_destroyed_stuff also uses. It blows off both of the subsystem's submodels and shows any linked -destroyed submodel, unless the subsystem has "no disappear". This handles the cases where a subsystem appears destroyed without going through do_subobj_destroyed_stuff:
- the subsystem is destroyed at the start of the mission
- the subsystem was destroyed in the previous mission of a red-alert sequence
- the ship changes class while the subsystem's submodels are blown off
Previously the first two blew off only the first submodel, ignored "no disappear" for it, and never showed a linked -destroyed submodel; the third did nothing. For red alert, subsystems with no hitpoints at all are not treated as destroyed. For a class change, the visible state is carried over rather than inferred from hitpoints, so a subsystem set to zero without being blown up, or repaired without its submodels, keeps its appearance.
Red alert also restores the submodels of a subsystem that the mission file starts destroyed if the previous mission's data gives it hitpoints. Ship-copy-damage now shows or hides the -destroyed submodel linked to a special-point subsystem to match the copied state.
As before, the link between special-point subsystems and -destroyed submodels requires "$Link special-point subsystems to -destroyed submodels:" in game_settings.tbl.
The multiplayer hitpoint-update path is not changed. A client receives subsystem hitpoints separately from the notification that a subsystem was destroyed, so a hitpoint update sent just before the destruction could arrive after it and wrongly restore the submodels.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Follow-up to #7606. Restore the MaxGuardRadius property, restore the ability to set specific ranges using set-guard-range, and add scripting functions for setting the specific ranges.
Fixes#7756
Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
* Add command-line option overrides to options system
- Extend `OptionsManager` to support storing and displaying reasons for overrides.
- Update related code paths to prioritize command-line overrides, ensure correct option resolution, and display override details in the UI.
- Add unit tests for override behavior and round-trip validation.
- Remove unused legacy functions (`removeVSyncOption`, `removeWindowModeOption`) in favor of the new system.
* Add unit tests for `Graphics.Anisotropy` option validation with and without OpenGL
* Add support for associating command-line option names to `OptionsManager` overrides
- Update HDR, shadow rendering, and graphics API option overrides to include their respective command-line flag names.
- Refactor `Graphics.RenderAPI` to use the `GraphicsAPI` enum instead of raw integers for better type safety and clarity.
- Ensure Vulkan support logic and fallback behavior function correctly with the updated options system.
- Update unit tests to validate override functionality and enum-based API configuration.
* Remove unused `removeVSyncOption` function
* Apply the PR review feedback for the command-line option overrides
Delete the unit-test run artifacts that this branch committed by mistake, and
add .gitignore rules for the files that the unittests binary writes.
Give an override priority over an in-session edit again. The options UI now
shows a disabled control for an overridden option, and setConfigValue()
refuses an edit for an overridden key. Before this change, a save wrote the
edit to the config file and applied it to the bound global, but getValue()
kept returning the override. The game and the options menu then showed
different values for the rest of the session.
Add a setOverride() overload that takes a JSON value. Every command-line flag
now builds its override with json_integer, json_real, json_true, json_false
or json_pack, in place of a hand-formatted string.
Make Graphics.CockpitFOV and Graphics.CockpitFOVToggle independent of the
order in which the options manager loads them, and remove the forced override
on the player's toggle preference. The toggle listener also replaces an
identical bind_to() call that made the listener dead code.
Add 16 to the values of Graphics.MSAASamples. The -msaa flag accepts 16, so an
override with that value made printValues() report an invalid value.
Keep the render API that the caller gives to gr_init(). Only a Default value
now comes from the Graphics.RenderAPI option, so FRED and the unit tests keep
the backend that they ask for.
Make the default_func of Graphics.Shadows and Graphics.ShadowRenderMethod
return their global. A constant discarded the value that -enable_shadows and
the mod table set. Set Max_rt_shadow_local_lights to 4, because 3 is not one
of the values that the option offers.
Also: resolve -lang one time, share the render API name with version.cpp,
correct the stale comments, and add unit tests with a fixture that clears the
overrides after each test.
Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01UGh4ZM2u4oUGSbrP122Uyr
* Fix the order-dependent crash in the MSAA option test
The test `MsaaOverrideAcceptsEverySampleCountTheFlagAllows` calls
`getValidValues()`. `Option<T>` makes a display string for each value, and the
display function of `Graphics.MSAASamples` calls `XSTR()`. `XSTR()` runs
`Int3()` if `lcl_xstr_init()` did not run before. Only `FSTestFixture` runs it,
and `Xstr_inited` stays set for the remainder of the process. Thus the shuffled
test order decided if the test passed or if it stopped the test binary with
SIGILL.
Move that one test to a new fixture that derives from `FSTestFixture`. The
fixture initializes localization, and it also clears the overrides after each
test. Refresh the comment that told that the test binary never runs
`lcl_init()`.
Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01UGh4ZM2u4oUGSbrP122Uyr
---------
Co-authored-by: Claude Opus 5 <[email protected]>
* Automatically generate the isValid() Lua API function for handle types
If an ADE object's stored type has an isValid() member, ade_obj now registers an isValid function for it automatically. An explicit ADE_FUNC(isValid, ...) declared after the ADE_OBJ replaces the generated one, so types with special semantics (e.g. sound) keep their own. The 67 hand-written functions that merely delegated to the member are removed, and 26 types that previously exposed no isValid() gain one.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
* Add ADE_OBJ_VALIDATOR for index-based Lua handles
Int-index handles (shipclass, weaponclass, team, etc.) can now declare their validity predicate with a single ADE_OBJ_VALIDATOR or ADE_OBJ_VALIDATOR_RANGE line after the ADE_OBJ. The registered validator drives the generated isValid() function and is also honored by ade_set_args when the mod table asks for nil instead of invalid objects, which previously never applied to these handles. The 19 hand-written range-check functions are replaced, and loadout_ship, default_primary, default_secondary, and support_rearm_pool_team gain isValid(). Add a validator to HudGauge while we're at it.
Also fully qualify the scripting namespace in LuaConvert.h so its templates can be instantiated from the unit tests.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
---------
Co-authored-by: Claude Fable 5.1 <[email protected]>
`support_ship_info::rearm_weapon_pool` was a dense per-weapon-class array where -1 = unlimited (the default), 0 = not rearmable, and >0 = remaining stockpile. Convert it to a sparse per-team map:
`std::array<SCP_map<int, int>, MAX_TVT_TEAMS> rearm_weapon_pool;`
`int rearm_pool_default() const; // -1, or 0 under rearm_pool_from_loadout`
Mission-file format and save output are unchanged. Also tightens the two weapon-class bounds checks in ship.cpp. This removes another `MAX_WEAPON_TYPES` sized structure on the way to lifting the weapon class limit.
Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
The giveOrder scripting function now accepts optional DockerPoint and
DockeePoint arguments for ORDER_DOCK, each given as a dockpoint name or
one-based index (matching the setDocked convention). DockerPoint refers
to a dockpoint on the ordering ship and DockeePoint to one on the target.
If only one point is specified, a compatible point is chosen automatically
on the other ship using the same dock-type precedence as the existing
automatic dockpoint negotiation. When neither is specified, behavior is
unchanged.
Fixes#7119
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
team_data was the last persistence-adjacent structure sized by MAX_SHIP_CLASSES: four parallel ship arrays plus four parallel weapon arrays (one slot per mission-file loadout line, holding a class or a sexp variable name, and a count or a sexp variable name) with manual num_*_choices counters, plus a dense per-class weapon_required flag array. Convert all of it to the sparse-container family:
ship_list / ship_list_variables / ship_count / ship_count_variables
-> SCP_vector<loadout_entry> ship_choices
weaponry_pool / weaponry_count / weaponry_pool_variable /
weaponry_amount_variable
-> SCP_vector<loadout_entry> weapon_choices
bool weapon_required[MAX_WEAPON_TYPES]
-> SCP_set<int> required_weapons
where loadout_entry is {class_index, count, class_variable, count_variable}, with empty variable strings meaning the value was given literally. num_ship_choices / num_weapon_choices become .size().
Intentional behavior fixes, all previously latent memory errors or
data bugs:
* is_ship_assignable stored an alt ship CLASS in a variable used as a
loadout list INDEX when default_to_this_class was false, then
swapped to ship_list[class] - garbage for any mission where the two
domains diverge. It now swaps to the alt class itself, per the
evident intent.
* The default-ship fallback read ship_list[0] even when a team had
zero ship choices; it now leaves default_ship at -1, and
ss_fixup_team_data guards its append of the default ship accordingly.
* FRED2's dumpstats indexed Ships[] with a ship class when printing
loadout entries; it now prints the entry's class or variable name
directly.
* Team parse now clears the previous mission's entries explicitly
rather than relying on overwrite-in-place.
This clears the largest remaining cluster of MAX_SHIP_CLASSES references on the way to lifting the ship class limit.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
- A parse flag to indicate when the mission is being parsed, and a new `ship_class_index()` accessor in the ship registry, both currently unused
- Make the ship registry more robust, especially in FRED: properly update it when ships are renamed or deleted
* New guard-range sexp
New guard-range sexp which allows different ranges for different guarders
* cast value
* Clang
* bugfixes
* fix CI
* Address Feedback
* address feedback
* typo
Convert MsgItems from an array to a vector, and remove the associated limit value and counter.
* vectorize msgitems
* appeasement
* leave me alone clang
* remove optional
* cleanup
* changes requested
* clang
* clang what is WRONG with you
* feedback
* remove flag in .h
* Fix scripting copy-paste bug
The iterators for secondary banks for lua access had a copy-paste bug from primaries, so we should fix that. The early == -1 break masks it whenever a ship uses 3 or fewer secondary banks, which is probably why it went unnoticed.
Also add some additional low-hanging fruit optimizations.
* radar distance and other cleanup
* clang
Convert the allowed-ships and allowed-weapons lists to sets, rather than naive vectors that stored a true/false for every class. Since these are ordered sets, iteration occurs in the same sequence as iteration of `Ship_info` or `Weapon_info`. Associated data structures such as `Granted_ships` and `Granted_weapons` are converted as well.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Fix a regression from #7393: `last_targeted_subobject` must be updated when the player's target and target subsystem are changed, or the subsystem will be retargeted due to `hud_target_change_check`.
Also fix related issues:
- the `ship.Target` and `ship.TargetSubsystem` scripting setters now use the standard `set_target_objnum` function and also correctly handle the new target subsystem
- fix an indexing error in `order.TargetSubsystem`
Co-Authored-By: Claude Fable 5 <[email protected]>
* Fix C4709 Warnings on latest VS
* more
* suppress false positive
* Switch to using any/none_of
* Address Feedback
* Fix Line endings?
* Fix commits and strip out flag_combinator
* more fixes
* nolint
* Adapt particle at runtime
* Refactor EffectHosts
* fix issues
* cleanup
* Remove live transitive particles
* paticle hosts now do local correctly
* Fix effecthostparticle local mode
* again
* Set particle host orientation
* I don't like mixed local and global vectors
* agaiiin
* Fix validity check
* parent foo
* Fix bugs, hopefully finally
* refactor
* Flip new table option for intuitiveness
* Fix support for GCC9 due to issues with LWG3052
* Address feedback
* avoid undefined behavior from UNREACHABLE on reachable code paths
UNREACHABLE expands to __assume(false) / __builtin_unreachable() in
release builds, which is undefined behavior if the code is ever actually
reached. Many UNREACHABLE sites only guard against unexpected-but-
possible runtime states (values from tables, mission/save files, network
packets, scripting, or user input), so reaching them should degrade
gracefully rather than invoke UB.
Replace UNREACHABLE with Assertion(false, ...) at all such sites. It
still pops an error in debug builds, but in release it is a harmless
no-op that simply falls through to the following code. Sites that are
genuinely unreachable by local invariants (e.g. a switch over a bounded
random number) are left as UNREACHABLE.
A few converted sites had no safe fall-through -- an uninitialized read,
null dereference, or null call would have followed -- so they also get a
minimal recovery: initialize the value, guard the dereference, return
early, or skip the offending item.
Where the converted site was a simple guard of the form
if (x) { Assertion(false, ...); return; }
hoist the check onto the invariant instead:
Assertion(!x, ...);
if (x) { return; }
This states the expectation directly and shows the offending value in
the debug popup, while keeping the same graceful runtime degradation.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* addendum
After consultation with Lafiel, use UNREACHABLE in situations that are effectively impossible even if not provably impossible. UNREACHABLE = "this shouldn't ever happen"; Assertion = "if this ever happens, it's a coding error". Also fix a few other sites and change a couple to Warnings.
---------
Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
The SWH_SECONDARY case of the ship_bank setter was copy-pasted from the
primary case and still assigned primary_bank_* fields; it now assigns
the secondary_bank_* fields.
Co-Authored-By: Claude Fable 5 <[email protected]>
Replaces the copy-then-reset idiom with std::exchange in five
steal-and-reset move constructors and assignment operators.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* framework for support ship rearm pool and flags
* scripting api
* FRED support
* QtFRED support
* do not save non-player-allowed weapons
* tvt and mission versioning
* fix variable name
* minor fixes and clarity
* oops
* add qtfred help docs
* fixes and cleanup
* clang and readability
* address feedback
* address feedback
* new mine weapon type
* some fixes and cleanup
* fix some parsing issues
* make sure mines get plotted
* make mine spawned objects can get a correct target
* some cleanup and additional features
* chasing mines
* some Lua tweaks
* generalize proximity detonations
* minor tweaks
* collapse namespace
* address feedback
Two related fixes flagged by an audit of vm_vector_2_matrix_norm callers:
ParticleEffectAction: locals.direction defaulted to vmd_zero_vector,
which would trip vm_vector_2_matrix_norm's unit-length assertion (and
divide by zero in release) when an action program emitted a particle
effect without first running a Set Direction action. Default to
vmd_z_vector so the unset-direction path always feeds a unit vector.
matrix_h / Lua orientation type: the element indexer (m[1..9] = x)
wrote arbitrary floats straight into mtx.a1d[idx] and the resulting
non-orthonormal matrix could flow into obj->orient via the Orientation
virt-vars on object/modelinstance/subsystem, breaking downstream engine
code that assumes the basis vectors are unit length. getInterpolated
had the same problem by design (pure linear interp, no normalization).
Adds a fourth MatrixState, NeedsOrthonormalize, which the indexer
setter and getInterpolated use to flag their output. ValidateMatrix
and ValidateAngles now call vm_orthogonalize_matrix when they observe
that state, so any consumer that reads through GetMatrix/GetAngles
gets a valid orientation without each call site needing its own guard.
getInterpolated is also marked ADE_FUNC_DEPRECATED at FSO 26.0 in
favor of rotationalInterpolate; mods targeting older versions keep
the legacy linear blend (now safely orthonormalized on use).
* add wing display names
Adds display name capability for wings, matching the pattern and infrastructure of display names for ships. As in #7234, wing display names take precedence over ship display names, if both are provided; because if ship display names took precedence, a ship would have the same display name in every wave.
All the relevant display locations are handled, including the HUD, escort list, message box, and mission log. Lua API support is provided as well.
Adds full support for both FRED and QtFRED, including the always-save-display-names setting.
Addresses #7189 and the last request from #1730.
* add help text from #7234
Follow-up to #7251, which added `set-guard-range`
This PR exposes that value to scripting, so scripters do not have to use the more expensive `runSEXP` to use it.
The value is initialized as -1 (`max_guard_radius = -1.0f;` line 7913 in ship.cpp), hence the default value for the script is -1.
* 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]>
The waypoint class stored its position in m_position separately from the game object's pos field, requiring callers to manually keep both in sync. Now get_pos() and set_pos() delegate to the object position when the game object exists, falling back to the parsed position only if the object does not exist.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* Tune ups for standalones
Fixes two warnings from building in standalone and also provide, also adds needed function to actually tell if game is in standalone mode.
* better fix for armor flags
1. Modify `ship_get_subsys_for_submodel` to use the subsystem indexer. This changes the operation from O(n) to O(1) and also allows the function to work with turret gun barrels.
2. Add an `isValid` check to `ParticleSource::finishCreation` to prevent unknown state from being accessed if the source is not valid.
3. Tweak `EffectHostTurret` and make sure it works for fighter beams.
4. Prevent `createOnTurret` from running on submodels that are not turrets.
Testing in FotG exposed a bug that dates back to retail. During mission parsing, arrival and departure anchors were represented by indexes into `Parse_names`. After parsing, they were converted to ship indexes -- but only in FRED. Since some functions are shared between FRED and FS2, this can produce inconsistent results, which can be hard to detect since parse indexes and ship indexes are usually the same.
This refactors arrival and departure anchors to be represented by ship registry indexes in all cases. It also adds conversion functions for the benefit of FRED and QtFRED since those dialogs were designed with ship indexes in mind.