`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]>
Mission flags are saved to the mission file as the full 64-bit flagset value, but three issues prevented anything larger than 32 bits from being saved as intended:
* `stuff_flagset` parsed the value with `stuff_long`, where `atol` clamps anything above `LONG_MAX`
* `flagset::from_u64` cast the incoming value to `unsigned long`, truncating bits 32-63
* `flagset::to_u64` used `bitset::to_ulong` where it should use `bitset::to_ullong`
Fix these issues and add `static_assert` in the flagset functions to defend against future issues. Also use the proper UINT64_T_ARG where it should be used.
This was first triggered by `Limited_support_rearm_pool`, the first mission flag to occupy bit 31. Only mission flags are affected; the other flagset serialization paths were audited and are safe.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
FRED2, qtFRED, and the shared mission save code each carried their own copy of the loadout usage builders. The weaponry pair already had shared versions in missioneditor/common; this converts those to the SCP_map<int,int> signature the editors now use, adds the missing ship builder alongside them, and deletes both editors' private copies.
FRED2's playerstarteditor/freddoc and qtFRED's Editor/ErrorChecker all call the shared functions now, following the update_custom_wing_indexes consolidation precedent. missionsave's used_pool becomes a map as well, which removes its MAX_WEAPON_TYPES stack array.
Co-Authored-By: Claude Opus 4.7 (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]>
Although #7249 added the new support features to `check_for_25_1_data()`, the new fields were still written to the mission file in all formats except retail. Use a version check when writing the fields too.
If a player ship is changed to a regular ship, be sure that
Player_start_shipnum does not still point to the old ship.
Consolidate the several near-duplicate copies of the "repoint
Player_start_shipnum to a valid player start" logic, in both FRED and
qtFRED, into a single shared helper, ensure_valid_player_start_shipnum()
in missioneditor/common.cpp.
Also add some guards in situations where Player_start_shipnum might
be -1.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* 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]>
* long project will be long
* add shared foundation files
* unify some shared types across both editors
* sexptreemodel framework
* move opf functions to the model
* move shared functions into the model
* move variable/container stuff to the model
* sexptreeactions and ui interface
* wire the model into the ui
* move some shared logic to the model
* move more methods to the model
* move clipboard to model
* create new right click menu model
* begin connecting the new right click menu
* wire up the new right click menu in fred
* wire up the new right click menu in qtfred
* move help text and visuals to model
* move menu option enable disable to model
* wire up sexp_tree to the model
* rename sexp_tree files to sexp_tree_ui
* fix build errors
* fix warning
* remove all opf forwarders
* remove some forwarder methods
* connect qtfred events editor directly to model
* wite plan for unifying tree types
* replace legacy m_mode with treeflags
* fix qtfred campaign dialog
* fix qtfred ship/wing dialog trees
* build error
* documentation
* more documentation
* even more documentation
* move more opf functions to opf file
* clean up context menu function
* move default argument to opf file
* build errors
* fix qtfred dialogs and annoations
* new shared annotations model
* sexp_tree_ui to sexp_tree_view
* documentation pass
* document annotations model
* delete the plan
* move event annotations to the new model
* generic cleanup
* remove some old defines
* cleanup includes
* more cleanup
* parenthesis
* more parenthesis
* variable collisions
* too many parenthesis this time
* clang modernization
* variable names again
* more clang and assertions
* static cast
* clang again
* fix some bugs and issues
* Refactor sexp tree action duplication into SexpTreeActions
* fix a few more minor issues
* avoid clang false positive
* rebase tweaks
* fix annotations issue
* fix typo and two small bugs claude found
* fix event editor tree widget loading
* fix ship and wing sexp tree editors in qtfred
* fix qtfred briefing and debriefing trees
* fix a regression Claude found
* address feedback
* fix annotations for root items in qtfred by copying fred's method
* address feedback
* rescope variables
* clang
* 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
* change to new fog system
* Fix fog conversion issues
* remove unused old neb values
* Update scripting API
* temporary old-value round trip
* Rewrite nebula fog with four explicit parameters
Replace the legacy `+Fog Near Mult` / `+Fog Far Mult` model — which
depended on fixed engine constants — with four parameters that map
directly to renderer behavior:
+Fog 1000m Visibility fraction of light surviving 1000m of fog
+Fog Near Distance meters from camera where fog begins
+Fog Skybox Clip Distance max render distance for the skybox in fog
+Fog Clip Distance max render distance for the scene in fog
The deferred-lighting fog shader, material system, model renderer,
object sort, and the Lua mission API all consume the new parameters
directly. The legacy `Neb2_fog_near_mult` / `Neb2_fog_far_mult`
globals are removed.
Mission parsing accepts either the legacy keys or the modern keys.
Legacy values are converted to the modern set at parse time, and a
per-mission flag (`Neb2_fog_use_legacy_values`) records which keys to
write back on save. The flag is cleared only when something actually
changes, so opening and OK'ing an unmodified legacy mission preserves
its on-disk format. Saving uses `optional_string_fred` to preserve
user comments through edit cycles.
FRED2 and QtFRED background editors both expose the four parameters
in place of the old multipliers, with friendly labels and tooltips.
Both editors validate input (visibility clamped to [0, 1]; near
distance must be > 0; skybox and scene clip distances may be 0 to
disable fog at that range) and use a snapshot-and-compare at close
time so that a no-op open-then-OK never spuriously rewrites a legacy
mission in the modern format.
FRED2's background dialog is also reorganized so that "Display bg
bitmaps in nebula", "Override nebula fog palette", the RGB row, and
the four fog parameters all sit inside the Nebula group box; the
"Old Nebula" group box is restored below it with its Pattern, Color,
Pitch, Bank, and Heading controls back inside the frame. The dialog
now overrides PreTranslateMessage so that the new fog tooltips —
and the previously broken "Save sun/bitmap angles in correct format"
tooltip — actually fire.
QtFRED's BackgroundEditor.ui adds four QDoubleSpinBox controls in
the existing nebula form layout, with per-field decimals chosen to
avoid scientific notation, and per-field min/max declaring the
validation rules. The QtFRED help page is updated to describe the
new fields.
* fix tooltips
* restore forward-renderer fog
* Clang Tidy
* address feedback
* Refine nebula fog rewrite
- Rename Neb2_fog_use_legacy_values to Neb2_fog_save_legacy_values
to reflect that the flag governs save format, not runtime
behavior.
- Decide both the save flag and the legacy-to-modern conversion
purely from whether the file contains modern fog keys, dropping
the prior heuristic based on required_fso_version. A mission
without modern keys (legacy- or retail-format) converts on load
and saves back in legacy format; missions with modern keys are
saved as modern.
- Snapshot the four fog values as floats at editor open and use
fl_equal for the no-change comparison in both FRED2 and QtFRED,
replacing the FRED2 CString comparison and the QtFRED strict
inequality.
- Document the on-disk invariant in missionsave: when not saving
legacy keys, the four modern keys must always be written (never
gated on "only if non-default"), since the parser uses their
presence to decide whether to run the conversion on next load.
---------
Co-authored-by: Goober5000 <[email protected]>
* 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
Some code refactoring to allow mission titles to be strings of any length. Titles that are too long to actually display (based on string width) will be visibly truncated using `font::force_fit_string`, but the underlying title will not be affected. Multiplayer is still subject to the 31-character length limit, so titles sent over the network will still have characters truncated.
Also removes some redundant calls to `gr_get_string_size`, since `font::force_fit_string` returns the fitted string width.
* 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]>
* qtfred dockable objects list
* fix jumpnode rendering
* more ouline panel features
* fix for wings
* remember list expansions
* clean up wing selection and layer handling
* disable top and bottom docking
* give scene outliner some padding
* replace old selection dialog with the scene browser
* fix some minor issues
* right click for wings and waypoint paths
* clang
* clang again
* fix status bar message
* 25 1 checks
* clang
* more clang
* address feedback
1. When loading and saving lighting profiles, treat an empty string as equivalent to the default name
2. Remove unused fields from the mission struct
3. Properly guard union initialization in `add_message`
Not using the return value from `optional_string_fred` triggers Coverity warnings, but it turns out those "consume token" blocks aren't actually needed.
While we're at it, make the token parsing a little more robust in campaignsave.cpp and clean up the newline behavior.
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.
The existing `cannot-perform-scan` flag prevents the cargo from being shown on the HUD, but some modders would like to see the cargo while still preventing a ship from being able to perform a scan. So, this renames the existing flag to `cannot-perform-scan-hide-cargo` and adds a new `cannot-perform-scan-show-cargo` flag. Compatibility code in both mission parsing and flag evaluation ensures that missions using the old flag will still work.
Note that if both `cannot-perform-scan-hide-cargo` and `cannot-perform-scan-show-cargo` are set, the former will take precedence.
Make sure that missions without props will still be compatible with earlier mission file formats. If props are included, bump the mission file version. Also, fix some newline behavior in the save code.
* First draft of prop object type
* Add prop editor dialog in FRED
* prop sexps
* lua table prop support
* lab support
* lua mission access to props
* prop collision support
* use std::optional so the object->instance id is always valid
* allow props to block suns
* minor cleanup and remove arbitrary limit. Still limited to 500 objects.
* Sort props by category
* cleanup
* implement collision hooks
* parse prop flags and further ensure props are inited and closed correctly
* Add Cyborg's multi suggestions
* fix missing header
* add props to empty slots if available
* add multi signature
* fix rebase issues and match collisions to the new multithread style
* appease Clang the Conquerer
* save/parse mission prop classes by name
* Clean up comments and finish TODOs
* fix string errors
* clang wants this to be static now
* address feedback
* address feedback
* Use model num from prop info instead
* merge prop and ship collision group sexps together
* fix rebase bug
* address feedback
* fix name shadowing
* ship heavy submodel
* clang
---------
Co-authored-by: Asteroth <[email protected]>
* Generalize mission save from original FRED
* make qtfred use the new generalized missionsave
* split campaign save into a sister class
* allow unlimited campaign description length to be saved
* have the campaign editor pass the links to save properly
* cleanup
* future todo
* fixes
* more fixes
* cleanup vars
* clang
* add missing guardian threshold and fix var name
* more clang
* conflict resolution error
* update qtfred to use the new campaign save