Skip to content

Commit 18aa931

Browse files
Goober5000claude
andcommitted
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) <noreply@anthropic.com>
1 parent a5bdef3 commit 18aa931

7 files changed

Lines changed: 114 additions & 51 deletions

File tree

code/missioneditor/common.cpp

Lines changed: 87 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,20 @@ anchor_t target_to_anchor(int target)
9898
return anchor_t(target);
9999
}
100100

101+
void update_custom_wing_indexes()
102+
{
103+
int i;
104+
105+
for (i = 0; i < MAX_STARTING_WINGS; i++)
106+
Starting_wings[i] = wing_name_lookup(Starting_wing_names[i], 1);
107+
108+
for (i = 0; i < MAX_SQUADRON_WINGS; i++)
109+
Squadron_wings[i] = wing_name_lookup(Squadron_wing_names[i], 1);
110+
111+
for (i = 0; i < MAX_TVT_WINGS; i++)
112+
TVT_wings[i] = wing_name_lookup(TVT_wing_names[i], 1);
113+
}
114+
101115
void generate_weaponry_usage_list_team(int team, int* arr)
102116
{
103117
int i;
@@ -214,14 +228,14 @@ void reassign_ship_slot(int from, int to, const FredShipSlotConfig& cfg)
214228
// Wing membership: scan every wing and re-point any reference to the old slot.
215229
// (wing.special_ship is wing-relative, NOT a Ships[] index, so it is intentionally
216230
// not touched here.)
217-
for (int w = 0; w < MAX_WINGS; ++w)
231+
for (auto& w : Wings)
218232
{
219-
if (Wings[w].wave_count == 0)
233+
if (w.wave_count == 0)
220234
continue;
221-
for (int k = 0; k < Wings[w].wave_count; ++k)
235+
for (int k = 0; k < w.wave_count; ++k)
222236
{
223-
if (Wings[w].ship_index[k] == from)
224-
Wings[w].ship_index[k] = to;
237+
if (w.ship_index[k] == from)
238+
w.ship_index[k] = to;
225239
}
226240
}
227241

@@ -268,6 +282,74 @@ void swap_ship_slots(int a, int b, const FredShipSlotConfig& cfg)
268282
reassign_ship_slot(tmp, b, cfg);
269283
}
270284

