|
2 | 2 |
|
3 | 3 | #include "cfile/cfile.h" |
4 | 4 | #include "mission/missionparse.h" |
5 | | -#include "../src/mission/missionsave.h" |
| 5 | +#include "missioneditor/campaignsave.h" |
6 | 6 | #include "parse/sexp.h" |
7 | 7 | #include "ship/ship.h" |
8 | 8 | #include "weapon/weapon.h" |
@@ -51,7 +51,7 @@ void CampaignEditorDialogModel::initializeData(const char* filename) |
51 | 51 | // Copy simple properties from the global Campaign struct |
52 | 52 | m_campaign_filename = Campaign.filename; |
53 | 53 | m_campaign_name = Campaign.name; |
54 | | - m_campaign_descr = Campaign.desc ? Campaign.desc : ""; |
| 54 | + m_campaign_descr = Campaign.description; |
55 | 55 | m_campaign_type = Campaign.type; |
56 | 56 | m_num_players = Campaign.num_players; |
57 | 57 | m_flags = Campaign.flags; |
@@ -256,7 +256,7 @@ void CampaignEditorDialogModel::commitWorkingCopyToGlobal() |
256 | 256 |
|
257 | 257 | // Copy simple properties |
258 | 258 | strcpy_s(Campaign.name, m_campaign_name.c_str()); |
259 | | - Campaign.desc = m_campaign_descr.empty() ? nullptr : strdup(m_campaign_descr.c_str()); |
| 259 | + Campaign.description = m_campaign_descr; |
260 | 260 | Campaign.type = m_campaign_type; |
261 | 261 | Campaign.num_players = m_num_players; |
262 | 262 | Campaign.flags = m_flags; |
@@ -461,19 +461,83 @@ void CampaignEditorDialogModel::saveCampaign(const SCP_string& filename) |
461 | 461 | // Copy our working data to the global Campaign struct. |
462 | 462 | commitWorkingCopyToGlobal(); |
463 | 463 |
|
464 | | - // Call the global save function. |
465 | | - CFred_mission_save mission_saver; |
466 | | - if (mission_saver.save_campaign_file(target_filename.c_str())) { |
467 | | - // Save failed, clean up the global. |
468 | | - clearCampaignGlobal(); |
469 | | - return; |
| 464 | + Fred_campaign_save save; |
| 465 | + |
| 466 | + // This if/else is not strictly necessary as the underlying enum values match |
| 467 | + // the Mission_save_format values but it is clearer to read and more robust against |
| 468 | + // future changes. |
| 469 | + if (m_save_format == CampaignFormat::Retail) { |
| 470 | + save.set_save_format(MissionFormat::RETAIL); |
| 471 | + } else if (m_save_format == CampaignFormat::CompatibilityMode) { |
| 472 | + save.set_save_format(MissionFormat::COMPATIBILITY_MODE); |
| 473 | + } else { |
| 474 | + save.set_save_format(MissionFormat::STANDARD); |
470 | 475 | } |
471 | 476 |
|
472 | | - // On success, update our internal state. |
473 | | - modify(m_campaign_filename, target_filename); |
| 477 | + // Create a lookup map for mission indices by filename for efficient lookup. |
| 478 | + std::map<SCP_string, int> mission_indices; |
| 479 | + for (int i = 0; i < static_cast<int>(m_missions.size()); ++i) { |
| 480 | + mission_indices[m_missions[i].filename] = i; |
| 481 | + } |
474 | 482 |
|
475 | | - // Clean up the global struct now that the save is complete. |
476 | | - clearCampaignGlobal(); |
| 483 | + SCP_vector<campaign_link> links; |
| 484 | + // Iterate through each mission to find its outgoing branches. |
| 485 | + for (int i = 0; i < static_cast<int>(m_missions.size()); ++i) { |
| 486 | + const auto& mission = m_missions[i]; |
| 487 | + |
| 488 | + // Iterate through each branch of the current mission. |
| 489 | + for (const auto& branch : mission.branches) { |
| 490 | + |
| 491 | + // Find the 'to' mission index using our lookup map. |
| 492 | + int to_index = -1; |
| 493 | + if (!branch.next_mission_name.empty()) { |
| 494 | + auto it = mission_indices.find(branch.next_mission_name); |
| 495 | + if (it != mission_indices.end()) { |
| 496 | + to_index = it->second; |
| 497 | + } |
| 498 | + } |
| 499 | + |
| 500 | + campaign_link link; |
| 501 | + link.from = i; |
| 502 | + link.to = to_index; |
| 503 | + link.sexp = m_tree_ops.saveSexp(branch.sexp_formula); |
| 504 | + link.node = branch.sexp_formula; |
| 505 | + link.is_mission_loop = branch.is_loop; |
| 506 | + link.is_mission_fork = branch.is_fork; |
| 507 | + |
| 508 | + // The descriptive text fields only apply to special (loop/fork) branches. |
| 509 | + if (branch.is_loop || branch.is_fork) { |
| 510 | + link.mission_branch_txt = |
| 511 | + branch.loop_description.empty() ? nullptr : const_cast<char*>(branch.loop_description.c_str()); |
| 512 | + link.mission_branch_brief_anim = |
| 513 | + branch.loop_briefing_anim.empty() ? nullptr : const_cast<char*>(branch.loop_briefing_anim.c_str()); |
| 514 | + link.mission_branch_brief_sound = branch.loop_briefing_sound.empty() |
| 515 | + ? nullptr |
| 516 | + : const_cast<char*>(branch.loop_briefing_sound.c_str()); |
| 517 | + } else { |
| 518 | + link.mission_branch_txt = nullptr; |
| 519 | + link.mission_branch_brief_anim = nullptr; |
| 520 | + link.mission_branch_brief_sound = nullptr; |
| 521 | + } |
| 522 | + |
| 523 | + links.emplace_back(link); |
| 524 | + } |
| 525 | + } |
| 526 | + |
| 527 | + bool failure = save.save_campaign_file(target_filename.c_str(), links); |
| 528 | + |
| 529 | + if (failure) { |
| 530 | + _viewport->dialogProvider->showButtonDialog(DialogType::Error, |
| 531 | + "Save Error", |
| 532 | + "An error occurred while saving the campaign.", |
| 533 | + {DialogButton::Ok}); |
| 534 | + }else{ |
| 535 | + // On success, update our internal state. |
| 536 | + modify(m_campaign_filename, target_filename); |
| 537 | + |
| 538 | + // Clean up the global struct now that the save is complete. |
| 539 | + clearCampaignGlobal(); |
| 540 | + } |
477 | 541 | } |
478 | 542 |
|
479 | 543 | bool CampaignEditorDialogModel::checkValidity() |
|
0 commit comments