From 17f6d04bc78687f0ad784379bf26d3765b82a40a Mon Sep 17 00:00:00 2001 From: Mike Nelson Date: Thu, 7 May 2026 10:00:46 -0500 Subject: [PATCH 1/4] ship texture replacement stylization pass --- .../ShipTextureReplacementDialogModel.cpp | 1015 ++++++++--------- .../ShipTextureReplacementDialogModel.h | 89 +- .../ShipTextureReplacementDialog.cpp | 108 +- .../ShipEditor/ShipTextureReplacementDialog.h | 11 +- 4 files changed, 586 insertions(+), 637 deletions(-) diff --git a/qtfred/src/mission/dialogs/ShipEditor/ShipTextureReplacementDialogModel.cpp b/qtfred/src/mission/dialogs/ShipEditor/ShipTextureReplacementDialogModel.cpp index 92a36b7814b..b37aa649e2c 100644 --- a/qtfred/src/mission/dialogs/ShipEditor/ShipTextureReplacementDialogModel.cpp +++ b/qtfred/src/mission/dialogs/ShipEditor/ShipTextureReplacementDialogModel.cpp @@ -25,467 +25,361 @@ bool is_known_subtexture_type(const SCP_string& type) } } -namespace fso { - namespace fred { - namespace dialogs { - ShipTextureReplacementDialogModel::ShipTextureReplacementDialogModel(QObject* parent, EditorViewport* viewport, bool multi) : - AbstractDialogModel(parent, viewport) +namespace fso::fred::dialogs { + +ShipTextureReplacementDialogModel::ShipTextureReplacementDialogModel(QObject* parent, EditorViewport* viewport, bool multi) + : AbstractDialogModel(parent, viewport) +{ + initializeData(multi); +} + +void ShipTextureReplacementDialogModel::initializeData(bool multi) +{ + _multi = multi; + char texture_file[MAX_FILENAME_LEN]; + char* p = nullptr; + int duplicate; + polymodel* pm = model_get(Ship_info[Ships[_editor->cur_ship].ship_info_index].model_num); + _defaultTextures.clear(); + _defaultTextures.resize(pm->n_textures); + _currentTextures.clear(); + _currentTextures.resize(pm->n_textures); + _subTypesAvailable.clear(); + _subTypesAvailable.resize(pm->n_textures); + _replaceMap.clear(); + _replaceMap.resize(pm->n_textures); + _inheritMap.clear(); + _inheritMap.resize(pm->n_textures); + + // look for textures to populate the list + for (int i = 0; i < pm->n_textures; i++) { + // get texture file name + bm_get_filename(pm->maps[i].textures[0].GetOriginalTexture(), texture_file); + + // skip blank textures + if (!strlen(texture_file)) + continue; + + // get rid of file extension + p = strchr(texture_file, '.'); + if (p) + { + *p = 0; + } + + // check for duplicate textures in list + duplicate = -1; + for (size_t k = 0; k < _defaultTextures.size(); k++) + { + if (!stricmp(_defaultTextures[k].c_str(), texture_file)) { - initialiseData(multi); + duplicate = static_cast(k); + break; } + } - void ShipTextureReplacementDialogModel::initialiseData(bool multi) + if (duplicate >= 0) + continue; + + // make old texture lowercase + strlwr(texture_file); + + // add it to the field + _defaultTextures[i] = texture_file; + _currentTextures[i].insert(std::pair("main", "")); + //Get all Available SubTypes + initSubTypes(pm, i); + } + + if (!_multi) { + for (auto& Fred_texture_replacement : Fred_texture_replacements) + { + if (!stricmp(Ships[_editor->cur_ship].ship_name, Fred_texture_replacement.ship_name) && !(Fred_texture_replacement.from_table)) { - m_multi = multi; - char texture_file[MAX_FILENAME_LEN]; - char* p = nullptr; - int duplicate; - polymodel* pm = model_get(Ship_info[Ships[_editor->cur_ship].ship_info_index].model_num); - defaultTextures.clear(); - defaultTextures.resize(pm->n_textures); - currentTextures.clear(); - currentTextures.resize(pm->n_textures); - subTypesAvailable.clear(); - subTypesAvailable.resize(pm->n_textures); - replaceMap.clear(); - replaceMap.resize(pm->n_textures); - inheritMap.clear(); - inheritMap.resize(pm->n_textures); - - // look for textures to populate the list - for (int i = 0; i < pm->n_textures; i++) { - // get texture file name - bm_get_filename(pm->maps[i].textures[0].GetOriginalTexture(), texture_file); - - // skip blank textures - if (!strlen(texture_file)) - continue; - - // get rid of file extension - p = strchr(texture_file, '.'); - if (p) - { - //mprintf(( "ignoring extension on file '%s'\n", texture_file )); - *p = 0; + // old_texture is stored as the bare base name by this dialog (no type suffix). + // However, entries loaded from old mission files may have a type suffix + // (e.g. "fenris-body-misc"), so fall back to stripping if no direct match. + SCP_string pureName = Fred_texture_replacement.old_texture; + + // Find the matching default texture slot. + // Try direct match first; fall back to stripping the last '-' segment + // for old mission-file entries that stored old_texture with a type suffix. + size_t matchIdx = _defaultTextures.size(); + for (size_t i = 0; i < _defaultTextures.size(); i++) { + if (lcase_equal(_defaultTextures[i], pureName)) { + matchIdx = i; + break; } - - // check for duplicate textures in list - duplicate = -1; - for (size_t k = 0; k < defaultTextures.size(); k++) - { - if (!stricmp(defaultTextures[k].c_str(), texture_file)) - { - duplicate = static_cast(k); - break; + } + if (matchIdx == _defaultTextures.size()) { + auto stripPos = pureName.find_last_of('-'); + if (stripPos != SCP_string::npos) { + SCP_string stripped = pureName.substr(0, stripPos); + for (size_t i = 0; i < _defaultTextures.size(); i++) { + if (lcase_equal(_defaultTextures[i], stripped)) { + matchIdx = i; + break; + } } } - - if (duplicate >= 0) - continue; - - // make old texture lowercase - strlwr(texture_file); - - // add it to the field - defaultTextures[i] = texture_file; - currentTextures[i].insert(std::pair("main", "")); - //Get all Available SubTypes - initSubTypes(pm, i); - } - if (!m_multi) { - for (auto& Fred_texture_replacement : Fred_texture_replacements) + if (matchIdx < _defaultTextures.size()) + { + size_t i = matchIdx; { - if (!stricmp(Ships[_editor->cur_ship].ship_name, Fred_texture_replacement.ship_name) && !(Fred_texture_replacement.from_table)) + SCP_string newText = Fred_texture_replacement.new_texture; + SCP_string type; { - // old_texture is stored as the bare base name by this dialog (no type suffix). - // However, entries loaded from old mission files may have a type suffix - // (e.g. "fenris-body-misc"), so fall back to stripping if no direct match. - SCP_string pureName = Fred_texture_replacement.old_texture; - - // Find the matching default texture slot. - // Try direct match first; fall back to stripping the last '-' segment - // for old mission-file entries that stored old_texture with a type suffix. - size_t matchIdx = defaultTextures.size(); - for (size_t i = 0; i < defaultTextures.size(); i++) { - if (lcase_equal(defaultTextures[i], pureName)) { - matchIdx = i; - break; - } - } - if (matchIdx == defaultTextures.size()) { - auto stripPos = pureName.find_last_of('-'); - if (stripPos != SCP_string::npos) { - SCP_string stripped = pureName.substr(0, stripPos); - for (size_t i = 0; i < defaultTextures.size(); i++) { - if (lcase_equal(defaultTextures[i], stripped)) { - matchIdx = i; - break; - } - } - } - } - - if (matchIdx < defaultTextures.size()) - { - size_t i = matchIdx; - { - SCP_string newText = Fred_texture_replacement.new_texture; - SCP_string type; - { - auto npos = newText.find_last_of('-'); - if (npos != SCP_string::npos) { - SCP_string possibleType = newText.substr(npos + 1); - // Only treat the suffix as a type if it's a known sub-texture type. - // Texture names themselves can contain hyphens (e.g. "fighter01-01a"), - // so we must not blindly strip the last segment. - for (const auto& kt : get_replaceable_texture_types()) { - if (lcase_equal(possibleType, kt)) { - type = possibleType; - newText = newText.substr(0, npos); - break; - } - } - } - } - if (!type.empty()) { - if (type == "misc") { - currentTextures[i]["misc"] = newText; - replaceMap[i]["misc"] = true; - inheritMap[i]["misc"] = (newText == pureName); - } - if (type == "shine") { - currentTextures[i]["shine"] = newText; - replaceMap[i]["shine"] = true; - inheritMap[i]["shine"] = (newText == pureName); - - } - if (type == "glow") { - currentTextures[i]["glow"] = newText; - replaceMap[i]["glow"] = true; - inheritMap[i]["glow"] = (newText == pureName); - - } - if (type == "normal") { - currentTextures[i]["normal"] = newText; - replaceMap[i]["normal"] = true; - inheritMap[i]["normal"] = (newText == pureName); - - } - if (type == "height") { - currentTextures[i]["height"] = newText; - replaceMap[i]["height"] = true; - inheritMap[i]["height"] = (newText == pureName); - - } - if (type == "ao") { - currentTextures[i]["ao"] = newText; - replaceMap[i]["ao"] = true; - inheritMap[i]["ao"] = (newText == pureName); - - } - if (type == "reflect") { - currentTextures[i]["reflect"] = newText; - replaceMap[i]["reflect"] = true; - inheritMap[i]["reflect"] = (newText == pureName); - - } - } - else { - currentTextures[i]["main"] = newText; - } - + auto npos = newText.find_last_of('-'); + if (npos != SCP_string::npos) { + SCP_string possibleType = newText.substr(npos + 1); + // Only treat the suffix as a type if it's a known sub-texture type. + // Texture names themselves can contain hyphens (e.g. "fighter01-01a"), + // so we must not blindly strip the last segment. + if (is_known_subtexture_type(possibleType)) { + type = possibleType; + newText = newText.substr(0, npos); } } } - } - } - modelChanged(); - _modified = false; - } - void ShipTextureReplacementDialogModel::initSubTypes(polymodel* model, int MapNum) - { - for (const auto& type : get_replaceable_texture_types()) { - subTypesAvailable[MapNum].insert(std::pair(type, false)); - currentTextures[MapNum].insert(std::pair(type, "")); - replaceMap[MapNum].insert(std::pair(type, false)); - inheritMap[MapNum].insert(std::pair(type, true)); - } - char subMap[MAX_FILENAME_LEN]; - //init saftly, probly not necessary - for (int j = 1; j < TM_NUM_TYPES; j++) { - bm_get_filename(model->maps[MapNum].textures[j].GetOriginalTexture(), subMap); - char* p = strchr(subMap, '.'); - if (p) - { - //mprintf(( "ignoring extension on file '%s'\n", texture_file )); - *p = 0; - } - SCP_string subMapClean = subMap; - SCP_tolower(subMapClean); - SCP_string type; - auto npos = subMapClean.find_last_of('-'); - if (npos != SCP_string::npos) { - type = subMapClean.substr(npos + 1); - } - else { - continue; - } - if (!type.empty()) { - if (lcase_equal(type, MODEL_TEXTURE_SUFFIX_TRANS.substr(1))) { - // transparency map, not a replaceable subtype - } else { + if (!type.empty()) { if (is_known_subtexture_type(type)) { - subTypesAvailable[MapNum][type] = true; - } else { - error_display(1, "Invalid Map type %s. Check your model's texture names or get a programmer", type.c_str()); + _currentTextures[i][type] = newText; + _replaceMap[i][type] = true; + _inheritMap[i][type] = (newText == pureName); } } + else { + _currentTextures[i]["main"] = newText; + } } } } + } + } + modelChanged(); + _modified = false; +} - bool ShipTextureReplacementDialogModel::apply() - { - if (query_modified()) { - for (size_t i = 0; i < getSize(); i++) { - if ((!currentTextures[i]["main"].empty()) && (currentTextures[i]["main"] != defaultTextures[i])) { - mainChanged = true; - SCP_string name = currentTextures[i]["main"]; - if (testTexture(name)) { - SCP_vector::iterator ii, end; - end = Fred_texture_replacements.end(); - if (!m_multi) { - end = Fred_texture_replacements.end(); - for (ii = Fred_texture_replacements.begin(); ii != end; ++ii) - { - if (!stricmp(ii->ship_name, Ships[_editor->cur_ship].ship_name)) - { - do { - end--; - } while (end != ii && !stricmp(end->ship_name, Ships[_editor->cur_ship].ship_name)); - if (end == ii) - break; - texture_set(&(*ii), &(*end)); - } - } +void ShipTextureReplacementDialogModel::initSubTypes(polymodel* model, int mapNum) +{ + for (const auto& type : get_replaceable_texture_types()) { + _subTypesAvailable[mapNum].insert({type, false}); + _currentTextures[mapNum].insert({type, ""}); + _replaceMap[mapNum].insert({type, false}); + _inheritMap[mapNum].insert({type, true}); + } - if (end != Fred_texture_replacements.end()) - Fred_texture_replacements.erase(end, Fred_texture_replacements.end()); + char subMap[MAX_FILENAME_LEN]; + for (int j = 1; j < TM_NUM_TYPES; j++) { + bm_get_filename(model->maps[mapNum].textures[j].GetOriginalTexture(), subMap); + char* p = strchr(subMap, '.'); + if (p) + { + *p = 0; + } + SCP_string subMapClean = subMap; + SCP_tolower(subMapClean); + SCP_string type; + auto npos = subMapClean.find_last_of('-'); + if (npos != SCP_string::npos) { + type = subMapClean.substr(npos + 1); + } + else { + continue; + } + if (!type.empty()) { + if (type == MODEL_TEXTURE_SUFFIX_TRANS.substr(1)) { + // transparency map, not a replaceable subtype + } else { + if (is_known_subtexture_type(type)) { + _subTypesAvailable[mapNum][type] = true; + } else { + error_display(1, "Invalid Map type %s. Check your model's texture names or get a programmer", type.c_str()); + } + } + } + } +} - // now put the new entries on the end of the list - texture_replace tr; - strcpy_s(tr.old_texture, defaultTextures[i].c_str()); - strcpy_s(tr.new_texture, name.c_str()); - strcpy_s(tr.ship_name, Ships[_editor->cur_ship].ship_name); - tr.new_texture_id = -1; - tr.from_table = false; +bool ShipTextureReplacementDialogModel::apply() +{ + if (query_modified()) { + for (size_t i = 0; i < getSize(); i++) { + if ((!_currentTextures[i]["main"].empty()) && (_currentTextures[i]["main"] != _defaultTextures[i])) { + _mainChanged = true; + SCP_string name = _currentTextures[i]["main"]; + if (testTexture(name)) { + SCP_vector::iterator ii, end; + end = Fred_texture_replacements.end(); + if (!_multi) { + end = Fred_texture_replacements.end(); + for (ii = Fred_texture_replacements.begin(); ii != end; ++ii) + { + if (!stricmp(ii->ship_name, Ships[_editor->cur_ship].ship_name)) + { + do { + end--; + } while (end != ii && !stricmp(end->ship_name, Ships[_editor->cur_ship].ship_name)); + if (end == ii) + break; + texture_set(&(*ii), &(*end)); + } + } - // assign to global FRED array - Fred_texture_replacements.push_back(tr); - _editor->missionChanged(); - } - else { - object* objp = nullptr; - objp = GET_FIRST(&obj_used_list); - while (objp != END_OF_LIST(&obj_used_list)) { - if (((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) && - (objp->flags[Object::Object_Flags::Marked])) { - Assert((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)); - auto shipp = &Ships[get_ship_from_obj(objp)]; - end = Fred_texture_replacements.end(); - for (ii = Fred_texture_replacements.begin(); ii != end; ++ii) - { - if (!stricmp(ii->ship_name, shipp->ship_name)) - { - do { - end--; - } while (end != ii && !stricmp(end->ship_name, shipp->ship_name)); - if (end == ii) - break; - texture_set(&(*ii), &(*end)); - } - } - if (end != Fred_texture_replacements.end()) - Fred_texture_replacements.erase(end, Fred_texture_replacements.end()); - - // now put the new entries on the end of the list - texture_replace tr; - strcpy_s(tr.old_texture, defaultTextures[i].c_str()); - strcpy_s(tr.new_texture, name.c_str()); - strcpy_s(tr.ship_name, shipp->ship_name); - tr.new_texture_id = -1; - tr.from_table = false; - - // assign to global FRED array - Fred_texture_replacements.push_back(tr); - _editor->missionChanged(); - } + if (end != Fred_texture_replacements.end()) + Fred_texture_replacements.erase(end, Fred_texture_replacements.end()); + + // now put the new entries on the end of the list + texture_replace tr; + strcpy_s(tr.old_texture, _defaultTextures[i].c_str()); + strcpy_s(tr.new_texture, name.c_str()); + strcpy_s(tr.ship_name, Ships[_editor->cur_ship].ship_name); + tr.new_texture_id = -1; + tr.from_table = false; - objp = GET_NEXT(objp); + // assign to global FRED array + Fred_texture_replacements.push_back(tr); + _editor->missionChanged(); + } + else { + object* objp = nullptr; + objp = GET_FIRST(&obj_used_list); + while (objp != END_OF_LIST(&obj_used_list)) { + if (((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) && + (objp->flags[Object::Object_Flags::Marked])) { + Assert((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)); + auto shipp = &Ships[get_ship_from_obj(objp)]; + end = Fred_texture_replacements.end(); + for (ii = Fred_texture_replacements.begin(); ii != end; ++ii) + { + if (!stricmp(ii->ship_name, shipp->ship_name)) + { + do { + end--; + } while (end != ii && !stricmp(end->ship_name, shipp->ship_name)); + if (end == ii) + break; + texture_set(&(*ii), &(*end)); } } + if (end != Fred_texture_replacements.end()) + Fred_texture_replacements.erase(end, Fred_texture_replacements.end()); + + // now put the new entries on the end of the list + texture_replace tr; + strcpy_s(tr.old_texture, _defaultTextures[i].c_str()); + strcpy_s(tr.new_texture, name.c_str()); + strcpy_s(tr.ship_name, shipp->ship_name); + tr.new_texture_id = -1; + tr.from_table = false; + + // assign to global FRED array + Fred_texture_replacements.push_back(tr); + _editor->missionChanged(); } - else { - auto button = _viewport->dialogProvider->showButtonDialog(DialogType::Error, "Missing Texture", "FRED was unable to find Main Texture %s \n Aborting at this point", - { DialogButton::Ok }); - if (button == DialogButton::Ok) { - return false; - } - } + + objp = GET_NEXT(objp); } - saveSubMap(i, "misc"); - saveSubMap(i, "shine"); - saveSubMap(i, "glow"); - saveSubMap(i, "normal"); - saveSubMap(i, "height"); - saveSubMap(i, "ao"); - saveSubMap(i, "reflect"); - _editor->missionChanged(); } - _editor->missionChanged(); - return true; } else { - return true; + auto button = _viewport->dialogProvider->showButtonDialog(DialogType::Error, "Missing Texture", "FRED was unable to find Main Texture %s \n Aborting at this point", + { DialogButton::Ok }); + if (button == DialogButton::Ok) { + return false; + } } } - void ShipTextureReplacementDialogModel::reject() - { - } - texture_replace* ShipTextureReplacementDialogModel::texture_set(texture_replace* dest, const texture_replace* src) - { - dest->new_texture_id = src->new_texture_id; - strcpy_s(dest->ship_name, src->ship_name); - strcpy_s(dest->old_texture, src->old_texture); - strcpy_s(dest->new_texture, src->new_texture); - dest->from_table = src->from_table; - - return dest; + for (const auto& type : get_replaceable_texture_types()) { + saveSubMap(i, type); } + _editor->missionChanged(); + } + _editor->missionChanged(); + return true; + } + else { + return true; + } +} - void ShipTextureReplacementDialogModel::saveSubMap(const size_t index, const SCP_string& type) { - SCP_string fullName; - if (replaceMap[index][type]) { - if (inheritMap[index][type]) { - if (mainChanged) { - if (!currentTextures[index]["main"].empty()) { - if (currentTextures[index]["main"] != "invisible") { - fullName = currentTextures[index]["main"] + "-" + type; - } - else { - fullName = currentTextures[index]["main"]; - } - if (testTexture(fullName)) { - SCP_vector::iterator ii, end; - end = Fred_texture_replacements.end(); - if (!m_multi) { - end = Fred_texture_replacements.end(); - for (ii = Fred_texture_replacements.begin(); ii != end; ++ii) - { - if (!stricmp(ii->ship_name, Ships[_editor->cur_ship].ship_name)) - { - do { - end--; - } while (end != ii && !stricmp(end->ship_name, Ships[_editor->cur_ship].ship_name)); - if (end == ii) - break; - texture_set(&(*ii), &(*end)); - } - } +void ShipTextureReplacementDialogModel::reject() {} - if (end != Fred_texture_replacements.end()) - Fred_texture_replacements.erase(end, Fred_texture_replacements.end()); +texture_replace* ShipTextureReplacementDialogModel::texture_set(texture_replace* dest, const texture_replace* src) +{ + dest->new_texture_id = src->new_texture_id; + strcpy_s(dest->ship_name, src->ship_name); + strcpy_s(dest->old_texture, src->old_texture); + strcpy_s(dest->new_texture, src->new_texture); + dest->from_table = src->from_table; - // now put the new entries on the end of the list - texture_replace tr; - strcpy_s(tr.old_texture, defaultTextures[index].c_str()); - strcpy_s(tr.new_texture, fullName.c_str()); - strcpy_s(tr.ship_name, Ships[_editor->cur_ship].ship_name); - tr.new_texture_id = -1; - tr.from_table = false; + return dest; +} - // assign to global FRED array - Fred_texture_replacements.push_back(tr); - _editor->missionChanged(); - } - else { - object* objp = nullptr; - objp = GET_FIRST(&obj_used_list); - while (objp != END_OF_LIST(&obj_used_list)) { - if (((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) && - (objp->flags[Object::Object_Flags::Marked])) { - Assert((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)); - auto shipp = &Ships[get_ship_from_obj(objp)]; - end = Fred_texture_replacements.end(); - for (ii = Fred_texture_replacements.begin(); ii != end; ++ii) - { - if (!stricmp(ii->ship_name, shipp->ship_name)) - { - do { - end--; - } while (end != ii && !stricmp(end->ship_name, shipp->ship_name)); - if (end == ii) - break; - texture_set(&(*ii), &(*end)); - } - } - - if (end != Fred_texture_replacements.end()) - Fred_texture_replacements.erase(end, Fred_texture_replacements.end()); - - // now put the new entries on the end of the list - texture_replace tr; - strcpy_s(tr.old_texture, defaultTextures[index].c_str()); - strcpy_s(tr.new_texture, fullName.c_str()); - strcpy_s(tr.ship_name, shipp->ship_name); - tr.new_texture_id = -1; - tr.from_table = false; - - // assign to global FRED array - Fred_texture_replacements.push_back(tr); - _editor->missionChanged(); - } - - objp = GET_NEXT(objp); - } - } - } - else { - auto button = _viewport->dialogProvider->showButtonDialog(DialogType::Error, "Missing Texture", "FRED was unable to find %s \n Skipping", - { DialogButton::Ok }); - if (button == DialogButton::Ok) { - return; - } +void ShipTextureReplacementDialogModel::saveSubMap(size_t index, const SCP_string& type) +{ + SCP_string fullName; + if (_replaceMap[index][type]) { + if (_inheritMap[index][type]) { + if (_mainChanged) { + if (!_currentTextures[index]["main"].empty()) { + if (_currentTextures[index]["main"] != "invisible") { + fullName = _currentTextures[index]["main"] + "-" + type; + } + else { + fullName = _currentTextures[index]["main"]; + } + if (testTexture(fullName)) { + SCP_vector::iterator ii, end; + end = Fred_texture_replacements.end(); + if (!_multi) { + end = Fred_texture_replacements.end(); + for (ii = Fred_texture_replacements.begin(); ii != end; ++ii) + { + if (!stricmp(ii->ship_name, Ships[_editor->cur_ship].ship_name)) + { + do { + end--; + } while (end != ii && !stricmp(end->ship_name, Ships[_editor->cur_ship].ship_name)); + if (end == ii) + break; + texture_set(&(*ii), &(*end)); } - } + + if (end != Fred_texture_replacements.end()) + Fred_texture_replacements.erase(end, Fred_texture_replacements.end()); + + // now put the new entries on the end of the list + texture_replace tr; + strcpy_s(tr.old_texture, _defaultTextures[index].c_str()); + strcpy_s(tr.new_texture, fullName.c_str()); + strcpy_s(tr.ship_name, Ships[_editor->cur_ship].ship_name); + tr.new_texture_id = -1; + tr.from_table = false; + + // assign to global FRED array + Fred_texture_replacements.push_back(tr); + _editor->missionChanged(); } else { - error_display(0, "Cannot use inherited data without changing the main texture name. Ignoring %s map change.", type.c_str()); - } - } - else { - if (!currentTextures[index][type].empty()) { - if (currentTextures[index][type] != "invisible") { - fullName = currentTextures[index][type] + "-" + type; - } - else { - fullName = currentTextures[index][type]; - } - if (testTexture(fullName)) { - SCP_vector::iterator ii, end; - end = Fred_texture_replacements.end(); - if (!m_multi) { + object* objp = nullptr; + objp = GET_FIRST(&obj_used_list); + while (objp != END_OF_LIST(&obj_used_list)) { + if (((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) && + (objp->flags[Object::Object_Flags::Marked])) { + Assert((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)); + auto shipp = &Ships[get_ship_from_obj(objp)]; end = Fred_texture_replacements.end(); for (ii = Fred_texture_replacements.begin(); ii != end; ++ii) { - if (!stricmp(ii->ship_name, Ships[_editor->cur_ship].ship_name)) + if (!stricmp(ii->ship_name, shipp->ship_name)) { do { end--; - } while (end != ii && !stricmp(end->ship_name, Ships[_editor->cur_ship].ship_name)); + } while (end != ii && !stricmp(end->ship_name, shipp->ship_name)); if (end == ii) break; texture_set(&(*ii), &(*end)); @@ -497,9 +391,9 @@ namespace fso { // now put the new entries on the end of the list texture_replace tr; - strcpy_s(tr.old_texture, defaultTextures[index].c_str()); + strcpy_s(tr.old_texture, _defaultTextures[index].c_str()); strcpy_s(tr.new_texture, fullName.c_str()); - strcpy_s(tr.ship_name, Ships[_editor->cur_ship].ship_name); + strcpy_s(tr.ship_name, shipp->ship_name); tr.new_texture_id = -1; tr.from_table = false; @@ -507,137 +401,200 @@ namespace fso { Fred_texture_replacements.push_back(tr); _editor->missionChanged(); } - else { - object* objp = nullptr; - objp = GET_FIRST(&obj_used_list); - while (objp != END_OF_LIST(&obj_used_list)) { - if (((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) && - (objp->flags[Object::Object_Flags::Marked])) { - Assert((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)); - auto shipp = &Ships[get_ship_from_obj(objp)]; - end = Fred_texture_replacements.end(); - for (ii = Fred_texture_replacements.begin(); ii != end; ++ii) - { - if (!stricmp(ii->ship_name, shipp->ship_name)) - { - do { - end--; - } while (end != ii && !stricmp(end->ship_name, shipp->ship_name)); - if (end == ii) - break; - texture_set(&(*ii), &(*end)); - } - } - - if (end != Fred_texture_replacements.end()) - Fred_texture_replacements.erase(end, Fred_texture_replacements.end()); - - // now put the new entries on the end of the list - texture_replace tr; - strcpy_s(tr.old_texture, defaultTextures[index].c_str()); - strcpy_s(tr.new_texture, fullName.c_str()); - strcpy_s(tr.ship_name, shipp->ship_name); - tr.new_texture_id = -1; - tr.from_table = false; - - // assign to global FRED array - Fred_texture_replacements.push_back(tr); - _editor->missionChanged(); - } - objp = GET_NEXT(objp); - } - } - } - else { - auto button = _viewport->dialogProvider->showButtonDialog(DialogType::Error, "Missing Texture", "FRED was unable to find %s \n Skipping", - { DialogButton::Ok }); - if (button == DialogButton::Ok) { - return; - } + objp = GET_NEXT(objp); } - } } - } - } - - bool ShipTextureReplacementDialogModel::testTexture(const SCP_string& fullName) - { - int temp_bmp, temp_frames, temp_fps; - if (fullName == "invisible") { - return true; - } - else { - // try loading the texture (bmpman should take care of eventually unloading it) - temp_bmp = bm_load(fullName); - if (temp_bmp < 0) - { - temp_bmp = bm_load_animation(fullName.c_str(), &temp_frames, &temp_fps, nullptr, nullptr, false, true); + else { + auto button = _viewport->dialogProvider->showButtonDialog(DialogType::Error, "Missing Texture", "FRED was unable to find %s \n Skipping", + { DialogButton::Ok }); + if (button == DialogButton::Ok) { + return; + } } - return temp_bmp >= 0; } } - - size_t ShipTextureReplacementDialogModel::getSize() const - { - return defaultTextures.size(); + else { + error_display(0, "Cannot use inherited data without changing the main texture name. Ignoring %s map change.", type.c_str()); } - SCP_string ShipTextureReplacementDialogModel::getDefaultName(const size_t index) const - { - Assert(index <= defaultTextures.size()); - return defaultTextures[index]; - } - void ShipTextureReplacementDialogModel::setMap(const size_t index, const SCP_string& type, const SCP_string& newName) - { - Assert(index < currentTextures.size()); - auto pos = currentTextures[index].find(type); - if (pos == currentTextures[index].end()) { - //handle the error - error_display(1, "Tried to set non existant map type %s. Get a programmer", type.c_str()); + } + else { + if (!_currentTextures[index][type].empty()) { + if (_currentTextures[index][type] != "invisible") { + fullName = _currentTextures[index][type] + "-" + type; } else { - modify(currentTextures[index][type], newName); + fullName = _currentTextures[index][type]; } + if (testTexture(fullName)) { + SCP_vector::iterator ii, end; + end = Fred_texture_replacements.end(); + if (!_multi) { + end = Fred_texture_replacements.end(); + for (ii = Fred_texture_replacements.begin(); ii != end; ++ii) + { + if (!stricmp(ii->ship_name, Ships[_editor->cur_ship].ship_name)) + { + do { + end--; + } while (end != ii && !stricmp(end->ship_name, Ships[_editor->cur_ship].ship_name)); + if (end == ii) + break; + texture_set(&(*ii), &(*end)); + } + } - } - SCP_string ShipTextureReplacementDialogModel::getMap(const size_t index, const SCP_string& type) const { - Assert(index < currentTextures.size()); - auto pos = currentTextures[index].find(type); - if (pos == currentTextures[index].end()) { - error_display(1, "Asked for non existant map type %s. Get a programmer", type.c_str()); - return nullptr; + if (end != Fred_texture_replacements.end()) + Fred_texture_replacements.erase(end, Fred_texture_replacements.end()); + + // now put the new entries on the end of the list + texture_replace tr; + strcpy_s(tr.old_texture, _defaultTextures[index].c_str()); + strcpy_s(tr.new_texture, fullName.c_str()); + strcpy_s(tr.ship_name, Ships[_editor->cur_ship].ship_name); + tr.new_texture_id = -1; + tr.from_table = false; + + // assign to global FRED array + Fred_texture_replacements.push_back(tr); + _editor->missionChanged(); + } + else { + object* objp = nullptr; + objp = GET_FIRST(&obj_used_list); + while (objp != END_OF_LIST(&obj_used_list)) { + if (((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) && + (objp->flags[Object::Object_Flags::Marked])) { + Assert((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)); + auto shipp = &Ships[get_ship_from_obj(objp)]; + end = Fred_texture_replacements.end(); + for (ii = Fred_texture_replacements.begin(); ii != end; ++ii) + { + if (!stricmp(ii->ship_name, shipp->ship_name)) + { + do { + end--; + } while (end != ii && !stricmp(end->ship_name, shipp->ship_name)); + if (end == ii) + break; + texture_set(&(*ii), &(*end)); + } + } + + if (end != Fred_texture_replacements.end()) + Fred_texture_replacements.erase(end, Fred_texture_replacements.end()); + + // now put the new entries on the end of the list + texture_replace tr; + strcpy_s(tr.old_texture, _defaultTextures[index].c_str()); + strcpy_s(tr.new_texture, fullName.c_str()); + strcpy_s(tr.ship_name, shipp->ship_name); + tr.new_texture_id = -1; + tr.from_table = false; + + // assign to global FRED array + Fred_texture_replacements.push_back(tr); + _editor->missionChanged(); + } + + objp = GET_NEXT(objp); + } + } } else { - return pos->second; + auto button = _viewport->dialogProvider->showButtonDialog(DialogType::Error, "Missing Texture", "FRED was unable to find %s \n Skipping", + { DialogButton::Ok }); + if (button == DialogButton::Ok) { + return; + } } } - SCP_map ShipTextureReplacementDialogModel::getSubtypesForMap(const size_t index) const - { - Assert(index < currentTextures.size()); - return subTypesAvailable[index]; - } - SCP_map ShipTextureReplacementDialogModel::getReplace(const size_t index) const - { - Assert(index < currentTextures.size()); - return replaceMap[index]; - } - SCP_map ShipTextureReplacementDialogModel::getInherit(const size_t index) const - { - Assert(index < currentTextures.size()); - return inheritMap[index]; - } + } + } +} - void ShipTextureReplacementDialogModel::setReplace(const size_t index, const SCP_string& type, const bool state) - { - Assert(index < currentTextures.size()); - modify(replaceMap[index][type], state); - } - void ShipTextureReplacementDialogModel::setInherit(const size_t index, const SCP_string& type, const bool state) - { - Assert(index < currentTextures.size()); - modify(inheritMap[index][type], state); - } +bool ShipTextureReplacementDialogModel::testTexture(const SCP_string& fullName) +{ + int temp_bmp, temp_frames, temp_fps; + if (fullName == "invisible") { + return true; + } + else { + // try loading the texture (bmpman should take care of eventually unloading it) + temp_bmp = bm_load(fullName); + if (temp_bmp < 0) + { + temp_bmp = bm_load_animation(fullName.c_str(), &temp_frames, &temp_fps, nullptr, nullptr, false, true); } + return temp_bmp >= 0; + } +} + +size_t ShipTextureReplacementDialogModel::getSize() const +{ + return _defaultTextures.size(); +} + +SCP_string ShipTextureReplacementDialogModel::getDefaultName(size_t index) const +{ + Assert(index <= _defaultTextures.size()); + return _defaultTextures[index]; +} + +void ShipTextureReplacementDialogModel::setMap(size_t index, const SCP_string& type, const SCP_string& newName) +{ + Assert(index < _currentTextures.size()); + auto pos = _currentTextures[index].find(type); + if (pos == _currentTextures[index].end()) { + error_display(1, "Tried to set non existant map type %s. Get a programmer", type.c_str()); } -} \ No newline at end of file + else { + modify(_currentTextures[index][type], newName); + } +} + +SCP_string ShipTextureReplacementDialogModel::getMap(size_t index, const SCP_string& type) const +{ + Assert(index < _currentTextures.size()); + auto pos = _currentTextures[index].find(type); + if (pos == _currentTextures[index].end()) { + error_display(1, "Asked for non existant map type %s. Get a programmer", type.c_str()); + return nullptr; + } + else { + return pos->second; + } +} + +SCP_map ShipTextureReplacementDialogModel::getSubtypesForMap(size_t index) const +{ + Assert(index < _currentTextures.size()); + return _subTypesAvailable[index]; +} + +SCP_map ShipTextureReplacementDialogModel::getReplace(size_t index) const +{ + Assert(index < _currentTextures.size()); + return _replaceMap[index]; +} + +SCP_map ShipTextureReplacementDialogModel::getInherit(size_t index) const +{ + Assert(index < _currentTextures.size()); + return _inheritMap[index]; +} + +void ShipTextureReplacementDialogModel::setReplace(size_t index, const SCP_string& type, bool state) +{ + Assert(index < _currentTextures.size()); + modify(_replaceMap[index][type], state); +} + +void ShipTextureReplacementDialogModel::setInherit(size_t index, const SCP_string& type, bool state) +{ + Assert(index < _currentTextures.size()); + modify(_inheritMap[index][type], state); +} + +} // namespace fso::fred::dialogs diff --git a/qtfred/src/mission/dialogs/ShipEditor/ShipTextureReplacementDialogModel.h b/qtfred/src/mission/dialogs/ShipEditor/ShipTextureReplacementDialogModel.h index d8a4ca4cf45..90524db4dcb 100644 --- a/qtfred/src/mission/dialogs/ShipEditor/ShipTextureReplacementDialogModel.h +++ b/qtfred/src/mission/dialogs/ShipEditor/ShipTextureReplacementDialogModel.h @@ -2,51 +2,44 @@ #include "../AbstractDialogModel.h" -namespace fso { - namespace fred { - namespace dialogs { - constexpr auto NUM__SUBTEXTURE_TYPES = 7; - class ShipTextureReplacementDialogModel : public AbstractDialogModel { - private: - void initSubTypes(polymodel* model, int); - - bool m_multi; - //Used to dermeine what type of texutre a map has. - SCP_vector> subTypesAvailable; - - //Wether to replace textures. - SCP_vector> replaceMap; - //Wether to inherit name from base texture. - SCP_vector> inheritMap; - - - SCP_vector defaultTextures; - SCP_vector> currentTextures; - bool mainChanged = false; - void saveSubMap(const size_t index, const SCP_string& type); - static bool testTexture(const SCP_string&); - static texture_replace* texture_set(texture_replace* dest, const texture_replace* src); - public: - ShipTextureReplacementDialogModel(QObject* parent, EditorViewport* viewport, bool multi); - void initialiseData(bool); - - bool apply() override; - void reject() override; - - size_t getSize() const; - SCP_string getDefaultName(const size_t) const; - - void setMap(const size_t index, const SCP_string& type, const SCP_string& newName); - SCP_string getMap(const size_t index, const SCP_string& type) const; - - SCP_map getSubtypesForMap(const size_t index) const; - SCP_map getReplace(const size_t index) const; - SCP_map getInherit(const size_t index) const; - void setReplace(const size_t index, const SCP_string& type, const bool state); - void setInherit(const size_t index, const SCP_string& type, const bool state); - - - }; - } - } -} \ No newline at end of file +namespace fso::fred::dialogs { + +constexpr auto NUM__SUBTEXTURE_TYPES = 7; + +class ShipTextureReplacementDialogModel : public AbstractDialogModel { + Q_OBJECT + public: + ShipTextureReplacementDialogModel(QObject* parent, EditorViewport* viewport, bool multi); + + bool apply() override; + void reject() override; + + size_t getSize() const; + SCP_string getDefaultName(size_t index) const; + + void setMap(size_t index, const SCP_string& type, const SCP_string& newName); + SCP_string getMap(size_t index, const SCP_string& type) const; + + SCP_map getSubtypesForMap(size_t index) const; + SCP_map getReplace(size_t index) const; + SCP_map getInherit(size_t index) const; + void setReplace(size_t index, const SCP_string& type, bool state); + void setInherit(size_t index, const SCP_string& type, bool state); + + private: // NOLINT(readability-redundant-access-specifiers) + void initializeData(bool multi); + void initSubTypes(polymodel* model, int mapNum); + void saveSubMap(size_t index, const SCP_string& type); + static bool testTexture(const SCP_string& name); + static texture_replace* texture_set(texture_replace* dest, const texture_replace* src); + + bool _multi; + SCP_vector> _subTypesAvailable; + SCP_vector> _replaceMap; + SCP_vector> _inheritMap; + SCP_vector _defaultTextures; + SCP_vector> _currentTextures; + bool _mainChanged = false; +}; + +} // namespace fso::fred::dialogs diff --git a/qtfred/src/ui/dialogs/ShipEditor/ShipTextureReplacementDialog.cpp b/qtfred/src/ui/dialogs/ShipEditor/ShipTextureReplacementDialog.cpp index cf38f032a75..f4be1dfb7a3 100644 --- a/qtfred/src/ui/dialogs/ShipEditor/ShipTextureReplacementDialog.cpp +++ b/qtfred/src/ui/dialogs/ShipEditor/ShipTextureReplacementDialog.cpp @@ -49,14 +49,14 @@ ShipTextureReplacementDialog::ShipTextureReplacementDialog(QDialog* parent, Edit ui->NormalTextureLineEdit->setMaxLength(MAX_FILENAME_LEN - 1); ui->ReflectTextureLineEdit->setMaxLength(MAX_FILENAME_LEN - 1); - listmodel = new MapModel(_model.get(), this); - ui->TexturesList->setModel(listmodel); + _listModel = new MapModel(_model.get(), this); + ui->TexturesList->setModel(_listModel); QItemSelectionModel* selectionModel = ui->TexturesList->selectionModel(); - connect(selectionModel, &QItemSelectionModel::selectionChanged, this, &ShipTextureReplacementDialog::updateUIFull); - QModelIndex index = listmodel->index(0); + connect(selectionModel, &QItemSelectionModel::selectionChanged, this, &ShipTextureReplacementDialog::updateUiFull); + QModelIndex index = _listModel->index(0); ui->TexturesList->setCurrentIndex(index); - connect(_model.get(), &AbstractDialogModel::modelChanged, this, &ShipTextureReplacementDialog::updateUI); + connect(_model.get(), &AbstractDialogModel::modelChanged, this, &ShipTextureReplacementDialog::updateUi); // Resize the dialog to the minimum size resize(QDialog::sizeHint()); @@ -100,7 +100,7 @@ void ShipTextureReplacementDialog::on_newTextureLineEdit_editingFinished() if (!ui->newTextureLineEdit->text().isEmpty()) { newText = ui->newTextureLineEdit->text().toUtf8().constData(); } - _model->setMap(row, "main", newText); + _model->setMap(_selectedRow, "main", newText); } void ShipTextureReplacementDialog::on_MiscTextureLineEdit_editingFinished() @@ -108,7 +108,7 @@ void ShipTextureReplacementDialog::on_MiscTextureLineEdit_editingFinished() SCP_string newText; if (!ui->MiscTextureLineEdit->text().isEmpty()) { newText = ui->MiscTextureLineEdit->text().toUtf8().constData(); - _model->setMap(row, "misc", newText); + _model->setMap(_selectedRow, "misc", newText); } } @@ -117,7 +117,7 @@ void ShipTextureReplacementDialog::on_GlowTextureLineEdit_editingFinished() SCP_string newText; if (!ui->GlowTextureLineEdit->text().isEmpty()) { newText = ui->GlowTextureLineEdit->text().toUtf8().constData(); - _model->setMap(row, "glow", newText); + _model->setMap(_selectedRow, "glow", newText); } } @@ -126,7 +126,7 @@ void ShipTextureReplacementDialog::on_ShineTextureLineEdit_editingFinished() SCP_string newText; if (!ui->ShineTextureLineEdit->text().isEmpty()) { newText = ui->ShineTextureLineEdit->text().toUtf8().constData(); - _model->setMap(row, "shine", newText); + _model->setMap(_selectedRow, "shine", newText); } } @@ -135,7 +135,7 @@ void ShipTextureReplacementDialog::on_NormalTextureLineEdit_editingFinished() SCP_string newText; if (!ui->NormalTextureLineEdit->text().isEmpty()) { newText = ui->NormalTextureLineEdit->text().toUtf8().constData(); - _model->setMap(row, "normal", newText); + _model->setMap(_selectedRow, "normal", newText); } } @@ -144,7 +144,7 @@ void ShipTextureReplacementDialog::on_HeightTextureLineEdit_editingFinished() SCP_string newText; if (!ui->HeightTextureLineEdit->text().isEmpty()) { newText = ui->HeightTextureLineEdit->text().toUtf8().constData(); - _model->setMap(row, "height", newText); + _model->setMap(_selectedRow, "height", newText); } } @@ -153,7 +153,7 @@ void ShipTextureReplacementDialog::on_AmbiantTextureLineEdit_editingFinished() SCP_string newText; if (!ui->AmbiantTextureLineEdit->text().isEmpty()) { newText = ui->AmbiantTextureLineEdit->text().toUtf8().constData(); - _model->setMap(row, "ao", newText); + _model->setMap(_selectedRow, "ao", newText); } } @@ -162,184 +162,184 @@ void ShipTextureReplacementDialog::on_ReflectTextureLineEdit_editingFinished() SCP_string newText; if (!ui->ReflectTextureLineEdit->text().isEmpty()) { newText = ui->ReflectTextureLineEdit->text().toUtf8().constData(); - _model->setMap(row, "reflect", newText); + _model->setMap(_selectedRow, "reflect", newText); } } void ShipTextureReplacementDialog::on_useMiscTexturecheckbox_toggled(bool state) { - _model->setReplace(row, "misc", state); + _model->setReplace(_selectedRow, "misc", state); } void ShipTextureReplacementDialog::on_useGlowTexturecheckbox_toggled(bool state) { - _model->setReplace(row, "glow", state); + _model->setReplace(_selectedRow, "glow", state); } void ShipTextureReplacementDialog::on_useShineTexturecheckbox_toggled(bool state) { - _model->setReplace(row, "shine", state); + _model->setReplace(_selectedRow, "shine", state); } void ShipTextureReplacementDialog::on_useNormalTexturecheckbox_toggled(bool state) { - _model->setReplace(row, "normal", state); + _model->setReplace(_selectedRow, "normal", state); } void ShipTextureReplacementDialog::on_useHeightTexturecheckbox_toggled(bool state) { - _model->setReplace(row, "height", state); + _model->setReplace(_selectedRow, "height", state); } void ShipTextureReplacementDialog::on_useAmbiantTexturecheckbox_toggled(bool state) { - _model->setReplace(row, "ao", state); + _model->setReplace(_selectedRow, "ao", state); } void ShipTextureReplacementDialog::on_useReflectTexturecheckbox_toggled(bool state) { - _model->setReplace(row, "reflect", state); + _model->setReplace(_selectedRow, "reflect", state); } void ShipTextureReplacementDialog::on_inheritMiscTexturecheckbox_toggled(bool state) { - _model->setInherit(row, "misc", state); + _model->setInherit(_selectedRow, "misc", state); } void ShipTextureReplacementDialog::on_inheritGlowTexturecheckbox_toggled(bool state) { - _model->setInherit(row, "glow", state); + _model->setInherit(_selectedRow, "glow", state); } void ShipTextureReplacementDialog::on_inheritShineTexturecheckbox_toggled(bool state) { - _model->setInherit(row, "shine", state); + _model->setInherit(_selectedRow, "shine", state); } void ShipTextureReplacementDialog::on_inheritNormalTexturecheckbox_toggled(bool state) { - _model->setInherit(row, "normal", state); + _model->setInherit(_selectedRow, "normal", state); } void ShipTextureReplacementDialog::on_inheritHeightTexturecheckbox_toggled(bool state) { - _model->setInherit(row, "height", state); + _model->setInherit(_selectedRow, "height", state); } void ShipTextureReplacementDialog::on_inheritAmbiantTexturecheckbox_toggled(bool state) { - _model->setInherit(row, "ao", state); + _model->setInherit(_selectedRow, "ao", state); } void ShipTextureReplacementDialog::on_inheritReflectTexturecheckbox_toggled(bool state) { - _model->setInherit(row, "reflect", state); + _model->setInherit(_selectedRow, "reflect", state); } -void ShipTextureReplacementDialog::updateUI() +void ShipTextureReplacementDialog::updateUi() { util::SignalBlockers blockers(this); - ui->newTextureLineEdit->setText(_model->getMap(row, "main").c_str()); + ui->newTextureLineEdit->setText(_model->getMap(_selectedRow, "main").c_str()); bool replace; bool inherit; - ui->useMiscTexturecheckbox->setChecked(replace = _model->getReplace(row)["misc"]); + ui->useMiscTexturecheckbox->setChecked(replace = _model->getReplace(_selectedRow)["misc"]); if (replace) { - ui->inheritMiscTextureCheckbox->setChecked(inherit = _model->getInherit(row)["misc"]); + ui->inheritMiscTextureCheckbox->setChecked(inherit = _model->getInherit(_selectedRow)["misc"]); ui->inheritMiscTextureCheckbox->setEnabled(replace); ui->MiscTextureLineEdit->setEnabled(!inherit); if (!inherit) { - ui->MiscTextureLineEdit->setText(_model->getMap(row, "misc").c_str()); + ui->MiscTextureLineEdit->setText(_model->getMap(_selectedRow, "misc").c_str()); } } else { ui->inheritMiscTextureCheckbox->setDisabled(true); ui->MiscTextureLineEdit->setDisabled(true); } - ui->useShineTexturecheckbox->setChecked(replace = _model->getReplace(row)["shine"]); + ui->useShineTexturecheckbox->setChecked(replace = _model->getReplace(_selectedRow)["shine"]); if (replace) { - ui->inheritShineTextureCheckbox->setChecked(inherit = _model->getInherit(row)["shine"]); + ui->inheritShineTextureCheckbox->setChecked(inherit = _model->getInherit(_selectedRow)["shine"]); ui->inheritShineTextureCheckbox->setEnabled(replace); ui->ShineTextureLineEdit->setEnabled(!inherit); if (!inherit) { - ui->ShineTextureLineEdit->setText(_model->getMap(row, "shine").c_str()); + ui->ShineTextureLineEdit->setText(_model->getMap(_selectedRow, "shine").c_str()); } } else { ui->inheritShineTextureCheckbox->setDisabled(true); ui->ShineTextureLineEdit->setDisabled(true); } - ui->useGlowTexturecheckbox->setChecked(replace = _model->getReplace(row)["glow"]); + ui->useGlowTexturecheckbox->setChecked(replace = _model->getReplace(_selectedRow)["glow"]); if (replace) { ui->inheritGlowTextureCheckbox->setEnabled(replace); - ui->inheritGlowTextureCheckbox->setChecked(inherit = _model->getInherit(row)["glow"]); + ui->inheritGlowTextureCheckbox->setChecked(inherit = _model->getInherit(_selectedRow)["glow"]); ui->GlowTextureLineEdit->setEnabled(!inherit); if (!inherit) { - ui->GlowTextureLineEdit->setText(_model->getMap(row, "glow").c_str()); + ui->GlowTextureLineEdit->setText(_model->getMap(_selectedRow, "glow").c_str()); } } else { ui->inheritGlowTextureCheckbox->setDisabled(true); ui->GlowTextureLineEdit->setDisabled(true); } - ui->useNormalTexturecheckbox->setChecked(replace = _model->getReplace(row)["normal"]); + ui->useNormalTexturecheckbox->setChecked(replace = _model->getReplace(_selectedRow)["normal"]); if (replace) { - ui->inheritNormalTextureCheckbox->setChecked(inherit = _model->getInherit(row)["normal"]); + ui->inheritNormalTextureCheckbox->setChecked(inherit = _model->getInherit(_selectedRow)["normal"]); ui->inheritNormalTextureCheckbox->setEnabled(replace); ui->NormalTextureLineEdit->setEnabled(!inherit); if (!inherit) { - ui->NormalTextureLineEdit->setText(_model->getMap(row, "normal").c_str()); + ui->NormalTextureLineEdit->setText(_model->getMap(_selectedRow, "normal").c_str()); } } else { ui->inheritNormalTextureCheckbox->setDisabled(true); ui->NormalTextureLineEdit->setDisabled(true); } - ui->useHeightTexturecheckbox->setChecked(replace = _model->getReplace(row)["height"]); + ui->useHeightTexturecheckbox->setChecked(replace = _model->getReplace(_selectedRow)["height"]); if (replace) { - ui->inheritHeightTextureCheckbox->setChecked(inherit = _model->getInherit(row)["height"]); + ui->inheritHeightTextureCheckbox->setChecked(inherit = _model->getInherit(_selectedRow)["height"]); ui->inheritHeightTextureCheckbox->setEnabled(replace); ui->HeightTextureLineEdit->setEnabled(!inherit); if (!inherit) { - ui->HeightTextureLineEdit->setText(_model->getMap(row, "height").c_str()); + ui->HeightTextureLineEdit->setText(_model->getMap(_selectedRow, "height").c_str()); } } else { ui->inheritHeightTextureCheckbox->setDisabled(true); ui->HeightTextureLineEdit->setDisabled(true); } - ui->useAmbiantTexturecheckbox->setChecked(replace = _model->getReplace(row)["ao"]); + ui->useAmbiantTexturecheckbox->setChecked(replace = _model->getReplace(_selectedRow)["ao"]); if (replace) { - ui->inheritAmbiantTextureCheckbox->setChecked(inherit = _model->getInherit(row)["ao"]); + ui->inheritAmbiantTextureCheckbox->setChecked(inherit = _model->getInherit(_selectedRow)["ao"]); ui->AmbiantTextureLineEdit->setEnabled(!inherit); ui->inheritAmbiantTextureCheckbox->setEnabled(replace); if (!inherit) { - ui->AmbiantTextureLineEdit->setText(_model->getMap(row, "ao").c_str()); + ui->AmbiantTextureLineEdit->setText(_model->getMap(_selectedRow, "ao").c_str()); } } else { ui->inheritAmbiantTextureCheckbox->setDisabled(true); ui->AmbiantTextureLineEdit->setDisabled(true); } - ui->useReflectTexturecheckbox->setChecked(replace = _model->getReplace(row)["reflect"]); + ui->useReflectTexturecheckbox->setChecked(replace = _model->getReplace(_selectedRow)["reflect"]); if (replace) { - ui->inheritReflectTextureCheckbox->setChecked(inherit = _model->getInherit(row)["reflect"]); + ui->inheritReflectTextureCheckbox->setChecked(inherit = _model->getInherit(_selectedRow)["reflect"]); ui->ReflectTextureLineEdit->setEnabled(!inherit); ui->inheritReflectTextureCheckbox->setEnabled(replace); if (!inherit) { - ui->ReflectTextureLineEdit->setText(_model->getMap(row, "reflect").c_str()); + ui->ReflectTextureLineEdit->setText(_model->getMap(_selectedRow, "reflect").c_str()); } } else { ui->inheritReflectTextureCheckbox->setDisabled(true); ui->ReflectTextureLineEdit->setDisabled(true); } } -void ShipTextureReplacementDialog::updateUIFull() +void ShipTextureReplacementDialog::updateUiFull() { util::SignalBlockers blockers(this); const QModelIndex index = ui->TexturesList->selectionModel()->currentIndex(); - row = index.row(); - SCP_map subtypes = _model->getSubtypesForMap(row); + _selectedRow = index.row(); + SCP_map subtypes = _model->getSubtypesForMap(_selectedRow); bool hide = !(subtypes)["misc"]; ui->inheritMiscTextureCheckbox->setHidden(hide); ui->MiscTextureLabel->setHidden(hide); @@ -376,6 +376,6 @@ void ShipTextureReplacementDialog::updateUIFull() ui->ReflectTextureLineEdit->setHidden(hide); ui->useReflectTexturecheckbox->setHidden(hide); resize(QDialog::sizeHint()); - updateUI(); + updateUi(); } } // namespace fso::fred::dialogs diff --git a/qtfred/src/ui/dialogs/ShipEditor/ShipTextureReplacementDialog.h b/qtfred/src/ui/dialogs/ShipEditor/ShipTextureReplacementDialog.h index 7bf8dc541f5..1cbffc474aa 100644 --- a/qtfred/src/ui/dialogs/ShipEditor/ShipTextureReplacementDialog.h +++ b/qtfred/src/ui/dialogs/ShipEditor/ShipTextureReplacementDialog.h @@ -22,7 +22,6 @@ class MapModel : public QAbstractListModel { }; class ShipTextureReplacementDialog : public QDialog { - Q_OBJECT public: @@ -63,13 +62,13 @@ class ShipTextureReplacementDialog : public QDialog { void on_inheritAmbiantTexturecheckbox_toggled(bool); void on_inheritReflectTexturecheckbox_toggled(bool); - private:// NOLINT(readability-redundant-access-specifiers) + private: // NOLINT(readability-redundant-access-specifiers) std::unique_ptr ui; std::unique_ptr _model; EditorViewport* _viewport; - int row = 0; - MapModel* listmodel; - void updateUI(); - void updateUIFull(); + int _selectedRow = 0; + MapModel* _listModel; + void updateUi(); + void updateUiFull(); }; } // namespace fso::fred::dialogs \ No newline at end of file From 25d2b3b1586457e58ee8bffbaf7eae57471196d5 Mon Sep 17 00:00:00 2001 From: Mike Nelson Date: Thu, 7 May 2026 12:40:01 -0500 Subject: [PATCH 2/4] refactor UI to be dynamic --- .../dialogs/ShipTextureReplacementDialog.html | 12 +- .../ShipTextureReplacementDialogModel.cpp | 319 +++++------------ .../ShipTextureReplacementDialogModel.h | 3 - .../ShipTextureReplacementDialog.cpp | 326 ++++-------------- .../ShipEditor/ShipTextureReplacementDialog.h | 35 +- qtfred/ui/ShipTextureReplacementDialog.ui | 171 +-------- 6 files changed, 157 insertions(+), 709 deletions(-) diff --git a/qtfred/help-src/doc/dialogs/ShipTextureReplacementDialog.html b/qtfred/help-src/doc/dialogs/ShipTextureReplacementDialog.html index 5efc835ad20..a462c688520 100644 --- a/qtfred/help-src/doc/dialogs/ShipTextureReplacementDialog.html +++ b/qtfred/help-src/doc/dialogs/ShipTextureReplacementDialog.html @@ -24,11 +24,13 @@

Other Maps

  • Replace - check to enable a replacement for this map type.
  • -
  • Inherit - automatically derives the replacement name by - appending the map type suffix to the main texture replacement name (e.g. - replacing fighter01 with myfighter and inheriting - the normal map produces myfighter-normal). Uncheck to enter the - name manually.
  • +
  • Inherit - when checked (the default), automatically derives + the replacement name by appending the map type suffix to the main texture + replacement name (e.g. replacing fighter01 with + myfighter and inheriting the normal map produces + myfighter-normal). Uncheck to enter a custom replacement name + for just this map type; the name field becomes active when Inherit is + unchecked.

Models that only define a base diffuse texture will show no controls in this section.

diff --git a/qtfred/src/mission/dialogs/ShipEditor/ShipTextureReplacementDialogModel.cpp b/qtfred/src/mission/dialogs/ShipEditor/ShipTextureReplacementDialogModel.cpp index b37aa649e2c..c1b09712b4d 100644 --- a/qtfred/src/mission/dialogs/ShipEditor/ShipTextureReplacementDialogModel.cpp +++ b/qtfred/src/mission/dialogs/ShipEditor/ShipTextureReplacementDialogModel.cpp @@ -3,6 +3,8 @@ #include "mission/object.h" #include "model/model.h" +#include + namespace { const SCP_vector& get_replaceable_texture_types() { @@ -23,6 +25,16 @@ bool is_known_subtexture_type(const SCP_string& type) get_replaceable_texture_types().end(), [&type](const SCP_string& knownType) { return lcase_equal(type, knownType); }); } + +void remove_ship_entries(const char* ship_name) +{ + Fred_texture_replacements.erase( + std::remove_if(Fred_texture_replacements.begin(), Fred_texture_replacements.end(), + [ship_name](const texture_replace& tr) { + return !tr.from_table && !stricmp(tr.ship_name, ship_name); + }), + Fred_texture_replacements.end()); +} } namespace fso::fred::dialogs { @@ -147,7 +159,8 @@ void ShipTextureReplacementDialogModel::initializeData(bool multi) if (is_known_subtexture_type(type)) { _currentTextures[i][type] = newText; _replaceMap[i][type] = true; - _inheritMap[i][type] = (newText == pureName); + _inheritMap[i][type] = !_currentTextures[i]["main"].empty() && + lcase_equal(newText, _currentTextures[i]["main"]); } } else { @@ -206,80 +219,55 @@ void ShipTextureReplacementDialogModel::initSubTypes(polymodel* model, int mapNu bool ShipTextureReplacementDialogModel::apply() { if (query_modified()) { + _mainChanged = false; + + // Remove all existing non-table entries for affected ships before writing new ones. + // This ensures that each save operation is a simple append rather than a remove-and-add, + // which was causing each type's save to clobber the previous type's entry. + if (!_multi) { + remove_ship_entries(Ships[_editor->cur_ship].ship_name); + } else { + object* objp = GET_FIRST(&obj_used_list); + while (objp != END_OF_LIST(&obj_used_list)) { + if (((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) && + (objp->flags[Object::Object_Flags::Marked])) { + remove_ship_entries(Ships[get_ship_from_obj(objp)].ship_name); + } + objp = GET_NEXT(objp); + } + } + for (size_t i = 0; i < getSize(); i++) { if ((!_currentTextures[i]["main"].empty()) && (_currentTextures[i]["main"] != _defaultTextures[i])) { _mainChanged = true; SCP_string name = _currentTextures[i]["main"]; if (testTexture(name)) { - SCP_vector::iterator ii, end; - end = Fred_texture_replacements.end(); if (!_multi) { - end = Fred_texture_replacements.end(); - for (ii = Fred_texture_replacements.begin(); ii != end; ++ii) - { - if (!stricmp(ii->ship_name, Ships[_editor->cur_ship].ship_name)) - { - do { - end--; - } while (end != ii && !stricmp(end->ship_name, Ships[_editor->cur_ship].ship_name)); - if (end == ii) - break; - texture_set(&(*ii), &(*end)); - } - } - - if (end != Fred_texture_replacements.end()) - Fred_texture_replacements.erase(end, Fred_texture_replacements.end()); - - // now put the new entries on the end of the list texture_replace tr; strcpy_s(tr.old_texture, _defaultTextures[i].c_str()); strcpy_s(tr.new_texture, name.c_str()); strcpy_s(tr.ship_name, Ships[_editor->cur_ship].ship_name); tr.new_texture_id = -1; tr.from_table = false; - - // assign to global FRED array Fred_texture_replacements.push_back(tr); _editor->missionChanged(); } else { - object* objp = nullptr; - objp = GET_FIRST(&obj_used_list); + object* objp = GET_FIRST(&obj_used_list); while (objp != END_OF_LIST(&obj_used_list)) { if (((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) && (objp->flags[Object::Object_Flags::Marked])) { Assert((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)); auto shipp = &Ships[get_ship_from_obj(objp)]; - end = Fred_texture_replacements.end(); - for (ii = Fred_texture_replacements.begin(); ii != end; ++ii) - { - if (!stricmp(ii->ship_name, shipp->ship_name)) - { - do { - end--; - } while (end != ii && !stricmp(end->ship_name, shipp->ship_name)); - if (end == ii) - break; - texture_set(&(*ii), &(*end)); - } - } - if (end != Fred_texture_replacements.end()) - Fred_texture_replacements.erase(end, Fred_texture_replacements.end()); - - // now put the new entries on the end of the list texture_replace tr; strcpy_s(tr.old_texture, _defaultTextures[i].c_str()); strcpy_s(tr.new_texture, name.c_str()); strcpy_s(tr.ship_name, shipp->ship_name); tr.new_texture_id = -1; tr.from_table = false; - - // assign to global FRED array Fred_texture_replacements.push_back(tr); _editor->missionChanged(); } - objp = GET_NEXT(objp); } } @@ -307,209 +295,58 @@ bool ShipTextureReplacementDialogModel::apply() void ShipTextureReplacementDialogModel::reject() {} -texture_replace* ShipTextureReplacementDialogModel::texture_set(texture_replace* dest, const texture_replace* src) -{ - dest->new_texture_id = src->new_texture_id; - strcpy_s(dest->ship_name, src->ship_name); - strcpy_s(dest->old_texture, src->old_texture); - strcpy_s(dest->new_texture, src->new_texture); - dest->from_table = src->from_table; - - return dest; -} - void ShipTextureReplacementDialogModel::saveSubMap(size_t index, const SCP_string& type) { - SCP_string fullName; - if (_replaceMap[index][type]) { - if (_inheritMap[index][type]) { - if (_mainChanged) { - if (!_currentTextures[index]["main"].empty()) { - if (_currentTextures[index]["main"] != "invisible") { - fullName = _currentTextures[index]["main"] + "-" + type; - } - else { - fullName = _currentTextures[index]["main"]; - } - if (testTexture(fullName)) { - SCP_vector::iterator ii, end; - end = Fred_texture_replacements.end(); - if (!_multi) { - end = Fred_texture_replacements.end(); - for (ii = Fred_texture_replacements.begin(); ii != end; ++ii) - { - if (!stricmp(ii->ship_name, Ships[_editor->cur_ship].ship_name)) - { - do { - end--; - } while (end != ii && !stricmp(end->ship_name, Ships[_editor->cur_ship].ship_name)); - if (end == ii) - break; - texture_set(&(*ii), &(*end)); - } - } - - if (end != Fred_texture_replacements.end()) - Fred_texture_replacements.erase(end, Fred_texture_replacements.end()); + if (!_replaceMap[index][type]) + return; - // now put the new entries on the end of the list - texture_replace tr; - strcpy_s(tr.old_texture, _defaultTextures[index].c_str()); - strcpy_s(tr.new_texture, fullName.c_str()); - strcpy_s(tr.ship_name, Ships[_editor->cur_ship].ship_name); - tr.new_texture_id = -1; - tr.from_table = false; - - // assign to global FRED array - Fred_texture_replacements.push_back(tr); - _editor->missionChanged(); - } - else { - object* objp = nullptr; - objp = GET_FIRST(&obj_used_list); - while (objp != END_OF_LIST(&obj_used_list)) { - if (((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) && - (objp->flags[Object::Object_Flags::Marked])) { - Assert((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)); - auto shipp = &Ships[get_ship_from_obj(objp)]; - end = Fred_texture_replacements.end(); - for (ii = Fred_texture_replacements.begin(); ii != end; ++ii) - { - if (!stricmp(ii->ship_name, shipp->ship_name)) - { - do { - end--; - } while (end != ii && !stricmp(end->ship_name, shipp->ship_name)); - if (end == ii) - break; - texture_set(&(*ii), &(*end)); - } - } - - if (end != Fred_texture_replacements.end()) - Fred_texture_replacements.erase(end, Fred_texture_replacements.end()); - - // now put the new entries on the end of the list - texture_replace tr; - strcpy_s(tr.old_texture, _defaultTextures[index].c_str()); - strcpy_s(tr.new_texture, fullName.c_str()); - strcpy_s(tr.ship_name, shipp->ship_name); - tr.new_texture_id = -1; - tr.from_table = false; - - // assign to global FRED array - Fred_texture_replacements.push_back(tr); - _editor->missionChanged(); - } - - objp = GET_NEXT(objp); - } - } - } - else { - auto button = _viewport->dialogProvider->showButtonDialog(DialogType::Error, "Missing Texture", "FRED was unable to find %s \n Skipping", - { DialogButton::Ok }); - if (button == DialogButton::Ok) { - return; - } - } - } - } - else { - error_display(0, "Cannot use inherited data without changing the main texture name. Ignoring %s map change.", type.c_str()); - } - } - else { - if (!_currentTextures[index][type].empty()) { - if (_currentTextures[index][type] != "invisible") { - fullName = _currentTextures[index][type] + "-" + type; - } - else { - fullName = _currentTextures[index][type]; - } - if (testTexture(fullName)) { - SCP_vector::iterator ii, end; - end = Fred_texture_replacements.end(); - if (!_multi) { - end = Fred_texture_replacements.end(); - for (ii = Fred_texture_replacements.begin(); ii != end; ++ii) - { - if (!stricmp(ii->ship_name, Ships[_editor->cur_ship].ship_name)) - { - do { - end--; - } while (end != ii && !stricmp(end->ship_name, Ships[_editor->cur_ship].ship_name)); - if (end == ii) - break; - texture_set(&(*ii), &(*end)); - } - } - - if (end != Fred_texture_replacements.end()) - Fred_texture_replacements.erase(end, Fred_texture_replacements.end()); - - // now put the new entries on the end of the list - texture_replace tr; - strcpy_s(tr.old_texture, _defaultTextures[index].c_str()); - strcpy_s(tr.new_texture, fullName.c_str()); - strcpy_s(tr.ship_name, Ships[_editor->cur_ship].ship_name); - tr.new_texture_id = -1; - tr.from_table = false; - - // assign to global FRED array - Fred_texture_replacements.push_back(tr); - _editor->missionChanged(); - } - else { - object* objp = nullptr; - objp = GET_FIRST(&obj_used_list); - while (objp != END_OF_LIST(&obj_used_list)) { - if (((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) && - (objp->flags[Object::Object_Flags::Marked])) { - Assert((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)); - auto shipp = &Ships[get_ship_from_obj(objp)]; - end = Fred_texture_replacements.end(); - for (ii = Fred_texture_replacements.begin(); ii != end; ++ii) - { - if (!stricmp(ii->ship_name, shipp->ship_name)) - { - do { - end--; - } while (end != ii && !stricmp(end->ship_name, shipp->ship_name)); - if (end == ii) - break; - texture_set(&(*ii), &(*end)); - } - } - - if (end != Fred_texture_replacements.end()) - Fred_texture_replacements.erase(end, Fred_texture_replacements.end()); + SCP_string fullName; + if (_inheritMap[index][type]) { + if (!_mainChanged) + return; + if (_currentTextures[index]["main"].empty()) + return; + fullName = (_currentTextures[index]["main"] != "invisible") + ? _currentTextures[index]["main"] + "-" + type + : _currentTextures[index]["main"]; + } else { + if (_currentTextures[index][type].empty()) + return; + fullName = (_currentTextures[index][type] != "invisible") + ? _currentTextures[index][type] + "-" + type + : _currentTextures[index][type]; + } - // now put the new entries on the end of the list - texture_replace tr; - strcpy_s(tr.old_texture, _defaultTextures[index].c_str()); - strcpy_s(tr.new_texture, fullName.c_str()); - strcpy_s(tr.ship_name, shipp->ship_name); - tr.new_texture_id = -1; - tr.from_table = false; + if (!testTexture(fullName)) { + _viewport->dialogProvider->showButtonDialog(DialogType::Error, "Missing Texture", + "FRED was unable to find %s \n Skipping", { DialogButton::Ok }); + return; + } - // assign to global FRED array - Fred_texture_replacements.push_back(tr); - _editor->missionChanged(); - } + // Existing entries for this ship were already removed at the start of apply(), + // so just append the new entry for each affected ship. + auto push_entry = [&](const char* ship_name) { + texture_replace tr; + strcpy_s(tr.old_texture, _defaultTextures[index].c_str()); + strcpy_s(tr.new_texture, fullName.c_str()); + strcpy_s(tr.ship_name, ship_name); + tr.new_texture_id = -1; + tr.from_table = false; + Fred_texture_replacements.push_back(tr); + _editor->missionChanged(); + }; - objp = GET_NEXT(objp); - } - } - } - else { - auto button = _viewport->dialogProvider->showButtonDialog(DialogType::Error, "Missing Texture", "FRED was unable to find %s \n Skipping", - { DialogButton::Ok }); - if (button == DialogButton::Ok) { - return; - } - } + if (!_multi) { + push_entry(Ships[_editor->cur_ship].ship_name); + } else { + object* objp = GET_FIRST(&obj_used_list); + while (objp != END_OF_LIST(&obj_used_list)) { + if (((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) && + (objp->flags[Object::Object_Flags::Marked])) { + Assert((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)); + push_entry(Ships[get_ship_from_obj(objp)].ship_name); } + objp = GET_NEXT(objp); } } } diff --git a/qtfred/src/mission/dialogs/ShipEditor/ShipTextureReplacementDialogModel.h b/qtfred/src/mission/dialogs/ShipEditor/ShipTextureReplacementDialogModel.h index 90524db4dcb..518a6fdc9fa 100644 --- a/qtfred/src/mission/dialogs/ShipEditor/ShipTextureReplacementDialogModel.h +++ b/qtfred/src/mission/dialogs/ShipEditor/ShipTextureReplacementDialogModel.h @@ -4,8 +4,6 @@ namespace fso::fred::dialogs { -constexpr auto NUM__SUBTEXTURE_TYPES = 7; - class ShipTextureReplacementDialogModel : public AbstractDialogModel { Q_OBJECT public: @@ -31,7 +29,6 @@ class ShipTextureReplacementDialogModel : public AbstractDialogModel { void initSubTypes(polymodel* model, int mapNum); void saveSubMap(size_t index, const SCP_string& type); static bool testTexture(const SCP_string& name); - static texture_replace* texture_set(texture_replace* dest, const texture_replace* src); bool _multi; SCP_vector> _subTypesAvailable; diff --git a/qtfred/src/ui/dialogs/ShipEditor/ShipTextureReplacementDialog.cpp b/qtfred/src/ui/dialogs/ShipEditor/ShipTextureReplacementDialog.cpp index f4be1dfb7a3..b44494bdab6 100644 --- a/qtfred/src/ui/dialogs/ShipEditor/ShipTextureReplacementDialog.cpp +++ b/qtfred/src/ui/dialogs/ShipEditor/ShipTextureReplacementDialog.cpp @@ -4,9 +4,11 @@ #include #include +#include #include #include +#include namespace fso::fred::dialogs { // Model for list view, Should really have its own file but its not big enught for me to bother. @@ -41,13 +43,42 @@ ShipTextureReplacementDialog::ShipTextureReplacementDialog(QDialog* parent, Edit ui->setupUi(this); ui->newTextureLineEdit->setMaxLength(MAX_FILENAME_LEN - 1); - ui->AmbiantTextureLineEdit->setMaxLength(MAX_FILENAME_LEN - 1); - ui->MiscTextureLineEdit->setMaxLength(MAX_FILENAME_LEN - 1); - ui->ShineTextureLineEdit->setMaxLength(MAX_FILENAME_LEN - 1); - ui->HeightTextureLineEdit->setMaxLength(MAX_FILENAME_LEN - 1); - ui->GlowTextureLineEdit->setMaxLength(MAX_FILENAME_LEN - 1); - ui->NormalTextureLineEdit->setMaxLength(MAX_FILENAME_LEN - 1); - ui->ReflectTextureLineEdit->setMaxLength(MAX_FILENAME_LEN - 1); + + auto* subLayout = qobject_cast(ui->subTextureBox->layout()); + int gridRow = 0; + for (const auto& [tmType, suffix] : MODEL_TEXTURE_SUFFIXES) { + SCP_string typeKey = suffix.substr(1); + SCP_string displayStr = typeKey; + if (!displayStr.empty()) + displayStr[0] = static_cast(std::toupper(static_cast(displayStr[0]))); + + auto* label = new QLabel(displayStr.c_str(), this); + auto* useBox = new QCheckBox(tr("Replace"), this); + auto* inheritBox = new QCheckBox(tr("Inherit"), this); + auto* lineEdit = new QLineEdit(this); + lineEdit->setMaxLength(MAX_FILENAME_LEN - 1); + + subLayout->addWidget(label, gridRow, 0); + subLayout->addWidget(useBox, gridRow, 1); + subLayout->addWidget(inheritBox, gridRow, 2); + subLayout->addWidget(lineEdit, gridRow, 3); + + connect(lineEdit, &QLineEdit::editingFinished, this, [this, typeKey, lineEdit]() { + SCP_string newText; + if (!lineEdit->text().isEmpty()) + newText = lineEdit->text().toUtf8().constData(); + _model->setMap(_selectedRow, typeKey, newText); + }); + connect(useBox, &QCheckBox::toggled, this, [this, typeKey](bool state) { + _model->setReplace(_selectedRow, typeKey, state); + }); + connect(inheritBox, &QCheckBox::toggled, this, [this, typeKey](bool state) { + _model->setInherit(_selectedRow, typeKey, state); + }); + + _textureRows[typeKey] = { label, useBox, inheritBox, lineEdit }; + ++gridRow; + } _listModel = new MapModel(_model.get(), this); ui->TexturesList->setModel(_listModel); @@ -103,235 +134,26 @@ void ShipTextureReplacementDialog::on_newTextureLineEdit_editingFinished() _model->setMap(_selectedRow, "main", newText); } -void ShipTextureReplacementDialog::on_MiscTextureLineEdit_editingFinished() -{ - SCP_string newText; - if (!ui->MiscTextureLineEdit->text().isEmpty()) { - newText = ui->MiscTextureLineEdit->text().toUtf8().constData(); - _model->setMap(_selectedRow, "misc", newText); - } -} - -void ShipTextureReplacementDialog::on_GlowTextureLineEdit_editingFinished() -{ - SCP_string newText; - if (!ui->GlowTextureLineEdit->text().isEmpty()) { - newText = ui->GlowTextureLineEdit->text().toUtf8().constData(); - _model->setMap(_selectedRow, "glow", newText); - } -} - -void ShipTextureReplacementDialog::on_ShineTextureLineEdit_editingFinished() -{ - SCP_string newText; - if (!ui->ShineTextureLineEdit->text().isEmpty()) { - newText = ui->ShineTextureLineEdit->text().toUtf8().constData(); - _model->setMap(_selectedRow, "shine", newText); - } -} - -void ShipTextureReplacementDialog::on_NormalTextureLineEdit_editingFinished() -{ - SCP_string newText; - if (!ui->NormalTextureLineEdit->text().isEmpty()) { - newText = ui->NormalTextureLineEdit->text().toUtf8().constData(); - _model->setMap(_selectedRow, "normal", newText); - } -} - -void ShipTextureReplacementDialog::on_HeightTextureLineEdit_editingFinished() -{ - SCP_string newText; - if (!ui->HeightTextureLineEdit->text().isEmpty()) { - newText = ui->HeightTextureLineEdit->text().toUtf8().constData(); - _model->setMap(_selectedRow, "height", newText); - } -} - -void ShipTextureReplacementDialog::on_AmbiantTextureLineEdit_editingFinished() -{ - SCP_string newText; - if (!ui->AmbiantTextureLineEdit->text().isEmpty()) { - newText = ui->AmbiantTextureLineEdit->text().toUtf8().constData(); - _model->setMap(_selectedRow, "ao", newText); - } -} - -void ShipTextureReplacementDialog::on_ReflectTextureLineEdit_editingFinished() -{ - SCP_string newText; - if (!ui->ReflectTextureLineEdit->text().isEmpty()) { - newText = ui->ReflectTextureLineEdit->text().toUtf8().constData(); - _model->setMap(_selectedRow, "reflect", newText); - } -} - -void ShipTextureReplacementDialog::on_useMiscTexturecheckbox_toggled(bool state) -{ - _model->setReplace(_selectedRow, "misc", state); -} - -void ShipTextureReplacementDialog::on_useGlowTexturecheckbox_toggled(bool state) -{ - _model->setReplace(_selectedRow, "glow", state); -} - -void ShipTextureReplacementDialog::on_useShineTexturecheckbox_toggled(bool state) -{ - _model->setReplace(_selectedRow, "shine", state); -} - -void ShipTextureReplacementDialog::on_useNormalTexturecheckbox_toggled(bool state) -{ - _model->setReplace(_selectedRow, "normal", state); -} - -void ShipTextureReplacementDialog::on_useHeightTexturecheckbox_toggled(bool state) -{ - _model->setReplace(_selectedRow, "height", state); -} - -void ShipTextureReplacementDialog::on_useAmbiantTexturecheckbox_toggled(bool state) -{ - _model->setReplace(_selectedRow, "ao", state); -} - -void ShipTextureReplacementDialog::on_useReflectTexturecheckbox_toggled(bool state) -{ - _model->setReplace(_selectedRow, "reflect", state); -} - -void ShipTextureReplacementDialog::on_inheritMiscTexturecheckbox_toggled(bool state) -{ - _model->setInherit(_selectedRow, "misc", state); -} - -void ShipTextureReplacementDialog::on_inheritGlowTexturecheckbox_toggled(bool state) -{ - _model->setInherit(_selectedRow, "glow", state); -} - -void ShipTextureReplacementDialog::on_inheritShineTexturecheckbox_toggled(bool state) -{ - _model->setInherit(_selectedRow, "shine", state); -} - -void ShipTextureReplacementDialog::on_inheritNormalTexturecheckbox_toggled(bool state) -{ - _model->setInherit(_selectedRow, "normal", state); -} - -void ShipTextureReplacementDialog::on_inheritHeightTexturecheckbox_toggled(bool state) -{ - _model->setInherit(_selectedRow, "height", state); -} - -void ShipTextureReplacementDialog::on_inheritAmbiantTexturecheckbox_toggled(bool state) -{ - _model->setInherit(_selectedRow, "ao", state); -} - -void ShipTextureReplacementDialog::on_inheritReflectTexturecheckbox_toggled(bool state) -{ - _model->setInherit(_selectedRow, "reflect", state); -} - void ShipTextureReplacementDialog::updateUi() { util::SignalBlockers blockers(this); ui->newTextureLineEdit->setText(_model->getMap(_selectedRow, "main").c_str()); - bool replace; - bool inherit; - ui->useMiscTexturecheckbox->setChecked(replace = _model->getReplace(_selectedRow)["misc"]); - if (replace) { - ui->inheritMiscTextureCheckbox->setChecked(inherit = _model->getInherit(_selectedRow)["misc"]); - ui->inheritMiscTextureCheckbox->setEnabled(replace); - ui->MiscTextureLineEdit->setEnabled(!inherit); - if (!inherit) { - ui->MiscTextureLineEdit->setText(_model->getMap(_selectedRow, "misc").c_str()); - } - } else { - ui->inheritMiscTextureCheckbox->setDisabled(true); - ui->MiscTextureLineEdit->setDisabled(true); - } - - ui->useShineTexturecheckbox->setChecked(replace = _model->getReplace(_selectedRow)["shine"]); - if (replace) { - ui->inheritShineTextureCheckbox->setChecked(inherit = _model->getInherit(_selectedRow)["shine"]); - ui->inheritShineTextureCheckbox->setEnabled(replace); - - ui->ShineTextureLineEdit->setEnabled(!inherit); - if (!inherit) { - ui->ShineTextureLineEdit->setText(_model->getMap(_selectedRow, "shine").c_str()); - } - } else { - ui->inheritShineTextureCheckbox->setDisabled(true); - ui->ShineTextureLineEdit->setDisabled(true); - } - - ui->useGlowTexturecheckbox->setChecked(replace = _model->getReplace(_selectedRow)["glow"]); - if (replace) { - ui->inheritGlowTextureCheckbox->setEnabled(replace); - ui->inheritGlowTextureCheckbox->setChecked(inherit = _model->getInherit(_selectedRow)["glow"]); - ui->GlowTextureLineEdit->setEnabled(!inherit); - if (!inherit) { - ui->GlowTextureLineEdit->setText(_model->getMap(_selectedRow, "glow").c_str()); - } - } else { - ui->inheritGlowTextureCheckbox->setDisabled(true); - ui->GlowTextureLineEdit->setDisabled(true); - } - - ui->useNormalTexturecheckbox->setChecked(replace = _model->getReplace(_selectedRow)["normal"]); - if (replace) { - ui->inheritNormalTextureCheckbox->setChecked(inherit = _model->getInherit(_selectedRow)["normal"]); - ui->inheritNormalTextureCheckbox->setEnabled(replace); - ui->NormalTextureLineEdit->setEnabled(!inherit); - if (!inherit) { - ui->NormalTextureLineEdit->setText(_model->getMap(_selectedRow, "normal").c_str()); - } - } else { - ui->inheritNormalTextureCheckbox->setDisabled(true); - ui->NormalTextureLineEdit->setDisabled(true); - } - - ui->useHeightTexturecheckbox->setChecked(replace = _model->getReplace(_selectedRow)["height"]); - if (replace) { - ui->inheritHeightTextureCheckbox->setChecked(inherit = _model->getInherit(_selectedRow)["height"]); - ui->inheritHeightTextureCheckbox->setEnabled(replace); - ui->HeightTextureLineEdit->setEnabled(!inherit); - if (!inherit) { - ui->HeightTextureLineEdit->setText(_model->getMap(_selectedRow, "height").c_str()); - } - } else { - ui->inheritHeightTextureCheckbox->setDisabled(true); - ui->HeightTextureLineEdit->setDisabled(true); - } - - ui->useAmbiantTexturecheckbox->setChecked(replace = _model->getReplace(_selectedRow)["ao"]); - if (replace) { - ui->inheritAmbiantTextureCheckbox->setChecked(inherit = _model->getInherit(_selectedRow)["ao"]); - ui->AmbiantTextureLineEdit->setEnabled(!inherit); - ui->inheritAmbiantTextureCheckbox->setEnabled(replace); - if (!inherit) { - ui->AmbiantTextureLineEdit->setText(_model->getMap(_selectedRow, "ao").c_str()); - } - } else { - ui->inheritAmbiantTextureCheckbox->setDisabled(true); - ui->AmbiantTextureLineEdit->setDisabled(true); - } - - ui->useReflectTexturecheckbox->setChecked(replace = _model->getReplace(_selectedRow)["reflect"]); - if (replace) { - ui->inheritReflectTextureCheckbox->setChecked(inherit = _model->getInherit(_selectedRow)["reflect"]); - ui->ReflectTextureLineEdit->setEnabled(!inherit); - ui->inheritReflectTextureCheckbox->setEnabled(replace); - if (!inherit) { - ui->ReflectTextureLineEdit->setText(_model->getMap(_selectedRow, "reflect").c_str()); + for (auto& [type, widgets] : _textureRows) { + bool replace = _model->getReplace(_selectedRow)[type]; + widgets.useCheckbox->setChecked(replace); + if (replace) { + bool inherit = _model->getInherit(_selectedRow)[type]; + widgets.inheritCheckbox->setEnabled(true); + widgets.inheritCheckbox->setChecked(inherit); + widgets.lineEdit->setEnabled(!inherit); + if (inherit) + widgets.lineEdit->setText(_model->getMap(_selectedRow, "main").c_str()); + else + widgets.lineEdit->setText(_model->getMap(_selectedRow, type).c_str()); + } else { + widgets.inheritCheckbox->setDisabled(true); + widgets.lineEdit->setDisabled(true); } - } else { - ui->inheritReflectTextureCheckbox->setDisabled(true); - ui->ReflectTextureLineEdit->setDisabled(true); } } void ShipTextureReplacementDialog::updateUiFull() @@ -340,41 +162,13 @@ void ShipTextureReplacementDialog::updateUiFull() const QModelIndex index = ui->TexturesList->selectionModel()->currentIndex(); _selectedRow = index.row(); SCP_map subtypes = _model->getSubtypesForMap(_selectedRow); - bool hide = !(subtypes)["misc"]; - ui->inheritMiscTextureCheckbox->setHidden(hide); - ui->MiscTextureLabel->setHidden(hide); - ui->MiscTextureLineEdit->setHidden(hide); - ui->useMiscTexturecheckbox->setHidden(hide); - hide = !(subtypes)["shine"]; - ui->inheritShineTextureCheckbox->setHidden(hide); - ui->ShineTextureLabel->setHidden(hide); - ui->ShineTextureLineEdit->setHidden(hide); - ui->useShineTexturecheckbox->setHidden(hide); - hide = !(subtypes)["glow"]; - ui->inheritGlowTextureCheckbox->setHidden(hide); - ui->GlowTextureLabel->setHidden(hide); - ui->GlowTextureLineEdit->setHidden(hide); - ui->useGlowTexturecheckbox->setHidden(hide); - hide = !(subtypes)["normal"]; - ui->inheritNormalTextureCheckbox->setHidden(hide); - ui->NormalTextureLabel->setHidden(hide); - ui->NormalTextureLineEdit->setHidden(hide); - ui->useNormalTexturecheckbox->setHidden(hide); - hide = !(subtypes)["height"]; - ui->inheritHeightTextureCheckbox->setHidden(hide); - ui->HeightTextureLabel->setHidden(hide); - ui->HeightTextureLineEdit->setHidden(hide); - ui->useHeightTexturecheckbox->setHidden(hide); - hide = !(subtypes)["ao"]; - ui->inheritAmbiantTextureCheckbox->setHidden(hide); - ui->AmbiantTextureLabel->setHidden(hide); - ui->AmbiantTextureLineEdit->setHidden(hide); - ui->useAmbiantTexturecheckbox->setHidden(hide); - hide = !(subtypes)["reflect"]; - ui->inheritReflectTextureCheckbox->setHidden(hide); - ui->ReflectTextureLabel->setHidden(hide); - ui->ReflectTextureLineEdit->setHidden(hide); - ui->useReflectTexturecheckbox->setHidden(hide); + for (auto& [type, widgets] : _textureRows) { + bool hide = !subtypes[type]; + widgets.label->setHidden(hide); + widgets.useCheckbox->setHidden(hide); + widgets.inheritCheckbox->setHidden(hide); + widgets.lineEdit->setHidden(hide); + } resize(QDialog::sizeHint()); updateUi(); } diff --git a/qtfred/src/ui/dialogs/ShipEditor/ShipTextureReplacementDialog.h b/qtfred/src/ui/dialogs/ShipEditor/ShipTextureReplacementDialog.h index 1cbffc474aa..5fc056ad874 100644 --- a/qtfred/src/ui/dialogs/ShipEditor/ShipTextureReplacementDialog.h +++ b/qtfred/src/ui/dialogs/ShipEditor/ShipTextureReplacementDialog.h @@ -3,7 +3,10 @@ #include #include +#include #include +#include +#include #include namespace fso::fred::dialogs { @@ -36,38 +39,22 @@ class ShipTextureReplacementDialog : public QDialog { private slots: void on_buttonBox_accepted(); void on_buttonBox_rejected(); - void on_newTextureLineEdit_editingFinished(); - void on_MiscTextureLineEdit_editingFinished(); - void on_GlowTextureLineEdit_editingFinished(); - void on_ShineTextureLineEdit_editingFinished(); - void on_NormalTextureLineEdit_editingFinished(); - void on_HeightTextureLineEdit_editingFinished(); - void on_AmbiantTextureLineEdit_editingFinished(); - void on_ReflectTextureLineEdit_editingFinished(); - - void on_useMiscTexturecheckbox_toggled(bool); - void on_useGlowTexturecheckbox_toggled(bool); - void on_useShineTexturecheckbox_toggled(bool); - void on_useNormalTexturecheckbox_toggled(bool); - void on_useHeightTexturecheckbox_toggled(bool); - void on_useAmbiantTexturecheckbox_toggled(bool); - void on_useReflectTexturecheckbox_toggled(bool); - - void on_inheritMiscTexturecheckbox_toggled(bool); - void on_inheritGlowTexturecheckbox_toggled(bool); - void on_inheritShineTexturecheckbox_toggled(bool); - void on_inheritNormalTexturecheckbox_toggled(bool); - void on_inheritHeightTexturecheckbox_toggled(bool); - void on_inheritAmbiantTexturecheckbox_toggled(bool); - void on_inheritReflectTexturecheckbox_toggled(bool); private: // NOLINT(readability-redundant-access-specifiers) + struct TextureTypeRow { + QLabel* label; + QCheckBox* useCheckbox; + QCheckBox* inheritCheckbox; + QLineEdit* lineEdit; + }; + std::unique_ptr ui; std::unique_ptr _model; EditorViewport* _viewport; int _selectedRow = 0; MapModel* _listModel; + SCP_map _textureRows; void updateUi(); void updateUiFull(); }; diff --git a/qtfred/ui/ShipTextureReplacementDialog.ui b/qtfred/ui/ShipTextureReplacementDialog.ui index cf4ba715e19..50abad213be 100644 --- a/qtfred/ui/ShipTextureReplacementDialog.ui +++ b/qtfred/ui/ShipTextureReplacementDialog.ui @@ -50,176 +50,7 @@ Other Maps - - - - - Replace - - - - - - - - - - - - - - - - - - - Inherit - - - - - - - - - - Shine - - - - - - - Height - - - - - - - Misc - - - - - - - Replace - - - - - - - Replace - - - - - - - Glow - - - - - - - Inherit - - - - - - - Replace - - - - - - - Inherit - - - - - - - Inherit - - - - - - - Inherit - - - - - - - Replace - - - - - - - Normal - - - - - - - Inherit - - - - - - - - - - Replace - - - - - - - AO - - - - - - - Reflect - - - - - - - Replace - - - - - - - Inherit - - - - - - - + From 251b2d0fc21fe54db7e872d33f94ea6f040d384c Mon Sep 17 00:00:00 2001 From: Mike Nelson Date: Thu, 7 May 2026 13:17:30 -0500 Subject: [PATCH 3/4] properly save textures and load them on apply --- .../ShipTextureReplacementDialogModel.cpp | 84 ++++++++++++++++++- 1 file changed, 83 insertions(+), 1 deletion(-) diff --git a/qtfred/src/mission/dialogs/ShipEditor/ShipTextureReplacementDialogModel.cpp b/qtfred/src/mission/dialogs/ShipEditor/ShipTextureReplacementDialogModel.cpp index c1b09712b4d..189760f2f5c 100644 --- a/qtfred/src/mission/dialogs/ShipEditor/ShipTextureReplacementDialogModel.cpp +++ b/qtfred/src/mission/dialogs/ShipEditor/ShipTextureReplacementDialogModel.cpp @@ -286,6 +286,88 @@ bool ShipTextureReplacementDialogModel::apply() _editor->missionChanged(); } _editor->missionChanged(); + + // Update each affected ship's pmi so the viewport reflects the new textures immediately. + // We cannot use ship::apply_replacement_textures here because all our Fred_texture_replacements + // entries share old_texture = base name, so FindTexture always resolves to TM_BASE_TYPE and + // sub-type entries would overwrite the base slot. Instead, write slots directly using the + // TM_* indices from MODEL_TEXTURE_SUFFIXES. + auto load_tex = [](const SCP_string& name) -> int { + int id = bm_load(name); + if (id < 0) { + int nf, fps; + id = bm_load_animation(name.c_str(), &nf, &fps, nullptr, nullptr, false, true); + } + return id; + }; + + auto apply_to_ship = [&](int ship_index) { + auto& shipp = Ships[ship_index]; + auto* pmi = model_get_instance(shipp.model_instance_num); + auto* pm = model_get(Ship_info[shipp.ship_info_index].model_num); + + pmi->texture_replace = std::make_shared(); + + for (size_t i = 0; i < getSize(); i++) { + if (_defaultTextures[i].empty()) + continue; + + // Find which texture group owns this base texture. + int groupIdx = -1; + for (int j = 0; j < pm->n_textures; j++) { + if (pm->maps[j].FindTexture(_defaultTextures[i].c_str()) >= 0) { + groupIdx = j; + break; + } + } + if (groupIdx < 0) + continue; + + // Main (base) slot. + const SCP_string& mainName = _currentTextures[i].at("main"); + if (!mainName.empty() && !lcase_equal(mainName, _defaultTextures[i])) { + int id = load_tex(mainName); + if (id >= 0) + (*pmi->texture_replace)[groupIdx * TM_NUM_TYPES + TM_BASE_TYPE] = id; + } + + // Sub-type slots. + for (const auto& [tmType, suffix] : MODEL_TEXTURE_SUFFIXES) { + const SCP_string typeKey = suffix.substr(1); + if (!_replaceMap[i].at(typeKey)) + continue; + + SCP_string fullName; + if (_inheritMap[i].at(typeKey)) { + if (mainName.empty() || lcase_equal(mainName, _defaultTextures[i])) + continue; + fullName = (mainName != "invisible") ? mainName + suffix : mainName; + } else { + const SCP_string& typeName = _currentTextures[i].at(typeKey); + if (typeName.empty()) + continue; + fullName = (typeName != "invisible") ? typeName + suffix : typeName; + } + + int id = load_tex(fullName); + if (id >= 0) + (*pmi->texture_replace)[groupIdx * TM_NUM_TYPES + tmType] = id; + } + } + }; + + if (!_multi) { + apply_to_ship(_editor->cur_ship); + } else { + object* objp = GET_FIRST(&obj_used_list); + while (objp != END_OF_LIST(&obj_used_list)) { + if (((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) && + objp->flags[Object::Object_Flags::Marked]) + apply_to_ship(get_ship_from_obj(objp)); + objp = GET_NEXT(objp); + } + } + return true; } else { @@ -327,7 +409,7 @@ void ShipTextureReplacementDialogModel::saveSubMap(size_t index, const SCP_strin // so just append the new entry for each affected ship. auto push_entry = [&](const char* ship_name) { texture_replace tr; - strcpy_s(tr.old_texture, _defaultTextures[index].c_str()); + strcpy_s(tr.old_texture, (_defaultTextures[index] + "-" + type).c_str()); strcpy_s(tr.new_texture, fullName.c_str()); strcpy_s(tr.ship_name, ship_name); tr.new_texture_id = -1; From a53765aad98adb2e57ca06434c7cd6d312e4e427 Mon Sep 17 00:00:00 2001 From: Mike Nelson Date: Thu, 7 May 2026 13:39:05 -0500 Subject: [PATCH 4/4] clang --- .../dialogs/ShipEditor/ShipTextureReplacementDialogModel.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qtfred/src/mission/dialogs/ShipEditor/ShipTextureReplacementDialogModel.cpp b/qtfred/src/mission/dialogs/ShipEditor/ShipTextureReplacementDialogModel.cpp index 189760f2f5c..6514ac483a1 100644 --- a/qtfred/src/mission/dialogs/ShipEditor/ShipTextureReplacementDialogModel.cpp +++ b/qtfred/src/mission/dialogs/ShipEditor/ShipTextureReplacementDialogModel.cpp @@ -257,7 +257,7 @@ bool ShipTextureReplacementDialogModel::apply() while (objp != END_OF_LIST(&obj_used_list)) { if (((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) && (objp->flags[Object::Object_Flags::Marked])) { - Assert((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)); + Assertion((objp->type == OBJ_SHIP) || (objp->type == OBJ_START), "Expected ship or start object"); // NOLINT(readability-simplify-boolean-expr) auto shipp = &Ships[get_ship_from_obj(objp)]; texture_replace tr; strcpy_s(tr.old_texture, _defaultTextures[i].c_str()); @@ -425,7 +425,7 @@ void ShipTextureReplacementDialogModel::saveSubMap(size_t index, const SCP_strin while (objp != END_OF_LIST(&obj_used_list)) { if (((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) && (objp->flags[Object::Object_Flags::Marked])) { - Assert((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)); + Assertion((objp->type == OBJ_SHIP) || (objp->type == OBJ_START), "Expected ship or start object"); // NOLINT(readability-simplify-boolean-expr) push_entry(Ships[get_ship_from_obj(objp)].ship_name); } objp = GET_NEXT(objp);