285+
void reassign_wing_slot(int from, int to, const FredWingSlotConfig& cfg)
286+
{
287+
Assertion(from != to, "reassign_wing_slot: from == to (%d)", from);
288+
Assertion(from >= 0 && from < MAX_WINGS, "reassign_wing_slot: 'from' slot %d out of range", from);
289+
Assertion(to >= 0 && to < MAX_WINGS, "reassign_wing_slot: 'to' slot %d out of range", to);
290+
Assertion(Wings[from].wave_count > 0, "reassign_wing_slot: source slot %d is empty", from);
291+
Assertion(Wings[to].wave_count == 0, "reassign_wing_slot: destination slot %d is occupied", to);
292+
293+
// Move the wing struct itself. wing::clear() is the engine's canonical
294+
// empty-slot state (matches ship_level_init); wave_count == 0 is the sentinel.
295+
Wings[to] = Wings[from];
296+
Wings[from].clear();
297+
298+
// Move FRED-side parallel array if the caller supplied it.
299+
if (cfg.wing_objects != nullptr)
300+
{
301+
for (int k = 0; k < MAX_SHIPS_PER_WING; ++k)
302+
{
303+
cfg.wing_objects[to][k] = cfg.wing_objects[from][k];
304+
cfg.wing_objects[from][k] = -1;
305+
}
306+
}
307+
308+
// Per-ship parent-wing back-reference.
309+
for (auto& sh : Ships)
310+
{
311+
if (sh.objnum < 0)
312+
continue;
313+
if (sh.wingnum == from)
314+
sh.wingnum = to;
315+
}
316+
317+
// FRED's current-wing pointer, if the caller is tracking one.
318+
if (cfg.cur_wing != nullptr && *cfg.cur_wing == from)
319+
*cfg.cur_wing = to;
320+
321+
// Rebuild Starting/Squadron/TVT_wings caches from the parallel name arrays.
322+
update_custom_wing_indexes();
323+
}
324+
325+
void swap_wing_slots(int a, int b, const FredWingSlotConfig& cfg)
326+
{
327+
if (a == b)
328+
return;
329+
330+
Assertion(a >= 0 && a < MAX_WINGS, "swap_wing_slots: slot 'a' %d out of range", a);
331+
Assertion(b >= 0 && b < MAX_WINGS, "swap_wing_slots: slot 'b' %d out of range", b);
332+
Assertion(Wings[a].wave_count > 0 && Wings[b].wave_count > 0,
333+
"swap_wing_slots: both slots must be valid (a=%d, b=%d)", a, b);
334+
335+
// Find a free temporary slot.
336+
int tmp = -1;
337+
for (int i = 0; i < MAX_WINGS; ++i)
338+
{
339+
if (Wings[i].wave_count == 0)
340+
{
341+
tmp = i;
342+
break;
343+
}
344+
}
345+
Assertion(tmp >= 0, "swap_wing_slots: no free Wings[] slot available for the temporary leg");
346+
347+
// Three-leg swap; each call's preconditions hold by construction.
348+
reassign_wing_slot(a, tmp, cfg);
349+
reassign_wing_slot(b, a, cfg);
350+
reassign_wing_slot(tmp, b, cfg);
351+
}
352+
271353
// Bulk-re-sort one type's subset of obj_used_list while keeping non-matching
272354
// entries in their original relative positions. Each callsite supplies a
273355
// type matcher and a key function; the i-th matching slot (in original list

code/missioneditor/common.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ int anchor_to_target(anchor_t anchor);
4545

4646
anchor_t target_to_anchor(int target);
4747

48+
// Rebuild Starting_wings[], Squadron_wings[], TVT_wings[] from their parallel
49+
// name arrays via wing_name_lookup. Consolidated from FRED's and qtFRED's
50+
// previously-duplicated copies.
51+
void update_custom_wing_indexes();
52+
4853
void generate_weaponry_usage_list_team(int team, int* arr);
4954

5055
void generate_weaponry_usage_list_wing(int wing_num, int* arr);
@@ -70,6 +75,25 @@ void reassign_ship_slot(int from, int to, const FredShipSlotConfig& cfg);
7075
// via a temporary empty slot.
7176
void swap_ship_slots(int a, int b, const FredShipSlotConfig& cfg);
7277

78+
struct FredWingSlotConfig
79+
{
80+
int (*wing_objects)[MAX_SHIPS_PER_WING] = nullptr;
81+
int *cur_wing = nullptr;
82+
};
83+
84+
// Move the wing currently in Wings[from] into Wings[to], updating every
85+
// back-reference (Ships[i].wingnum, Starting/Squadron/TVT_wings caches, and
86+
// editor-side fields supplied via cfg). Leaves Wings[from] empty.
87+
// Preconditions: from != to, Wings[from].wave_count > 0, Wings[to].wave_count == 0.
88+
// No caller may hold a wing* to either slot across this call.
89+
// Fields in cfg whose pointers are nullptr are skipped.
90+
void reassign_wing_slot(int from, int to, const FredWingSlotConfig& cfg);
91+
92+
// Swap the contents of two slots. Both must be valid (Wings[a].wave_count > 0
93+
// and Wings[b].wave_count > 0). Implemented as three calls to
94+
// reassign_wing_slot via a temporary empty slot.
95+
void swap_wing_slots(int a, int b, const FredWingSlotConfig& cfg);
96+
7397
// Restore the obj_used_list invariant for the OBJ_SHIP/OBJ_START subset:
7498
// among ship-type entries, list order matches Ships[] index order.
7599
void resort_ships_in_obj_used_list();

fred2/management.cpp

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2625,29 +2625,6 @@ int wing_is_player_wing(int wing)
26252625
return 0;
26262626
}
26272627

