From 00a20ffd4bc2a00fdcb6fbf2866b68254c961589 Mon Sep 17 00:00:00 2001 From: Goober5000 Date: Thu, 6 Nov 2025 23:34:59 -0500 Subject: [PATCH 1/2] refactor ship anchors 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. --- code/ai/aicode.cpp | 2 +- code/mission/missionparse.cpp | 156 ++++++++++-------- code/mission/missionparse.h | 4 +- code/missioneditor/common.cpp | 22 ++- code/missioneditor/common.h | 6 +- code/missioneditor/missionsave.cpp | 54 +++--- code/parse/sexp.cpp | 16 +- code/scripting/api/objs/parse_object.cpp | 8 +- code/scripting/api/objs/ship.cpp | 5 +- code/scripting/api/objs/wing.cpp | 5 +- code/ship/ship.cpp | 9 +- code/ship/ship.h | 8 +- fred2/fredview.cpp | 10 +- fred2/shipeditordlg.cpp | 12 +- fred2/wing_editor.cpp | 8 +- qtfred/src/mission/Editor.cpp | 10 +- .../ShipEditor/ShipEditorDialogModel.cpp | 12 +- .../mission/dialogs/WingEditorDialogModel.cpp | 49 +++--- 18 files changed, 211 insertions(+), 185 deletions(-) diff --git a/code/ai/aicode.cpp b/code/ai/aicode.cpp index 99e6337d841..bd48755ba89 100644 --- a/code/ai/aicode.cpp +++ b/code/ai/aicode.cpp @@ -14060,7 +14060,7 @@ void ai_bay_depart() // check if parent ship valid; if not, abort depart if (gameseq_get_state() != GS_STATE_LAB) { - auto anchor_ship_entry = ship_registry_get(Parse_names[Ships[Pl_objp->instance].departure_anchor]); + auto anchor_ship_entry = ship_registry_get(Ships[Pl_objp->instance].departure_anchor); if (!anchor_ship_entry || !ship_useful_for_departure(anchor_ship_entry->shipnum, Ships[Pl_objp->instance].departure_path_mask)) { mprintf(("Aborting bay departure!\n")); diff --git a/code/mission/missionparse.cpp b/code/mission/missionparse.cpp index 34a5c7e550f..bc6a81a0137 100644 --- a/code/mission/missionparse.cpp +++ b/code/mission/missionparse.cpp @@ -4437,7 +4437,7 @@ int parse_wing_create_ships( wing *wingp, int num_to_create, bool force_create, // (or will exist). if ( wingp->arrival_location == ArrivalLocation::FROM_DOCK_BAY ) { Assert( wingp->arrival_anchor >= 0 ); - auto anchor_ship_entry = ship_registry_get(Parse_names[wingp->arrival_anchor]); + auto anchor_ship_entry = ship_registry_get(wingp->arrival_anchor); // see if ship is yet to arrive. If so, then return 0 so we can evaluate again later. if (!anchor_ship_entry || anchor_ship_entry->status == ShipStatus::NOT_YET_PRESENT) @@ -5216,6 +5216,60 @@ void parse_props(mission* pm) } } +// Goober5000 +void resolve_and_check_anchor(bool check_for_hangar, SCP_set &anchors_checked, int &anchor, const char *other_name, bool other_is_ship, bool is_arrival) +{ + if ((anchor < 0) || (anchor & SPECIAL_ARRIVAL_ANCHOR_FLAG)) + return; + + if (Parse_names.in_bounds(anchor)) + anchor = ship_registry_get_index(Parse_names[anchor]); + else + anchor = -1; + + if (check_for_hangar && anchor >= 0) + { + SCP_string message; + check_anchor_for_hangar_bay(message, anchors_checked, anchor, other_name, other_is_ship, is_arrival); + if (!message.empty()) + Warning(LOCATION, "%s", message.c_str()); + } +} + +/** + * Resolve parse names, particularly for arrival/departure anchors + * NB: between parsing and the time this function is run, the anchors store the index into Parse_names; + * at all other times, they store the index into the ship registry + */ +void post_process_parse_names() +{ + SCP_set anchors_checked; + + // check the parse names + for (const auto &parse_name : Parse_names) + { + auto ship_entry = ship_registry_get(parse_name); + if (!ship_entry) + Warning(LOCATION, "Ship name \"%s\" was referenced, but this ship doesn't exist!", parse_name.c_str()); + } + + // resolve anchors for parse objects (ships) + for (auto &pobj: Parse_objects) + { + resolve_and_check_anchor(pobj.arrival_location == ArrivalLocation::FROM_DOCK_BAY, anchors_checked, pobj.arrival_anchor, pobj.name, true, true); + resolve_and_check_anchor(pobj.departure_location == DepartureLocation::TO_DOCK_BAY, anchors_checked, pobj.departure_anchor, pobj.name, true, false); + } + + // resolve anchors for wings + for (int i = 0; i < Num_wings; ++i) + { + auto wingp = &Wings[i]; + + resolve_and_check_anchor(wingp->arrival_location == ArrivalLocation::FROM_DOCK_BAY, anchors_checked, wingp->arrival_anchor, wingp->name, false, true); + resolve_and_check_anchor(wingp->departure_location == DepartureLocation::TO_DOCK_BAY, anchors_checked, wingp->departure_anchor, wingp->name, false, false); + } +} + // Goober5000 void resolve_path_masks(int anchor, int *path_mask) { @@ -5235,16 +5289,14 @@ void resolve_path_masks(int anchor, int *path_mask) if (prp->cached_mask & (1 << MAX_SHIP_BAY_PATHS)) { int j, bay_path, modelnum; - p_object *parent_pobjp; // get anchor ship Assert(!(anchor & SPECIAL_ARRIVAL_ANCHOR_FLAG)); - auto parent_ship_entry = ship_registry_get(Parse_names[anchor]); - parent_pobjp = parent_ship_entry->p_objp(); + auto anchor_ship_entry = ship_registry_get(anchor); // Load the anchor ship model with subsystems and all; it'll need to be done for this mission anyway - ship_info *sip = &Ship_info[parent_pobjp->ship_class]; - modelnum = model_load(sip->pof_file, sip); + auto anchor_sip = anchor_ship_entry->sip(); + modelnum = model_load(anchor_sip->pof_file, anchor_sip); // resolve names to indexes *path_mask = 0; @@ -5351,6 +5403,9 @@ void post_process_ships_wings() Ship_registry_map[p_obj.name] = static_cast(Ship_registry.size() - 1); } + // Goober5000 - resolve the parse names. Needs to be done once the ship registry is valid but before the path masks are resolved. + post_process_parse_names(); + // Goober5000 - resolve the path masks. Needs to be done early because // mission_parse_maybe_create_parse_object relies on it. post_process_path_stuff(); @@ -6669,9 +6724,8 @@ bool parse_mission(mission *pm, int flags) bool post_process_mission(mission *pm) { - int i; - int indices[MAX_SHIPS], objnum; - ship_weapon *swp; + int i, objnum; + ship_weapon *swp; ship_obj *so; post_process_mission_props(); @@ -6738,55 +6792,6 @@ bool post_process_mission(mission *pm) Arriving_support_ship = nullptr; Num_arriving_repair_targets = 0; - // convert all ship name indices to ship indices now that mission has been loaded - if (Fred_running) { - // lambda for seeing whether the anchors actually work for arrival/departure - SCP_string message; - SCP_set anchors_checked; - auto check_anchor = [&message, &anchors_checked](int anchor_shipnum, const char *other_name, bool other_is_ship, bool is_arrival) { - check_anchor_for_hangar_bay(message, anchors_checked, anchor_shipnum, other_name, other_is_ship, is_arrival); - if (!message.empty()) - Warning(LOCATION, "%s", message.c_str()); - }; - - i = 0; - for (const auto &parse_name: Parse_names) { - auto ship_entry = ship_registry_get(parse_name); - indices[i] = ship_entry ? ship_entry->shipnum : -1; - if (indices[i] < 0) - Warning(LOCATION, "Ship name \"%s\" referenced, but this ship doesn't exist", parse_name.c_str()); - i++; - } - - for (i=0; i= 0) && (Ships[i].arrival_anchor >= 0) && (Ships[i].arrival_anchor < SPECIAL_ARRIVAL_ANCHOR_FLAG)) { - Ships[i].arrival_anchor = indices[Ships[i].arrival_anchor]; - if (Ships[i].arrival_location == ArrivalLocation::FROM_DOCK_BAY) - check_anchor(Ships[i].arrival_anchor, Ships[i].ship_name, true, true); - } - - if ((Ships[i].objnum >= 0) && (Ships[i].departure_anchor >= 0)) { - Ships[i].departure_anchor = indices[Ships[i].departure_anchor]; - if (Ships[i].departure_location == DepartureLocation::TO_DOCK_BAY) - check_anchor(Ships[i].departure_anchor, Ships[i].ship_name, true, false); - } - } - - for (i=0; i= 0) && (Wings[i].arrival_anchor < SPECIAL_ARRIVAL_ANCHOR_FLAG)) { - Wings[i].arrival_anchor = indices[Wings[i].arrival_anchor]; - if (Wings[i].arrival_location == ArrivalLocation::FROM_DOCK_BAY) - check_anchor(Wings[i].arrival_anchor, Wings[i].name, false, true); - } - - if (Wings[i].wave_count && (Wings[i].departure_anchor >= 0)) { - Wings[i].departure_anchor = indices[Wings[i].departure_anchor]; - if (Wings[i].departure_location == DepartureLocation::TO_DOCK_BAY) - check_anchor(Wings[i].departure_anchor, Wings[i].name, false, false); - } - } - } - // before doing anything else, we must validate all of the sexpressions that were loaded into the mission. // Loop through the Sexp_nodes array and send the top level functions to the check_sexp_syntax parser // Cyborg -- If you are a ingame joiner, your sexps will be taken care of by the server, and checking will @@ -7817,11 +7822,10 @@ int mission_set_arrival_location(int anchor, ArrivalLocation location, int dist, // get ship shipnum = ship_get_random_team_ship(iff_get_mask(iff_index), get_players ? SHIP_GET_ONLY_PLAYERS : SHIP_GET_ANY_SHIP); } - // if we didn't find the arrival anchor in the list of special nodes, then do a - // ship name lookup on the anchor + // if we didn't find the arrival anchor in the list of special nodes, then it must be a ship registry index else { - auto anchor_entry = ship_registry_get(Parse_names[anchor]); + auto anchor_entry = ship_registry_get(anchor); shipnum = anchor_entry ? anchor_entry->shipnum : -1; } @@ -8019,7 +8023,7 @@ int mission_did_ship_arrive(p_object *objp, bool force_arrival) // doesn't exist, don't create. if ( objp->arrival_location == ArrivalLocation::FROM_DOCK_BAY ) { Assert( objp->arrival_anchor >= 0 ); - auto anchor_ship_entry = ship_registry_get(Parse_names[objp->arrival_anchor]); + auto anchor_ship_entry = ship_registry_get(objp->arrival_anchor); // see if ship is yet to arrive. If so, then return -1 so we can evaluate again later. if (!anchor_ship_entry || anchor_ship_entry->status == ShipStatus::NOT_YET_PRESENT) @@ -8430,13 +8434,13 @@ int mission_do_departure(object *objp, bool goal_is_to_warp) { Assert(anchor >= 0); auto anchor_ship_entry = (anchor >= 0) - ? ship_registry_get(Parse_names[anchor]) + ? ship_registry_get(anchor) : nullptr; // should never happen, but if it does, fail gracefully // see if ship is yet to arrive. If so, then warp. if (!anchor_ship_entry || anchor_ship_entry->status == ShipStatus::NOT_YET_PRESENT) { - mprintf(("Anchor ship %s hasn't arrived yet! Trying to warp...\n", Parse_names[anchor].c_str())); + mprintf(("Anchor ship %s hasn't arrived yet! Trying to warp...\n", anchor_ship_entry ? anchor_ship_entry->name : "")); goto try_to_warp; } @@ -8822,21 +8826,29 @@ int get_anchor(const char *name) /** * See if an arrival/departure anchor is missing a hangar bay. If it is, the message parameter will be populated with an appropriate error. */ -void check_anchor_for_hangar_bay(SCP_string &message, SCP_set &anchor_shipnums_checked, int anchor_shipnum, const char *other_name, bool other_is_ship, bool is_arrival) +void check_anchor_for_hangar_bay(SCP_string &message, SCP_set &anchors_checked, int anchor, const char *other_name, bool other_is_ship, bool is_arrival) { message.clear(); - if (anchor_shipnum < 0) + if (anchor < 0) return; - if (anchor_shipnums_checked.contains(anchor_shipnum)) + if (anchors_checked.contains(anchor)) return; - anchor_shipnums_checked.insert(anchor_shipnum); + anchors_checked.insert(anchor); - if (!ship_has_hangar_bay(anchor_shipnum)) + auto anchor_ship_entry = ship_registry_get(anchor); + if (anchor_ship_entry) { - auto shipp = &Ships[anchor_shipnum]; - 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, - Ship_info[shipp->ship_info_index].name, is_arrival ? "n arrival" : " departure", other_is_ship ? "ship" : "wing", other_name); + // Load the anchor ship model with subsystems and all; it'll need to be done for this mission anyway + auto anchor_sip = anchor_ship_entry->sip(); + int modelnum = model_load(anchor_sip->pof_file, anchor_sip); + + // Check if this model has a hangar bay + if (!model_has_hangar_bay(modelnum)) + { + 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!", anchor_ship_entry->name, + anchor_sip->name, is_arrival ? "n arrival" : " departure", other_is_ship ? "ship" : "wing", other_name); + } } }; diff --git a/code/mission/missionparse.h b/code/mission/missionparse.h index e6a8c69d89a..766fa5c5647 100644 --- a/code/mission/missionparse.h +++ b/code/mission/missionparse.h @@ -49,7 +49,7 @@ enum class DepartureLocation; #define MIN_TARGET_ARRIVAL_MULTIPLIER 2.0f // minimum distance is 2 * target radius, but at least 500 int get_special_anchor(const char *name); -void check_anchor_for_hangar_bay(SCP_string &message, SCP_set &anchor_shipnums_checked, int anchor_shipnum, const char *other_name, bool other_is_ship, bool is_arrival); +void check_anchor_for_hangar_bay(SCP_string &message, SCP_set &anchors_checked, int anchor, const char *other_name, bool other_is_ship, bool is_arrival); // MISSION_VERSION should be the earliest version of FSO that can load the current mission format without // requiring version-specific comments. It should be updated whenever the format changes, but it should @@ -448,7 +448,7 @@ class p_object ArrivalLocation arrival_location = ArrivalLocation::AT_LOCATION; int arrival_distance = 0; // used when arrival location is near or in front of some ship - int arrival_anchor = -1; // ship used for anchoring an arrival point + int arrival_anchor = -1; // ship registry entry used for anchoring an arrival point int arrival_path_mask = 0; // Goober5000 int arrival_cue = -1; // Index in Sexp_nodes of this sexp. int arrival_delay = 0; diff --git a/code/missioneditor/common.cpp b/code/missioneditor/common.cpp index 29074739910..9b0ed89898d 100644 --- a/code/missioneditor/common.cpp +++ b/code/missioneditor/common.cpp @@ -60,6 +60,26 @@ void stuff_special_arrival_anchor_name(char* buf, int anchor_num, bool retail_fo stuff_special_arrival_anchor_name(buf, iff_index, restrict_to_players, retail_format); } +// Ship and wing arrival and departure anchors should always be ship registry entry indexes, except for a very brief window during mission parsing. +// But FRED and QtFRED dialogs use ship indexes instead. So, rather than refactor all the dialogs, this converts between the two. If an anchor +// is a valid ship registry index, the equivalent ship index is returned; otherwise the special value (-1 or a flag) is returned instead. +int anchor_to_target(int anchor) +{ + auto anchor_entry = ship_registry_get(anchor); + return anchor_entry ? anchor_entry->shipnum : anchor; +} + +// Ship and wing arrival and departure anchors should always be ship registry entry indexes, except for a very brief window during mission parsing. +// But FRED and QtFRED dialogs use ship indexes instead. So, rather than refactor all the dialogs, this converts between the two. If a target +// is a valid ship index, the equivalent ship registry index is returned; otherwise the special value (-1 or a flag) is returned instead. +int target_to_anchor(int target) +{ + if (target >= 0 && target < MAX_SHIPS) + return ship_registry_get_index(Ships[target].ship_name); + else + return target; +} + void generate_weaponry_usage_list_team(int team, int* arr) { int i; @@ -112,4 +132,4 @@ void generate_weaponry_usage_list_wing(int wing_num, int* arr) } } } -} \ No newline at end of file +} diff --git a/code/missioneditor/common.h b/code/missioneditor/common.h index 9e89a75811a..5a2de142216 100644 --- a/code/missioneditor/common.h +++ b/code/missioneditor/common.h @@ -31,6 +31,10 @@ void stuff_special_arrival_anchor_name(char* buf, int iff_index, int restrict_to void stuff_special_arrival_anchor_name(char* buf, int anchor_num, bool retail_format); +int anchor_to_target(int anchor); + +int target_to_anchor(int target); + void generate_weaponry_usage_list_team(int team, int* arr); -void generate_weaponry_usage_list_wing(int wing_num, int* arr); \ No newline at end of file +void generate_weaponry_usage_list_wing(int wing_num, int* arr); diff --git a/code/missioneditor/missionsave.cpp b/code/missioneditor/missionsave.cpp index ad02a50ba64..373ef8585ca 100644 --- a/code/missioneditor/missionsave.cpp +++ b/code/missioneditor/missionsave.cpp @@ -3570,22 +3570,20 @@ int Fred_mission_save::save_objects() // save it fout(" %s", tmp); } else { - fout(" %s", Ships[z].ship_name); + auto anchor_entry = ship_registry_get(z); + fout(" %s", anchor_entry ? anchor_entry->name : ""); } } // Goober5000 if (save_config.save_format != MissionFormat::RETAIL) { if ((shipp->arrival_location == ArrivalLocation::FROM_DOCK_BAY) && (shipp->arrival_path_mask > 0)) { - int anchor_shipnum; - polymodel* pm; - - anchor_shipnum = shipp->arrival_anchor; - Assert(anchor_shipnum >= 0 && anchor_shipnum < MAX_SHIPS); + auto anchor_entry = ship_registry_get(shipp->arrival_anchor); + Assertion(anchor_entry, "Could not find arrival anchor for ship %s!", shipp->ship_name); + auto pm = model_get(anchor_entry->sip()->model_num); fout("\n+Arrival Paths: ( "); - pm = model_get(Ship_info[Ships[anchor_shipnum].ship_info_index].model_num); for (auto n = 0; n < pm->ship_bay->num_paths; n++) { if (shipp->arrival_path_mask & (1 << n)) { fout("\"%s\" ", pm->paths[pm->ship_bay->path_indexes[n]].name); @@ -3622,24 +3620,19 @@ int Fred_mission_save::save_objects() required_string_fred("$Departure Anchor:"); parse_comments(); - if (shipp->departure_anchor >= 0) - fout(" %s", Ships[shipp->departure_anchor].ship_name); - else - fout(" "); + auto anchor_entry = ship_registry_get(shipp->departure_anchor); + fout(" %s", anchor_entry ? anchor_entry->name : ""); } // Goober5000 if (save_config.save_format != MissionFormat::RETAIL) { if ((shipp->departure_location == DepartureLocation::TO_DOCK_BAY) && (shipp->departure_path_mask > 0)) { - int anchor_shipnum; - polymodel* pm; - - anchor_shipnum = shipp->departure_anchor; - Assert(anchor_shipnum >= 0 && anchor_shipnum < MAX_SHIPS); + auto anchor_entry = ship_registry_get(shipp->departure_anchor); + Assertion(anchor_entry, "Could not find departure anchor for ship %s!", shipp->ship_name); + auto pm = model_get(anchor_entry->sip()->model_num); fout("\n+Departure Paths: ( "); - pm = model_get(Ship_info[Ships[anchor_shipnum].ship_info_index].model_num); for (auto n = 0; n < pm->ship_bay->num_paths; n++) { if (shipp->departure_path_mask & (1 << n)) { fout("\"%s\" ", pm->paths[pm->ship_bay->path_indexes[n]].name); @@ -4947,22 +4940,20 @@ int Fred_mission_save::save_wings() // save it fout(" %s", tmp); } else { - fout(" %s", Ships[z].ship_name); + auto anchor_entry = ship_registry_get(z); + fout(" %s", anchor_entry ? anchor_entry->name : ""); } } // Goober5000 if (save_config.save_format != MissionFormat::RETAIL) { if ((w.arrival_location == ArrivalLocation::FROM_DOCK_BAY) && (w.arrival_path_mask > 0)) { - int anchor_shipnum; - polymodel* pm; - - anchor_shipnum = w.arrival_anchor; - Assert(anchor_shipnum >= 0 && anchor_shipnum < MAX_SHIPS); + auto anchor_entry = ship_registry_get(w.arrival_anchor); + Assertion(anchor_entry, "Could not find arrival anchor for wing %s!", w.name); + auto pm = model_get(anchor_entry->sip()->model_num); fout("\n+Arrival Paths: ( "); - pm = model_get(Ship_info[Ships[anchor_shipnum].ship_info_index].model_num); for (auto n = 0; n < pm->ship_bay->num_paths; n++) { if (w.arrival_path_mask & (1 << n)) { fout("\"%s\" ", pm->paths[pm->ship_bay->path_indexes[n]].name); @@ -4995,24 +4986,19 @@ int Fred_mission_save::save_wings() required_string_fred("$Departure Anchor:"); parse_comments(); - if (w.departure_anchor >= 0) - fout(" %s", Ships[w.departure_anchor].ship_name); - else - fout(" "); + auto anchor_entry = ship_registry_get(w.departure_anchor); + fout(" %s", anchor_entry ? anchor_entry->name : ""); } // Goober5000 if (save_config.save_format != MissionFormat::RETAIL) { if ((w.departure_location == DepartureLocation::TO_DOCK_BAY) && (w.departure_path_mask > 0)) { - int anchor_shipnum; - polymodel* pm; - - anchor_shipnum = w.departure_anchor; - Assert(anchor_shipnum >= 0 && anchor_shipnum < MAX_SHIPS); + auto anchor_entry = ship_registry_get(w.departure_anchor); + Assertion(anchor_entry, "Could not find departure anchor for wing %s!", w.name); + auto pm = model_get(anchor_entry->sip()->model_num); fout("\n+Departure Paths: ( "); - pm = model_get(Ship_info[Ships[anchor_shipnum].ship_info_index].model_num); for (auto n = 0; n < pm->ship_bay->num_paths; n++) { if (w.departure_path_mask & (1 << n)) { fout("\"%s\" ", pm->paths[pm->ship_bay->path_indexes[n]].name); diff --git a/code/parse/sexp.cpp b/code/parse/sexp.cpp index 7b75b5d4ad4..12fe70d6395 100644 --- a/code/parse/sexp.cpp +++ b/code/parse/sexp.cpp @@ -23594,8 +23594,8 @@ void sexp_set_support_ship(int n) } else { - // find or create the anchor - The_mission.support_ships.arrival_anchor = get_parse_name_index(CTEXT(n)); + // find the anchor + The_mission.support_ships.arrival_anchor = ship_registry_get_index(CTEXT(n)); } // get departure location @@ -23620,8 +23620,8 @@ void sexp_set_support_ship(int n) } else { - // find or create the anchor - The_mission.support_ships.departure_anchor = get_parse_name_index(CTEXT(n)); + // find the anchor + The_mission.support_ships.departure_anchor = ship_registry_get_index(CTEXT(n)); } // get ship class @@ -23691,8 +23691,8 @@ void sexp_set_arrival_info(int node) } else { - // find or create the anchor - arrival_anchor = get_parse_name_index(CTEXT(n)); + // find the anchor + arrival_anchor = ship_registry_get_index(CTEXT(n)); } n = CDR(n); @@ -23791,8 +23791,8 @@ void sexp_set_departure_info(int node) } else { - // find or create the anchor - departure_anchor = get_parse_name_index(CTEXT(n)); + // find the anchor + departure_anchor = ship_registry_get_index(CTEXT(n)); } n = CDR(n); diff --git a/code/scripting/api/objs/parse_object.cpp b/code/scripting/api/objs/parse_object.cpp index 9ed1f8b5eb9..58f3967528c 100644 --- a/code/scripting/api/objs/parse_object.cpp +++ b/code/scripting/api/objs/parse_object.cpp @@ -10,11 +10,10 @@ #include "team_colors.h" #include "globalincs/alphacolors.h" //Needed for team colors -#include "mission/missionparse.h" - #include "network/multi.h" #include "network/multimsgs.h" #include "network/multiutil.h" +#include "ship/ship.h" extern bool sexp_check_flag_arrays(const char *flag_name, Object::Object_Flags &object_flag, Ship::Ship_Flags &ship_flags, Mission::Parse_Object_Flags &parse_obj_flag, AI::AI_Flags &ai_flag); extern void sexp_alter_ship_flag_helper(object_ship_wing_point_team &oswpt, bool future_ships, Object::Object_Flags object_flag, Ship::Ship_Flags ship_flag, Mission::Parse_Object_Flags parse_obj_flag, AI::AI_Flags ai_flag, bool set_flag); @@ -514,10 +513,11 @@ static int parse_object_getset_anchor_helper(lua_State* L, int p_object::* field if (ADE_SETTING_VAR && s != nullptr) { - poh->getObject()->*field = (stricmp(s, "") == 0) ? -1 : get_parse_name_index(s); + poh->getObject()->*field = (stricmp(s, "") == 0) ? -1 : ship_registry_get_index(s); } - return ade_set_args(L, "s", (poh->getObject()->*field >= 0) ? Parse_names[poh->getObject()->*field].c_str() : ""); + auto anchor_entry = ship_registry_get(poh->getObject()->*field); + return ade_set_args(L, "s", anchor_entry ? anchor_entry->name : ""); } ADE_VIRTVAR(ArrivalAnchor, l_ParseObject, "string", "The ship's arrival anchor", "string", "Arrival anchor, or nil if handle is invalid") diff --git a/code/scripting/api/objs/ship.cpp b/code/scripting/api/objs/ship.cpp index 95f30bb1527..172bad89d81 100644 --- a/code/scripting/api/objs/ship.cpp +++ b/code/scripting/api/objs/ship.cpp @@ -1286,10 +1286,11 @@ static int ship_getset_anchor_helper(lua_State* L, int ship::* field) if (ADE_SETTING_VAR && s != nullptr) { - shipp->*field = (stricmp(s, "") == 0) ? -1 : get_parse_name_index(s); + shipp->*field = (stricmp(s, "") == 0) ? -1 : ship_registry_get_index(s); } - return ade_set_args(L, "s", (shipp->*field >= 0) ? Parse_names[shipp->*field].c_str() : ""); + auto anchor_entry = ship_registry_get(shipp->*field); + return ade_set_args(L, "s", anchor_entry ? anchor_entry->name : ""); } ADE_VIRTVAR(ArrivalAnchor, l_Ship, "string", "The ship's arrival anchor", "string", "Arrival anchor, or nil if handle is invalid") diff --git a/code/scripting/api/objs/wing.cpp b/code/scripting/api/objs/wing.cpp index 3143049f346..3b0169589fb 100644 --- a/code/scripting/api/objs/wing.cpp +++ b/code/scripting/api/objs/wing.cpp @@ -322,10 +322,11 @@ static int wing_getset_anchor_helper(lua_State* L, int wing::* field) if (ADE_SETTING_VAR && s != nullptr) { - Wings[wingnum].*field = (stricmp(s, "") == 0) ? -1 : get_parse_name_index(s); + Wings[wingnum].*field = (stricmp(s, "") == 0) ? -1 : ship_registry_get_index(s); } - return ade_set_args(L, "s", (Wings[wingnum].*field >= 0) ? Parse_names[Wings[wingnum].*field].c_str() : ""); + auto anchor_entry = ship_registry_get(Wings[wingnum].*field); + return ade_set_args(L, "s", anchor_entry ? anchor_entry->name : ""); } ADE_VIRTVAR(ArrivalAnchor, l_Wing, "string", "The wing's arrival anchor", "string", "Arrival anchor, or nil if handle is invalid") diff --git a/code/ship/ship.cpp b/code/ship/ship.cpp index 0bed76538e3..9bf4b95414b 100644 --- a/code/ship/ship.cpp +++ b/code/ship/ship.cpp @@ -9161,7 +9161,8 @@ void ship_cleanup(int shipnum, int cleanup_mode) Assertion(Ship_registry_map.find(shipp->ship_name) != Ship_registry_map.end(), "Ship %s was destroyed, but was never stored in the ship registry!", shipp->ship_name); // Goober5000 - handle ship registry, part 1 - auto entry = &Ship_registry[Ship_registry_map[shipp->ship_name]]; + auto entry_index = Ship_registry_map[shipp->ship_name]; + auto entry = &Ship_registry[entry_index]; entry->status = ShipStatus::EXITED; entry->cleanup_mode = cleanup_mode; @@ -9295,7 +9296,7 @@ void ship_cleanup(int shipnum, int cleanup_mode) if (The_mission.ai_profile->flags[AI::Profile_Flags::Cancel_future_waves_of_any_wing_launched_from_an_exited_ship]) { for (int child_wingnum = 0; child_wingnum < Num_wings; ++child_wingnum) { auto child_wingp = &Wings[child_wingnum]; - if (child_wingp->arrival_location == ArrivalLocation::FROM_DOCK_BAY && Parse_names.in_bounds(child_wingp->arrival_anchor) && !stricmp(Parse_names[child_wingp->arrival_anchor].c_str(), shipp->ship_name)) { + if (child_wingp->arrival_location == ArrivalLocation::FROM_DOCK_BAY && child_wingp->arrival_anchor.value() == entry_index) { // prevent any more waves from arriving by marking this as the last wave child_wingp->num_waves = child_wingp->current_wave; @@ -17285,7 +17286,7 @@ bool ship_can_bay_depart(ship* sp) if ( departure_location == DepartureLocation::TO_DOCK_BAY ) { Assertion( departure_anchor >= 0, "Ship %s must have a valid departure anchor", sp->ship_name ); - auto anchor_ship_entry = ship_registry_get(Parse_names[departure_anchor]); + auto anchor_ship_entry = ship_registry_get(departure_anchor); if (anchor_ship_entry && anchor_ship_entry->has_shipp() && ship_useful_for_departure(anchor_ship_entry->shipnum, departure_path_mask)) { // can bay depart at this time return true; @@ -19431,7 +19432,7 @@ int is_support_allowed(object *objp, bool do_simple_check) Assert(The_mission.support_ships.arrival_anchor != -1); // ensure it's in-mission - auto anchor_ship_entry = ship_registry_get(Parse_names[The_mission.support_ships.arrival_anchor]); + auto anchor_ship_entry = ship_registry_get(The_mission.support_ships.arrival_anchor); if (!anchor_ship_entry || !anchor_ship_entry->has_shipp()) { return 0; diff --git a/code/ship/ship.h b/code/ship/ship.h index 5c1bc8f0454..6571f5e50d2 100644 --- a/code/ship/ship.h +++ b/code/ship/ship.h @@ -633,13 +633,13 @@ class ship ArrivalLocation arrival_location; int arrival_distance; // how far away this ship should arrive - int arrival_anchor; // name of object this ship arrives near (or in front of) + int arrival_anchor; // ship registry index of object this ship arrives near (or in front of) int arrival_path_mask; // Goober5000 - possible restrictions on which bay paths to use int arrival_cue; int arrival_delay; DepartureLocation departure_location; // depart to hyperspace or someplace else (like docking bay) - int departure_anchor; // when docking bay -- index of ship to use + int departure_anchor; // when docking bay -- ship registry index of ship to use int departure_path_mask; // Goober5000 - possible restrictions on which bay paths to use int departure_cue; // sexpression to eval when departing int departure_delay; // time in seconds after sexp is true that we delay. @@ -1596,13 +1596,13 @@ typedef struct wing { ArrivalLocation arrival_location; // arrival and departure information for wings -- similar to info for ships int arrival_distance; // distance from some ship where this ship arrives - int arrival_anchor; // name of object this ship arrives near (or in front of) + int arrival_anchor; // ship registry index of object this wing arrives near (or in front of) int arrival_path_mask; // Goober5000 - possible restrictions on which bay paths to use int arrival_cue; int arrival_delay; DepartureLocation departure_location; - int departure_anchor; // name of object that we depart to (in case of dock bays) + int departure_anchor; // ship registry index of object that we depart to (in case of dock bays) int departure_path_mask; // Goober5000 - possible restrictions on which bay paths to use int departure_cue; int departure_delay; diff --git a/fred2/fredview.cpp b/fred2/fredview.cpp index 352a98e86e5..0a86f2dc268 100644 --- a/fred2/fredview.cpp +++ b/fred2/fredview.cpp @@ -2469,7 +2469,7 @@ int CFREDView::global_error_check() object *ptr; brief_stage *sp; SCP_string anchor_message; - SCP_set anchor_shipnums_checked; + SCP_set anchors_checked; g_err = multi = 0; if ( The_mission.game_type & MISSION_TYPE_MULTI ) @@ -2669,7 +2669,7 @@ int CFREDView::global_error_check() } } if (Ships[i].arrival_location == ArrivalLocation::FROM_DOCK_BAY) { - check_anchor_for_hangar_bay(anchor_message, anchor_shipnums_checked, Ships[i].arrival_anchor, Ships[i].ship_name, true, true); + check_anchor_for_hangar_bay(anchor_message, anchors_checked, Ships[i].arrival_anchor, Ships[i].ship_name, true, true); if (!anchor_message.empty() && error("%s", anchor_message.c_str())) { return 1; } @@ -2683,7 +2683,7 @@ int CFREDView::global_error_check() } } if (Ships[i].departure_location == DepartureLocation::TO_DOCK_BAY) { - check_anchor_for_hangar_bay(anchor_message, anchor_shipnums_checked, Ships[i].departure_anchor, Ships[i].ship_name, true, false); + check_anchor_for_hangar_bay(anchor_message, anchors_checked, Ships[i].departure_anchor, Ships[i].ship_name, true, false); if (!anchor_message.empty() && error("%s", anchor_message.c_str())) { return 1; } @@ -2884,7 +2884,7 @@ int CFREDView::global_error_check() if (error("Wing \"%s\" requires a valid arrival target", Wings[i].name)) return 1; if (Wings[i].arrival_location == ArrivalLocation::FROM_DOCK_BAY) { - check_anchor_for_hangar_bay(anchor_message, anchor_shipnums_checked, Wings[i].arrival_anchor, Wings[i].name, false, true); + check_anchor_for_hangar_bay(anchor_message, anchors_checked, Wings[i].arrival_anchor, Wings[i].name, false, true); if (!anchor_message.empty() && error("%s", anchor_message.c_str())) { return 1; } @@ -2896,7 +2896,7 @@ int CFREDView::global_error_check() if (error("Wing \"%s\" requires a valid departure target", Wings[i].name)) return 1; if (Wings[i].departure_location == DepartureLocation::TO_DOCK_BAY) { - check_anchor_for_hangar_bay(anchor_message, anchor_shipnums_checked, Wings[i].departure_anchor, Wings[i].name, false, false); + check_anchor_for_hangar_bay(anchor_message, anchors_checked, Wings[i].departure_anchor, Wings[i].name, false, false); if (!anchor_message.empty() && error("%s", anchor_message.c_str())) { return 1; } diff --git a/fred2/shipeditordlg.cpp b/fred2/shipeditordlg.cpp index 2d9d0b6d575..6a20282b45f 100644 --- a/fred2/shipeditordlg.cpp +++ b/fred2/shipeditordlg.cpp @@ -567,11 +567,11 @@ void CShipEditorDlg::initialize_data(int full_update) d_cue = Ships[i].departure_cue; m_arrival_location = static_cast(Ships[i].arrival_location); m_arrival_dist.init(Ships[i].arrival_distance); - m_arrival_target = Ships[i].arrival_anchor; + m_arrival_target = anchor_to_target(Ships[i].arrival_anchor); m_arrival_delay.init(Ships[i].arrival_delay); m_departure_location = static_cast(Ships[i].departure_location); m_departure_delay.init(Ships[i].departure_delay); - m_departure_target = Ships[i].departure_anchor; + m_departure_target = anchor_to_target(Ships[i].departure_anchor); } else { cue_init++; @@ -587,7 +587,7 @@ void CShipEditorDlg::initialize_data(int full_update) m_arrival_delay.set(Ships[i].arrival_delay); m_departure_delay.set(Ships[i].departure_delay); - if (Ships[i].arrival_anchor != m_arrival_target){ + if (Ships[i].arrival_anchor != target_to_anchor(m_arrival_target)){ m_arrival_target = -1; } @@ -601,7 +601,7 @@ void CShipEditorDlg::initialize_data(int full_update) m_update_departure = 0; } - if ( Ships[i].departure_anchor != m_departure_target ){ + if (Ships[i].departure_anchor != target_to_anchor(m_departure_target)){ m_departure_target = -1; } } @@ -1408,7 +1408,7 @@ int CShipEditorDlg::update_ship(int ship) m_departure_delay.save(&Ships[ship].departure_delay); if (m_arrival_target >= 0) { z = (int)((CComboBox *) GetDlgItem(IDC_ARRIVAL_TARGET))->GetItemData(m_arrival_target); - MODIFY(Ships[ship].arrival_anchor, z); + MODIFY(Ships[ship].arrival_anchor, target_to_anchor(z)); // if the arrival is not hyperspace or docking bay -- force arrival distance to be // greater than 2*radius of target. @@ -1431,7 +1431,7 @@ int CShipEditorDlg::update_ship(int ship) } if (m_departure_target >= 0) { z = (int)((CComboBox *) GetDlgItem(IDC_DEPARTURE_TARGET))->GetItemData(m_departure_target); - MODIFY(Ships[ship].departure_anchor, z ); + MODIFY(Ships[ship].departure_anchor, target_to_anchor(z)); } } diff --git a/fred2/wing_editor.cpp b/fred2/wing_editor.cpp index a878a0d180f..77be8d57e59 100644 --- a/fred2/wing_editor.cpp +++ b/fred2/wing_editor.cpp @@ -366,8 +366,8 @@ void wing_editor::initialize_data_safe(int full_update) m_arrival_delay_min = Wings[cur_wing].wave_delay_min; m_arrival_delay_max = Wings[cur_wing].wave_delay_max; m_arrival_dist = Wings[cur_wing].arrival_distance; - m_arrival_target = Wings[cur_wing].arrival_anchor; - m_departure_target = Wings[cur_wing].departure_anchor; + m_arrival_target = anchor_to_target(Wings[cur_wing].arrival_anchor); + m_departure_target = anchor_to_target(Wings[cur_wing].departure_anchor); m_no_dynamic = (Wings[cur_wing].flags[Ship::Wing_Flags::No_dynamic])?1:0; // Add the ships/special items to the combo box here before data is updated @@ -818,7 +818,7 @@ void wing_editor::update_data_safe() MODIFY(Wings[cur_wing].arrival_distance, m_arrival_dist); if (m_arrival_target >= 0) { i = (int)((CComboBox *) GetDlgItem(IDC_ARRIVAL_TARGET))->GetItemData(m_arrival_target); - MODIFY(Wings[cur_wing].arrival_anchor, i); + MODIFY(Wings[cur_wing].arrival_anchor, target_to_anchor(i)); // when arriving near or in front of a ship, be sure that we are far enough away from it!!! if (((m_arrival_location != static_cast(ArrivalLocation::AT_LOCATION)) && (m_arrival_location != static_cast(ArrivalLocation::FROM_DOCK_BAY))) && (i >= 0) && !(i & SPECIAL_ARRIVAL_ANCHOR_FLAG)) { @@ -843,7 +843,7 @@ void wing_editor::update_data_safe() } if (m_departure_target >= 0) { i = (int)((CComboBox *) GetDlgItem(IDC_DEPARTURE_TARGET))->GetItemData(m_departure_target); - MODIFY(Wings[cur_wing].departure_anchor, i); + MODIFY(Wings[cur_wing].departure_anchor, target_to_anchor(i)); } MODIFY(Wings[cur_wing].departure_delay, m_departure_delay); diff --git a/qtfred/src/mission/Editor.cpp b/qtfred/src/mission/Editor.cpp index ff0f573b89f..619fe5f3436 100644 --- a/qtfred/src/mission/Editor.cpp +++ b/qtfred/src/mission/Editor.cpp @@ -1977,7 +1977,7 @@ int Editor::global_error_check_impl() { object* ptr; brief_stage* sp; SCP_string anchor_message; - SCP_set anchor_shipnums_checked; + SCP_set anchors_checked; g_err = multi = 0; if (The_mission.game_type & MISSION_TYPE_MULTI) { @@ -2173,7 +2173,7 @@ int Editor::global_error_check_impl() { } } if (Ships[i].arrival_location == ArrivalLocation::FROM_DOCK_BAY) { - check_anchor_for_hangar_bay(anchor_message, anchor_shipnums_checked, Ships[i].arrival_anchor, Ships[i].ship_name, true, true); + check_anchor_for_hangar_bay(anchor_message, anchors_checked, Ships[i].arrival_anchor, Ships[i].ship_name, true, true); if (!anchor_message.empty() && error("%s", anchor_message.c_str())) { return 1; } @@ -2187,7 +2187,7 @@ int Editor::global_error_check_impl() { } } if (Ships[i].departure_location == DepartureLocation::TO_DOCK_BAY) { - check_anchor_for_hangar_bay(anchor_message, anchor_shipnums_checked, Ships[i].departure_anchor, Ships[i].ship_name, true, false); + check_anchor_for_hangar_bay(anchor_message, anchors_checked, Ships[i].departure_anchor, Ships[i].ship_name, true, false); if (!anchor_message.empty() && error("%s", anchor_message.c_str())) { return 1; } @@ -2401,7 +2401,7 @@ int Editor::global_error_check_impl() { } } if (Wings[i].arrival_location == ArrivalLocation::FROM_DOCK_BAY) { - check_anchor_for_hangar_bay(anchor_message, anchor_shipnums_checked, Wings[i].arrival_anchor, Wings[i].name, false, true); + check_anchor_for_hangar_bay(anchor_message, anchors_checked, Wings[i].arrival_anchor, Wings[i].name, false, true); if (!anchor_message.empty() && error("%s", anchor_message.c_str())) { return 1; } @@ -2415,7 +2415,7 @@ int Editor::global_error_check_impl() { } } if (Wings[i].departure_location == DepartureLocation::TO_DOCK_BAY) { - check_anchor_for_hangar_bay(anchor_message, anchor_shipnums_checked, Wings[i].departure_anchor, Wings[i].name, false, false); + check_anchor_for_hangar_bay(anchor_message, anchors_checked, Wings[i].departure_anchor, Wings[i].name, false, false); if (!anchor_message.empty() && error("%s", anchor_message.c_str())) { return 1; } diff --git a/qtfred/src/mission/dialogs/ShipEditor/ShipEditorDialogModel.cpp b/qtfred/src/mission/dialogs/ShipEditor/ShipEditorDialogModel.cpp index c5881284c97..a198d69b321 100644 --- a/qtfred/src/mission/dialogs/ShipEditor/ShipEditorDialogModel.cpp +++ b/qtfred/src/mission/dialogs/ShipEditor/ShipEditorDialogModel.cpp @@ -273,11 +273,11 @@ void ShipEditorDialogModel::initializeData() _m_departure_tree_formula = Ships[i].departure_cue; _m_arrival_location = static_cast(Ships[i].arrival_location); _m_arrival_dist = Ships[i].arrival_distance; - _m_arrival_target = Ships[i].arrival_anchor; + _m_arrival_target = anchor_to_target(Ships[i].arrival_anchor); _m_arrival_delay = Ships[i].arrival_delay; _m_departure_location = static_cast(Ships[i].departure_location); _m_departure_delay = Ships[i].departure_delay; - _m_departure_target = Ships[i].departure_anchor; + _m_departure_target = anchor_to_target(Ships[i].departure_anchor); } else { cue_init++; @@ -293,7 +293,7 @@ void ShipEditorDialogModel::initializeData() _m_arrival_delay = Ships[i].arrival_delay; _m_departure_delay = Ships[i].departure_delay; - if (Ships[i].arrival_anchor != _m_arrival_target) { + if (Ships[i].arrival_anchor != target_to_anchor(_m_arrival_target)) { _m_arrival_target = -1; } @@ -307,7 +307,7 @@ void ShipEditorDialogModel::initializeData() _m_update_departure = false; } - if (Ships[i].departure_anchor != _m_departure_target) { + if (Ships[i].departure_anchor != target_to_anchor(_m_departure_target)) { _m_departure_target = -1; } } @@ -818,7 +818,7 @@ bool ShipEditorDialogModel::update_ship(int ship) Ships[ship].arrival_delay = _m_arrival_delay; Ships[ship].departure_delay = _m_departure_delay; if (_m_arrival_target >= 0) { - Ships[ship].arrival_anchor = _m_arrival_target; + Ships[ship].arrival_anchor = target_to_anchor(_m_arrival_target); // if the arrival is not hyperspace or docking bay -- force arrival distance to be // greater than 2*radius of target. @@ -847,7 +847,7 @@ bool ShipEditorDialogModel::update_ship(int ship) } if (_m_departure_target >= 0) { - Ships[ship].departure_anchor = _m_departure_target; + Ships[ship].departure_anchor = target_to_anchor(_m_departure_target); } } diff --git a/qtfred/src/mission/dialogs/WingEditorDialogModel.cpp b/qtfred/src/mission/dialogs/WingEditorDialogModel.cpp index 301182dc2b8..d320a8bdbfc 100644 --- a/qtfred/src/mission/dialogs/WingEditorDialogModel.cpp +++ b/qtfred/src/mission/dialogs/WingEditorDialogModel.cpp @@ -240,8 +240,9 @@ int WingEditorDialogModel::getMinArrivalDistance() const return 0; // Anchor should be a real ship - if (anchor >= 0 && anchor < MAX_SHIPS) { - const int objnum = Ships[anchor].objnum; + const int shipnum = anchor_to_target(anchor); + if (shipnum >= 0 && shipnum < MAX_SHIPS) { + const int objnum = Ships[shipnum].objnum; if (objnum >= 0) { const object& obj = Objects[objnum]; @@ -779,15 +780,15 @@ void WingEditorDialogModel::setArrivalType(ArrivalLocation newArrivalType) return; } - const int currentAnchor = w->arrival_anchor; + const int currentTarget = anchor_to_target(w->arrival_anchor); - bool valid_anchor = std::find_if(targets.begin(), targets.end(), [currentAnchor](const auto& entry) { - return entry.first == currentAnchor; + bool valid_target = std::find_if(targets.begin(), targets.end(), [currentTarget](const auto& entry) { + return entry.first == currentTarget; }) != targets.end(); - if (!valid_anchor) { + if (!valid_target) { // Set to the first available target - modify(w->arrival_anchor, targets[0].first); + modify(w->arrival_anchor, target_to_anchor(targets[0].first)); } // Set the distance to minimum if current is smaller @@ -880,7 +881,7 @@ int WingEditorDialogModel::getArrivalTarget() const return -1; } - return w->arrival_anchor; + return anchor_to_target(w->arrival_anchor); } void WingEditorDialogModel::setArrivalTarget(int targetIndex) @@ -908,11 +909,11 @@ void WingEditorDialogModel::setArrivalTarget(int targetIndex) targetIndex = -1; } - if (w->arrival_anchor == targetIndex) { + if (w->arrival_anchor == target_to_anchor(targetIndex)) { return; // no change } - modify(w->arrival_anchor, targetIndex); + modify(w->arrival_anchor, target_to_anchor(targetIndex)); // Set the distance to minimum if current is smaller int minDistance = getMinArrivalDistance(); @@ -960,7 +961,7 @@ std::vector> WingEditorDialogModel::getArrivalPaths( if (w->arrival_location != ArrivalLocation::FROM_DOCK_BAY) return {}; - return getDockBayPathsForWingMask(w->arrival_path_mask, w->arrival_anchor); + return getDockBayPathsForWingMask(w->arrival_path_mask, anchor_to_target(w->arrival_anchor)); } void WingEditorDialogModel::setArrivalPaths(const std::vector>& chosen) @@ -973,8 +974,8 @@ void WingEditorDialogModel::setArrivalPaths(const std::vectorarrival_location != ArrivalLocation::FROM_DOCK_BAY) return; - const int anchor = w->arrival_anchor; - if (anchor < 0 || !ship_has_hangar_bay(anchor)) + const int shipnum = anchor_to_target(w->arrival_anchor); + if (shipnum < 0 || !ship_has_hangar_bay(shipnum)) return; // Rebuild mask in the same order we produced the list @@ -1110,15 +1111,15 @@ void WingEditorDialogModel::setDepartureType(DepartureLocation newDepartureType) return; } - const int currentAnchor = w->departure_anchor; + const int currentTarget = anchor_to_target(w->departure_anchor); - bool valid_anchor = std::find_if(targets.begin(), targets.end(), [currentAnchor](const auto& entry) { - return entry.first == currentAnchor; + bool valid_target = std::find_if(targets.begin(), targets.end(), [currentTarget](const auto& entry) { + return entry.first == currentTarget; }) != targets.end(); - if (!valid_anchor) { + if (!valid_target) { // Set to the first available target - modify(w->departure_anchor, targets[0].first); + modify(w->departure_anchor, target_to_anchor(targets[0].first)); } } } @@ -1155,7 +1156,7 @@ int WingEditorDialogModel::getDepartureTarget() const return -1; } - return w->departure_anchor; + return anchor_to_target(w->departure_anchor); } void WingEditorDialogModel::setDepartureTarget(int targetIndex) @@ -1182,11 +1183,11 @@ void WingEditorDialogModel::setDepartureTarget(int targetIndex) targetIndex = -1; // invalid choice -> clear } - if (w->departure_anchor == targetIndex) { + if (w->departure_anchor == target_to_anchor(targetIndex)) { return; // no change } - modify(w->departure_anchor, targetIndex); + modify(w->departure_anchor, target_to_anchor(targetIndex)); modify(w->departure_path_mask, 0); } @@ -1199,7 +1200,7 @@ std::vector> WingEditorDialogModel::getDeparturePath if (w->departure_location != DepartureLocation::TO_DOCK_BAY) return {}; - return getDockBayPathsForWingMask(w->departure_path_mask, w->departure_anchor); + return getDockBayPathsForWingMask(w->departure_path_mask, anchor_to_target(w->departure_anchor)); } void WingEditorDialogModel::setDeparturePaths(const std::vector>& chosen) @@ -1212,8 +1213,8 @@ void WingEditorDialogModel::setDeparturePaths(const std::vectordeparture_location != DepartureLocation::TO_DOCK_BAY) return; - const int anchor = w->departure_anchor; - if (anchor < 0 || !ship_has_hangar_bay(anchor)) + const int shipnum = anchor_to_target(w->departure_anchor); + if (shipnum < 0 || !ship_has_hangar_bay(shipnum)) return; // Rebuild mask in the same order we produced the list From 28d622f1d291ff51c6ae6eeb85580cf8afa908a0 Mon Sep 17 00:00:00 2001 From: Goober5000 Date: Sat, 8 Nov 2025 02:05:28 -0500 Subject: [PATCH 2/2] make anchors type-safe, and adjust support_ship_info accordingly --- code/mission/missionparse.cpp | 118 ++++++++++-------- code/mission/missionparse.h | 43 ++++--- code/missioneditor/common.cpp | 16 +-- code/missioneditor/common.h | 5 +- code/missioneditor/missionsave.cpp | 8 +- code/parse/sexp.cpp | 18 +-- code/scripting/api/objs/parse_object.cpp | 4 +- code/scripting/api/objs/ship.cpp | 4 +- code/scripting/api/objs/wing.cpp | 4 +- code/ship/anchor_t.h | 22 ++++ code/ship/ship.cpp | 20 +-- code/ship/ship.h | 14 +-- code/source_groups.cmake | 1 + fred2/fredview.cpp | 10 +- fred2/management.cpp | 2 +- fred2/shipeditordlg.cpp | 4 +- fred2/wing_editor.cpp | 4 +- qtfred/src/mission/Editor.cpp | 10 +- .../ShipEditor/ShipEditorDialogModel.cpp | 3 +- .../mission/dialogs/WingEditorDialogModel.cpp | 14 +-- .../dialogs/ShipEditor/ShipEditorDialog.cpp | 2 +- 21 files changed, 182 insertions(+), 144 deletions(-) create mode 100644 code/ship/anchor_t.h diff --git a/code/mission/missionparse.cpp b/code/mission/missionparse.cpp index bc6a81a0137..dab13c74366 100644 --- a/code/mission/missionparse.cpp +++ b/code/mission/missionparse.cpp @@ -656,8 +656,8 @@ bool post_process_mission(mission *pm); int allocate_subsys_status(); void parse_common_object_data(p_object *objp); void parse_asteroid_fields(mission *pm); -int mission_set_arrival_location(int anchor, ArrivalLocation location, int distance, int objnum, int path_mask, vec3d *new_pos, matrix *new_orient); -int get_anchor(const char *name); +int mission_set_arrival_location(anchor_t anchor, ArrivalLocation location, int distance, int objnum, int path_mask, vec3d *new_pos, matrix *new_orient); +anchor_t get_anchor(const char *name); void mission_parse_set_up_initial_docks(); void mission_parse_set_arrival_locations(); void mission_set_wing_arrival_location( wing *wingp, int num_to_set ); @@ -4436,7 +4436,7 @@ int parse_wing_create_ships( wing *wingp, int num_to_create, bool force_create, // if wing is coming from docking bay, then be sure that ship we are arriving from actually exists // (or will exist). if ( wingp->arrival_location == ArrivalLocation::FROM_DOCK_BAY ) { - Assert( wingp->arrival_anchor >= 0 ); + Assert( wingp->arrival_anchor.isValid() ); auto anchor_ship_entry = ship_registry_get(wingp->arrival_anchor); // see if ship is yet to arrive. If so, then return 0 so we can evaluate again later. @@ -5217,17 +5217,22 @@ void parse_props(mission* pm) } // Goober5000 -void resolve_and_check_anchor(bool check_for_hangar, SCP_set &anchors_checked, int &anchor, const char *other_name, bool other_is_ship, bool is_arrival) +void resolve_and_check_anchor(bool check_for_hangar, SCP_set &anchors_checked, anchor_t &anchor, const char *other_name, bool other_is_ship, bool is_arrival) { - if ((anchor < 0) || (anchor & SPECIAL_ARRIVAL_ANCHOR_FLAG)) + if (!anchor.isValid()) return; + int anchor_val = anchor.value(); - if (Parse_names.in_bounds(anchor)) - anchor = ship_registry_get_index(Parse_names[anchor]); - else - anchor = -1; + // if it's a parse names index, convert it to a ship registry index + if (anchor_val & ANCHOR_IS_PARSE_NAMES_INDEX) + { + anchor_val &= ~ANCHOR_IS_PARSE_NAMES_INDEX; + Assertion(Parse_names.in_bounds(anchor_val), "Anchor %d is out of bounds. Get a coder!", anchor_val); + anchor_val = ship_registry_get_index(Parse_names[anchor_val]); + anchor = anchor_t(anchor_val); + } - if (check_for_hangar && anchor >= 0) + if (check_for_hangar) { SCP_string message; check_anchor_for_hangar_bay(message, anchors_checked, anchor, other_name, other_is_ship, is_arrival); @@ -5243,7 +5248,7 @@ void resolve_and_check_anchor(bool check_for_hangar, SCP_set &anchors_check */ void post_process_parse_names() { - SCP_set anchors_checked; + SCP_set anchors_checked; // check the parse names for (const auto &parse_name : Parse_names) @@ -5271,7 +5276,7 @@ void post_process_parse_names() } // Goober5000 -void resolve_path_masks(int anchor, int *path_mask) +void resolve_path_masks(anchor_t anchor, int *path_mask) { path_restriction_t *prp; @@ -5291,7 +5296,7 @@ void resolve_path_masks(int anchor, int *path_mask) int j, bay_path, modelnum; // get anchor ship - Assert(!(anchor & SPECIAL_ARRIVAL_ANCHOR_FLAG)); + Assert(anchor.isValid() && !(anchor.value() & ANCHOR_SPECIAL_ARRIVAL)); auto anchor_ship_entry = ship_registry_get(anchor); // Load the anchor ship model with subsystems and all; it'll need to be done for this mission anyway @@ -7070,20 +7075,7 @@ void mission::Reset() max_respawn_delay = -1; memset(&Ignored_keys, 0, sizeof(int)*CCFG_MAX); - memset( &support_ships, 0, sizeof( support_ships ) ); - support_ships.arrival_anchor = -1; - support_ships.departure_anchor = -1; - support_ships.max_subsys_repair_val = 100.0f; //ASSUMPTION: full repair capabilities - support_ships.max_support_ships = -1; // infinite - support_ships.max_concurrent_ships = 1; - support_ships.ship_class = -1; - - // for each species, store whether support is available - for (int species = 0; species < (int)Species_info.size(); species++) { - if (Species_info[species].support_ship_index >= 0) { - support_ships.support_available_for_species |= (1 << species); - } - } + support_ships.reset(); squad_filename[ 0 ] = '\0'; squad_name[ 0 ] = '\0'; @@ -7122,6 +7114,28 @@ void mission::Reset() custom_strings.clear(); } +void support_ship_info::reset() +{ + arrival_location = ArrivalLocation::AT_LOCATION; + arrival_anchor = anchor_t::invalid(); + departure_location = DepartureLocation::AT_LOCATION; + departure_anchor = anchor_t::invalid(); + max_hull_repair_val = 0.0f; // hull cannot be repaired + max_subsys_repair_val = 100.0f; //ASSUMPTION: full repair capabilities + max_support_ships = -1; // infinite + max_concurrent_ships = 1; + ship_class = -1; // ship class will be determined by the summoning ship's species + tally = 0; + support_available_for_species = 0; // will be filled in by the next loop + + // for each species, store whether support is available + for (int species = 0; species < sz2i(Species_info.size()); species++) { + if (Species_info[species].support_ship_index >= 0) { + support_available_for_species |= (1 << species); + } + } +} + /** * Initialize the mission and related data structures. */ @@ -7795,7 +7809,7 @@ bool mission_check_ship_yet_to_arrive(const char *name) * Sets the arrival location of a parse object according to the arrival location of the object. * @return objnum of anchor ship if there is one, -1 otherwise. */ -int mission_set_arrival_location(int anchor, ArrivalLocation location, int dist, int objnum, int path_mask, vec3d *new_pos, matrix *new_orient) +int mission_set_arrival_location(anchor_t anchor, ArrivalLocation location, int dist, int objnum, int path_mask, vec3d *new_pos, matrix *new_orient) { int shipnum, anchor_objnum; vec3d anchor_pos, rand_vec, new_fvec; @@ -7804,20 +7818,20 @@ int mission_set_arrival_location(int anchor, ArrivalLocation location, int dist, if ( location == ArrivalLocation::AT_LOCATION ) return -1; - Assert(anchor >= 0); - if (anchor < 0) + Assert(anchor.isValid()); + if (!anchor.isValid()) return -1; // should never happen, but if it does, fail gracefully // this ship might possibly arrive at another location. The location is based on the // proximity of some ship (and some other special tokens) - if (anchor & SPECIAL_ARRIVAL_ANCHOR_FLAG) + if (anchor.value() & ANCHOR_SPECIAL_ARRIVAL) { - bool get_players = (anchor & SPECIAL_ARRIVAL_ANCHOR_PLAYER_FLAG) > 0; + bool get_players = (anchor.value() & ANCHOR_SPECIAL_ARRIVAL_PLAYER) > 0; // filter out iff - int iff_index = anchor; - iff_index &= ~SPECIAL_ARRIVAL_ANCHOR_FLAG; - iff_index &= ~SPECIAL_ARRIVAL_ANCHOR_PLAYER_FLAG; + int iff_index = anchor.value(); + iff_index &= ~ANCHOR_SPECIAL_ARRIVAL; + iff_index &= ~ANCHOR_SPECIAL_ARRIVAL_PLAYER; // get ship shipnum = ship_get_random_team_ship(iff_get_mask(iff_index), get_players ? SHIP_GET_ONLY_PLAYERS : SHIP_GET_ANY_SHIP); @@ -8022,7 +8036,7 @@ int mission_did_ship_arrive(p_object *objp, bool force_arrival) // check to see if this ship is to arrive via a docking bay. If so, and the ship to arrive from // doesn't exist, don't create. if ( objp->arrival_location == ArrivalLocation::FROM_DOCK_BAY ) { - Assert( objp->arrival_anchor >= 0 ); + Assert( objp->arrival_anchor.isValid() ); auto anchor_ship_entry = ship_registry_get(objp->arrival_anchor); // see if ship is yet to arrive. If so, then return -1 so we can evaluate again later. @@ -8365,7 +8379,8 @@ int mission_do_departure(object *objp, bool goal_is_to_warp) Assert(objp->type == OBJ_SHIP); bool beginning_departure; DepartureLocation location; - int anchor, path_mask; + anchor_t anchor; + int path_mask; ship *shipp = &Ships[objp->instance]; ai_info *aip = &Ai_info[shipp->ai_index]; @@ -8432,10 +8447,7 @@ int mission_do_departure(object *objp, bool goal_is_to_warp) // just make it warp out like anything else. if (location == DepartureLocation::TO_DOCK_BAY) { - Assert(anchor >= 0); - auto anchor_ship_entry = (anchor >= 0) - ? ship_registry_get(anchor) - : nullptr; // should never happen, but if it does, fail gracefully + auto anchor_ship_entry = ship_registry_get(anchor); // see if ship is yet to arrive. If so, then warp. if (!anchor_ship_entry || anchor_ship_entry->status == ShipStatus::NOT_YET_PRESENT) @@ -8784,19 +8796,19 @@ int add_path_restriction() /** * Look for \, \, etc. */ -int get_special_anchor(const char *name) +anchor_t get_special_anchor(const char *name) { char tmp[NAME_LENGTH + 15]; const char *iff_name; int iff_index; if (strnicmp(name, ""); if (iff_name == nullptr) - return -1; + return anchor_t::invalid(); // hack substitute "hostile" for "enemy" if (!stricmp(iff_name, "enemy")) @@ -8804,34 +8816,32 @@ int get_special_anchor(const char *name) iff_index = iff_lookup(iff_name); if (iff_index < 0) - return -1; + return anchor_t::invalid(); // restrict to players? if (stristr(name+5, "player") != NULL) - return (iff_index | SPECIAL_ARRIVAL_ANCHOR_FLAG | SPECIAL_ARRIVAL_ANCHOR_PLAYER_FLAG); + return anchor_t(iff_index | ANCHOR_SPECIAL_ARRIVAL | ANCHOR_SPECIAL_ARRIVAL_PLAYER); else - return (iff_index | SPECIAL_ARRIVAL_ANCHOR_FLAG); + return anchor_t(iff_index | ANCHOR_SPECIAL_ARRIVAL); } -int get_anchor(const char *name) +anchor_t get_anchor(const char *name) { - int special_anchor = get_special_anchor(name); + auto special_anchor = get_special_anchor(name); - if (special_anchor >= 0) + if (special_anchor.isValid()) return special_anchor; - return get_parse_name_index(name); + return anchor_t(get_parse_name_index(name) | ANCHOR_IS_PARSE_NAMES_INDEX); } /** * See if an arrival/departure anchor is missing a hangar bay. If it is, the message parameter will be populated with an appropriate error. */ -void check_anchor_for_hangar_bay(SCP_string &message, SCP_set &anchors_checked, int anchor, const char *other_name, bool other_is_ship, bool is_arrival) +void check_anchor_for_hangar_bay(SCP_string &message, SCP_set &anchors_checked, anchor_t anchor, const char *other_name, bool other_is_ship, bool is_arrival) { message.clear(); - if (anchor < 0) - return; if (anchors_checked.contains(anchor)) return; anchors_checked.insert(anchor); diff --git a/code/mission/missionparse.h b/code/mission/missionparse.h index 766fa5c5647..fef23bdfa79 100644 --- a/code/mission/missionparse.h +++ b/code/mission/missionparse.h @@ -25,6 +25,7 @@ #include "sound/sound.h" #include "mission/mission_flags.h" #include "nebula/volumetrics.h" +#include "ship/anchor_t.h" #include "stats/scoring.h" //WMC - This should be here @@ -40,16 +41,11 @@ enum class DepartureLocation; #define DEFAULT_AMBIENT_LIGHT_LEVEL 0x00787878 -// arrival anchor types -// mask should be high enough to avoid conflicting with ship anchors -#define SPECIAL_ARRIVAL_ANCHOR_FLAG 0x1000 -#define SPECIAL_ARRIVAL_ANCHOR_PLAYER_FLAG 0x0100 - #define MIN_TARGET_ARRIVAL_DISTANCE 500.0f // float because that's how FRED does the math #define MIN_TARGET_ARRIVAL_MULTIPLIER 2.0f // minimum distance is 2 * target radius, but at least 500 -int get_special_anchor(const char *name); -void check_anchor_for_hangar_bay(SCP_string &message, SCP_set &anchors_checked, int anchor, const char *other_name, bool other_is_ship, bool is_arrival); +anchor_t get_special_anchor(const char *name); +void check_anchor_for_hangar_bay(SCP_string &message, SCP_set &anchors_checked, anchor_t anchor, const char *other_name, bool other_is_ship, bool is_arrival); // MISSION_VERSION should be the earliest version of FSO that can load the current mission format without // requiring version-specific comments. It should be updated whenever the format changes, but it should @@ -102,19 +98,22 @@ inline const std::vector> Mission_event_teams_tvt = [ }(); // Goober5000 -typedef struct support_ship_info { - ArrivalLocation arrival_location; // arrival location - int arrival_anchor; // arrival anchor - DepartureLocation departure_location; // departure location - int departure_anchor; // departure anchor - float max_hull_repair_val; // % of a ship's hull that can be repaired -C - float max_subsys_repair_val; // same thing, except for subsystems -C - int max_support_ships; // max number of consecutive support ships - int max_concurrent_ships; // max number of concurrent support ships in mission per team - int ship_class; // ship class of support ship - int tally; // number of support ships so far - int support_available_for_species; // whether support is available for a given species (this is a bitfield) -} support_ship_info; +struct support_ship_info +{ + ArrivalLocation arrival_location; // arrival location + anchor_t arrival_anchor; // arrival anchor + DepartureLocation departure_location; // departure location + anchor_t departure_anchor; // departure anchor + float max_hull_repair_val; // % of a ship's hull that can be repaired -C + float max_subsys_repair_val; // same thing, except for subsystems -C + int max_support_ships; // max number of consecutive support ships + int max_concurrent_ships; // max number of concurrent support ships in mission per team + int ship_class; // ship class of support ship + int tally; // number of support ships so far + int support_available_for_species; // whether support is available for a given species (this is a bitfield) + + void reset(); +}; // movie type defines // If you add one here, you must also add a description to missioncutscenedlg.cpp for FRED @@ -448,13 +447,13 @@ class p_object ArrivalLocation arrival_location = ArrivalLocation::AT_LOCATION; int arrival_distance = 0; // used when arrival location is near or in front of some ship - int arrival_anchor = -1; // ship registry entry used for anchoring an arrival point + anchor_t arrival_anchor = anchor_t::invalid(); // ship registry entry used for anchoring an arrival point int arrival_path_mask = 0; // Goober5000 int arrival_cue = -1; // Index in Sexp_nodes of this sexp. int arrival_delay = 0; DepartureLocation departure_location = DepartureLocation::AT_LOCATION; - int departure_anchor = -1; + anchor_t departure_anchor = anchor_t::invalid(); int departure_path_mask = 0; // Goober5000 int departure_cue = -1; // Index in Sexp_nodes of this sexp. int departure_delay = 0; diff --git a/code/missioneditor/common.cpp b/code/missioneditor/common.cpp index 9b0ed89898d..924c38e961a 100644 --- a/code/missioneditor/common.cpp +++ b/code/missioneditor/common.cpp @@ -50,11 +50,11 @@ void stuff_special_arrival_anchor_name(char* buf, int anchor_num, bool retail_fo { // filter out iff int iff_index = anchor_num; - iff_index &= ~SPECIAL_ARRIVAL_ANCHOR_FLAG; - iff_index &= ~SPECIAL_ARRIVAL_ANCHOR_PLAYER_FLAG; + iff_index &= ~ANCHOR_SPECIAL_ARRIVAL; + iff_index &= ~ANCHOR_SPECIAL_ARRIVAL_PLAYER; // filter players - int restrict_to_players = (anchor_num & SPECIAL_ARRIVAL_ANCHOR_PLAYER_FLAG); + int restrict_to_players = (anchor_num & ANCHOR_SPECIAL_ARRIVAL_PLAYER); // get name stuff_special_arrival_anchor_name(buf, iff_index, restrict_to_players, retail_format); @@ -63,21 +63,21 @@ void stuff_special_arrival_anchor_name(char* buf, int anchor_num, bool retail_fo // Ship and wing arrival and departure anchors should always be ship registry entry indexes, except for a very brief window during mission parsing. // But FRED and QtFRED dialogs use ship indexes instead. So, rather than refactor all the dialogs, this converts between the two. If an anchor // is a valid ship registry index, the equivalent ship index is returned; otherwise the special value (-1 or a flag) is returned instead. -int anchor_to_target(int anchor) +int anchor_to_target(anchor_t anchor) { auto anchor_entry = ship_registry_get(anchor); - return anchor_entry ? anchor_entry->shipnum : anchor; + return anchor_entry ? anchor_entry->shipnum : anchor.value(); } // Ship and wing arrival and departure anchors should always be ship registry entry indexes, except for a very brief window during mission parsing. // But FRED and QtFRED dialogs use ship indexes instead. So, rather than refactor all the dialogs, this converts between the two. If a target // is a valid ship index, the equivalent ship registry index is returned; otherwise the special value (-1 or a flag) is returned instead. -int target_to_anchor(int target) +anchor_t target_to_anchor(int target) { if (target >= 0 && target < MAX_SHIPS) - return ship_registry_get_index(Ships[target].ship_name); + return anchor_t(ship_registry_get_index(Ships[target].ship_name)); else - return target; + return anchor_t(target); } void generate_weaponry_usage_list_team(int team, int* arr) diff --git a/code/missioneditor/common.h b/code/missioneditor/common.h index 5a2de142216..cc9efc4088c 100644 --- a/code/missioneditor/common.h +++ b/code/missioneditor/common.h @@ -1,6 +1,7 @@ #pragma once #include "globalincs/globals.h" #include "mission/missionmessage.h" +#include "ship/anchor_t.h" // Voice acting manager #define INVALID_MESSAGE ((MMessage*)SIZE_MAX) // was originally SIZE_T_MAX but that wasn't available outside fred. May need more research. @@ -31,9 +32,9 @@ void stuff_special_arrival_anchor_name(char* buf, int iff_index, int restrict_to void stuff_special_arrival_anchor_name(char* buf, int anchor_num, bool retail_format); -int anchor_to_target(int anchor); +int anchor_to_target(anchor_t anchor); -int target_to_anchor(int target); +anchor_t target_to_anchor(int target); void generate_weaponry_usage_list_team(int team, int* arr); diff --git a/code/missioneditor/missionsave.cpp b/code/missioneditor/missionsave.cpp index 373ef8585ca..c8e9a843622 100644 --- a/code/missioneditor/missionsave.cpp +++ b/code/missioneditor/missionsave.cpp @@ -3559,10 +3559,10 @@ int Fred_mission_save::save_objects() fout("\n$Arrival Anchor:"); } - z = shipp->arrival_anchor; + z = shipp->arrival_anchor.value(); if (z < 0) { fout(" "); - } else if (z & SPECIAL_ARRIVAL_ANCHOR_FLAG) { + } else if (z & ANCHOR_SPECIAL_ARRIVAL) { // get name char tmp[NAME_LENGTH + 15]; stuff_special_arrival_anchor_name(tmp, z, save_config.save_format == MissionFormat::RETAIL); @@ -4929,10 +4929,10 @@ int Fred_mission_save::save_wings() else fout("\n$Arrival Anchor:"); - int z = w.arrival_anchor; + int z = w.arrival_anchor.value(); if (z < 0) { fout(" "); - } else if (z & SPECIAL_ARRIVAL_ANCHOR_FLAG) { + } else if (z & ANCHOR_SPECIAL_ARRIVAL) { // get name char tmp[NAME_LENGTH + 15]; stuff_special_arrival_anchor_name(tmp, z, save_config.save_format == MissionFormat::RETAIL); diff --git a/code/parse/sexp.cpp b/code/parse/sexp.cpp index 12fe70d6395..f20b0584c66 100644 --- a/code/parse/sexp.cpp +++ b/code/parse/sexp.cpp @@ -2981,7 +2981,7 @@ int check_sexp_syntax(int node, int return_type, int recursive, int *bad_node, s int valid = 0; // , etc. - if (get_special_anchor(CTEXT(node)) >= 0) + if (get_special_anchor(CTEXT(node)).isValid()) { valid = 1; } @@ -23595,7 +23595,7 @@ void sexp_set_support_ship(int n) else { // find the anchor - The_mission.support_ships.arrival_anchor = ship_registry_get_index(CTEXT(n)); + The_mission.support_ships.arrival_anchor = anchor_t(ship_registry_get_index(CTEXT(n))); } // get departure location @@ -23621,7 +23621,7 @@ void sexp_set_support_ship(int n) else { // find the anchor - The_mission.support_ships.departure_anchor = ship_registry_get_index(CTEXT(n)); + The_mission.support_ships.departure_anchor = anchor_t(ship_registry_get_index(CTEXT(n))); } // get ship class @@ -23662,7 +23662,7 @@ void sexp_set_support_ship(int n) // Goober5000 - set stuff for arriving ships or wings void sexp_set_arrival_info(int node) { - int arrival_anchor, arrival_mask, arrival_distance, arrival_delay, n = node; + int arrival_mask, arrival_distance, arrival_delay, n = node; bool show_warp, adjust_warp_when_docked, is_nan, is_nan_forever; object_ship_wing_point_team oswpt; @@ -23683,7 +23683,7 @@ void sexp_set_arrival_info(int node) n = CDR(n); // get arrival anchor - arrival_anchor = -1; + anchor_t arrival_anchor; if ((n < 0) || !stricmp(CTEXT(n), "")) { // if no anchor, set arrival location to hyperspace @@ -23692,7 +23692,7 @@ void sexp_set_arrival_info(int node) else { // find the anchor - arrival_anchor = ship_registry_get_index(CTEXT(n)); + arrival_anchor = anchor_t(ship_registry_get_index(CTEXT(n))); } n = CDR(n); @@ -23762,7 +23762,7 @@ void sexp_set_arrival_info(int node) // Goober5000 - set stuff for departing ships or wings void sexp_set_departure_info(int node) { - int departure_anchor, departure_mask, departure_delay, n = node; + int departure_mask, departure_delay, n = node; bool show_warp, adjust_warp_when_docked, is_nan, is_nan_forever; object_ship_wing_point_team oswpt; @@ -23783,7 +23783,7 @@ void sexp_set_departure_info(int node) n = CDR(n); // get departure anchor - departure_anchor = -1; + anchor_t departure_anchor; if ((n < 0) || !stricmp(CTEXT(n), "")) { // if no anchor, set departure location to hyperspace @@ -23792,7 +23792,7 @@ void sexp_set_departure_info(int node) else { // find the anchor - departure_anchor = ship_registry_get_index(CTEXT(n)); + departure_anchor = anchor_t(ship_registry_get_index(CTEXT(n))); } n = CDR(n); diff --git a/code/scripting/api/objs/parse_object.cpp b/code/scripting/api/objs/parse_object.cpp index 58f3967528c..ec5bdb2afce 100644 --- a/code/scripting/api/objs/parse_object.cpp +++ b/code/scripting/api/objs/parse_object.cpp @@ -501,7 +501,7 @@ ADE_VIRTVAR(DepartureLocation, l_ParseObject, "string", "The ship's departure lo return parse_object_getset_location_helper(L, &p_object::departure_location, "Departure", Departure_location_names, MAX_DEPARTURE_NAMES); } -static int parse_object_getset_anchor_helper(lua_State* L, int p_object::* field) +static int parse_object_getset_anchor_helper(lua_State* L, anchor_t p_object::* field) { parse_object_h* poh; const char* s = nullptr; @@ -513,7 +513,7 @@ static int parse_object_getset_anchor_helper(lua_State* L, int p_object::* field if (ADE_SETTING_VAR && s != nullptr) { - poh->getObject()->*field = (stricmp(s, "") == 0) ? -1 : ship_registry_get_index(s); + poh->getObject()->*field = (stricmp(s, "") == 0) ? anchor_t::invalid() : anchor_t(ship_registry_get_index(s)); } auto anchor_entry = ship_registry_get(poh->getObject()->*field); diff --git a/code/scripting/api/objs/ship.cpp b/code/scripting/api/objs/ship.cpp index 172bad89d81..f7edbd837e1 100644 --- a/code/scripting/api/objs/ship.cpp +++ b/code/scripting/api/objs/ship.cpp @@ -1272,7 +1272,7 @@ ADE_VIRTVAR(DepartureLocation, l_Ship, "string", "The ship's departure location" return ship_getset_location_helper(L, &ship::departure_location, "Departure", Departure_location_names, MAX_DEPARTURE_NAMES); } -static int ship_getset_anchor_helper(lua_State* L, int ship::* field) +static int ship_getset_anchor_helper(lua_State* L, anchor_t ship::* field) { object_h* objh; const char* s = nullptr; @@ -1286,7 +1286,7 @@ static int ship_getset_anchor_helper(lua_State* L, int ship::* field) if (ADE_SETTING_VAR && s != nullptr) { - shipp->*field = (stricmp(s, "") == 0) ? -1 : ship_registry_get_index(s); + shipp->*field = (stricmp(s, "") == 0) ? anchor_t::invalid() : anchor_t(ship_registry_get_index(s)); } auto anchor_entry = ship_registry_get(shipp->*field); diff --git a/code/scripting/api/objs/wing.cpp b/code/scripting/api/objs/wing.cpp index 3b0169589fb..dcfa283875c 100644 --- a/code/scripting/api/objs/wing.cpp +++ b/code/scripting/api/objs/wing.cpp @@ -310,7 +310,7 @@ ADE_VIRTVAR(DepartureLocation, l_Wing, "string", "The wing's departure location" return wing_getset_location_helper(L, &wing::departure_location, "Departure", Departure_location_names, MAX_DEPARTURE_NAMES); } -static int wing_getset_anchor_helper(lua_State* L, int wing::* field) +static int wing_getset_anchor_helper(lua_State* L, anchor_t wing::* field) { int wingnum; const char* s = nullptr; @@ -322,7 +322,7 @@ static int wing_getset_anchor_helper(lua_State* L, int wing::* field) if (ADE_SETTING_VAR && s != nullptr) { - Wings[wingnum].*field = (stricmp(s, "") == 0) ? -1 : ship_registry_get_index(s); + Wings[wingnum].*field = (stricmp(s, "") == 0) ? anchor_t::invalid() : anchor_t(ship_registry_get_index(s)); } auto anchor_entry = ship_registry_get(Wings[wingnum].*field); diff --git a/code/ship/anchor_t.h b/code/ship/anchor_t.h new file mode 100644 index 00000000000..292187e2caa --- /dev/null +++ b/code/ship/anchor_t.h @@ -0,0 +1,22 @@ +#ifndef SHIP_ANCHOR_T_H +#define SHIP_ANCHOR_T_H + +#include "utils/id.h" + +// The "strong typedef" pattern; see timer.h for more information +struct anchor_tag {}; +class anchor_t : public util::ID +{ +public: + static anchor_t invalid() { return {}; } + anchor_t() = default; + explicit anchor_t(int val) : ID(val) { } +}; + +// use high non-negative bits to mark anchor flags; flags should be high enough to avoid conflicting with ship registry indexes +constexpr int ANCHOR_SPECIAL_ARRIVAL = (1 << 30); +constexpr int ANCHOR_SPECIAL_ARRIVAL_PLAYER = (1 << 29); +constexpr int ANCHOR_IS_PARSE_NAMES_INDEX = (1 << 28); + + +#endif // SHIP_ANCHOR_T_H diff --git a/code/ship/ship.cpp b/code/ship/ship.cpp index 9bf4b95414b..3f9da69c2ed 100644 --- a/code/ship/ship.cpp +++ b/code/ship/ship.cpp @@ -292,6 +292,12 @@ const ship_registry_entry *ship_registry_get(int index) return nullptr; } +const ship_registry_entry *ship_registry_get(anchor_t anchor) +{ + // anchors are just ship registry indexes (if in bounds) under the hood + return ship_registry_get(anchor.value()); +} + int Num_engine_wash_types; int Num_ship_subobj_types; @@ -7193,13 +7199,13 @@ void ship::clear() arrival_location = ArrivalLocation::AT_LOCATION; arrival_distance = 0; - arrival_anchor = -1; + arrival_anchor = anchor_t::invalid(); arrival_path_mask = 0; arrival_cue = -1; arrival_delay = 0; departure_location = DepartureLocation::AT_LOCATION; - departure_anchor = -1; + departure_anchor = anchor_t::invalid(); departure_path_mask = 0; departure_cue = -1; departure_delay = 0; @@ -7554,13 +7560,13 @@ void wing::clear() arrival_location = ArrivalLocation::AT_LOCATION; arrival_distance = 0; - arrival_anchor = -1; + arrival_anchor = anchor_t::invalid(); arrival_path_mask = 0; arrival_cue = -1; arrival_delay = 0; departure_location = DepartureLocation::AT_LOCATION; - departure_anchor = -1; + departure_anchor = anchor_t::invalid(); departure_path_mask = 0; departure_cue = -1; departure_delay = 0; @@ -17269,7 +17275,7 @@ bool ship_can_bay_depart(ship* sp) { // if this ship belongs to a wing, then use the wing departure information DepartureLocation departure_location; - int departure_anchor; + anchor_t departure_anchor; int departure_path_mask; if (sp->wingnum >= 0) { @@ -17285,7 +17291,7 @@ bool ship_can_bay_depart(ship* sp) if ( departure_location == DepartureLocation::TO_DOCK_BAY ) { - Assertion( departure_anchor >= 0, "Ship %s must have a valid departure anchor", sp->ship_name ); + Assertion( departure_anchor.isValid(), "Ship %s must have a valid departure anchor", sp->ship_name ); auto anchor_ship_entry = ship_registry_get(departure_anchor); if (anchor_ship_entry && anchor_ship_entry->has_shipp() && ship_useful_for_departure(anchor_ship_entry->shipnum, departure_path_mask)) { // can bay depart at this time @@ -19429,7 +19435,7 @@ int is_support_allowed(object *objp, bool do_simple_check) // make sure, if exiting from bay, that parent ship is in the mission! if ((result == 0 || result == 2) && (The_mission.support_ships.arrival_location == ArrivalLocation::FROM_DOCK_BAY)) { - Assert(The_mission.support_ships.arrival_anchor != -1); + Assert(The_mission.support_ships.arrival_anchor.isValid()); // ensure it's in-mission auto anchor_ship_entry = ship_registry_get(The_mission.support_ships.arrival_anchor); diff --git a/code/ship/ship.h b/code/ship/ship.h index 6571f5e50d2..87b79f48211 100644 --- a/code/ship/ship.h +++ b/code/ship/ship.h @@ -7,13 +7,9 @@ * */ - - #ifndef _SHIP_H #define _SHIP_H - - #include "ai/ai.h" #include "fireball/fireballs.h" #include "globalincs/globals.h" // for defintions of token lengths -- maybe move this elsewhere later (Goober5000 - moved to globals.h) @@ -29,6 +25,7 @@ #include "species_defs/species_defs.h" #include "weapon/shockwave.h" #include "weapon/trails.h" +#include "ship/anchor_t.h" #include "ship/ship_flags.h" #include "weapon/weapon_flags.h" #include "weapon/weapon.h" @@ -633,13 +630,13 @@ class ship ArrivalLocation arrival_location; int arrival_distance; // how far away this ship should arrive - int arrival_anchor; // ship registry index of object this ship arrives near (or in front of) + anchor_t arrival_anchor; // ship registry index of object this ship arrives near (or in front of) int arrival_path_mask; // Goober5000 - possible restrictions on which bay paths to use int arrival_cue; int arrival_delay; DepartureLocation departure_location; // depart to hyperspace or someplace else (like docking bay) - int departure_anchor; // when docking bay -- ship registry index of ship to use + anchor_t departure_anchor; // when docking bay -- ship registry index of ship to use int departure_path_mask; // Goober5000 - possible restrictions on which bay paths to use int departure_cue; // sexpression to eval when departing int departure_delay; // time in seconds after sexp is true that we delay. @@ -969,6 +966,7 @@ extern bool ship_registry_exists(int index); extern const ship_registry_entry *ship_registry_get(const char *name); extern const ship_registry_entry *ship_registry_get(const SCP_string &name); extern const ship_registry_entry *ship_registry_get(int index); +extern const ship_registry_entry *ship_registry_get(anchor_t anchor); #define REGULAR_WEAPON (1<<0) #define DOGFIGHT_WEAPON (1<<1) @@ -1596,13 +1594,13 @@ typedef struct wing { ArrivalLocation arrival_location; // arrival and departure information for wings -- similar to info for ships int arrival_distance; // distance from some ship where this ship arrives - int arrival_anchor; // ship registry index of object this wing arrives near (or in front of) + anchor_t arrival_anchor; // ship registry index of object this wing arrives near (or in front of) int arrival_path_mask; // Goober5000 - possible restrictions on which bay paths to use int arrival_cue; int arrival_delay; DepartureLocation departure_location; - int departure_anchor; // ship registry index of object that we depart to (in case of dock bays) + anchor_t departure_anchor; // ship registry index of object that we depart to (in case of dock bays) int departure_path_mask; // Goober5000 - possible restrictions on which bay paths to use int departure_cue; int departure_delay; diff --git a/code/source_groups.cmake b/code/source_groups.cmake index 29b19ca55a6..89a6cef8506 100644 --- a/code/source_groups.cmake +++ b/code/source_groups.cmake @@ -1578,6 +1578,7 @@ add_file_folder("Scripting\\\\Lua\\\\BitOp" # Ship files add_file_folder("Ship" + ship/anchor_t.h ship/afterburner.cpp ship/afterburner.h ship/awacs.cpp diff --git a/fred2/fredview.cpp b/fred2/fredview.cpp index 0a86f2dc268..2ff79cf1497 100644 --- a/fred2/fredview.cpp +++ b/fred2/fredview.cpp @@ -2469,7 +2469,7 @@ int CFREDView::global_error_check() object *ptr; brief_stage *sp; SCP_string anchor_message; - SCP_set anchors_checked; + SCP_set anchors_checked; g_err = multi = 0; if ( The_mission.game_type & MISSION_TYPE_MULTI ) @@ -2663,7 +2663,7 @@ int CFREDView::global_error_check() } if (Ships[i].arrival_location != ArrivalLocation::AT_LOCATION) { - if (Ships[i].arrival_anchor < 0){ + if (!Ships[i].arrival_anchor.isValid()){ if (error("Ship \"%s\" requires a valid arrival target", Ships[i].ship_name)){ return 1; } @@ -2677,7 +2677,7 @@ int CFREDView::global_error_check() } if (Ships[i].departure_location != DepartureLocation::AT_LOCATION) { - if (Ships[i].departure_anchor < 0){ + if (!Ships[i].departure_anchor.isValid()){ if (error("Ship \"%s\" requires a valid departure target", Ships[i].ship_name)){ return 1; } @@ -2880,7 +2880,7 @@ int CFREDView::global_error_check() } if (Wings[i].arrival_location != ArrivalLocation::AT_LOCATION) { - if (Wings[i].arrival_anchor < 0) + if (!Wings[i].arrival_anchor.isValid()) if (error("Wing \"%s\" requires a valid arrival target", Wings[i].name)) return 1; if (Wings[i].arrival_location == ArrivalLocation::FROM_DOCK_BAY) { @@ -2892,7 +2892,7 @@ int CFREDView::global_error_check() } if (Wings[i].departure_location != DepartureLocation::AT_LOCATION) { - if (Wings[i].departure_anchor < 0) + if (!Wings[i].departure_anchor.isValid()) if (error("Wing \"%s\" requires a valid departure target", Wings[i].name)) return 1; if (Wings[i].departure_location == DepartureLocation::TO_DOCK_BAY) { diff --git a/fred2/management.cpp b/fred2/management.cpp index 92321a5f663..73595ee4030 100644 --- a/fred2/management.cpp +++ b/fred2/management.cpp @@ -2481,7 +2481,7 @@ void management_add_ships_to_combo( CComboBox *box, int flags ) stuff_special_arrival_anchor_name(tmp, i, restrict_to_players, false); id = box->AddString(tmp); - box->SetItemData(id, get_special_anchor(tmp)); + box->SetItemData(id, get_special_anchor(tmp).value()); } } } diff --git a/fred2/shipeditordlg.cpp b/fred2/shipeditordlg.cpp index 6a20282b45f..c2be434cb0e 100644 --- a/fred2/shipeditordlg.cpp +++ b/fred2/shipeditordlg.cpp @@ -806,7 +806,7 @@ void CShipEditorDlg::initialize_data(int full_update) // of the drop-down list. if (m_arrival_target >= 0) { - if (m_arrival_target & SPECIAL_ARRIVAL_ANCHOR_FLAG) + if (m_arrival_target & ANCHOR_SPECIAL_ARRIVAL) { // figure out what the box represents this as char tmp[NAME_LENGTH + 15]; @@ -1412,7 +1412,7 @@ int CShipEditorDlg::update_ship(int ship) // if the arrival is not hyperspace or docking bay -- force arrival distance to be // greater than 2*radius of target. - if (((m_arrival_location != static_cast(ArrivalLocation::FROM_DOCK_BAY)) && (m_arrival_location != static_cast(ArrivalLocation::AT_LOCATION))) && (z >= 0) && !(z & SPECIAL_ARRIVAL_ANCHOR_FLAG)) { + if (((m_arrival_location != static_cast(ArrivalLocation::FROM_DOCK_BAY)) && (m_arrival_location != static_cast(ArrivalLocation::AT_LOCATION))) && (z >= 0) && !(z & ANCHOR_SPECIAL_ARRIVAL)) { d = int(std::min(500.0f, 2.0f * Objects[Ships[ship].objnum].radius)); if ((Ships[ship].arrival_distance < d) && (Ships[ship].arrival_distance > -d)) { str.Format("Ship must arrive at least %d meters away from target.\n" diff --git a/fred2/wing_editor.cpp b/fred2/wing_editor.cpp index 77be8d57e59..ef8223a45d5 100644 --- a/fred2/wing_editor.cpp +++ b/fred2/wing_editor.cpp @@ -382,7 +382,7 @@ void wing_editor::initialize_data_safe(int full_update) // of the drop-down list. if (m_arrival_target >= 0) { - if (m_arrival_target & SPECIAL_ARRIVAL_ANCHOR_FLAG) + if (m_arrival_target & ANCHOR_SPECIAL_ARRIVAL) { // figure out what the box represents this as char tmp[NAME_LENGTH + 15]; @@ -821,7 +821,7 @@ void wing_editor::update_data_safe() MODIFY(Wings[cur_wing].arrival_anchor, target_to_anchor(i)); // when arriving near or in front of a ship, be sure that we are far enough away from it!!! - if (((m_arrival_location != static_cast(ArrivalLocation::AT_LOCATION)) && (m_arrival_location != static_cast(ArrivalLocation::FROM_DOCK_BAY))) && (i >= 0) && !(i & SPECIAL_ARRIVAL_ANCHOR_FLAG)) { + if (((m_arrival_location != static_cast(ArrivalLocation::AT_LOCATION)) && (m_arrival_location != static_cast(ArrivalLocation::FROM_DOCK_BAY))) && (i >= 0) && !(i & ANCHOR_SPECIAL_ARRIVAL)) { d = int(std::min(MIN_TARGET_ARRIVAL_DISTANCE, MIN_TARGET_ARRIVAL_MULTIPLIER * Objects[Ships[i].objnum].radius)); if ((Wings[cur_wing].arrival_distance < d) && (Wings[cur_wing].arrival_distance > -d)) { if (!bypass_errors) { diff --git a/qtfred/src/mission/Editor.cpp b/qtfred/src/mission/Editor.cpp index 619fe5f3436..412bf878fa7 100644 --- a/qtfred/src/mission/Editor.cpp +++ b/qtfred/src/mission/Editor.cpp @@ -1977,7 +1977,7 @@ int Editor::global_error_check_impl() { object* ptr; brief_stage* sp; SCP_string anchor_message; - SCP_set anchors_checked; + SCP_set anchors_checked; g_err = multi = 0; if (The_mission.game_type & MISSION_TYPE_MULTI) { @@ -2167,7 +2167,7 @@ int Editor::global_error_check_impl() { } if (Ships[i].arrival_location != ArrivalLocation::AT_LOCATION) { - if (Ships[i].arrival_anchor < 0) { + if (!Ships[i].arrival_anchor.isValid()) { if (error("Ship \"%s\" requires a valid arrival target", Ships[i].ship_name)) { return 1; } @@ -2181,7 +2181,7 @@ int Editor::global_error_check_impl() { } if (Ships[i].departure_location != DepartureLocation::AT_LOCATION) { - if (Ships[i].departure_anchor < 0) { + if (!Ships[i].departure_anchor.isValid()) { if (error("Ship \"%s\" requires a valid departure target", Ships[i].ship_name)) { return 1; } @@ -2395,7 +2395,7 @@ int Editor::global_error_check_impl() { } if (Wings[i].arrival_location != ArrivalLocation::AT_LOCATION) { - if (Wings[i].arrival_anchor < 0) { + if (!Wings[i].arrival_anchor.isValid()) { if (error("Wing \"%s\" requires a valid arrival target", Wings[i].name)) { return 1; } @@ -2409,7 +2409,7 @@ int Editor::global_error_check_impl() { } if (Wings[i].departure_location != DepartureLocation::AT_LOCATION) { - if (Wings[i].departure_anchor < 0) { + if (!Wings[i].departure_anchor.isValid()) { if (error("Wing \"%s\" requires a valid departure target", Wings[i].name)) { return 1; } diff --git a/qtfred/src/mission/dialogs/ShipEditor/ShipEditorDialogModel.cpp b/qtfred/src/mission/dialogs/ShipEditor/ShipEditorDialogModel.cpp index a198d69b321..e276d9542a6 100644 --- a/qtfred/src/mission/dialogs/ShipEditor/ShipEditorDialogModel.cpp +++ b/qtfred/src/mission/dialogs/ShipEditor/ShipEditorDialogModel.cpp @@ -12,6 +12,7 @@ #include "iff_defs/iff_defs.h" #include "jumpnode/jumpnode.h" #include "mission/missionmessage.h" +#include "missioneditor/common.h" #include #include @@ -824,7 +825,7 @@ bool ShipEditorDialogModel::update_ship(int ship) // greater than 2*radius of target. if (((_m_arrival_location != static_cast(ArrivalLocation::FROM_DOCK_BAY)) && (_m_arrival_location != static_cast(ArrivalLocation::AT_LOCATION))) && - (_m_arrival_target >= 0) && !(_m_arrival_target & SPECIAL_ARRIVAL_ANCHOR_FLAG)) { + (_m_arrival_target >= 0) && !(_m_arrival_target & ANCHOR_SPECIAL_ARRIVAL)) { d = int(std::min(500.0f, 2.0f * Objects[Ships[ship].objnum].radius)); if ((Ships[ship].arrival_distance < d) && (Ships[ship].arrival_distance > -d)) { sprintf(str, diff --git a/qtfred/src/mission/dialogs/WingEditorDialogModel.cpp b/qtfred/src/mission/dialogs/WingEditorDialogModel.cpp index d320a8bdbfc..93ed7f20359 100644 --- a/qtfred/src/mission/dialogs/WingEditorDialogModel.cpp +++ b/qtfred/src/mission/dialogs/WingEditorDialogModel.cpp @@ -233,10 +233,10 @@ int WingEditorDialogModel::getMinArrivalDistance() const break; } - const int anchor = w->arrival_anchor; + const anchor_t anchor = w->arrival_anchor; // If special anchor or invalid, no radius to enforce - if (anchor < 0 || (anchor & SPECIAL_ARRIVAL_ANCHOR_FLAG)) + if (!anchor.isValid() || (anchor.value() & ANCHOR_SPECIAL_ARRIVAL)) return 0; // Anchor should be a real ship @@ -351,7 +351,7 @@ std::vector> WingEditorDialogModel::getArrivalTarget for (int restrict_to_players = 0; restrict_to_players < 2; ++restrict_to_players) { for (int iff = 0; iff < (int)::Iff_info.size(); ++iff) { stuff_special_arrival_anchor_name(buf, iff, restrict_to_players, false); - items.emplace_back(get_special_anchor(buf), buf); + items.emplace_back(anchor_to_target(get_special_anchor(buf)), buf); } } } @@ -766,7 +766,7 @@ void WingEditorDialogModel::setArrivalType(ArrivalLocation newArrivalType) // If the new arrival type does not need a target, clear it if (newArrivalType == ArrivalLocation::AT_LOCATION) { - modify(w->arrival_anchor, -1); + modify(w->arrival_anchor, anchor_t::invalid()); modify(w->arrival_distance, 0); } else { @@ -775,7 +775,7 @@ void WingEditorDialogModel::setArrivalType(ArrivalLocation newArrivalType) if (targets.empty()) { // No targets available, set to -1 - modify(w->arrival_anchor, -1); + modify(w->arrival_anchor, anchor_t::invalid()); modify(w->arrival_distance, 0); return; } @@ -1099,7 +1099,7 @@ void WingEditorDialogModel::setDepartureType(DepartureLocation newDepartureType) // If the new departure type does not need a target, clear it if (newDepartureType == DepartureLocation::AT_LOCATION) { - modify(w->departure_anchor, -1); + modify(w->departure_anchor, anchor_t::invalid()); } else { // Set the target to the first available @@ -1107,7 +1107,7 @@ void WingEditorDialogModel::setDepartureType(DepartureLocation newDepartureType) if (targets.empty()) { // No targets available, set to -1 - modify(w->departure_anchor, -1); + modify(w->departure_anchor, anchor_t::invalid()); return; } diff --git a/qtfred/src/ui/dialogs/ShipEditor/ShipEditorDialog.cpp b/qtfred/src/ui/dialogs/ShipEditor/ShipEditorDialog.cpp index ec7b147e434..36d2ffbf099 100644 --- a/qtfred/src/ui/dialogs/ShipEditor/ShipEditorDialog.cpp +++ b/qtfred/src/ui/dialogs/ShipEditor/ShipEditorDialog.cpp @@ -264,7 +264,7 @@ void ShipEditorDialog::updateArrival(bool overwrite) char tmp[NAME_LENGTH + 15]; stuff_special_arrival_anchor_name(tmp, static_cast(j), restrict_to_players, false); - ui->arrivalTargetCombo->addItem(tmp, QVariant(get_special_anchor(tmp))); + ui->arrivalTargetCombo->addItem(tmp, QVariant(get_special_anchor(tmp).value())); } } // Add All Ships