67 Commits
Author SHA1 Message Date
dustinspace217andDustinspace217 499b3fb38b P4: qtFRED: rotate around the centre of a multi-selection (#7843)
* qtFRED: rotate around the centre of a multi-selection

Rotate Around Object (Alt+R) and the orbit pivot both used the current
object's position, so with several ships marked the camera still
circled whichever one happened to be current. Add
EditorViewport::selectionCenter(), which returns the centroid of the
marked objects when more than one is marked and the given object's
position otherwise, and use it at the three places that chose the
point: the look-at camera update, orbitCameraGetPivot(), and the Alt+R
handler that turns the camera toward the target when the mode is
switched on.

orbitCameraGetPivot() also serves middle-mouse orbit and wheel zoom in
normal mode, so those pivot on the selection centre as well. Every
marked type counts at full weight, including waypoints and jump nodes.
With a single object marked nothing changes.

Fixes #7724

* Make EditorViewport::selectionCenter const

It only reads the marked-object list and the editor's marked count;
clang-tidy (readability-make-member-function-const) flagged it in CI.

* Mirror the multi-selection orbit pivot to FRED, trim comments

FRED gets the same selection_center() as qtFRED's selectionCenter(),
used at the same three places: the look-at camera update, the orbit
pivot, and the Rotate Around Object toggle. Also removes the qtFRED
comments flagged in review.

* Make the selection-center pivot optional and share object bounds

Look-at and orbit use the current object again unless the new option is
on (qtFRED: Preferences > Controls; FRED: View menu), and the selection
center is now the bounding-box midpoint. view_universe in both editors,
the pivot, and qtFRED's selection radius all use get_object_bounds() in
missioneditor/common.

* Reset the cached orbit pivot when the selection-center option changes

Wheel zoom reuses the orbit pivot until the camera moves, so without this
the option only took effect for scrolling after the next camera move.

* Address review: move and rename the selection-centroid option

- FRED: the option moves from the View menu to Settings (and Misc in the
  classic layout), under Move Ships When Undocking, with hover help, and is
  renamed Rotate Around Selection Centroid.
- qtFRED: the checkbox moves next to Move Ships When Undocking in the
  preferences dialog, with the same name.
- setOrbitSelectionCenter only resets the orbit when the value changes.
- get_object_bounds iterates with list_range.
- The bounds test resets the object system with obj_init and takes objects
  off the free list, so it leaves a valid state for other tests.

* Rename the option to Selection Midpoint, move it to qtFRED's Controls tab, and document it

* Fix overlapping Appearance rows in qtFRED Preferences, realign a settings line

The last commit renumbered the Viewport label size row of the Appearance
group from 4 to 3, which is the Long list menu style row, so the label
size pair fell out of the layout and its spin box drew over the group
title. Put it back at row 4.

---------

Co-authored-by: Dustinspace217 <[email protected]>
2026-11-04 01:17:00 -05:00
Goober5000andClaude Opus 5 a159e67a10 share the loadout usage-list code between the two editors
Add generate_ship_usage_list_team alongside the weaponry twin that already existed in missioneditor/common, so both loadout editors can drop their TVT/non-TVT branches and simply iterate Num_teams.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-09-25 03:22:23 -04:00
Goober5000andOpenAI Codex db02a71319 three QtFred fixes (#7780)
* Fix QtFRED root notifications when inserting nested operators

Only emit rootNodeFormulaChanged when the wrapped node is a formula
root. Inserting above a nested expression previously reported a child
as an event, goal, or cutscene root, causing assertions and out-of-bounds
accesses in the receiving dialog model.

Co-Authored-By: OpenAI Codex

* Give each QtFRED ship independently owned arrival and departure cues

Pass the tree model to the cue update handlers and serialize a separate
expression for each eligible ship. Sharing one formula across selected
ships caused double frees on subsequent edits and left other ships
referencing freed nodes when only one ship was changed.

This also avoids allocating unused expressions when cue updates are
disabled or no selected ships are eligible.

Co-Authored-By: OpenAI Codex

* Synchronize QtFRED item editability after SEXP node changes

Update native Qt editability alongside the shared model flags so
variable and container references cannot be edited as plain text.
Restore inline editing when a reference is replaced with ordinary data,
and preserve item flags when moving or copying branches.

Reject edit requests for protected items before setting the editing
state, preventing later programmatic changes from being treated as
user edits.

Co-Authored-By: OpenAI Codex
2026-09-24 13:23:23 +09:00
Goober5000andClaude Opus 5 09fc7055b5 Fix FRED and qtFRED memory leaks at exit (#7784)
Neither editor had a teardown to match FSO's game_shutdown(), so the engine
data they set up was never freed.  Add editor_free_engine_data() to free it,
and call it from a new fred_shutdown() in FRED and from qtFRED's shutdown(),
before the graphics system closes.

Also:
- Delete FRED's modeless editors if they are still open at exit.
- Make FRED's dialog tooltips and sexp tree image lists members, since
  they were leaked or depended on OnDestroy() being called.
- Parent qtFRED's item models so they are deleted with their widgets.
- Enable the CRT leak report in qtFRED, which previously had none.
- In qtFRED, call shutdown() last in closeEvent(), since it destroys the
  window.
- Make the preferences path and user SID strings function-level statics,
  so MFC's leak dump no longer reports them as leaks.

Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
2026-09-21 05:12:35 +02:00
Goober5000andClaude Opus 4.7 a168f5a9c0 Store the support rearm pool sparsely (#7758)
`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]>
2026-09-08 10:50:22 -04:00
Goober5000andClaude Opus 4.7 ba7aa70794 Fix 32-bit truncation in the flagset u64 round-trip
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]>
2026-08-30 16:46:11 -04:00
Goober5000 1c817b5965 Merge pull request #7737 from Goober5000/refactor/team_data
refactor team loadout data structures in preparation for lifting the ship class limit
2026-08-28 16:48:39 -04:00
Goober5000 a7999b1a00 Merge pull request #7719 from Goober5000/fix/events_in_campaign_editor
restore mission/goal/event listing in FRED2's campaign editor
2026-08-27 13:12:51 -04:00
Goober5000andClaude Opus 4.7 47b2f0b3ca Consolidate the starting-wing usage builders in missioneditor
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]>
2026-08-27 00:58:20 -04:00
Goober5000andClaude Opus 4.7 49a9141073 Convert team_data to sparse loadout entry vectors
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]>
2026-08-27 00:58:20 -04:00
Fabian Woltermann f241ddb3e3 Use the OS-specific path separator when saving missions (#7744) 2026-08-25 07:30:29 +00:00
Goober5000andClaude Fable 5 c99ee7c741 restore mission/goal/event listing in FRED2's campaign editor
The sexp tree refactor (#7301) moved the OPF listing logic into a shared
model that asks the editor for campaign-aware lists via virtual methods
on SexpTreeEditorInterface.  QtFRED's campaign editor got the necessary
overrides, but FRED2's never did, so its OPF_MISSION_NAME /
OPF_GOAL_NAME / OPF_EVENT_NAME dropdowns fell back to the currently
loaded mission's data instead of the campaign's.

Backport the overrides to FRED2, restoring its pre-refactor behavior
(including listing only missions at earlier campaign levels), and hoist
QtFRED's loadAndFindCampaignMission helper into missioneditor/common so
both editors share one copy.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-08-24 21:07:10 -04:00
Goober5000 304dbb60c5 Merge pull request #7680 from Goober5000/refactor/dense_class_data
store campaign allowed-class lists in sets
2026-08-04 23:10:07 -04:00
Goober5000andClaude Opus 4.7 30d81a83fb store campaign allowed-class lists in sets
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]>
2026-08-04 22:14:58 -04:00
Goober5000andClaude Opus 4.8 41ce07b33a Extract common name conflict checker for FRED2 and qtFRED
The ship editor, wing editor, waypoint path dialog, and jump node
dialog all had near-identical code to check whether a new name
conflicts with an existing ship, wing, waypoint path, jump node, or
target priority group (or is empty, or begins with '<').  Both FRED2
and qtFRED carried their own copies of this logic.

Extract it into a single check_name_conflict() function in
code/missioneditor/common.cpp, shared by both editors, and replace the
duplicated validation at each call site with a one-line call:

  - FRED2:  shipeditordlg, wing_editor, waypointpathdlg, jumpnodedlg
  - qtFRED: ShipEditorDialogModel, WaypointEditorDialogModel,
            JumpNodeEditorDialogModel, and Editor::validate_wing_name

This also aligns a few small behavioral differences that had crept in
between the two editors: the waypoint and jump node dialogs now skip
empty wing slots (wave_count == 0) when checking for wing-name
collisions, and the qtFRED wing editor now rejects names beginning
with '<'.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-07-31 00:26:07 -04:00
John Fernandez 92384b3c60 More Search UI/UX Enhancements (#7612)
* Prioritize items that begin with search string

Items that begin with the search string should be prioritized before those that simply contain the search string.

* Add Search Operators option

* Add Safety to Action Handler

I didn't see any crashes, but this matches the pattern of other keyboard shortcuts.

* Allow search for numeric nodes

The working code existed already

* Limit matches on operator search

This limits matches on operator searches by setting a minimum size that a string match has to meet.  Function returns -1 on failure, and the warning has been removed to allow this behavior.

* Try this option name better

* Add keyboard shortcut for search

This allows ctrl+s (which can be listed instead of spacebar) to initiate search.  There is a new menu item added as well.

* Put this in the correct place

* Address Feedback

* Wrong state

We want the item to be inserted here, not ignored.
2026-07-30 12:59:33 -04:00
Mike Nelson e75f72456e QtFRED add object reorder dialog (#7636)
* Add Reorder Objects dialog to QtFRED

* clang

* clang again

* address feedback
2026-07-22 20:31:38 -04:00
Goober5000andClaude Opus 4.7 175f414c03 add Wings[] slot reassignment utilities for FRED/qtFRED
reassign_wing_slot moves a wing between Wings[] slots, fixing up every
back-reference: Ships[i].wingnum, the Starting/Squadron/TVT_wings caches
(via update_custom_wing_indexes), the FRED-side parallel array
wing_objects, and cur_wing (passed via FredWingSlotConfig so non-FRED
callers can opt out).  swap_wing_slots wraps it as a three-leg swap
through a temporary empty slot.

Also consolidates update_custom_wing_indexes (previously duplicated
verbatim in fred2/management.cpp and qtfred Editor) into common.cpp so
the new utility — and any future caller — has one shared implementation.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-07-20 23:04:56 -04:00
Goober5000andClaude Opus 4.7 74b2776c00 add Ships[] slot reassignment utilities for FRED/qtFRED
reassign_ship_slot moves a ship between Ships[] slots, fixing up every
back-reference: Objects[].instance, Ai_info[].shipnum, Wings[].ship_index[],
Player_start_shipnum, Ship_registry's cached shipnum, and the FRED-side
parallel arrays Fred_alt_names/Fred_callsigns plus cur_ship (passed via
FredShipSlotConfig so non-FRED callers can opt out).  swap_ship_slots
wraps it as a three-leg swap through a temporary empty slot.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-07-20 23:04:56 -04:00
Goober5000 cdb11f7357 patch mission format compatibility for new support features (#7624)
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.
2026-07-20 16:51:46 -04:00
Goober5000 b82fa2a996 refactor CJumpNode to use SCP_vector
Jump nodes previously used SCP_list; refactor to use SCP_vector.  Also improve resource handling and life cycle management.

Follow-up to #7431.
2026-07-19 22:25:33 -04:00
Goober5000andClaude Fable 5 9bebf2592f give the "Set as Player Ship" button exclusive semantics in both editors (#7605)
In single-player missions, the "Set as Player Ship" button is supposed to make
the selected ship the one and only player start.  FRED did this correctly, but
qtFRED routed the button through the same setPlayer() toggle used by the
multiplayer "Player Ship" checkbox, which only promoted the selected ship and
never demoted the previous player start.  Since the checkbox is disabled in
single player, there was no way to demote the old start, so single-player
missions could accumulate multiple player starts.

Consolidate the exclusive logic into a shared helper,
set_single_player_start() in missioneditor/common.cpp, and call it from both
FRED's OnSetAsPlayerShip() and a new qtFRED model method,
makeSolePlayerStart(), which the button now uses.  This also avoids
setPlayer()'s early return on stale dialog state, which could cause the button
to silently do nothing.

Follow-up to #7600.

Co-authored-by: Claude Fable 5 <[email protected]>
2026-07-14 20:05:13 -04:00
Goober5000 dab690eab8 Merge pull request #7600 from Goober5000/fix/ship_demotion
fix a bug with player ships being demoted to common ships
2026-07-12 20:15:54 -04:00
Goober5000andClaude Opus 4.8 7d5bab25e3 fix a bug with player ships being demoted to common ships
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]>
2026-07-12 19:32:39 -04:00
Goober5000 5c17ab6352 Merge pull request #7599 from Goober5000/fix/misc
miscellaneous small fixes
2026-07-11 22:22:07 -04:00
Goober5000 50f62b3ccf fix an Assertion and a latent campaign option 2026-07-11 21:02:36 -04:00
Goober5000andClaude Fable 5 35e1686c2f const-qualify read-only sexp tree methods
Add const to query methods that the sexp_tree refactor left non-const:

- FRED2/qtFRED views: node, type, handle, and root lookups; label-edit
  eligibility; annotation key resolution and comment getters; qtFRED's
  validOperatorsForNode
- SexpTreeOPF: all instance get_listing_opf_* functions plus the
  dispatcher and get_default_value (the class holds only a reference
  to its model, so these are read-only by construction)
- SexpTreeModel: compute_context_menu_state and the two remaining
  ctx_* helpers, which write only their state out-parameter
- ISexpTreeUI: the two read-only navigation callbacks
  (ui_get_child_item, ui_has_children) in the interface and both
  frontend implementations

Also convert the four listing functions that use no editor state at all
(species, language, functional_when_eval_type, wing_formation) to
static, removing the NOLINT suppressions that had silenced clang-tidy's
convert-to-static warning since before the refactor.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-11 17:11:05 -04:00
Goober5000andClaude Fable 5 50de61cf74 prevent zombie annotations and restore help box root-label comments
Root-label annotations (event-name comments/colors) that lose their
node — e.g. because their event was deleted — could survive
indefinitely and surface on the wrong items:

- saveToGlobal's root-key branch kept an unresolvable annotation in
  Event_annotations with an empty path instead of marking it for
  pruning, so once a zombie was persisted in a mission file it
  survived every save/load cycle.
- getByKey(-1) matched annotations whose node_index had been reset to
  the unresolved sentinel, so such a zombie's comment could display on
  any item that has no annotation key of its own.

Guard findByKey against the -1 sentinel, and prune any annotation
whose rebuilt path is empty at save time.  (The complementary
in-session fixes — removing a root-label annotation when its event is
deleted and re-keying it when insert-operator changes the formula
node — are handled in the shared model and actions by 'Keep event
annotations attached to their node across tree mutations'.)

Also restore root-label comments in the FRED2 help box: update_help
resolved comments by model node index, which is -1 for labels.  Route
it through a get_item_comment virtual that the event editor's tree
overrides with its annotation key lookup, matching pre-refactor
behavior.  (qtFRED renders comments per-item via NoteRole, which
already covers labels; its help box does not show comments by design.)

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-11 01:17:31 -04:00
Goober5000andClaude Fable 5 a7cb9bbf32 Restore pre-refactor on-disk format for event annotation paths
Annotation +Path: values written by pre-refactor FRED were built over
the UI tree, where each event's root item is the event-name label and
every element after the event index is a child position at successive
levels below it: [event] is the label, [event, 0] the top operator
(the label's only child), [event, 0, k...] its descendants.  The
refactored SexpAnnotationModel built paths over tree_nodes[], which
represents the label's single child directly as the formula node, so
paths came out one level shallower: existing missions' annotations
resolved one level too deep (and were re-saved wrong), and a
top-operator annotation produced the length-1 path that decodes as a
root-label key, silently migrating onto the event label.

Emit and consume the label-level child position in
buildPath/resolveFromPath to preserve the on-disk contract.  Child 0
resolves to the formula node and any other index fails resolution,
matching the old traverse_path behavior of walking off the label's
sibling list.  Annotations on the event label itself are unaffected:
they remain the length-1 path handled by the root-key branch.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-11 01:17:31 -04:00
Goober5000andClaude Fable 5 6825100f42 Keep event annotations attached to their node across tree mutations
Post-refactor successor of fix/annotations, rebuilt for the shared sexp
tree model introduced by the sexp_tree refactor (#7301).  The refactor
re-keyed annotations from UI item handles to tree_nodes[] indices, which
fixed most of the original decoupling by construction and already prunes
regular-node annotations in free_node2().  Two gaps remained, plus one
qtFRED regression:

- free_node2() only removed annotations keyed by node index, never a
  root-label annotation keyed rootKey(formula).  Deleting an event left
  that annotation live, so a later event whose formula reused the slot
  inherited the deleted event's annotation.  Remove the root key too;
  since the global Event_annotations are only rewritten at save, the
  removal still survives a Cancel.

- Inserting an operator above a labeled root changes the event's formula
  node, orphaning the root-label annotation keyed to the old index.
  Re-key it in insert_operator() so it follows the event.  (This is the
  only such flow: replace_operator() reuses the node in place.)

- qtFRED reorderByRootFormulaOrder() reloaded annotations from the
  global state, discarding any annotation edits made since the dialog
  was opened.  A root reorder changes neither node indices nor formulas,
  so drop the reload.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-11 01:17:31 -04:00
Goober5000 3a1e802965 nullptr and SIZE_T_ARG 2026-07-10 13:42:41 -04:00
Goober5000andClaude Fable 5 8ab9f907b5 remove vestigial text_buf parameter from get_default_value()
The buffer round-trip was left over from the pre-refactor char-array
days: *item = *list already copies the text, and no caller reads the
buffer after the call. Removing it also removes the unbounded strcpy
into the callers' 256-byte stack buffers.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-10 13:42:41 -04:00
Goober5000andClaude Fable 5 c6e2603e19 fix move_branch() reparenting to root when parent index is 0
The only caller, SexpTreeActions::insert_operator(), passes a freshly
allocated tree_nodes index as the parent, and allocate_node() reuses
freed slots, so 0 is a valid parent index. The old 'if (parent)' test
(and qtFRED's 'parent > 0' copy of it) treated node 0 as attach-at-
tree-root, visually reparenting the wrapped branch to the root while
the model data placed it under node 0. Since the assertion already
requires parent >= 0, attach to the parent's handle unconditionally.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-10 13:42:41 -04:00
Goober5000andClaude Fable 5 1aeb6a6957 delete copy/move on the sexp tree model, OPF, and actions classes
SexpTreeOPF and SexpTreeActions hold references to their owning model
and UI, and SexpTreeModel owns an OPF member back-referencing itself,
so a copy would compile (copy construction was not implicitly deleted,
only copy assignment) but leave the copy's _opf silently bound to the
original model.  Nothing copies or moves these classes today; delete
the operations so nothing can.

The other sexp tree types were checked and are correct as-is:
SexpContextMenuState already deletes copy and implements move via
swap; SexpAnnotationModel and the item structs are deliberate value
types; sexp_list_item's shallow copy with manual destroy() is the
inherited idiom its callers rely on; the views are non-copyable via
their MFC/Qt base classes.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-10 13:42:41 -04:00
Goober5000andClaude Opus 4.8 bed228d264 avoid undefined behavior from UNREACHABLE on reachable code paths (#7555)
* 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]>
2026-07-09 11:20:25 +09:00
Mike Nelson 4d8538e5cc Qtfred sexp tree refactor (#7301)
* 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
2026-07-04 02:12:34 +02:00
Mike Nelson f4d84ec47a Support Rearm Pool, Limits, and Other Features (#7249)
* 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
2026-06-05 11:33:19 +02:00
Mike Nelson 03ea836f86 Qtfred autosave system (#7463)
* remove legacy undo system from QtFRED

* implement timed autosave feature

* clang

* undo comments change
2026-06-04 13:35:13 +02:00
Mike Nelson 05534f709e Qtfred & FRED fix object cloning (#7460)
* fix ship duplication

* prop and jump node duplication

* make sure objects on layers are properly registered

* waypoint duplication

* some cleanup

* address feedback

* assertion for clang
2026-06-02 17:30:38 -04:00
BMagnuandGoober5000 20c8417e8b Fullneb Revamp (#7352)
* 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]>
2026-05-28 14:43:48 +09:00
Goober5000 7545e5d035 make the reinforcements collection dynamic
As requested by a FREDder who has a plausible situation where more than 32 reinforcements might be handy. :)
2026-05-26 10:55:05 -04:00
Goober5000 70c4010389 add wing display names (#7429)
* 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
2026-05-20 15:16:08 -04:00
Goober5000 ef73e2572d unlimited mission titles
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.
2026-05-12 11:31:20 -04:00
Mike Nelson 975ce277a7 QtFRED large ship collision group mission flag (#7425)
* large ship collision group mission flag

* update help doc
2026-05-12 07:51:00 -04:00
Goober5000andClaude Opus 4.6 3f85d6172a use std::move() for local variables that are copied but not reused (#7308)
* 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]>
2026-05-07 14:40:38 +09:00
Mike Nelson b1cefade1a Qtfred cargo title and scanning fields (#7387)
* implement newer cargo fields and clarify subsystem cargo settings

* static
2026-04-20 07:52:16 -04:00
Mike Nelson 4084c75678 Qtfred Dockable object list (#7368)
* 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
2026-04-15 23:07:01 -04:00
Mike Nelson 1dd8f76861 Qtfred bugfix pass (#7363) 2026-04-10 09:03:52 -04:00
Mike Nelson 41a8b18fad Qtfred visibility layers (#7314)
* add visibility layers

* save/load object layers using the mission file

* use fill

* fix rebase error
2026-04-04 09:11:55 -04:00
Mike Nelson f991bf14a8 Qtfred cleanup pass (#7345)
* set line edit fields to max length in code

* fix some dialog window titles

* remove unused context help button on Windows

* address some easy todo items

* static access
2026-04-04 09:11:31 -04:00