2628-
// Goober5000
2629-
// This must be done when either the wing name or the custom name is changed.
2630-
// (It's also duplicated in FS2, in post_process_mission, for setting the indexes at mission load.)
2631-
void update_custom_wing_indexes()
2632-
{
2633-
int i;
2634-
2635-
for (i = 0; i < MAX_STARTING_WINGS; i++)
2636-
{
2637-
Starting_wings[i] = wing_name_lookup(Starting_wing_names[i], 1);
2638-
}
2639-
2640-
for (i = 0; i < MAX_SQUADRON_WINGS; i++)
2641-
{
2642-
Squadron_wings[i] = wing_name_lookup(Squadron_wing_names[i], 1);
2643-
}
2644-
2645-
for (i = 0; i < MAX_TVT_WINGS; i++)
2646-
{
2647-
TVT_wings[i] = wing_name_lookup(TVT_wing_names[i], 1);
2648-
}
2649-
}
2650-
26512628
// Goober5000
26522629
void update_texture_replacements(const char *old_name, const char *new_name)
26532630
{

fred2/management.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,6 @@ extern void management_add_ships_to_combo(CComboBox* box, int flags);
131131

132132
// Goober5000
133133
extern int wing_is_player_wing(int wing);
134-
extern void update_custom_wing_indexes();
135134
extern void update_texture_replacements(const char* old_name, const char* new_name);
136135

137136
#endif

qtfred/src/mission/Editor.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -188,11 +188,6 @@ class Editor : public QObject {
188188

189189
subsys_to_render Render_subsys;
190190

191-
// Goober5000
192-
// This must be done when either the wing name or the custom name is changed.
193-
// (It's also duplicated in FS2, in post_process_mission, for setting the indexes at mission load.)
194-
static void update_custom_wing_indexes();
195-
196191
void ai_update_goal_references(sexp_ref_type type, const char* old_name, const char* new_name);
197192

198193
// Goober5000

qtfred/src/mission/EditorWing.cpp

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
#include <globalincs/linklist.h>
77
#include <globalincs/utility.h>
8+
#include <missioneditor/common.h>
89
#include <ship/ship.h>
910

1011
namespace {
@@ -69,22 +70,6 @@ void Editor::set_cur_wing(int wing)
6970
updateAllViewports();
7071
// TODO: Add notification for a changed selection
7172
}
72-
void Editor::update_custom_wing_indexes()
73-
{
74-
int i;
75-
76-
for (i = 0; i < MAX_STARTING_WINGS; i++) {
77-
Starting_wings[i] = wing_name_lookup(Starting_wing_names[i], 1);
78-
}
79-
80-
for (i = 0; i < MAX_SQUADRON_WINGS; i++) {
81-
Squadron_wings[i] = wing_name_lookup(Squadron_wing_names[i], 1);
82-
}
83-
84-
for (i = 0; i < MAX_TVT_WINGS; i++) {
85-
TVT_wings[i] = wing_name_lookup(TVT_wing_names[i], 1);
86-
}
87-
}
8873

8974
int Editor::create_wing()
9075
{

qtfred/src/mission/dialogs/MissionSpecDialogModel.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#include "cfile/cfile.h"
1414
#include "localization/localize.h"
15+
#include "missioneditor/common.h"
1516
#include "mission/missionmessage.h"
1617
#include "mission/mission_flags.h"
1718
#include "scripting/global_hooks.h"
@@ -227,7 +228,7 @@ bool MissionSpecDialogModel::apply() {
227228
strcpy_s(TVT_wing_names[i], _m_custom_tvt_wings[i].c_str());
228229
}
229230

230-
Editor::update_custom_wing_indexes();
231+
update_custom_wing_indexes();
231232

232233
// scripts may rebuild LuaEnums when custom data/strings change.
233234
if (scripting::hooks::FredOnMissionSpecsSave->isActive()) {

0 commit comments

Comments
 (0)