From 05639afea8fb78ff7fb9a5a340fb747d27e3529f Mon Sep 17 00:00:00 2001 From: Mike Nelson Date: Thu, 7 May 2026 10:00:03 -0500 Subject: [PATCH 1/2] ship goals stylization pass --- .../ShipEditor/ShipGoalsDialogModel.cpp | 1501 +++++++++-------- .../dialogs/ShipEditor/ShipGoalsDialogModel.h | 100 +- .../ui/dialogs/ShipEditor/ShipGoalsDialog.cpp | 16 +- .../ui/dialogs/ShipEditor/ShipGoalsDialog.h | 10 +- 4 files changed, 809 insertions(+), 818 deletions(-) diff --git a/qtfred/src/mission/dialogs/ShipEditor/ShipGoalsDialogModel.cpp b/qtfred/src/mission/dialogs/ShipEditor/ShipGoalsDialogModel.cpp index d9c7b5f1759..0e3edac0c65 100644 --- a/qtfred/src/mission/dialogs/ShipEditor/ShipGoalsDialogModel.cpp +++ b/qtfred/src/mission/dialogs/ShipEditor/ShipGoalsDialogModel.cpp @@ -2,797 +2,800 @@ #include #include #include -namespace fso { - namespace fred { - namespace dialogs { - ShipGoalsDialogModel::ShipGoalsDialogModel(QObject* parent, EditorViewport* viewport, bool multi, int shipp, int wingp) - : AbstractDialogModel(parent, viewport) - { - initializeData(multi, shipp, wingp); - } - void ShipGoalsDialogModel::init_combo_data() - { - // don't add more than one of the same string (case-insensitive) - SCP_unordered_map strings_to_indexes; - - // start by adding "None" - auto none_str = "None"; - strings_to_indexes.emplace(none_str, 0); - SCP_set none_set{ AI_GOAL_NONE }; - m_ai_goal_combo_data.clear(); - m_ai_goal_combo_data.emplace_back(none_str, std::move(none_set)); - - // initialize the data used in the combo boxes in the Initial Orders dialog - for (int i = 0; i < Ai_goal_list_size; ++i) - { - if (!valid[i]) - continue; - auto &entry = Editor::getAi_goal_list()[i]; - - // see if we already added the string - auto ii = strings_to_indexes.find(entry.name); - if (ii != strings_to_indexes.end()) - { - // skip adding the string, but add the entry's goal definition to the combo box data at the existing index - m_ai_goal_combo_data[ii->second].second.insert(entry.def); - } - else - { - // this string will correspond to the index that is about to be created - strings_to_indexes[entry.name] = m_ai_goal_combo_data.size(); - - // add the entry's goal definition as the first (maybe only) member of the set - SCP_set new_set{ entry.def }; - m_ai_goal_combo_data.emplace_back(entry.name, std::move(new_set)); - } + +namespace fso::fred::dialogs { + +ShipGoalsDialogModel::ShipGoalsDialogModel(QObject* parent, EditorViewport* viewport, bool multi, int selfShip, int selfWing) + : AbstractDialogModel(parent, viewport) +{ + initializeData(multi, selfShip, selfWing); +} + +void ShipGoalsDialogModel::initComboData() +{ + // don't add more than one of the same string (case-insensitive) + SCP_unordered_map strings_to_indexes; + + // start by adding "None" + auto none_str = "None"; + strings_to_indexes.emplace(none_str, 0); + SCP_set none_set{ AI_GOAL_NONE }; + _aiGoalComboData.clear(); + _aiGoalComboData.emplace_back(none_str, std::move(none_set)); + + // initialize the data used in the combo boxes in the Initial Orders dialog + for (int i = 0; i < _aiGoalListSize; ++i) { + if (!_valid[i]) + continue; + auto& entry = Editor::getAi_goal_list()[i]; + + // see if we already added the string + auto ii = strings_to_indexes.find(entry.name); + if (ii != strings_to_indexes.end()) { + // skip adding the string, but add the entry's goal definition to the combo box data at the existing index + _aiGoalComboData[ii->second].second.insert(entry.def); + } else { + // this string will correspond to the index that is about to be created + strings_to_indexes[entry.name] = _aiGoalComboData.size(); + + // add the entry's goal definition as the first (maybe only) member of the set + SCP_set new_set{ entry.def }; + _aiGoalComboData.emplace_back(entry.name, std::move(new_set)); + } + } +} + +const SCP_vector>>& ShipGoalsDialogModel::getAiGoalComboData() +{ + return _aiGoalComboData; +} + +ai_goal_mode ShipGoalsDialogModel::getFirstModeFromComboBox(int whichItem) +{ + // whichItem indicates initial goal 1 through MAX_AI_GOALS, so find that behavior... + int behavior_index = _behavior[whichItem]; + + // if we have a superposition of behaviors, bail here + if (behavior_index < 0) + return ai_goal_mode::AI_GOAL_SCHROEDINGER; + + // the behavior is the index into the combo box that contains a subset of goals from Ai_goal_list + const auto& set = _aiGoalComboData[behavior_index].second; + + // just get the first mode in the set, since chase/chase-wing and guard/guard-wing are handled respectively together + return *(set.begin()); +} + +bool ShipGoalsDialogModel::apply() +{ + int i; + + if (_goalp) { + for (i = 0; i < ED_MAX_GOALS; i++) + updateItem(i); + + verifyOrders(); + } else { + object* ptr; + + ptr = GET_FIRST(&obj_used_list); + while (ptr != END_OF_LIST(&obj_used_list)) { + if (((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START)) && (ptr->flags[Object::Object_Flags::Marked])) { + _goalp = Ai_info[Ships[ptr->instance].ai_index].goals; + for (i = 0; i < ED_MAX_GOALS; i++) { + updateItem(i); } } - const SCP_vector>> &ShipGoalsDialogModel::get_ai_goal_combo_data() - { - return m_ai_goal_combo_data; - }; - ai_goal_mode ShipGoalsDialogModel::get_first_mode_from_combo_box(int which_item) - { - // which_item indicates initial goal 1 through MAX_AI_GOALS, so find that behavior... - int behavior_index = m_behavior[which_item]; - - // if we have a superposition of behaviors, bail here - if (behavior_index < 0) - return ai_goal_mode::AI_GOAL_SCHROEDINGER; - - // the behavior is the index into the combo box that contains a subset of goals from Ai_goal_list - const auto &set = m_ai_goal_combo_data[behavior_index].second; - - // just get the first mode in the set, since chase/chase-wing and guard/guard-wing are handled respectively together - return *(set.begin()); - } - bool ShipGoalsDialogModel::apply() - { - int i; - if (goalp) { - for (i = 0; i < ED_MAX_GOALS; i++) - update_item(i); + ptr = GET_NEXT(ptr); + } - verify_orders(); + ptr = GET_FIRST(&obj_used_list); + while (ptr != END_OF_LIST(&obj_used_list)) { + if (((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START)) && (ptr->flags[Object::Object_Flags::Marked])) { + _selfShip = ptr->instance; + _goalp = Ai_info[Ships[_selfShip].ai_index].goals; + verifyOrders(_selfShip); + } + ptr = GET_NEXT(ptr); + } + } + + _editor->missionChanged(); + return true; +} + +int ShipGoalsDialogModel::verifyOrders(int shipNum) +{ + const char* str; + SCP_string error_message; + if ((str = _editor->error_check_initial_orders(_goalp, _selfShip, _selfWing)) != nullptr) { + if (*str == '!') + return 1; + else if (*str == '*') + str++; + + if (shipNum >= 0) + sprintf(error_message, "Initial orders error for ship \"%s\"\n\n%s", Ships[shipNum].ship_name, str); + else + error_message = str; + auto button = _viewport->dialogProvider->showButtonDialog(DialogType::Error, + "Order Error", + error_message, + { DialogButton::Ok, DialogButton::Cancel }); + if (button != DialogButton::Ok) + return 1; + } + + return 0; +} + +void ShipGoalsDialogModel::updateItem(int item) +{ + ai_goal_mode mode; + char save[80]{}; + SCP_string error_message; + waypoint_list* wp_list; + + if (item >= MAX_AI_GOALS) + return; + + if (!_multiEdit || _priority[item] >= 0) + _goalp[item].priority = _priority[item]; + + mode = getFirstModeFromComboBox(item); + switch (mode) { + case AI_GOAL_NONE: + case AI_GOAL_CHASE_ANY: + case AI_GOAL_UNDOCK: + case AI_GOAL_KEEP_SAFE_DISTANCE: + case AI_GOAL_PLAY_DEAD: + case AI_GOAL_PLAY_DEAD_PERSISTENT: + case AI_GOAL_WARP: + // these goals do not have a target in the dialog box, so let's set the goal and return immediately + // so that we don't run afoul of the "doesn't have a valid target" code at the bottom of the function + modify(_goalp[item].ai_mode, mode); + return; + + case AI_GOAL_SCHROEDINGER: + // return, but don't set the goal + return; + + case AI_GOAL_WAYPOINTS: + case AI_GOAL_WAYPOINTS_ONCE: + case AI_GOAL_DISABLE_SHIP: + case AI_GOAL_DISABLE_SHIP_TACTICAL: + case AI_GOAL_DISARM_SHIP: + case AI_GOAL_DISARM_SHIP_TACTICAL: + case AI_GOAL_IGNORE: + case AI_GOAL_IGNORE_NEW: + case AI_GOAL_EVADE_SHIP: + case AI_GOAL_STAY_NEAR_SHIP: + case AI_GOAL_FORM_ON_WING: + case AI_GOAL_STAY_STILL: + case AI_GOAL_CHASE_SHIP_CLASS: + break; + + case AI_GOAL_DESTROY_SUBSYSTEM: { + if (_subsys[item].empty()) { + sprintf(error_message, "Order #%d doesn't have valid subsystem name. Order will be removed", item + 1); + _viewport->dialogProvider->showButtonDialog(DialogType::Information, + "Order Error", + error_message, + { DialogButton::Ok }); + modify(_goalp[item].ai_mode, AI_GOAL_NONE); + return; + } + + // Look up the subsystem in the target ship to get a persistent name pointer. + // Storing _subsys[item].c_str() directly would dangle after the model is destroyed. + const char* persistent_name = nullptr; + int target_ship_idx = _object[item] & DATA_MASK; + if (target_ship_idx >= 0 && target_ship_idx < MAX_SHIPS) { + ship_subsys* cur_ss = GET_FIRST(&Ships[target_ship_idx].subsys_list); + while (cur_ss != END_OF_LIST(&Ships[target_ship_idx].subsys_list)) { + if (!stricmp(cur_ss->system_info->subobj_name, _subsys[item].c_str())) { + persistent_name = cur_ss->system_info->subobj_name; + break; } - else { - object* ptr; - - ptr = GET_FIRST(&obj_used_list); - while (ptr != END_OF_LIST(&obj_used_list)) { - if (((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START)) && (ptr->flags[Object::Object_Flags::Marked])) { - goalp = Ai_info[Ships[ptr->instance].ai_index].goals; - for (i = 0; i < ED_MAX_GOALS; i++) { - update_item(i); - } - } - - ptr = GET_NEXT(ptr); - } - - ptr = GET_FIRST(&obj_used_list); - while (ptr != END_OF_LIST(&obj_used_list)) { - if (((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START)) && (ptr->flags[Object::Object_Flags::Marked])) { - self_ship = ptr->instance; - goalp = Ai_info[Ships[self_ship].ai_index].goals; - verify_orders(self_ship); + cur_ss = GET_NEXT(cur_ss); + } + } + + if (!persistent_name) { + sprintf(error_message, "Order #%d doesn't have valid subsystem name. Order will be removed", item + 1); + _viewport->dialogProvider->showButtonDialog(DialogType::Information, + "Order Error", + error_message, + { DialogButton::Ok }); + modify(_goalp[item].ai_mode, AI_GOAL_NONE); + return; + } + + if (!_goalp[item].docker.name || stricmp(_goalp[item].docker.name, persistent_name) != 0) + set_modified(); + _goalp[item].docker.name = persistent_name; + break; + } + + case AI_GOAL_CHASE: + case AI_GOAL_CHASE_WING: + switch (_object[item] & TYPE_MASK) { + case TYPE_SHIP: + case TYPE_PLAYER: + mode = AI_GOAL_CHASE; + break; + + case TYPE_WING: + mode = AI_GOAL_CHASE_WING; + break; + } + + break; + + case AI_GOAL_DOCK: { + // Resolve persistent dock bay name pointers from the polymodel. + // Storing _subsys[item].c_str() directly would dangle after the model is destroyed. + char* docker = nullptr; + char* dockee = nullptr; + + if (!_multiEdit || (_object[item] && !_subsys[item].empty())) { + if (_selfShip >= 0) { + int model_num = Ship_info[Ships[_selfShip].ship_info_index].model_num; + polymodel* pm = model_get(model_num); + if (pm) { + for (int b = 0; b < pm->n_docks; b++) { + if (!stricmp(pm->docking_bays[b].name, _subsys[item].c_str())) { + docker = pm->docking_bays[b].name; + break; } - - ptr = GET_NEXT(ptr); } } - - _editor->missionChanged(); - return true; } - - int ShipGoalsDialogModel::verify_orders(const int ship) - { - const char* str; - SCP_string error_message; - if ((str = _editor->error_check_initial_orders(goalp, self_ship, self_wing)) != nullptr) { - if (*str == '!') - return 1; - else if (*str == '*') - str++; - - if (ship >= 0) - sprintf(error_message, "Initial orders error for ship \"%s\"\n\n%s", Ships[ship].ship_name, str); - else - error_message = str; - auto button = _viewport->dialogProvider->showButtonDialog(DialogType::Error, - "Order Error", - error_message, - { DialogButton::Ok, DialogButton::Cancel }); - if (button != DialogButton::Ok) - return 1; + } + + if (!_multiEdit || (_object[item] && (_dock2[item] >= 0))) { + int dockee_ship = _object[item] & DATA_MASK; + if (dockee_ship >= 0 && dockee_ship < MAX_SHIPS && _dock2[item] >= 0) { + int model_num = Ship_info[Ships[dockee_ship].ship_info_index].model_num; + polymodel* pm = model_get(model_num); + if (pm && _dock2[item] < pm->n_docks) { + dockee = pm->docking_bays[_dock2[item]].name; } - - return 0; } - - void ShipGoalsDialogModel::update_item(const int item) - { - ai_goal_mode mode; - char save[80]{}; - SCP_string error_message; - waypoint_list* wp_list; - - if (item >= MAX_AI_GOALS) - return; - - if (!m_multi_edit || m_priority[item] >= 0) - goalp[item].priority = m_priority[item]; - - mode = get_first_mode_from_combo_box(item); - switch (mode) { - case AI_GOAL_NONE: - case AI_GOAL_CHASE_ANY: - case AI_GOAL_UNDOCK: - case AI_GOAL_KEEP_SAFE_DISTANCE: - case AI_GOAL_PLAY_DEAD: - case AI_GOAL_PLAY_DEAD_PERSISTENT: - case AI_GOAL_WARP: - // these goals do not have a target in the dialog box, so let's set the goal and return immediately - // so that we don't run afoul of the "doesn't have a valid target" code at the bottom of the function - modify(goalp[item].ai_mode, mode); - return; - - case AI_GOAL_SCHROEDINGER: - // return, but don't set the goal - return; - - case AI_GOAL_WAYPOINTS: - case AI_GOAL_WAYPOINTS_ONCE: - case AI_GOAL_DISABLE_SHIP: - case AI_GOAL_DISABLE_SHIP_TACTICAL: - case AI_GOAL_DISARM_SHIP: - case AI_GOAL_DISARM_SHIP_TACTICAL: - case AI_GOAL_IGNORE: - case AI_GOAL_IGNORE_NEW: - case AI_GOAL_EVADE_SHIP: - case AI_GOAL_STAY_NEAR_SHIP: - case AI_GOAL_FORM_ON_WING: - case AI_GOAL_STAY_STILL: - case AI_GOAL_CHASE_SHIP_CLASS: - break; - - case AI_GOAL_DESTROY_SUBSYSTEM: { - if (m_subsys[item].empty()) { - sprintf(error_message, "Order #%d doesn't have valid subsystem name. Order will be removed", item + 1); - _viewport->dialogProvider->showButtonDialog(DialogType::Information, - "Order Error", - error_message, - { DialogButton::Ok }); - modify(goalp[item].ai_mode, AI_GOAL_NONE); - return; - } - - // Look up the subsystem in the target ship to get a persistent name pointer. - // Storing m_subsys[item].c_str() directly would dangle after the model is destroyed. - const char* persistent_name = nullptr; - int target_ship_idx = m_object[item] & DATA_MASK; - if (target_ship_idx >= 0 && target_ship_idx < MAX_SHIPS) { - ship_subsys* cur_ss = GET_FIRST(&Ships[target_ship_idx].subsys_list); - while (cur_ss != END_OF_LIST(&Ships[target_ship_idx].subsys_list)) { - if (!stricmp(cur_ss->system_info->subobj_name, m_subsys[item].c_str())) { - persistent_name = cur_ss->system_info->subobj_name; - break; - } - cur_ss = GET_NEXT(cur_ss); - } - } - - if (!persistent_name) { - sprintf(error_message, "Order #%d doesn't have valid subsystem name. Order will be removed", item + 1); - _viewport->dialogProvider->showButtonDialog(DialogType::Information, - "Order Error", - error_message, - { DialogButton::Ok }); - modify(goalp[item].ai_mode, AI_GOAL_NONE); - return; - } - - if (!goalp[item].docker.name || stricmp(goalp[item].docker.name, persistent_name) != 0) - set_modified(); - goalp[item].docker.name = persistent_name; - break; - } - - case AI_GOAL_CHASE: - case AI_GOAL_CHASE_WING: - switch (m_object[item] & TYPE_MASK) { - case TYPE_SHIP: - case TYPE_PLAYER: - mode = AI_GOAL_CHASE; - break; - - case TYPE_WING: - mode = AI_GOAL_CHASE_WING; - break; - } - - break; - - case AI_GOAL_DOCK: { - // Resolve persistent dock bay name pointers from the polymodel. - // Storing m_subsys[item].c_str() directly would dangle after the model is destroyed. - char* docker = nullptr; - char* dockee = nullptr; - - if (!m_multi_edit || (m_object[item] && !m_subsys[item].empty())) { - if (self_ship >= 0) { - int model_num = Ship_info[Ships[self_ship].ship_info_index].model_num; - polymodel* pm = model_get(model_num); - if (pm) { - for (int b = 0; b < pm->n_docks; b++) { - if (!stricmp(pm->docking_bays[b].name, m_subsys[item].c_str())) { - docker = pm->docking_bays[b].name; - break; - } - } - } - } - } - - if (!m_multi_edit || (m_object[item] && (m_dock2[item] >= 0))) { - int dockee_ship = m_object[item] & DATA_MASK; - if (dockee_ship >= 0 && dockee_ship < MAX_SHIPS && m_dock2[item] >= 0) { - int model_num = Ship_info[Ships[dockee_ship].ship_info_index].model_num; - polymodel* pm = model_get(model_num); - if (pm && m_dock2[item] < pm->n_docks) { - dockee = pm->docking_bays[m_dock2[item]].name; - } - } - } - - if (!docker || !dockee) { - sprintf(error_message, "Order #%d doesn't have valid docking points. Order will be removed", item + 1); - _viewport->dialogProvider->showButtonDialog(DialogType::Information, - "Order Error", - error_message, - { DialogButton::Ok }); - modify(goalp[item].ai_mode, AI_GOAL_NONE); - return; - } else { - if (!goalp[item].docker.name || stricmp(goalp[item].docker.name, docker) != 0) - set_modified(); - if (!goalp[item].dockee.name || stricmp(goalp[item].dockee.name, dockee) != 0) - set_modified(); - - goalp[item].docker.name = docker; - goalp[item].dockee.name = dockee; - } - - break; + } + + if (!docker || !dockee) { + sprintf(error_message, "Order #%d doesn't have valid docking points. Order will be removed", item + 1); + _viewport->dialogProvider->showButtonDialog(DialogType::Information, + "Order Error", + error_message, + { DialogButton::Ok }); + modify(_goalp[item].ai_mode, AI_GOAL_NONE); + return; + } else { + if (!_goalp[item].docker.name || stricmp(_goalp[item].docker.name, docker) != 0) + set_modified(); + if (!_goalp[item].dockee.name || stricmp(_goalp[item].dockee.name, dockee) != 0) + set_modified(); + + _goalp[item].docker.name = docker; + _goalp[item].dockee.name = dockee; + } + + break; + } + + case AI_GOAL_GUARD: + case AI_GOAL_GUARD_WING: + switch (_object[item] & TYPE_MASK) { + case TYPE_SHIP: + case TYPE_PLAYER: + mode = AI_GOAL_GUARD; + break; + + case TYPE_WING: + mode = AI_GOAL_GUARD_WING; + break; + } + + break; + + default: + Warning(LOCATION, "Unknown AI_GOAL type 0x%x", mode); + modify(_goalp[item].ai_mode, AI_GOAL_NONE); + return; + } + modify(_goalp[item].ai_mode, mode); + + *save = 0; + if (_goalp[item].target_name) + strcpy_s(save, _goalp[item].target_name); + + switch (_object[item] & TYPE_MASK) { + int not_used; + + case TYPE_SHIP: + case TYPE_PLAYER: + _goalp[item].target_name = ai_get_goal_target_name(Ships[_object[item] & DATA_MASK].ship_name, ¬_used); + break; + + case TYPE_WING: + _goalp[item].target_name = ai_get_goal_target_name(Wings[_object[item] & DATA_MASK].name, ¬_used); + break; + + case TYPE_PATH: + wp_list = find_waypoint_list_at_index(_object[item] & DATA_MASK); + Assert(wp_list != nullptr); + _goalp[item].target_name = ai_get_goal_target_name(wp_list->get_name(), ¬_used); + break; + + case TYPE_WAYPOINT: + _goalp[item].target_name = ai_get_goal_target_name(object_name(_object[item] & DATA_MASK), ¬_used); + break; + + case TYPE_SHIP_CLASS: + _goalp[item].target_name = ai_get_goal_target_name(Ship_info[_object[item] & DATA_MASK].name, ¬_used); + break; + + case 0: + case (-1 & TYPE_MASK): + if (_multiEdit) + return; + + sprintf(error_message, "Order #%d doesn't have a valid target. Order will be removed", item + 1); + _viewport->dialogProvider->showButtonDialog(DialogType::Information, + "Order Error", + error_message, + { DialogButton::Ok }); + modify(_goalp[item].ai_mode, AI_GOAL_NONE); + return; + + default: + Error(LOCATION, "Unhandled TYPE_X #define %d in ship goals dialog box", _object[item] & TYPE_MASK); + } + + if (stricmp(save, _goalp[item].target_name)) + set_modified(); +} + +void ShipGoalsDialogModel::reject() {} + +void ShipGoalsDialogModel::initializeData(bool multi, int selfShip, int selfWing) +{ + int i, j, z; + object* ptr; + for (i = 0; i < ED_MAX_GOALS; i++) { + _behavior[i] = -1; + _object[i] = -1; + _priority[i] = 0; + _subsys[i] = ""; + _dock2[i] = -1; + } + _goalp = nullptr; + _multiEdit = multi; + _selfShip = selfShip; + _selfWing = selfWing; + Assert(_aiGoalListSize <= MAX_VALID); + + // start off with all goals available + for (i = 0; i < _aiGoalListSize; i++) { + _valid[i] = 1; + } + + if (_selfShip >= 0) { // editing orders for just one ship + for (i = 0; i < _aiGoalListSize; i++) { + if (!(ai_query_goal_valid(_selfShip, Editor::getAi_goal_list()[i].def))) { + _valid[i] = 0; + } + } + } else if (_selfWing >= 0) { // editing orders for just one wing + for (i = 0; i < Wings[_selfWing].wave_count; i++) { + for (j = 0; j < _aiGoalListSize; j++) { + if (!ai_query_goal_valid(Wings[_selfWing].ship_index[i], Editor::getAi_goal_list()[j].def)) { + _valid[j] = 0; } - - case AI_GOAL_GUARD: - case AI_GOAL_GUARD_WING: - switch (m_object[item] & TYPE_MASK) { - case TYPE_SHIP: - case TYPE_PLAYER: - mode = AI_GOAL_GUARD; - break; - - case TYPE_WING: - mode = AI_GOAL_GUARD_WING; - break; + } + } + for (i = 0; i < _aiGoalListSize; i++) { + if (Editor::getAi_goal_list()[i].def == AI_GOAL_DOCK) { // a whole wing can't dock with one object.. + _valid[i] = 0; + } + } + } else { // editing orders for all marked ships + ptr = GET_FIRST(&obj_used_list); + while (ptr != END_OF_LIST(&obj_used_list)) { + if (((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START)) && (ptr->flags[Object::Object_Flags::Marked])) { + for (i = 0; i < _aiGoalListSize; i++) { + if (!ai_query_goal_valid(ptr->instance, Editor::getAi_goal_list()[i].def)) { + _valid[i] = 0; } - - break; - - default: - Warning(LOCATION, "Unknown AI_GOAL type 0x%x", mode); - modify(goalp[item].ai_mode, AI_GOAL_NONE); - return; } - modify(goalp[item].ai_mode, mode); - - *save = 0; - if (goalp[item].target_name) - strcpy_s(save, goalp[item].target_name); - - switch (m_object[item] & TYPE_MASK) { - int not_used; - - case TYPE_SHIP: - case TYPE_PLAYER: - goalp[item].target_name = ai_get_goal_target_name(Ships[m_object[item] & DATA_MASK].ship_name, ¬_used); - break; - - case TYPE_WING: - goalp[item].target_name = ai_get_goal_target_name(Wings[m_object[item] & DATA_MASK].name, ¬_used); - break; + } - case TYPE_PATH: - wp_list = find_waypoint_list_at_index(m_object[item] & DATA_MASK); - Assert(wp_list != nullptr); - goalp[item].target_name = ai_get_goal_target_name(wp_list->get_name(), ¬_used); - break; + ptr = GET_NEXT(ptr); + } + } + if (Waypoint_lists.empty()) { + for (i = 0; i < _aiGoalListSize; i++) { + switch (Editor::getAi_goal_list()[i].def) { + case AI_GOAL_WAYPOINTS: + case AI_GOAL_WAYPOINTS_ONCE: + _valid[i] = 0; + break; + default: + break; + } + } + } - case TYPE_WAYPOINT: - goalp[item].target_name = ai_get_goal_target_name(object_name(m_object[item] & DATA_MASK), ¬_used); - break; + z = 0; + ptr = GET_FIRST(&obj_used_list); + while (ptr != END_OF_LIST(&obj_used_list)) { + if ((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START)) { + i = ptr->instance; - case TYPE_SHIP_CLASS: - goalp[item].target_name = ai_get_goal_target_name(Ship_info[m_object[item] & DATA_MASK].name, ¬_used); + if ((_selfShip > 0) && (_selfShip != i) && ship_docking_valid(_selfShip, i)) { + z = 1; + } + } + ptr = GET_NEXT(ptr); + } + + if (!z) { + for (i = 0; i < _aiGoalListSize; i++) { + if (Editor::getAi_goal_list()[i].def == AI_GOAL_DOCK) { + _valid[i] = 0; + } + } + } + + // initialize the data for the behavior boxes (they remain constant while the dialog is open) + initComboData(); + + if (_selfShip >= 0) { + initialize(Ai_info[Ships[_selfShip].ai_index].goals); + } else if (_selfWing >= 0) { + initialize(Wings[_selfWing].ai_goals); + } else { + initializeMulti(); + } + modelChanged(); + _modified = false; +} + +void ShipGoalsDialogModel::initialize(ai_goal* goals) +{ + int i, item, inst, flag; + ai_goal_mode mode; + object* ptr; + SCP_vector docks; + + // note that the flag variable is a bitfield: + // 1 = ships + // 2 = wings + // 4 = waypoint paths + // 8 = individual waypoints + // 16 = ship classes + + _goalp = goals; + for (item = 0; item < ED_MAX_GOALS; item++) { + flag = 1; + _priority[item] = 0; + mode = AI_GOAL_NONE; + + if (item < MAX_AI_GOALS) { + _priority[item] = _goalp[item].priority; + mode = _goalp[item].ai_mode; + } + + if (_priority[item] < 0 || _priority[item] > MAX_EDITOR_GOAL_PRIORITY) { + _priority[item] = 50; + } + + _behavior[item] = 0; + if (mode != AI_GOAL_NONE) { + i = static_cast(_aiGoalComboData.size()); + while (i-- > 0) { + const auto& set = _aiGoalComboData[i].second; + if (set.find(mode) != set.end()) { + _behavior[item] = i; break; - - case 0: - case (-1 & TYPE_MASK): - if (m_multi_edit) - return; - - sprintf(error_message, "Order #%d doesn't have a valid target. Order will be removed", item + 1); - _viewport->dialogProvider->showButtonDialog(DialogType::Information, - "Order Error", - error_message, - { DialogButton::Ok }); - modify(goalp[item].ai_mode, AI_GOAL_NONE); - return; - - default: - Error(LOCATION, "Unhandled TYPE_X #define %d in ship goals dialog box", m_object[item] & TYPE_MASK); } - - if (stricmp(save, goalp[item].target_name)) - set_modified(); } - - void ShipGoalsDialogModel::reject() {} - - void ShipGoalsDialogModel::initializeData(const bool multi, const int shipp, const int wingp) - { - int i, j, z; - object* ptr; - for (i = 0; i < ED_MAX_GOALS; i++) { - m_behavior[i] = -1; - m_object[i] = -1; - m_priority[i] = 0; - m_subsys[i] = ""; - m_dock2[i] = -1; - // m_data[i] = 0; - } - goalp = nullptr; - m_multi_edit = multi; - self_ship = shipp; - self_wing = wingp; - Assert(Ai_goal_list_size <= MAX_VALID); - - - // start off with all goals available - for (i = 0; i < Ai_goal_list_size; i++) { - valid[i] = 1; - } - - if (self_ship >= 0) { // editing orders for just one ship - for (i = 0; i < Ai_goal_list_size; i++) { - if (!(ai_query_goal_valid(self_ship, Editor::getAi_goal_list()[i].def))) { - valid[i] = 0; - } - } - } - else if (self_wing >= 0) { // editing orders for just one wing - for (i = 0; i < Wings[self_wing].wave_count; i++) { - for (j = 0; j < Ai_goal_list_size; j++) { - if (!ai_query_goal_valid(Wings[self_wing].ship_index[i], Editor::getAi_goal_list()[j].def)) { - valid[j] = 0; - } - } - } - for (i = 0; i < Ai_goal_list_size; i++) { - if (Editor::getAi_goal_list()[i].def == AI_GOAL_DOCK) { // a whole wing can't dock with one object.. - valid[i] = 0; - } - } - } - else { // editing orders for all marked ships - ptr = GET_FIRST(&obj_used_list); - while (ptr != END_OF_LIST(&obj_used_list)) { - if (((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START)) && (ptr->flags[Object::Object_Flags::Marked])) { - for (i = 0; i < Ai_goal_list_size; i++) { - if (!ai_query_goal_valid(ptr->instance, Editor::getAi_goal_list()[i].def)) { - valid[i] = 0; - } - } - } - - ptr = GET_NEXT(ptr); - } - } - if (Waypoint_lists.empty()) { - for (i = 0; i < Ai_goal_list_size; i++) { - switch (Editor::getAi_goal_list()[i].def) { - case AI_GOAL_WAYPOINTS: - case AI_GOAL_WAYPOINTS_ONCE: - // case AI_GOAL_WARP: - valid[i] = 0; + } + + switch (mode) { + case AI_GOAL_NONE: + case AI_GOAL_CHASE_ANY: + case AI_GOAL_UNDOCK: + case AI_GOAL_KEEP_SAFE_DISTANCE: + case AI_GOAL_PLAY_DEAD: + case AI_GOAL_PLAY_DEAD_PERSISTENT: + case AI_GOAL_WARP: + continue; + + case AI_GOAL_CHASE_SHIP_CLASS: + flag = 16; // target is a ship class + break; + + case AI_GOAL_STAY_STILL: + flag = 9; // target is a ship or a waypoint + break; + + case AI_GOAL_CHASE: + case AI_GOAL_GUARD: + case AI_GOAL_DISABLE_SHIP: + case AI_GOAL_DISABLE_SHIP_TACTICAL: + case AI_GOAL_DISARM_SHIP: + case AI_GOAL_DISARM_SHIP_TACTICAL: + case AI_GOAL_IGNORE: + case AI_GOAL_IGNORE_NEW: + case AI_GOAL_EVADE_SHIP: + case AI_GOAL_STAY_NEAR_SHIP: + case AI_GOAL_FORM_ON_WING: + break; + + case AI_GOAL_WAYPOINTS: + case AI_GOAL_WAYPOINTS_ONCE: + flag = 4; // target is a waypoint + break; + + case AI_GOAL_DESTROY_SUBSYSTEM: + // docker.name already holds the subsystem name string... copy it directly. + // (ship_find_subsys returns an int index, not the name, so don't use it here.) + if (_goalp[item].docker.name != nullptr) + _subsys[item] = _goalp[item].docker.name; + break; + + case AI_GOAL_DOCK: + // Store the docker bay name string directly; the persistent pointer lives in the polymodel. + if (_goalp[item].docker.name != nullptr) + _subsys[item] = _goalp[item].docker.name; + break; + + case AI_GOAL_CHASE_WING: + case AI_GOAL_GUARD_WING: + flag = 2; // target is a wing + break; + + default: + Error(LOCATION, "Unhandled AI_GOAL_X #define %d in ship goals dialog box", mode); + } + + if (flag & 0x1) { + ptr = GET_FIRST(&obj_used_list); + while (ptr != END_OF_LIST(&obj_used_list)) { + if ((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START)) { + inst = ptr->instance; + if (ptr->type == OBJ_SHIP) { + Assert(inst >= 0 && inst < MAX_SHIPS); + if (!stricmp(_goalp[item].target_name, Ships[inst].ship_name)) { + _object[item] = inst | TYPE_SHIP; break; - default: + } + } else { + Assert(inst >= 0 && inst < MAX_SHIPS); + if (!stricmp(_goalp[item].target_name, Ships[inst].ship_name)) { + _object[item] = inst | TYPE_PLAYER; break; } } } - z = 0; - ptr = GET_FIRST(&obj_used_list); - while (ptr != END_OF_LIST(&obj_used_list)) { - if ((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START)) { - i = ptr->instance; - - if ((self_ship > 0) && (self_ship != i) && ship_docking_valid(self_ship, i)) { - z = 1; - } - } - ptr = GET_NEXT(ptr); - } + ptr = GET_NEXT(ptr); + } + } - if (!z) { - for (i = 0; i < Ai_goal_list_size; i++) { - if (Editor::getAi_goal_list()[i].def == AI_GOAL_DOCK) { - valid[i] = 0; - } + if (flag & 0x2) { + for (i = 0; i < MAX_WINGS; i++) { + if (Wings[i].wave_count) { + if (!stricmp(_goalp[item].target_name, Wings[i].name)) { + _object[item] = i | TYPE_WING; + break; } } + } + } - // initialize the data for the behavior boxes (they remain constant while the dialog is open) - init_combo_data(); - - if (self_ship >= 0) { - initialize(Ai_info[Ships[self_ship].ai_index].goals); - } - else if (self_wing >= 0) { - initialize(Wings[self_wing].ai_goals); + if (flag & 0x4) { // data is a waypoint path name + SCP_vector::iterator ii; + for (i = 0, ii = Waypoint_lists.begin(); ii != Waypoint_lists.end(); ++i, ++ii) { + if (!stricmp(_goalp[item].target_name, ii->get_name())) { + _object[item] = i | TYPE_PATH; + break; } - else { - initialize_multi(); + } + } + + if (flag & 0x8) { // data is a waypoint name + waypoint* wpt = find_matching_waypoint(_goalp[item].target_name); + if (wpt != nullptr) + _object[item] = wpt->get_objnum() | TYPE_WAYPOINT; + } + + if (flag & 0x10) { // data is a ship class + for (i = 0; i < ship_info_size(); i++) { + if (!stricmp(_goalp[item].target_name, Ship_info[i].name)) { + _object[item] = i | TYPE_SHIP_CLASS; + break; } - modelChanged(); - _modified = false; } - void ShipGoalsDialogModel::initialize(ai_goal* goals) - { - int i, item, inst, flag; - ai_goal_mode mode; - object* ptr; - SCP_vector docks; - - // note that the flag variable is a bitfield: - // 1 = ships - // 2 = wings - // 4 = waypoint paths - // 8 = individual waypoints - // 16 = ship classes - - goalp = goals; - for (item = 0; item < ED_MAX_GOALS; item++) { - flag = 1; - //m_data[item] = 0; - m_priority[item] = 0; - mode = AI_GOAL_NONE; - - if (item < MAX_AI_GOALS) { - m_priority[item] = goalp[item].priority; - mode = goalp[item].ai_mode; - } - - if (m_priority[item] < 0 || m_priority[item] > MAX_EDITOR_GOAL_PRIORITY) { - m_priority[item] = 50; - } - - m_behavior[item] = 0; - if (mode != AI_GOAL_NONE) { - i = static_cast(m_ai_goal_combo_data.size()); - while (i-- > 0) { - const auto &set = m_ai_goal_combo_data[i].second; - if (set.find(mode) != set.end()) { - m_behavior[item] = i; - break; - } - } - } - - switch (mode) { - case AI_GOAL_NONE: - case AI_GOAL_CHASE_ANY: - case AI_GOAL_UNDOCK: - case AI_GOAL_KEEP_SAFE_DISTANCE: - case AI_GOAL_PLAY_DEAD: - case AI_GOAL_PLAY_DEAD_PERSISTENT: - case AI_GOAL_WARP: - continue; - - case AI_GOAL_CHASE_SHIP_CLASS: - flag = 16; // target is a ship class - break; - - case AI_GOAL_STAY_STILL: - flag = 9; // target is a ship or a waypoint - break; - - case AI_GOAL_CHASE: - case AI_GOAL_GUARD: - case AI_GOAL_DISABLE_SHIP: - case AI_GOAL_DISABLE_SHIP_TACTICAL: - case AI_GOAL_DISARM_SHIP: - case AI_GOAL_DISARM_SHIP_TACTICAL: - case AI_GOAL_IGNORE: - case AI_GOAL_IGNORE_NEW: - case AI_GOAL_EVADE_SHIP: - case AI_GOAL_STAY_NEAR_SHIP: - case AI_GOAL_FORM_ON_WING: - break; - - case AI_GOAL_WAYPOINTS: - case AI_GOAL_WAYPOINTS_ONCE: - flag = 4; // target is a waypoint - break; - - case AI_GOAL_DESTROY_SUBSYSTEM: - // docker.name already holds the subsystem name string... copy it directly. - // (ship_find_subsys returns an int index, not the name, so don't use it here.) - if (goalp[item].docker.name != nullptr) - m_subsys[item] = goalp[item].docker.name; + } + + switch (mode) { + case AI_GOAL_DOCK: + _dock2[item] = -1; + if (_object[item]) { + docks = + _editor->get_docking_list(Ship_info[Ships[_object[item] & DATA_MASK].ship_info_index].model_num); + for (i = 0; unsigned(i) < docks.size(); i++) { + Assert(_goalp[item].dockee.name); + Assert(_goalp[item].dockee.index != -1); + if (!stricmp(_goalp[item].dockee.name, docks[i].c_str())) { + _dock2[item] = i; break; - - case AI_GOAL_DOCK: - // Store the docker bay name string directly; the persistent pointer lives in the polymodel. - if (goalp[item].docker.name != nullptr) - m_subsys[item] = goalp[item].docker.name; - break; - - case AI_GOAL_CHASE_WING: - case AI_GOAL_GUARD_WING: - flag = 2; // target is a wing - break; - - default: - Error(LOCATION, "Unhandled AI_GOAL_X #define %d in ship goals dialog box", mode); - } - - if (flag & 0x1) { - ptr = GET_FIRST(&obj_used_list); - while (ptr != END_OF_LIST(&obj_used_list)) { - if ((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START)) { - inst = ptr->instance; - if (ptr->type == OBJ_SHIP) { - Assert(inst >= 0 && inst < MAX_SHIPS); - if (!stricmp(goalp[item].target_name, Ships[inst].ship_name)) { - m_object[item] = inst | TYPE_SHIP; - break; - } - - } - else { - Assert(inst >= 0 && inst < MAX_SHIPS); - if (!stricmp(goalp[item].target_name, Ships[inst].ship_name)) { - m_object[item] = inst | TYPE_PLAYER; - break; - } - } - } - - ptr = GET_NEXT(ptr); - } } - - if (flag & 0x2) { - for (i = 0; i < MAX_WINGS; i++) { - if (Wings[i].wave_count) { - if (!stricmp(goalp[item].target_name, Wings[i].name)) { - m_object[item] = i | TYPE_WING; - break; - } - } - } - } - - if (flag & 0x4) { // data is a waypoint path name - SCP_vector::iterator ii; - for (i = 0, ii = Waypoint_lists.begin(); ii != Waypoint_lists.end(); ++i, ++ii) { - if (!stricmp(goalp[item].target_name, ii->get_name())) { - m_object[item] = i | TYPE_PATH; - break; - } - } + } + } + break; + default: + break; + } + } +} + +void ShipGoalsDialogModel::initializeMulti() +{ + int i, flag = 0; + object* ptr; + int behavior[ED_MAX_GOALS]{}; + int priority[ED_MAX_GOALS]{}; + SCP_string subsys[ED_MAX_GOALS]{}; + int dock2[ED_MAX_GOALS]{}; + int data[ED_MAX_GOALS]{}; + + ptr = GET_FIRST(&obj_used_list); + while (ptr != END_OF_LIST(&obj_used_list)) { + if (((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START)) && (ptr->flags[Object::Object_Flags::Marked])) { + initialize(Ai_info[Ships[ptr->instance].ai_index].goals); + if (!flag) { + flag = 1; + for (i = 0; i < ED_MAX_GOALS; i++) { + behavior[i] = _behavior[i]; + priority[i] = _priority[i]; + subsys[i] = _subsys[i]; + dock2[i] = _dock2[i]; + data[i] = _object[i]; + } + } else { + for (i = 0; i < ED_MAX_GOALS; i++) { + if (behavior[i] != _behavior[i]) { + behavior[i] = -1; + data[i] = -1; } - if (flag & 0x8) { // data is a waypoint name - waypoint* wpt = find_matching_waypoint(goalp[item].target_name); - if (wpt != nullptr) - m_object[item] = wpt->get_objnum() | TYPE_WAYPOINT; + if (data[i] != _object[i]) { + data[i] = -1; + subsys[i] = -1; + dock2[i] = -1; } - if (flag & 0x10) { // data is a ship class - for (i = 0; i < ship_info_size(); i++) { - if (!stricmp(goalp[item].target_name, Ship_info[i].name)) { - m_object[item] = i | TYPE_SHIP_CLASS; - break; - } - } + if (priority[i] != _priority[i]) { + priority[i] = -1; } - - switch (mode) { - case AI_GOAL_DOCK: - m_dock2[item] = -1; - if (m_object[item]) { - docks = - _editor->get_docking_list(Ship_info[Ships[m_object[item] & DATA_MASK].ship_info_index].model_num); - for (i = 0; unsigned(i) < docks.size(); i++) { - Assert(goalp[item].dockee.name); - Assert(goalp[item].dockee.index != -1); - if (!stricmp(goalp[item].dockee.name, docks[i].c_str())) { - m_dock2[item] = i; - break; - } - } - } - break; - default: - break; + if (subsys[i] != _subsys[i]) { + subsys[i] = -1; } - - // Assert(m_data[item]); - } - } - void ShipGoalsDialogModel::initialize_multi() - { - int i, flag = 0; - object* ptr; - int behavior[ED_MAX_GOALS]{}; - int priority[ED_MAX_GOALS]{}; - SCP_string subsys[ED_MAX_GOALS]{}; - int dock2[ED_MAX_GOALS]{}; - int data[ED_MAX_GOALS]{}; - - ptr = GET_FIRST(&obj_used_list); - while (ptr != END_OF_LIST(&obj_used_list)) { - if (((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START)) && (ptr->flags[Object::Object_Flags::Marked])) { - initialize(Ai_info[Ships[ptr->instance].ai_index].goals); - if (!flag) { - flag = 1; - for (i = 0; i < ED_MAX_GOALS; i++) { - behavior[i] = m_behavior[i]; - priority[i] = m_priority[i]; - subsys[i] = m_subsys[i]; - dock2[i] = m_dock2[i]; - data[i] = m_object[i]; - } - - } - else { - for (i = 0; i < ED_MAX_GOALS; i++) { - if (behavior[i] != m_behavior[i]) { - behavior[i] = -1; - data[i] = -1; - } - - if (data[i] != m_object[i]) { - data[i] = -1; - subsys[i] = -1; - dock2[i] = -1; - } - - if (priority[i] != m_priority[i]) { - priority[i] = -1; - } - if (subsys[i] != m_subsys[i]) { - subsys[i] = -1; - } - if (dock2[i] != m_dock2[i]) { - dock2[i] = -1; - } - } - } + if (dock2[i] != _dock2[i]) { + dock2[i] = -1; } - - ptr = GET_NEXT(ptr); } - - goalp = nullptr; - for (i = 0; i < ED_MAX_GOALS; i++) { - m_behavior[i] = behavior[i]; - m_priority[i] = priority[i]; - m_subsys[i] = subsys[i]; - m_dock2[i] = dock2[i]; - m_object[i] = data[i]; - } - } - void ShipGoalsDialogModel::setShip(const int ship) - { - self_ship = ship; - } - int ShipGoalsDialogModel::getShip() const - { - return self_ship; - } - void ShipGoalsDialogModel::setWing(const int data) - { - modify(self_wing, data); - } - int ShipGoalsDialogModel::getWing() const - { - return self_wing; - } - ai_goal* ShipGoalsDialogModel::getGoal() const - { - return goalp; - } - int ShipGoalsDialogModel::getValid(const int pos) const - { - return valid[pos]; - } - const ai_goal_list* ShipGoalsDialogModel::getGoalTypes() - { - return Editor::getAi_goal_list(); - } - int ShipGoalsDialogModel::getGoalsSize() const - { - return Ai_goal_list_size; - } - void ShipGoalsDialogModel::setBehavior(const int pos, const int data) - { - modify(m_behavior[pos], data); - } - int ShipGoalsDialogModel::getBehavior(const int pos) const - { - return m_behavior[pos]; - } - void ShipGoalsDialogModel::setObject(const int pos, const int data) - { - modify(m_object[pos], data); - } - int ShipGoalsDialogModel::getObject(const int pos) const - { - return m_object[pos]; - } - void ShipGoalsDialogModel::setSubsys(const int pos, const SCP_string& data) - { - modify(m_subsys[pos], data); - } - SCP_string ShipGoalsDialogModel::getSubsys(const int pos) const - { - return m_subsys[pos]; - } - void ShipGoalsDialogModel::setDock(const int pos, const long long data) - { - modify(m_dock2[pos], data); - } - int ShipGoalsDialogModel::getDock(const int pos) const - { - return m_dock2[pos]; - } - void ShipGoalsDialogModel::setPriority(const int pos, const int data) - { - modify(m_priority[pos], data); - } - int ShipGoalsDialogModel::getPriority(const int pos) const - { - return m_priority[pos]; } - } // namespace dialogs - } // namespace fred -} // namespace fso \ No newline at end of file + } + + ptr = GET_NEXT(ptr); + } + + _goalp = nullptr; + for (i = 0; i < ED_MAX_GOALS; i++) { + _behavior[i] = behavior[i]; + _priority[i] = priority[i]; + _subsys[i] = subsys[i]; + _dock2[i] = dock2[i]; + _object[i] = data[i]; + } +} + +void ShipGoalsDialogModel::setShip(int shipNum) +{ + _selfShip = shipNum; +} + +int ShipGoalsDialogModel::getShip() const +{ + return _selfShip; +} + +void ShipGoalsDialogModel::setWing(int wingNum) +{ + modify(_selfWing, wingNum); +} + +int ShipGoalsDialogModel::getWing() const +{ + return _selfWing; +} + +ai_goal* ShipGoalsDialogModel::getGoal() const +{ + return _goalp; +} + +int ShipGoalsDialogModel::getValid(int pos) const +{ + return _valid[pos]; +} + +const ai_goal_list* ShipGoalsDialogModel::getGoalTypes() +{ + return Editor::getAi_goal_list(); +} + +int ShipGoalsDialogModel::getGoalsSize() const +{ + return _aiGoalListSize; +} + +void ShipGoalsDialogModel::setBehavior(int index, int behavior) +{ + modify(_behavior[index], behavior); +} + +int ShipGoalsDialogModel::getBehavior(int index) const +{ + return _behavior[index]; +} + +void ShipGoalsDialogModel::setObject(int index, int objNum) +{ + modify(_object[index], objNum); +} + +int ShipGoalsDialogModel::getObject(int index) const +{ + return _object[index]; +} + +void ShipGoalsDialogModel::setSubsys(int index, const SCP_string& subsys) +{ + modify(_subsys[index], subsys); +} + +SCP_string ShipGoalsDialogModel::getSubsys(int index) const +{ + return _subsys[index]; +} + +void ShipGoalsDialogModel::setDock(int index, long long dock) +{ + modify(_dock2[index], dock); +} + +int ShipGoalsDialogModel::getDock(int index) const +{ + return _dock2[index]; +} + +void ShipGoalsDialogModel::setPriority(int index, int priority) +{ + modify(_priority[index], priority); +} + +int ShipGoalsDialogModel::getPriority(int index) const +{ + return _priority[index]; +} + +} // namespace fso::fred::dialogs diff --git a/qtfred/src/mission/dialogs/ShipEditor/ShipGoalsDialogModel.h b/qtfred/src/mission/dialogs/ShipEditor/ShipGoalsDialogModel.h index fd4ae77a06d..cf8a0221223 100644 --- a/qtfred/src/mission/dialogs/ShipEditor/ShipGoalsDialogModel.h +++ b/qtfred/src/mission/dialogs/ShipEditor/ShipGoalsDialogModel.h @@ -5,9 +5,8 @@ #include "ai/ai.h" #include "ai/aigoals.h" -namespace fso { -namespace fred { -namespace dialogs { +namespace fso::fred::dialogs { + constexpr auto ED_MAX_GOALS = MAX_AI_GOALS; constexpr auto MAX_EDITOR_GOAL_PRIORITY = 200; constexpr auto TYPE_PATH = 0x1000; @@ -21,72 +20,63 @@ constexpr auto DATA_MASK = 0x0fff; constexpr auto MAX_VALID = 99; class ShipGoalsDialogModel : public AbstractDialogModel { - private: - int Ai_goal_list_size = Editor::getAigoal_list_size(); - void initialize(ai_goal* goals); - void initialize_multi(); - void init_combo_data(); - - - - int self_ship, self_wing; - int m_behavior[ED_MAX_GOALS]; - int m_object[ED_MAX_GOALS]; - int m_priority[ED_MAX_GOALS]; - SCP_string m_subsys[ED_MAX_GOALS]; - long long m_dock2[ED_MAX_GOALS]; - //int m_data[ED_MAX_GOALS]; - SCP_vector>> m_ai_goal_combo_data; - int valid[MAX_VALID]; - - bool m_multi_edit; - - ai_goal* goalp; - int verify_orders(const int ship = -1); - - void update_item(const int item); - + Q_OBJECT public: - ShipGoalsDialogModel(QObject* parent, EditorViewport* viewport, bool multi, int self_ship, int self_wing); + ShipGoalsDialogModel(QObject* parent, EditorViewport* viewport, bool multi, int selfShip, int selfWing); - const SCP_vector>> &get_ai_goal_combo_data(); - ai_goal_mode get_first_mode_from_combo_box(int which_item); + const SCP_vector>>& getAiGoalComboData(); + ai_goal_mode getFirstModeFromComboBox(int whichItem); - void initializeData(bool multi, int self_ship, int self_wing); bool apply() override; void reject() override; - void setShip(const int); - int getShip() const; + void setShip(int shipNum); + int getShip() const; - void setWing(const int); - int getWing() const; - + void setWing(int wingNum); + int getWing() const; - ai_goal* getGoal() const; + ai_goal* getGoal() const; - //All getters take the index of the field thay are changeing + int getValid(int pos) const; + static const ai_goal_list* getGoalTypes(); + int getGoalsSize() const; - int getValid(const int) const; - static const ai_goal_list* getGoalTypes(); - int getGoalsSize() const; + void setBehavior(int index, int behavior); + int getBehavior(int index) const; - void setBehavior(const int, const int); - int getBehavior(const int) const; + void setObject(int index, int objNum); + int getObject(int index) const; - void setObject(const int, const int); - int getObject(const int) const; + void setSubsys(int index, const SCP_string& subsys); + SCP_string getSubsys(int index) const; - void setSubsys(const int, const SCP_string&); - SCP_string getSubsys(const int) const; + void setDock(int index, long long dock); + int getDock(int index) const; - void setDock(const int, const long long); - int getDock(const int) const; + void setPriority(int index, int priority); + int getPriority(int index) const; - void setPriority(const int, const int); - int getPriority(const int) const; + private: // NOLINT(readability-redundant-access-specifiers) + void initializeData(bool multi, int selfShip, int selfWing); + void initialize(ai_goal* goals); + void initializeMulti(); + void initComboData(); + int verifyOrders(int shipNum = -1); + void updateItem(int item); + + int _aiGoalListSize = Editor::getAigoal_list_size(); + int _selfShip; + int _selfWing; + int _behavior[ED_MAX_GOALS]; + int _object[ED_MAX_GOALS]; + int _priority[ED_MAX_GOALS]; + SCP_string _subsys[ED_MAX_GOALS]; + long long _dock2[ED_MAX_GOALS]; + SCP_vector>> _aiGoalComboData; + int _valid[MAX_VALID]; + bool _multiEdit; + ai_goal* _goalp; }; -} // namespace dialogs -} // namespace fred -} // namespace fso \ No newline at end of file +} // namespace fso::fred::dialogs diff --git a/qtfred/src/ui/dialogs/ShipEditor/ShipGoalsDialog.cpp b/qtfred/src/ui/dialogs/ShipEditor/ShipGoalsDialog.cpp index d75a431d61d..4efbcb3ffe4 100644 --- a/qtfred/src/ui/dialogs/ShipEditor/ShipGoalsDialog.cpp +++ b/qtfred/src/ui/dialogs/ShipEditor/ShipGoalsDialog.cpp @@ -34,7 +34,7 @@ ShipGoalsDialog::ShipGoalsDialog(QWidget* parent, EditorViewport* viewport, bool ui->gridLayout->addWidget(priority[i], row, 5); } - connect(_model.get(), &AbstractDialogModel::modelChanged, this, &ShipGoalsDialog::updateUI); + connect(_model.get(), &AbstractDialogModel::modelChanged, this, &ShipGoalsDialog::updateUi); for (int i = 0; i < ED_MAX_GOALS; i++) { connect(behaviors[i], QOverload::of(&QComboBox::currentIndexChanged), [=](int index) { _model->setBehavior(i, index); @@ -57,7 +57,7 @@ ShipGoalsDialog::ShipGoalsDialog(QWidget* parent, EditorViewport* viewport, bool }); } - updateUI(); + updateUi(); // Resize the dialog to the minimum size resize(QDialog::sizeHint()); @@ -97,11 +97,11 @@ void ShipGoalsDialog::on_cancelButton_clicked() { reject(); } -void ShipGoalsDialog::updateUI() +void ShipGoalsDialog::updateUi() { - if (m_updating_ui) + if (_updatingUi) return; - m_updating_ui = true; + _updatingUi = true; util::SignalBlockers blockers(this); for (int i = 0; i < ED_MAX_GOALS; i++) { @@ -110,14 +110,14 @@ void ShipGoalsDialog::updateUI() subsys[i]->clear(); docks[i]->clear(); - for (const auto& entry : _model->get_ai_goal_combo_data()) { + for (const auto& entry : _model->getAiGoalComboData()) { behaviors[i]->addItem(entry.first); } auto value = _model->getBehavior(i); behaviors[i]->setCurrentIndex(value); - auto mode = _model->get_first_mode_from_combo_box(i); + auto mode = _model->getFirstModeFromComboBox(i); SCP_vector::iterator ii; if (value < 1) { @@ -332,6 +332,6 @@ void ShipGoalsDialog::updateUI() } } } - m_updating_ui = false; + _updatingUi = false; } } // namespace fso::fred::dialogs \ No newline at end of file diff --git a/qtfred/src/ui/dialogs/ShipEditor/ShipGoalsDialog.h b/qtfred/src/ui/dialogs/ShipEditor/ShipGoalsDialog.h index 3b359c2f83e..b10a7ac89ca 100644 --- a/qtfred/src/ui/dialogs/ShipEditor/ShipGoalsDialog.h +++ b/qtfred/src/ui/dialogs/ShipEditor/ShipGoalsDialog.h @@ -1,5 +1,4 @@ -#ifndef SHIPGOALSDIALOG_H -#define SHIPGOALSDIALOG_H +#pragma once #include @@ -26,7 +25,7 @@ class ShipGoalsDialog : public QDialog { void on_okButton_clicked(); void on_cancelButton_clicked(); - private:// NOLINT(readability-redundant-access-specifiers) + private: // NOLINT(readability-redundant-access-specifiers) std::unique_ptr ui; std::unique_ptr _model; EditorViewport* _viewport; @@ -37,8 +36,7 @@ class ShipGoalsDialog : public QDialog { QComboBox* docks[ED_MAX_GOALS]; QSpinBox* priority[ED_MAX_GOALS]; - void updateUI(); - bool m_updating_ui = false; + void updateUi(); + bool _updatingUi = false; }; } // namespace fso::fred::dialogs -#endif // !SHIPGOALSDIALOG_H \ No newline at end of file From 11f13598c0cd9ce36ef6d752127f6f0ead10de8e Mon Sep 17 00:00:00 2001 From: Mike Nelson Date: Thu, 7 May 2026 13:25:01 -0500 Subject: [PATCH 2/2] clang --- .../src/mission/dialogs/ShipEditor/ShipGoalsDialogModel.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qtfred/src/mission/dialogs/ShipEditor/ShipGoalsDialogModel.cpp b/qtfred/src/mission/dialogs/ShipEditor/ShipGoalsDialogModel.cpp index 0e3edac0c65..59039c160b6 100644 --- a/qtfred/src/mission/dialogs/ShipEditor/ShipGoalsDialogModel.cpp +++ b/qtfred/src/mission/dialogs/ShipEditor/ShipGoalsDialogModel.cpp @@ -570,13 +570,13 @@ void ShipGoalsDialogModel::initialize(ai_goal* goals) if ((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START)) { inst = ptr->instance; if (ptr->type == OBJ_SHIP) { - Assert(inst >= 0 && inst < MAX_SHIPS); + Assertion(inst >= 0 && inst < MAX_SHIPS, "inst must be a valid ship index"); // NOLINT(readability-simplify-boolean-expr) if (!stricmp(_goalp[item].target_name, Ships[inst].ship_name)) { _object[item] = inst | TYPE_SHIP; break; } } else { - Assert(inst >= 0 && inst < MAX_SHIPS); + Assertion(inst >= 0 && inst < MAX_SHIPS, "inst must be a valid ship index"); // NOLINT(readability-simplify-boolean-expr) if (!stricmp(_goalp[item].target_name, Ships[inst].ship_name)) { _object[item] = inst | TYPE_PLAYER; break;