Skip to content

Commit c144478

Browse files
committed
check missions for missing hangar bays
It's possible for ships to be designated as arrival or departure anchors when these ships do not actually have hangar bays. This cannot normally happen in the interface but can happen when missions are edited in a text editor, or are imported, or the modpack is changed. So add warnings and checks for this situation.
1 parent 300cd8a commit c144478

File tree

5 files changed

+114
-8
lines changed

5 files changed

+114
-8
lines changed

code/mission/missionparse.cpp

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6622,6 +6622,15 @@ bool post_process_mission(mission *pm)
66226622

66236623
// convert all ship name indices to ship indices now that mission has been loaded
66246624
if (Fred_running) {
6625+
// lambda for seeing whether the anchors actually work for arrival/departure
6626+
SCP_string message;
6627+
SCP_set<int> anchors_checked;
6628+
auto check_anchor = [&message, &anchors_checked](int anchor_shipnum, const char *other_name, bool other_is_ship, bool is_arrival) {
6629+
check_anchor_for_hangar_bay(message, anchors_checked, anchor_shipnum, other_name, other_is_ship, is_arrival);
6630+
if (!message.empty())
6631+
Warning(LOCATION, "%s", message.c_str());
6632+
};
6633+
66256634
i = 0;
66266635
for (const auto &parse_name: Parse_names) {
66276636
auto ship_entry = ship_registry_get(parse_name);
@@ -6632,21 +6641,32 @@ bool post_process_mission(mission *pm)
66326641
}
66336642

66346643
for (i=0; i<MAX_SHIPS; i++) {
6635-
if ((Ships[i].objnum >= 0) && (Ships[i].arrival_anchor >= 0) && (Ships[i].arrival_anchor < SPECIAL_ARRIVAL_ANCHOR_FLAG))
6644+
if ((Ships[i].objnum >= 0) && (Ships[i].arrival_anchor >= 0) && (Ships[i].arrival_anchor < SPECIAL_ARRIVAL_ANCHOR_FLAG)) {
66366645
Ships[i].arrival_anchor = indices[Ships[i].arrival_anchor];
6646+
if (Ships[i].arrival_location == ArrivalLocation::FROM_DOCK_BAY)
6647+
check_anchor(Ships[i].arrival_anchor, Ships[i].ship_name, true, true);
6648+
}
66376649

6638-
if ( (Ships[i].objnum >= 0) && (Ships[i].departure_anchor >= 0) )
6650+
if ((Ships[i].objnum >= 0) && (Ships[i].departure_anchor >= 0)) {
66396651
Ships[i].departure_anchor = indices[Ships[i].departure_anchor];
6652+
if (Ships[i].departure_location == DepartureLocation::TO_DOCK_BAY)
6653+
check_anchor(Ships[i].departure_anchor, Ships[i].ship_name, true, false);
6654+
}
66406655
}
66416656

66426657
for (i=0; i<MAX_WINGS; i++) {
6643-
if (Wings[i].wave_count && (Wings[i].arrival_anchor >= 0) && (Wings[i].arrival_anchor < SPECIAL_ARRIVAL_ANCHOR_FLAG))
6658+
if (Wings[i].wave_count && (Wings[i].arrival_anchor >= 0) && (Wings[i].arrival_anchor < SPECIAL_ARRIVAL_ANCHOR_FLAG)) {
66446659
Wings[i].arrival_anchor = indices[Wings[i].arrival_anchor];
6660+
if (Wings[i].arrival_location == ArrivalLocation::FROM_DOCK_BAY)
6661+
check_anchor(Wings[i].arrival_anchor, Wings[i].name, false, true);
6662+
}
66456663

6646-
if (Wings[i].wave_count && (Wings[i].departure_anchor >= 0) )
6664+
if (Wings[i].wave_count && (Wings[i].departure_anchor >= 0)) {
66476665
Wings[i].departure_anchor = indices[Wings[i].departure_anchor];
6666+
if (Wings[i].departure_location == DepartureLocation::TO_DOCK_BAY)
6667+
check_anchor(Wings[i].departure_anchor, Wings[i].name, false, false);
6668+
}
66486669
}
6649-
66506670
}
66516671

66526672
// before doing anything else, we must validate all of the sexpressions that were loaded into the mission.
@@ -8673,6 +8693,27 @@ int get_anchor(const char *name)
86738693
return get_parse_name_index(name);
86748694
}
86758695

