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/globalincs/pstypes.h b/code/globalincs/pstypes.h index 90b80c7a8fb..2cfa432d84c 100644 --- a/code/globalincs/pstypes.h +++ b/code/globalincs/pstypes.h @@ -19,6 +19,7 @@ #include "globalincs/toolchain.h" #include "globalincs/vmallocator.h" #include "utils/strings.h" +#include "utils/id.h" #include // For NULL, etc #include @@ -261,6 +262,22 @@ typedef struct coord2d { int x,y; } coord2d; +// 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); + + #include "osapi/dialogs.h" extern int Global_warning_count; diff --git a/code/globalincs/vmallocator.h b/code/globalincs/vmallocator.h index 191e0771007..131fddbdf09 100644 --- a/code/globalincs/vmallocator.h +++ b/code/globalincs/vmallocator.h @@ -97,11 +97,11 @@ extern bool lcase_equal(const SCP_string& _Left, const SCP_string& _Right); extern bool lcase_lessthan(const SCP_string& _Left, const SCP_string& _Right); -template -using SCP_map = std::map, std::allocator>>; +template > +using SCP_map = std::map>>; -template -using SCP_multimap = std::multimap, std::allocator>>; +template > +using SCP_multimap = std::multimap>>; template using SCP_queue = std::queue>>; @@ -109,11 +109,11 @@ using SCP_queue = std::queue>>; template using SCP_deque = std::deque>; -template -class SCP_set : public std::set, std::allocator> +template > +class SCP_set : public std::set> { public: - using std::set, std::allocator>::set; // inherit all constructors + using std::set>::set; // inherit all constructors bool contains(const T& item) const { @@ -121,11 +121,11 @@ class SCP_set : public std::set, std::allocator> } }; -template -class SCP_multiset : public std::multiset, std::allocator> +template > +class SCP_multiset : public std::multiset> { public: - using std::multiset, std::allocator>::multiset; // inherit all constructors + using std::multiset>::multiset; // inherit all constructors bool contains(const T& item) const { diff --git a/code/mission/missionparse.cpp b/code/mission/missionparse.cpp index 8cfdaf5781e..d1c16146125 100644 --- a/code/mission/missionparse.cpp +++ b/code/mission/missionparse.cpp @@ -638,8 +638,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 ); @@ -4408,8 +4408,8 @@ 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 ); - auto anchor_ship_entry = ship_registry_get(Parse_names[wingp->arrival_anchor]); + 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. if (!anchor_ship_entry || anchor_ship_entry->status == ShipStatus::NOT_YET_PRESENT) @@ -5118,7 +5118,65 @@ void parse_wings(mission* pm) } // Goober5000 -void resolve_path_masks(int anchor, int *path_mask) +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.isValid()) + return; + int anchor_val = anchor.value(); + + // 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; + anchor_val = ship_registry_get_index(Parse_names[anchor_val]); + anchor = anchor_t(anchor_val); + } + + if (check_for_hangar) + { + 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(anchor_t anchor, int *path_mask) { path_restriction_t *prp; @@ -5136,16 +5194,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(); + 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 - 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; @@ -5236,6 +5292,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(); @@ -6550,9 +6609,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; // Goober5000 - this must be done even before post_process_ships_wings because it is a prerequisite @@ -6617,55 +6675,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 @@ -6945,8 +6954,8 @@ void mission::Reset() 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.arrival_anchor = anchor_t::invalid(); + support_ships.departure_anchor = anchor_t::invalid(); 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; @@ -7661,7 +7670,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; @@ -7670,29 +7679,28 @@ 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); } - // 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; } @@ -7889,8 +7897,8 @@ 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 ); - auto anchor_ship_entry = ship_registry_get(Parse_names[objp->arrival_anchor]); + 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. if (!anchor_ship_entry || anchor_ship_entry->status == ShipStatus::NOT_YET_PRESENT) @@ -8232,7 +8240,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]; @@ -8299,15 +8308,12 @@ 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(Parse_names[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) { - 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; } @@ -8651,19 +8657,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")) @@ -8671,43 +8677,49 @@ 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 &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, anchor_t anchor, const char *other_name, bool other_is_ship, bool is_arrival) { message.clear(); - if (anchor_shipnum < 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_dock_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 2b8f7f37aa8..51ad9b4d80b 100644 --- a/code/mission/missionparse.h +++ b/code/mission/missionparse.h @@ -40,16 +40,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 &anchor_shipnums_checked, int anchor_shipnum, 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 @@ -103,9 +98,9 @@ inline const std::vector> Mission_event_teams_tvt = [ // Goober5000 typedef struct support_ship_info { ArrivalLocation arrival_location; // arrival location - int arrival_anchor; // arrival anchor + anchor_t arrival_anchor; // arrival anchor DepartureLocation departure_location; // departure location - int departure_anchor; // departure anchor + 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 @@ -443,13 +438,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 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/model/model.h b/code/model/model.h index cd1c3562f8c..66d799700de 100644 --- a/code/model/model.h +++ b/code/model/model.h @@ -1254,6 +1254,7 @@ int model_get_dock_types(int modelnum); // Goober5000 // returns index in [0, MAX_SHIP_BAY_PATHS) int model_find_bay_path(int modelnum, char *bay_path_name); +bool model_has_hangar_bay(int modelnum); // Returns number of polygons in a submodel; int submodel_get_num_polys(int model_num, int submodel_num); diff --git a/code/model/modelread.cpp b/code/model/modelread.cpp index 11f671cb817..cf22498f26c 100644 --- a/code/model/modelread.cpp +++ b/code/model/modelread.cpp @@ -5431,6 +5431,14 @@ int model_find_bay_path(int modelnum, char *bay_path_name) return -1; } +bool model_has_hangar_bay(int modelnum) +{ + auto pm = model_get(modelnum); + Assertion(pm, "modelnum %d does not exist!", modelnum); + + return ( pm->ship_bay && (pm->ship_bay->num_paths > 0) ); +} + int model_create_bsp_collision_tree() { // first find an open slot diff --git a/code/parse/sexp.cpp b/code/parse/sexp.cpp index 10510d160c0..24b6666ad72 100644 --- a/code/parse/sexp.cpp +++ b/code/parse/sexp.cpp @@ -2951,7 +2951,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; } @@ -3017,8 +3017,8 @@ int check_sexp_syntax(int node, int return_type, int recursive, int *bad_node, s // ship exists at this point - // now determine if this ship has a docking bay - if (!ship_has_dock_bay(shipnum)) + // now determine if this ship has a hangar bay + if (!ship_has_hangar_bay(shipnum)) return SEXP_CHECK_INVALID_SHIP_WITH_BAY; break; @@ -23439,8 +23439,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 = anchor_t(ship_registry_get_index(CTEXT(n))); } // get departure location @@ -23465,8 +23465,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 = anchor_t(ship_registry_get_index(CTEXT(n))); } // get ship class @@ -23507,7 +23507,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; @@ -23528,7 +23528,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 @@ -23536,8 +23536,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 = anchor_t(ship_registry_get_index(CTEXT(n))); } n = CDR(n); @@ -23607,7 +23607,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; @@ -23628,7 +23628,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 @@ -23636,8 +23636,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 = 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 9ed1f8b5eb9..ec5bdb2afce 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); @@ -502,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; @@ -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) ? anchor_t::invalid() : anchor_t(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..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,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) ? anchor_t::invalid() : anchor_t(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..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,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) ? anchor_t::invalid() : anchor_t(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 2d48626c1a8..c2bb24cd99c 100644 --- a/code/ship/ship.cpp +++ b/code/ship/ship.cpp @@ -221,6 +221,19 @@ ship* ship_registry_entry::shipp_or_null() const return (shipnum < 0) ? nullptr : &Ships[shipnum]; } +ship_info* ship_registry_entry::sip() const +{ + if (pobj_num >= 0) + return &Ship_info[Parse_objects[pobj_num].ship_class]; + else if (shipnum >= 0) + return &Ship_info[Ships[shipnum].ship_info_index]; + else + { + Assertion(false, "A ship registry entry must have either a parse object or a ship!"); + return nullptr; + } +} + SCP_vector Ship_registry; SCP_unordered_map Ship_registry_map; @@ -252,6 +265,11 @@ bool ship_registry_exists(const SCP_string &name) return Ship_registry_map.find(name) != Ship_registry_map.end(); } +bool ship_registry_exists(int index) +{ + return Ship_registry.in_bounds(index); +} + const ship_registry_entry *ship_registry_get(const char *name) { auto ship_it = Ship_registry_map.find(name); @@ -270,6 +288,20 @@ const ship_registry_entry *ship_registry_get(const SCP_string &name) return nullptr; } +const ship_registry_entry *ship_registry_get(int index) +{ + if (Ship_registry.in_bounds(index)) + return &Ship_registry[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; @@ -7167,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; @@ -7528,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; @@ -9135,7 +9167,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 - 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->objnum = -1; entry->shipnum = -1; @@ -9271,7 +9304,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 && child_wingp->arrival_anchor == shipnum) { + 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; @@ -17228,7 +17261,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) { @@ -17244,8 +17277,8 @@ 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]); + 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 return true; @@ -19397,10 +19430,10 @@ 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(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; @@ -20065,7 +20098,7 @@ void wing_load_squad_bitmap(wing *w) // Goober5000 - needed by new hangar depart code // check whether this ship has a docking bay -bool ship_has_dock_bay(int shipnum) +bool ship_has_hangar_bay(int shipnum) { Assert(shipnum >= 0 && shipnum < MAX_SHIPS); @@ -20083,10 +20116,7 @@ bool ship_has_dock_bay(int shipnum) return false; } - auto pm = model_get(sip->model_num); - Assert( pm ); - - return ( pm->ship_bay && (pm->ship_bay->num_paths > 0) ); + return model_has_hangar_bay(sip->model_num); } // Goober5000 @@ -20101,7 +20131,7 @@ bool ship_useful_for_departure(int shipnum, int /*path_mask*/) return false; // no dockbay, can't depart to it - if (!ship_has_dock_bay(shipnum)) + if (!ship_has_hangar_bay(shipnum)) return false; // make sure that the bays are not all destroyed diff --git a/code/ship/ship.h b/code/ship/ship.h index c4e616027ff..0a252793e8d 100644 --- a/code/ship/ship.h +++ b/code/ship/ship.h @@ -635,13 +635,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) + 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 -- 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. @@ -956,6 +956,8 @@ struct ship_registry_entry p_object* p_objp_or_null() const; object* objp_or_null() const; ship* shipp_or_null() const; + + ship_info* sip() const; }; extern SCP_vector Ship_registry; @@ -963,10 +965,15 @@ extern SCP_unordered_map + bool operator()(const ID &lhs, const ID &rhs) const + { + return std::less{}(lhs.value(), rhs.value()); + } +}; + } diff --git a/fred2/fredview.cpp b/fred2/fredview.cpp index 81b9ae2d343..2f507f3bb5c 100644 --- a/fred2/fredview.cpp +++ b/fred2/fredview.cpp @@ -2441,7 +2441,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 ) @@ -2635,13 +2635,13 @@ 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; } } 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; } @@ -2649,13 +2649,13 @@ 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; } } 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; } @@ -2852,11 +2852,11 @@ 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) { - 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; } @@ -2864,11 +2864,11 @@ 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) { - 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/management.cpp b/fred2/management.cpp index 534a7dc22a4..8d759cde77a 100644 --- a/fred2/management.cpp +++ b/fred2/management.cpp @@ -2396,7 +2396,7 @@ void management_add_ships_to_combo( CComboBox *box, int flags ) stuff_special_arrival_anchor_name(tmp, i, restrict_to_players, 0); id = box->AddString(tmp); - box->SetItemData(id, get_special_anchor(tmp)); + box->SetItemData(id, get_special_anchor(tmp).value()); } } } @@ -2547,16 +2547,36 @@ void stuff_special_arrival_anchor_name(char *buf, int anchor_num, int retail_for { // 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); } +// 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(anchor_t anchor) +{ + auto anchor_entry = ship_registry_get(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. +anchor_t target_to_anchor(int target) +{ + if (target >= 0 && target < MAX_SHIPS) + return anchor_t(ship_registry_get_index(Ships[target].ship_name)); + else + return anchor_t(target); +} + // Goober5000 void update_texture_replacements(const char *old_name, const char *new_name) { diff --git a/fred2/management.h b/fred2/management.h index 00867f99507..99101ee7cf7 100644 --- a/fred2/management.h +++ b/fred2/management.h @@ -132,6 +132,8 @@ extern int wing_is_player_wing(int wing); extern void update_custom_wing_indexes(); extern void stuff_special_arrival_anchor_name(char* buf, int iff_index, int restrict_to_players, int retail_format); extern void stuff_special_arrival_anchor_name(char* buf, int anchor_num, int retail_format); +extern int anchor_to_target(anchor_t anchor); +extern anchor_t target_to_anchor(int target); extern void update_texture_replacements(const char* old_name, const char* new_name); extern void time_to_mission_info_string(const std::tm* src, char* dest, size_t dest_max_len); diff --git a/fred2/missionsave.cpp b/fred2/missionsave.cpp index a7a0fff94fe..3f007c422c0 100644 --- a/fred2/missionsave.cpp +++ b/fred2/missionsave.cpp @@ -3725,10 +3725,10 @@ int CFred_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, Mission_save_format == FSO_FORMAT_RETAIL ? 1 : 0); @@ -3736,22 +3736,18 @@ int CFred_mission_save::save_objects() // save it fout(" %s", tmp); } else { - fout(" %s", Ships[z].ship_name); + fout(" %s", ship_registry_get(z)->name); } } // Goober5000 if (Mission_save_format != FSO_FORMAT_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); + 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); @@ -3788,8 +3784,8 @@ int CFred_mission_save::save_objects() required_string_fred("$Departure Anchor:"); parse_comments(); - if (shipp->departure_anchor >= 0) - fout(" %s", Ships[shipp->departure_anchor].ship_name); + if (shipp->departure_anchor.isValid()) + fout(" %s", ship_registry_get(shipp->departure_anchor)->name); else fout(" "); } @@ -3797,15 +3793,11 @@ int CFred_mission_save::save_objects() // Goober5000 if (Mission_save_format != FSO_FORMAT_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); + 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); @@ -5076,10 +5068,10 @@ int CFred_mission_save::save_wings() else fout("\n$Arrival Anchor:"); - z = Wings[i].arrival_anchor; + z = Wings[i].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, Mission_save_format == FSO_FORMAT_RETAIL ? 1 : 0); @@ -5087,22 +5079,18 @@ int CFred_mission_save::save_wings() // save it fout(" %s", tmp); } else { - fout(" %s", Ships[z].ship_name); + fout(" %s", ship_registry_get(z)->name); } } // Goober5000 if (Mission_save_format != FSO_FORMAT_RETAIL) { if ((Wings[i].arrival_location == ArrivalLocation::FROM_DOCK_BAY) && (Wings[i].arrival_path_mask > 0)) { - int anchor_shipnum; - polymodel *pm; - - anchor_shipnum = Wings[i].arrival_anchor; - Assert(anchor_shipnum >= 0 && anchor_shipnum < MAX_SHIPS); + auto anchor_entry = ship_registry_get(Wings[i].arrival_anchor); + 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 (Wings[i].arrival_path_mask & (1 << n)) { fout("\"%s\" ", pm->paths[pm->ship_bay->path_indexes[n]].name); @@ -5135,8 +5123,8 @@ int CFred_mission_save::save_wings() required_string_fred("$Departure Anchor:"); parse_comments(); - if (Wings[i].departure_anchor >= 0) - fout(" %s", Ships[Wings[i].departure_anchor].ship_name); + if (Wings[i].departure_anchor.isValid()) + fout(" %s", ship_registry_get(Wings[i].departure_anchor)->name); else fout(" "); } @@ -5144,15 +5132,11 @@ int CFred_mission_save::save_wings() // Goober5000 if (Mission_save_format != FSO_FORMAT_RETAIL) { if ((Wings[i].departure_location == DepartureLocation::TO_DOCK_BAY) && (Wings[i].departure_path_mask > 0)) { - int anchor_shipnum; - polymodel *pm; - - anchor_shipnum = Wings[i].departure_anchor; - Assert(anchor_shipnum >= 0 && anchor_shipnum < MAX_SHIPS); + auto anchor_entry = ship_registry_get(Wings[i].departure_anchor); + 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 (Wings[i].departure_path_mask & (1 << n)) { fout("\"%s\" ", pm->paths[pm->ship_bay->path_indexes[n]].name); diff --git a/fred2/sexp_tree.cpp b/fred2/sexp_tree.cpp index 437886ca36b..1cbf2ed811d 100644 --- a/fred2/sexp_tree.cpp +++ b/fred2/sexp_tree.cpp @@ -6525,8 +6525,8 @@ sexp_list_item *sexp_tree::get_listing_opf_ship_with_bay() { if ( (objp->type == OBJ_SHIP) || (objp->type == OBJ_START) ) { - // determine if this ship has a docking bay - if (ship_has_dock_bay(objp->instance)) + // determine if this ship has a hangar bay + if (ship_has_hangar_bay(objp->instance)) { head.add_data(Ships[objp->instance].ship_name); } diff --git a/fred2/shipeditordlg.cpp b/fred2/shipeditordlg.cpp index a398fb5a9db..1ec64f7fa9b 100644 --- a/fred2/shipeditordlg.cpp +++ b/fred2/shipeditordlg.cpp @@ -566,11 +566,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++; @@ -586,7 +586,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; } @@ -600,7 +600,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; } } @@ -805,7 +805,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]; @@ -1407,11 +1407,11 @@ 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. - 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" @@ -1430,7 +1430,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)); } } @@ -2495,7 +2495,7 @@ void CShipEditorDlg::OnRestrictArrival() arrive_from_ship = (int)box->GetItemData(m_arrival_target); - if (!ship_has_dock_bay(arrive_from_ship)) + if (!ship_has_hangar_bay(arrive_from_ship)) { Int3(); return; @@ -2539,7 +2539,7 @@ void CShipEditorDlg::OnRestrictDeparture() depart_to_ship = (int)box->GetItemData(m_departure_target); - if (!ship_has_dock_bay(depart_to_ship)) + if (!ship_has_hangar_bay(depart_to_ship)) { Int3(); return; diff --git a/fred2/wing_editor.cpp b/fred2/wing_editor.cpp index c29f26da94b..c016a2df04c 100644 --- a/fred2/wing_editor.cpp +++ b/fred2/wing_editor.cpp @@ -365,8 +365,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 @@ -381,7 +381,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]; @@ -817,10 +817,10 @@ 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)) { + 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) { @@ -842,7 +842,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); @@ -1387,7 +1387,7 @@ void wing_editor::OnRestrictArrival() arrive_from_ship = (int)box->GetItemData(m_arrival_target); - if (!ship_has_dock_bay(arrive_from_ship)) + if (!ship_has_hangar_bay(arrive_from_ship)) { Int3(); return; @@ -1422,7 +1422,7 @@ void wing_editor::OnRestrictDeparture() depart_to_ship = (int)box->GetItemData(m_departure_target); - if (!ship_has_dock_bay(depart_to_ship)) + if (!ship_has_hangar_bay(depart_to_ship)) { Int3(); return; diff --git a/qtfred/src/mission/Editor.cpp b/qtfred/src/mission/Editor.cpp index f27b730aa48..7d1a04e60e4 100644 --- a/qtfred/src/mission/Editor.cpp +++ b/qtfred/src/mission/Editor.cpp @@ -1965,7 +1965,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) { @@ -2155,13 +2155,13 @@ 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; } } 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; } @@ -2169,13 +2169,13 @@ 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; } } 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; } @@ -2383,13 +2383,13 @@ 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; } } 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; } @@ -2397,13 +2397,13 @@ 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; } } 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 37ccfa9cc9a..0cf65b2c8eb 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,13 +818,13 @@ 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. 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, @@ -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 246e6f1eafb..1369874240b 100644 --- a/qtfred/src/mission/dialogs/WingEditorDialogModel.cpp +++ b/qtfred/src/mission/dialogs/WingEditorDialogModel.cpp @@ -68,7 +68,7 @@ std::vector> WingEditorDialogModel::getDockBayPathsF { std::vector> out; - if (anchorShipnum < 0 || !ship_has_dock_bay(anchorShipnum)) + if (anchorShipnum < 0 || !ship_has_hangar_bay(anchorShipnum)) return out; const int sii = Ships[anchorShipnum].ship_info_index; @@ -232,15 +232,16 @@ 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 - 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]; @@ -349,7 +350,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, 0); - items.emplace_back(get_special_anchor(buf), buf); + items.emplace_back(anchor_to_target(get_special_anchor(buf)), buf); } } } @@ -764,7 +765,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 { @@ -773,20 +774,20 @@ 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; } - 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 @@ -879,7 +880,7 @@ int WingEditorDialogModel::getArrivalTarget() const return -1; } - return w->arrival_anchor; + return anchor_to_target(w->arrival_anchor); } void WingEditorDialogModel::setArrivalTarget(int targetIndex) @@ -907,11 +908,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(); @@ -959,7 +960,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) @@ -972,8 +973,8 @@ void WingEditorDialogModel::setArrivalPaths(const std::vectorarrival_location != ArrivalLocation::FROM_DOCK_BAY) return; - const int anchor = w->arrival_anchor; - if (anchor < 0 || !ship_has_dock_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 @@ -1097,7 +1098,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 @@ -1105,19 +1106,19 @@ 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; } - 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)); } } } @@ -1154,7 +1155,7 @@ int WingEditorDialogModel::getDepartureTarget() const return -1; } - return w->departure_anchor; + return anchor_to_target(w->departure_anchor); } void WingEditorDialogModel::setDepartureTarget(int targetIndex) @@ -1181,11 +1182,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); } @@ -1198,7 +1199,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) @@ -1211,8 +1212,8 @@ void WingEditorDialogModel::setDeparturePaths(const std::vectordeparture_location != DepartureLocation::TO_DOCK_BAY) return; - const int anchor = w->departure_anchor; - if (anchor < 0 || !ship_has_dock_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 diff --git a/qtfred/src/mission/missionsave.cpp b/qtfred/src/mission/missionsave.cpp index d5a86a1346a..f92c40a846b 100644 --- a/qtfred/src/mission/missionsave.cpp +++ b/qtfred/src/mission/missionsave.cpp @@ -3640,10 +3640,10 @@ int CFred_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_format == MissionFormat::RETAIL); @@ -3651,22 +3651,18 @@ int CFred_mission_save::save_objects() // save it fout(" %s", tmp); } else { - fout(" %s", Ships[z].ship_name); + fout(" %s", ship_registry_get(z)->name); } } // Goober5000 if (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); + 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); @@ -3704,8 +3700,8 @@ int CFred_mission_save::save_objects() required_string_fred("$Departure Anchor:"); parse_comments(); - if (shipp->departure_anchor >= 0) { - fout(" %s", Ships[shipp->departure_anchor].ship_name); + if (shipp->departure_anchor.isValid()) { + fout(" %s", ship_registry_get(shipp->departure_anchor)->name); } else { fout(" "); } @@ -3714,15 +3710,11 @@ int CFred_mission_save::save_objects() // Goober5000 if (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); + 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); @@ -5253,10 +5245,10 @@ int CFred_mission_save::save_wings() fout("\n$Arrival Anchor:"); } - z = Wings[i].arrival_anchor; + z = Wings[i].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_format == MissionFormat::RETAIL); @@ -5264,22 +5256,18 @@ int CFred_mission_save::save_wings() // save it fout(" %s", tmp); } else { - fout(" %s", Ships[z].ship_name); + fout(" %s", ship_registry_get(z)->name); } } // Goober5000 if (save_format != MissionFormat::RETAIL) { if ((Wings[i].arrival_location == ArrivalLocation::FROM_DOCK_BAY) && (Wings[i].arrival_path_mask > 0)) { - int anchor_shipnum; - polymodel* pm; - - anchor_shipnum = Wings[i].arrival_anchor; - Assert(anchor_shipnum >= 0 && anchor_shipnum < MAX_SHIPS); + auto anchor_entry = ship_registry_get(Wings[i].arrival_anchor); + 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 (Wings[i].arrival_path_mask & (1 << n)) { fout("\"%s\" ", pm->paths[pm->ship_bay->path_indexes[n]].name); @@ -5313,8 +5301,8 @@ int CFred_mission_save::save_wings() required_string_fred("$Departure Anchor:"); parse_comments(); - if (Wings[i].departure_anchor >= 0) { - fout(" %s", Ships[Wings[i].departure_anchor].ship_name); + if (Wings[i].departure_anchor.isValid()) { + fout(" %s", ship_registry_get(Wings[i].departure_anchor)->name); } else { fout(" "); } @@ -5323,15 +5311,11 @@ int CFred_mission_save::save_wings() // Goober5000 if (save_format != MissionFormat::RETAIL) { if ((Wings[i].departure_location == DepartureLocation::TO_DOCK_BAY) && (Wings[i].departure_path_mask > 0)) { - int anchor_shipnum; - polymodel* pm; - - anchor_shipnum = Wings[i].departure_anchor; - Assert(anchor_shipnum >= 0 && anchor_shipnum < MAX_SHIPS); + auto anchor_entry = ship_registry_get(Wings[i].departure_anchor); + 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 (Wings[i].departure_path_mask & (1 << n)) { fout("\"%s\" ", pm->paths[pm->ship_bay->path_indexes[n]].name); diff --git a/qtfred/src/mission/util.cpp b/qtfred/src/mission/util.cpp index fe898efefd3..efe28b282a9 100644 --- a/qtfred/src/mission/util.cpp +++ b/qtfred/src/mission/util.cpp @@ -29,16 +29,36 @@ 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, int retail_format) { // 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); } +// 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(anchor_t anchor) +{ + auto anchor_entry = ship_registry_get(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. +anchor_t target_to_anchor(int target) +{ + if (target >= 0 && target < MAX_SHIPS) + return anchor_t(ship_registry_get_index(Ships[target].ship_name)); + else + return anchor_t(target); +} + void generate_weaponry_usage_list_team(int team, int* arr) { int i; diff --git a/qtfred/src/mission/util.h b/qtfred/src/mission/util.h index 3eb8400daaf..f3b61775547 100644 --- a/qtfred/src/mission/util.h +++ b/qtfred/src/mission/util.h @@ -7,6 +7,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, int retail_format); +int anchor_to_target(anchor_t anchor); + +anchor_t 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); diff --git a/qtfred/src/ui/dialogs/ShipEditor/ShipEditorDialog.cpp b/qtfred/src/ui/dialogs/ShipEditor/ShipEditorDialog.cpp index d5c616959a2..61a77ce7789 100644 --- a/qtfred/src/ui/dialogs/ShipEditor/ShipEditorDialog.cpp +++ b/qtfred/src/ui/dialogs/ShipEditor/ShipEditorDialog.cpp @@ -263,7 +263,7 @@ void ShipEditorDialog::updateArrival(bool overwrite) char tmp[NAME_LENGTH + 15]; stuff_special_arrival_anchor_name(tmp, static_cast(j), restrict_to_players, 0); - ui->arrivalTargetCombo->addItem(tmp, QVariant(get_special_anchor(tmp))); + ui->arrivalTargetCombo->addItem(tmp, QVariant(get_special_anchor(tmp).value())); } } // Add All Ships diff --git a/qtfred/src/ui/widgets/sexp_tree.cpp b/qtfred/src/ui/widgets/sexp_tree.cpp index 3562760b57a..9178aac7270 100644 --- a/qtfred/src/ui/widgets/sexp_tree.cpp +++ b/qtfred/src/ui/widgets/sexp_tree.cpp @@ -4414,8 +4414,8 @@ sexp_list_item* sexp_tree::get_listing_opf_ship_with_bay() { for (objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp)) { if ((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) { - // determine if this ship has a docking bay - if (ship_has_dock_bay(objp->instance)) { + // determine if this ship has a hangar bay + if (ship_has_hangar_bay(objp->instance)) { head.add_data(Ships[objp->instance].ship_name); } }