Skip to content

Commit 4951673

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 9bab7b3 commit 4951673

7 files changed

Lines changed: 102 additions & 46 deletions

File tree

code/missioneditor/common.cpp

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,20 @@ anchor_t target_to_anchor(int target)
9797
return anchor_t(target);
9898
}
9999

100+
void update_custom_wing_indexes()
101+
{
102+
int i;
103+
104+
for (i = 0; i < MAX_STARTING_WINGS; i++)
105+
Starting_wings[i] = wing_name_lookup(Starting_wing_names[i], 1);
106+
107+
for (i = 0; i < MAX_SQUADRON_WINGS; i++)
108+
Squadron_wings[i] = wing_name_lookup(Squadron_wing_names[i], 1);
109+
110+
for (i = 0; i < MAX_TVT_WINGS; i++)
111+
TVT_wings[i] = wing_name_lookup(TVT_wing_names[i], 1);
112+
}
113+
100114
void generate_weaponry_usage_list_team(int team, int* arr)
101115
{
102116
int i;
@@ -245,6 +259,11 @@ static bool ship_slot_is_empty(int i)
245259
return Ships[i].objnum < 0;
246260
}
247261

262+
static bool wing_slot_is_empty(int i)
263+
{
264+
return Wings[i].wave_count == 0;
265+
}
266+
248267
static int find_free_slot(int max_slots, bool (*slot_is_empty)(int), const char* caller)
249268
{
250269
for (int i = 0; i < max_slots; ++i)
@@ -314,6 +333,58 @@ void rotate_ship_slots(const SCP_vector<int>& slots, int from_pos, int to_pos, c
314333
rotate_slots(slots, from_pos, to_pos, cfg, MAX_SHIPS, ship_slot_is_empty, reassign_ship_slot, "rotate_ship_slots");
315334
}
316335

336+
void reassign_wing_slot(int from, int to, const FredWingSlotConfig& cfg, bool update_wing_indexes)
337+
{
338+
Assertion(from != to, "reassign_wing_slot: from == to (%d)", from);
339+
Assertion(from >= 0 && from < MAX_WINGS, "reassign_wing_slot: 'from' slot %d out of range", from);
340+
Assertion(to >= 0 && to < MAX_WINGS, "reassign_wing_slot: 'to' slot %d out of range", to);
341+
Assertion(Wings[from].wave_count > 0, "reassign_wing_slot: source slot %d is empty", from);
342+
Assertion(Wings[to].wave_count == 0, "reassign_wing_slot: destination slot %d is occupied", to);
343+
344+
// Move the wing struct itself; wave_count == 0 is the sentinel for an empty wing.
345+
Wings[to] = std::move(Wings[from]);
346+
Wings[from].wave_count = 0;
347+
348+
// Move FRED-side parallel array if the caller supplied it.
349+
if (cfg.wing_objects != nullptr)
350+
{
351+
for (int k = 0; k < MAX_SHIPS_PER_WING; ++k)
352+
{
353+
cfg.wing_objects[to][k] = cfg.wing_objects[from][k];
354+
cfg.wing_objects[from][k] = -1;
355+
}
356+
}
357+
358+
// Per-ship parent-wing back-reference.
359+
for (auto &s: Ships)
360+
{
361+
if (s.objnum < 0)
362+
continue;
363+
if (s.wingnum == from)
364+
s.wingnum = to;
365+
}
366+
367+
// FRED's current-wing pointer, if the caller is tracking one.
368+
if (cfg.cur_wing != nullptr && *cfg.cur_wing == from)
369+
*cfg.cur_wing = to;
370+
371+
// Rebuild Starting/Squadron/TVT_wings caches from the parallel name arrays.
372+
// The rebuild is total rather than incremental, so a caller making a batch
373+
// of reassignments may defer it to the final call.
374+
if (update_wing_indexes)
375+
update_custom_wing_indexes();
376+
}
377+
378+
void swap_wing_slots(int a, int b, const FredWingSlotConfig& cfg)
379+
{
380+
swap_slots(a, b, cfg, MAX_WINGS, wing_slot_is_empty, reassign_wing_slot, "swap_wing_slots");
381+
}
382+
383+
void rotate_wing_slots(const SCP_vector<int>& slots, int from_pos, int to_pos, const FredWingSlotConfig& cfg)
384+
{
385+
rotate_slots(slots, from_pos, to_pos, cfg, MAX_WINGS, wing_slot_is_empty, reassign_wing_slot, "rotate_wing_slots");
386+
}
387+
317388
// Bulk-re-sort one type's subset of obj_used_list while keeping non-matching
318389
// entries in their original relative positions. Each callsite supplies a
319390
// type matcher and a key function; the i-th matching slot (in original list

code/missioneditor/common.h

Lines changed: 28 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);
@@ -74,6 +79,29 @@ void swap_ship_slots(int a, int b, const FredShipSlotConfig& cfg);
7479
// items in between by one position.
7580
void rotate_ship_slots(const SCP_vector<int>& slots, int from_pos, int to_pos, const FredShipSlotConfig& cfg);
7681

82+
struct FredWingSlotConfig
83+
{
84+
int (*wing_objects)[MAX_SHIPS_PER_WING] = nullptr;
85+
int *cur_wing = nullptr;
86+
};
87+
88+
// Move the wing currently in Wings[from] into Wings[to], updating every
89+
// back-reference (Ships[i].wingnum, Starting/Squadron/TVT_wings caches, and
90+
// editor-side fields supplied via cfg). Leaves Wings[from] empty.
91+
// Preconditions: from != to, Wings[from].wave_count > 0, Wings[to].wave_count == 0.
92+
// No caller may hold a wing* to either slot across this call.
93+
// Fields in cfg whose pointers are nullptr are skipped.
94+
void reassign_wing_slot(int from, int to, const FredWingSlotConfig& cfg, bool update_wing_indexes = true);
95+
96+
// Swap the contents of two slots. Both must be valid (Wings[a].wave_count > 0
97+
// and Wings[b].wave_count > 0). Implemented as three calls to
98+
// reassign_wing_slot via a temporary empty slot.
99+
void swap_wing_slots(int a, int b, const FredWingSlotConfig& cfg);
100+
101+
// Move the item at position from_pos in slots to position to_pos, shifting the
102+
// items in between by one position.
103+
void rotate_wing_slots(const SCP_vector<int>& slots, int from_pos, int to_pos, const FredWingSlotConfig& cfg);
104+
77105
// Restore the obj_used_list invariant for the OBJ_SHIP/OBJ_START subset:
78106
// among ship-type entries, list order matches Ships[] index order.
79107
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)