8696+
/**
8697+
* See if an arrival/departure anchor is missing a hangar bay. If it is, the message parameter will be populated with an appropriate error.
8698+
*/
8699+
void check_anchor_for_hangar_bay(SCP_string &message, SCP_set<int> &anchor_shipnums_checked, int anchor_shipnum, const char *other_name, bool other_is_ship, bool is_arrival)
8700+
{
8701+
message.clear();
8702+
8703+
if (anchor_shipnum < 0)
8704+
return;
8705+
if (anchor_shipnums_checked.contains(anchor_shipnum))
8706+
return;
8707+
anchor_shipnums_checked.insert(anchor_shipnum);
8708+
8709+
if (!ship_has_dock_bay(anchor_shipnum))
8710+
{
8711+
auto shipp = &Ships[anchor_shipnum];
8712+
sprintf(message, "%s (%s) is used as a%s anchor by %s %s (and possibly elsewhere too), but it does not have a hangar bay!", shipp->ship_name,
8713+
Ship_info[shipp->ship_info_index].name, is_arrival ? "n arrival" : " departure", other_is_ship ? "ship" : "wing", other_name);
8714+
}
8715+
};
8716+
86768717
/**
86778718
* Fixup the goals/ai references for player objects in the mission
86788719
*/

code/mission/missionparse.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ enum class DepartureLocation;
4949
#define MIN_TARGET_ARRIVAL_MULTIPLIER 2.0f // minimum distance is 2 * target radius, but at least 500
5050

5151
int get_special_anchor(const char *name);
52+
void check_anchor_for_hangar_bay(SCP_string &message, SCP_set<int> &anchor_shipnums_checked, int anchor_shipnum, const char *other_name, bool other_is_ship, bool is_arrival);
5253

5354
// MISSION_VERSION should be the earliest version of FSO that can load the current mission format without
5455
// requiring version-specific comments. It should be updated whenever the format changes, but it should

code/ship/ship.cpp

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19981,9 +19981,21 @@ bool ship_has_dock_bay(int shipnum)
1998119981
{
1998219982
Assert(shipnum >= 0 && shipnum < MAX_SHIPS);
1998319983

19984-
polymodel *pm;
19985-
19986-
pm = model_get( Ship_info[Ships[shipnum].ship_info_index].model_num );
19984+
auto sip = &Ship_info[Ships[shipnum].ship_info_index];
19985+
19986+
// the model might not be loaded yet, so load it explicitly here
19987+
if (sip->model_num < 0)
19988+
{
19989+
if (VALID_FNAME(sip->pof_file))
19990+
sip->model_num = model_load(sip->pof_file, sip);
19991+
}
19992+
if (sip->model_num < 0)
19993+
{
19994+
Warning(LOCATION, "%s does not have a valid model number!", sip->name);
19995+
return false;
19996+
}
19997+
19998+
auto pm = model_get(sip->model_num);
1998719999
Assert( pm );
1998820000

1998920001
return ( pm->ship_bay && (pm->ship_bay->num_paths > 0) );

fred2/fredview.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2440,6 +2440,8 @@ int CFREDView::global_error_check()
24402440
int bs, i, j, n, s, t, z, ai, count, ship, wing, obj, team, point, multi;
24412441
object *ptr;
24422442
brief_stage *sp;
2443+
SCP_string anchor_message;
2444+
SCP_set<int> anchor_shipnums_checked;
24432445

24442446
g_err = multi = 0;
24452447
if ( The_mission.game_type & MISSION_TYPE_MULTI )
@@ -2638,6 +2640,12 @@ int CFREDView::global_error_check()
26382640
return 1;
26392641
}
26402642
}
2643+
if (Ships[i].arrival_location == ArrivalLocation::FROM_DOCK_BAY) {
2644+
check_anchor_for_hangar_bay(anchor_message, anchor_shipnums_checked, Ships[i].arrival_anchor, Ships[i].ship_name, true, true);
2645+
if (!anchor_message.empty() && error("%s", anchor_message.c_str())) {
2646+
return 1;
2647+
}
2648+
}
26412649
}
26422650

26432651
if (Ships[i].departure_location != DepartureLocation::AT_LOCATION) {
@@ -2646,6 +2654,12 @@ int CFREDView::global_error_check()
26462654
return 1;
26472655
}
26482656
}
2657+
if (Ships[i].departure_location == DepartureLocation::TO_DOCK_BAY) {
2658+
check_anchor_for_hangar_bay(anchor_message, anchor_shipnums_checked, Ships[i].departure_anchor, Ships[i].ship_name, true, false);
2659+
if (!anchor_message.empty() && error("%s", anchor_message.c_str())) {
2660+
return 1;
2661+
}
2662+
}
26492663
}
26502664

