mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-07 22:56:30 -10:00
Convert the allowed-ships and allowed-weapons lists to sets, rather than naive vectors that stored a true/false for every class. Since these are ordered sets, iteration occurs in the same sequence as iteration of `Ship_info` or `Weapon_info`. Associated data structures such as `Granted_ships` and `Granted_weapons` are converted as well. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
338 lines
9.2 KiB
C++
338 lines
9.2 KiB
C++
#include "campaignsave.h"
|
|
|
|
#include "globalincs/linklist.h"
|
|
#include "globalincs/version.h"
|
|
|
|
#include "mission/missioncampaign.h"
|
|
#include "parse/parselo.h"
|
|
|
|
#include <freespace.h>
|
|
|
|
// Bodies copied from the old implementations in missionsave.cpp,
|
|
// unchanged except for the new class name and include.
|
|
int Fred_campaign_save::save_campaign_file(const char* pathname, const SCP_vector<campaign_link>& links)
|
|
{
|
|
// This is original FRED code. These were moved to the call sites as the data should be fully
|
|
// prepared before calling this function.
|
|
// TODO check QtFRED's tree is sorted and saved before calling this function.
|
|
// Campaign_tree_formp->save_tree(); // flush all changes so they get saved.
|
|
// Campaign_tree_viewp->sort_elements();
|
|
|
|
reset_parse();
|
|
raw_ptr = Parse_text_raw;
|
|
fred_parse_flag = 0;
|
|
|
|
pathname = cf_add_ext(pathname, FS_CAMPAIGN_FILE_EXT);
|
|
fp = cfopen(pathname, "wt", CF_TYPE_MISSIONS);
|
|
if (!fp) {
|
|
nprintf(("Error", "Can't open campaign file to save.\n"));
|
|
return -1;
|
|
}
|
|
|
|
required_string_fred("$Name:");
|
|
parse_comments(0);
|
|
fout_ext(" ", "%s", Campaign.name);
|
|
|
|
// TODO make type an enum to clean this up
|
|
Assert((Campaign.type >= 0) && (Campaign.type < MAX_CAMPAIGN_TYPES));
|
|
required_string_fred("$Type:");
|
|
parse_comments();
|
|
fout(" %s", campaign_types[Campaign.type]);
|
|
|
|
// XSTR
|
|
if (!Campaign.description.empty()) {
|
|
required_string_fred("+Description:");
|
|
parse_comments();
|
|
fout_ext("\n", "%s", Campaign.description.c_str());
|
|
fout("\n$end_multi_text");
|
|
}
|
|
|
|
if (Campaign.type != CAMPAIGN_TYPE_SINGLE) {
|
|
required_string_fred("+Num Players:");
|
|
parse_comments();
|
|
fout(" %d", Campaign.num_players);
|
|
}
|
|
|
|
// campaign flags - Goober5000
|
|
if (save_config.save_format != MissionFormat::RETAIL) {
|
|
if (optional_string_fred("$Flags:")) {
|
|
parse_comments();
|
|
} else {
|
|
fout("\n$Flags:");
|
|
}
|
|
fout(" %d", Campaign.flags);
|
|
}
|
|
|
|
if (save_config.save_format != MissionFormat::RETAIL && !Campaign.custom_data.empty()) {
|
|
if (optional_string_fred("$begin_custom_data_map")) {
|
|
parse_comments(2);
|
|
} else {
|
|
fout("\n\n$begin_custom_data_map");
|
|
}
|
|
|
|
for (const auto& pair : Campaign.custom_data) {
|
|
fout("\n +Val: %s %s", pair.first.c_str(), pair.second.c_str());
|
|
}
|
|
|
|
if (optional_string_fred("$end_custom_data_map")) {
|
|
parse_comments();
|
|
} else {
|
|
fout("\n$end_custom_data_map");
|
|
}
|
|
}
|
|
|
|
// write out the ships and weapons which the player can start the campaign with
|
|
if (optional_string_fred("+Starting Ships:")) {
|
|
parse_comments(2);
|
|
} else {
|
|
fout("\n\n+Starting Ships:");
|
|
}
|
|
fout(" (");
|
|
for (int ship_class : Campaign.ships_allowed) {
|
|
fout(" \"%s\"", Ship_info[ship_class].name);
|
|
}
|
|
fout(" )");
|
|
|
|
if (optional_string_fred("+Starting Weapons:")) {
|
|
parse_comments(2);
|
|
} else {
|
|
fout("\n\n+Starting Weapons:");
|
|
}
|
|
fout(" (");
|
|
for (int weapon_class : Campaign.weapons_allowed) {
|
|
fout(" \"%s\"", Weapon_info[weapon_class].name);
|
|
}
|
|
fout(" )");
|
|
|
|
fred_parse_flag = 0;
|
|
for (int i = 0; i < Campaign.num_missions; i++) {
|
|
// Expect to get Campaign.missions ordered from FRED
|
|
cmission& cm = Campaign.missions[i];
|
|
|
|
required_string_either_fred("$Mission:", "#End");
|
|
required_string_fred("$Mission:");
|
|
parse_comments(2);
|
|
fout(" %s", cm.name);
|
|
|
|
if (strlen(cm.briefing_cutscene) > 0) {
|
|
if (optional_string_fred("+Briefing Cutscene:", "$Mission"))
|
|
parse_comments();
|
|
else
|
|
fout("\n+Briefing Cutscene:");
|
|
|
|
fout(" %s", cm.briefing_cutscene);
|
|
}
|
|
|
|
required_string_fred("+Flags:", "$Mission:");
|
|
parse_comments();
|
|
|
|
// don't save any internal flags
|
|
auto flags_to_save = cm.flags & CMISSION_EXTERNAL_FLAG_MASK;
|
|
|
|
// Goober5000
|
|
if (save_config.save_format != MissionFormat::RETAIL) {
|
|
// don't save Bastion flag
|
|
fout(" %d", flags_to_save & ~CMISSION_FLAG_BASTION);
|
|
|
|
// new main hall stuff
|
|
if (optional_string_fred("+Main Hall:", "$Mission:"))
|
|
parse_comments();
|
|
else
|
|
fout("\n+Main Hall:");
|
|
|
|
fout(" %s", cm.main_hall.c_str());
|
|
} else {
|
|
// save Bastion flag properly
|
|
fout(" %d", flags_to_save | ((!cm.main_hall.empty() && cm.main_hall != "0") ? CMISSION_FLAG_BASTION : 0));
|
|
}
|
|
|
|
if (!cm.substitute_main_hall.empty()) {
|
|
fso_comment_push(";;FSO 3.7.2;;");
|
|
if (optional_string_fred("+Substitute Main Hall:")) {
|
|
parse_comments(1);
|
|
fout(" %s", cm.substitute_main_hall.c_str());
|
|
} else {
|
|
fout_version("\n+Substitute Main Hall: %s", cm.substitute_main_hall.c_str());
|
|
}
|
|
fso_comment_pop();
|
|
} else {
|
|
bypass_comment(";;FSO 3.7.2;; +Substitute Main Hall:");
|
|
}
|
|
|
|
if (cm.debrief_persona_index > 0) {
|
|
fso_comment_push(";;FSO 3.6.8;;");
|
|
if (optional_string_fred("+Debriefing Persona Index:")) {
|
|
parse_comments(1);
|
|
fout(" %d", cm.debrief_persona_index);
|
|
} else {
|
|
fout_version("\n+Debriefing Persona Index: %d", cm.debrief_persona_index);
|
|
}
|
|
fso_comment_pop();
|
|
} else {
|
|
bypass_comment(";;FSO 3.6.8;; +Debriefing Persona Index:");
|
|
}
|
|
|
|
// save campaign link sexp
|
|
bool mission_loop = false;
|
|
bool mission_fork = false;
|
|
int flag = 0;
|
|
for (auto& link : links) {
|
|
if (link.from == i) {
|
|
if (!flag) {
|
|
if (optional_string_fred("+Formula:", "$Mission:"))
|
|
parse_comments();
|
|
else
|
|
fout("\n+Formula:");
|
|
|
|
fout(" ( cond\n");
|
|
flag = 1;
|
|
}
|
|
|
|
if (link.is_mission_loop) {
|
|
mission_loop = true;
|
|
} else if (link.is_mission_fork && (save_config.save_format != MissionFormat::RETAIL)) {
|
|
mission_fork = true;
|
|
} else {
|
|
save_campaign_sexp(link.sexp, link.to);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (flag) {
|
|
fout(")");
|
|
}
|
|
|
|
// now save campaign loop sexp
|
|
if (mission_loop || mission_fork) {
|
|
if (mission_loop)
|
|
required_string_fred("+Mission Loop:");
|
|
else
|
|
required_string_fred("+Mission Fork:");
|
|
parse_comments();
|
|
|
|
int num_mission_special = 0;
|
|
for (auto& link : links) {
|
|
if ((link.from == i) && (link.is_mission_loop || link.is_mission_fork)) {
|
|
|
|
num_mission_special++;
|
|
|
|
if ((num_mission_special == 1) && link.mission_branch_txt) {
|
|
if (mission_loop)
|
|
required_string_fred("+Mission Loop Text:");
|
|
else
|
|
required_string_fred("+Mission Fork Text:");
|
|
parse_comments();
|
|
fout_ext("\n", "%s", link.mission_branch_txt);
|
|
fout("\n$end_multi_text");
|
|
}
|
|
|
|
if ((num_mission_special == 1) && link.mission_branch_brief_anim) {
|
|
auto str = mission_loop ? "+Mission Loop Brief Anim:" : "+Mission Fork Brief Anim:";
|
|
if (optional_string_fred(str))
|
|
parse_comments();
|
|
else
|
|
fout("\n%s", str);
|
|
|
|
if (save_config.save_format != MissionFormat::RETAIL) {
|
|
fout(" %s", link.mission_branch_brief_anim);
|
|
} else {
|
|
// previous formats used multitext, which causes problems
|
|
fout_ext("\n", "%s", link.mission_branch_brief_anim);
|
|
fout("\n$end_multi_text");
|
|
}
|
|
}
|
|
|
|
if ((num_mission_special == 1) && link.mission_branch_brief_sound) {
|
|
auto str = mission_loop ? "+Mission Loop Brief Sound:" : "+Mission Fork Brief Sound:";
|
|
if (optional_string_fred(str))
|
|
parse_comments();
|
|
else
|
|
fout("\n%s", str);
|
|
|
|
if (save_config.save_format != MissionFormat::RETAIL) {
|
|
fout(" %s", link.mission_branch_brief_sound);
|
|
} else {
|
|
// previous formats used multitext, which causes problems
|
|
fout_ext("\n", "%s", link.mission_branch_brief_sound);
|
|
fout("\n$end_multi_text");
|
|
}
|
|
}
|
|
|
|
if (num_mission_special == 1) {
|
|
// write out mission loop formula
|
|
fout("\n+Formula:");
|
|
fout(" ( cond\n");
|
|
save_campaign_sexp(link.sexp, link.to);
|
|
fout(")");
|
|
}
|
|
if (mission_fork) {
|
|
fout("\n+Option: %s", Campaign.missions[link.to].name);
|
|
}
|
|
}
|
|
}
|
|
if (mission_loop && num_mission_special > 1) {
|
|
char buffer[1024];
|
|
sprintf(buffer,
|
|
"Multiple branching loop error from mission %s\nEdit campaign for *at most* 1 loop from each "
|
|
"mission.",
|
|
cm.name);
|
|
Message(os::dialogs::MESSAGEBOX_ERROR, buffer);
|
|
}
|
|
}
|
|
|
|
if (optional_string_fred("+Level:", "$Mission:")) {
|
|
parse_comments();
|
|
} else {
|
|
fout("\n+Level:");
|
|
}
|
|
|
|
fout(" %d", cm.level);
|
|
|
|
if (optional_string_fred("+Position:", "$Mission:")) {
|
|
parse_comments();
|
|
} else {
|
|
fout("\n+Position:");
|
|
}
|
|
|
|
fout(" %d", cm.pos);
|
|
|
|
fso_comment_pop();
|
|
}
|
|
|
|
required_string_fred("#End");
|
|
parse_comments(2);
|
|
token_found = nullptr;
|
|
parse_comments();
|
|
fout("\n");
|
|
|
|
cfclose(fp);
|
|
fso_comment_pop(true);
|
|
|
|
// Mission editor should run the error checker *before* calling the save function
|
|
Assertion(!err, "Nothing in here should have a side effect to raise the mission error saving flag.");
|
|
|
|
return err;
|
|
}
|
|
|
|
void Fred_campaign_save::save_campaign_sexp(int node, int link_num)
|
|
{
|
|
SCP_string sexp_out;
|
|
Assert(node >= 0);
|
|
|
|
// if the link num is -1, then this is a end-of-campaign location
|
|
if (link_num != -1) {
|
|
if (build_sexp_string(sexp_out, node, 2, SEXP_SAVE_MODE)) {
|
|
fout(" (\n %s\n ( next-mission \"%s\" )\n )\n",
|
|
sexp_out.c_str(),
|
|
Campaign.missions[link_num].name);
|
|
} else {
|
|
fout(" ( %s( next-mission \"%s\" ) )\n", sexp_out.c_str(), Campaign.missions[link_num].name);
|
|
}
|
|
} else {
|
|
if (build_sexp_string(sexp_out, node, 2, SEXP_SAVE_MODE)) {
|
|
fout(" (\n %s\n ( end-of-campaign )\n )\n", sexp_out.c_str());
|
|
} else {
|
|
fout(" ( %s( end-of-campaign ) )\n", sexp_out.c_str());
|
|
}
|
|
}
|
|
} |