26512665
ai = Ships[i].ai_index;
@@ -2841,12 +2855,24 @@ int CFREDView::global_error_check()
28412855
if (Wings[i].arrival_anchor < 0)
28422856
if (error("Wing \"%s\" requires a valid arrival target", Wings[i].name))
28432857
return 1;
2858+
if (Wings[i].arrival_location == ArrivalLocation::FROM_DOCK_BAY) {
2859+
check_anchor_for_hangar_bay(anchor_message, anchor_shipnums_checked, Wings[i].arrival_anchor, Wings[i].name, false, true);
2860+
if (!anchor_message.empty() && error("%s", anchor_message.c_str())) {
2861+
return 1;
2862+
}
2863+
}
28442864
}
28452865

28462866
if (Wings[i].departure_location != DepartureLocation::AT_LOCATION) {
28472867
if (Wings[i].departure_anchor < 0)
28482868
if (error("Wing \"%s\" requires a valid departure target", Wings[i].name))
28492869
return 1;
2870+
if (Wings[i].departure_location == DepartureLocation::TO_DOCK_BAY) {
2871+
check_anchor_for_hangar_bay(anchor_message, anchor_shipnums_checked, Wings[i].departure_anchor, Wings[i].name, false, false);
2872+
if (!anchor_message.empty() && error("%s", anchor_message.c_str())) {
2873+
return 1;
2874+
}
2875+
}
28502876
}
28512877

28522878
if ((str = error_check_initial_orders(Wings[i].ai_goals, -1, i))>0) {

qtfred/src/mission/Editor.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1964,6 +1964,8 @@ int Editor::global_error_check_impl() {
19641964
int bs, i, j, n, s, t, z, ai, count, ship, wing, obj, team, point, multi;
19651965
object* ptr;
19661966
brief_stage* sp;
1967+
SCP_string anchor_message;
1968+
SCP_set<int> anchor_shipnums_checked;
19671969

19681970
g_err = multi = 0;
19691971
if (The_mission.game_type & MISSION_TYPE_MULTI) {
@@ -2158,6 +2160,12 @@ int Editor::global_error_check_impl() {
21582160
return 1;
21592161
}
21602162
}
2163+
if (Ships[i].arrival_location == ArrivalLocation::FROM_DOCK_BAY) {
2164+
check_anchor_for_hangar_bay(anchor_message, anchor_shipnums_checked, Ships[i].arrival_anchor, Ships[i].ship_name, true, true);
2165+
if (!anchor_message.empty() && error("%s", anchor_message.c_str())) {
2166+
return 1;
2167+
}
2168+
}
21612169
}
21622170

21632171
if (Ships[i].departure_location != DepartureLocation::AT_LOCATION) {
@@ -2166,6 +2174,12 @@ int Editor::global_error_check_impl() {
21662174
return 1;
21672175
}
21682176
}
2177+
if (Ships[i].departure_location == DepartureLocation::TO_DOCK_BAY) {
2178+
check_anchor_for_hangar_bay(anchor_message, anchor_shipnums_checked, Ships[i].departure_anchor, Ships[i].ship_name, true, false);
2179+
if (!anchor_message.empty() && error("%s", anchor_message.c_str())) {
2180+
return 1;
2181+
}
2182+
}
21692183
}
21702184

21712185
ai = Ships[i].ai_index;
@@ -2374,6 +2388,12 @@ int Editor::global_error_check_impl() {
23742388
return 1;
23752389
}
23762390
}
2391+
if (Wings[i].arrival_location == ArrivalLocation::FROM_DOCK_BAY) {
2392+
check_anchor_for_hangar_bay(anchor_message, anchor_shipnums_checked, Wings[i].arrival_anchor, Wings[i].name, false, true);
2393+
if (!anchor_message.empty() && error("%s", anchor_message.c_str())) {
2394+
return 1;
2395+
}
2396+
}
23772397
}
23782398

23792399
if (Wings[i].departure_location != DepartureLocation::AT_LOCATION) {
@@ -2382,6 +2402,12 @@ int Editor::global_error_check_impl() {
23822402
return 1;
23832403
}
23842404
}
2405+
if (Wings[i].departure_location == DepartureLocation::TO_DOCK_BAY) {
2406+
check_anchor_for_hangar_bay(anchor_message, anchor_shipnums_checked, Wings[i].departure_anchor, Wings[i].name, false, false);
2407+
if (!anchor_message.empty() && error("%s", anchor_message.c_str())) {
2408+
return 1;
2409+
}
2410+
}
23852411
}
23862412

23872413
if ((str = error_check_initial_orders(Wings[i].ai_goals, -1, i)) != nullptr) {

0 commit comments

Comments
 (0)