Skip to content

Commit 490c4fd

Browse files
committed
split campaign save into a sister class
1 parent d344727 commit 490c4fd

File tree

7 files changed

+316
-295
lines changed

7 files changed

+316
-295
lines changed
Lines changed: 272 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,272 @@
1+
#include "campaignsave.h"
2+
3+
#include "globalincs/linklist.h"
4+
#include "globalincs/version.h"
5+
6+
#include "mission/missioncampaign.h"
7+
#include "parse/parselo.h"
8+
9+
#include <freespace.h>
10+
11+
// Bodies copied from the old implementations in missionsave.cpp,
12+
// unchanged except for the new class name and include.
13+
int Fred_campaign_save::save_campaign_file(const char* pathname)
14+
{
15+
// This is original FRED code. These were moved to the call sites as the data should be fully
16+
// prepared before calling this function.
17+
// TODO check QtFRED's tree is sorted and saved before calling this function.
18+
// Campaign_tree_formp->save_tree(); // flush all changes so they get saved.
19+
// Campaign_tree_viewp->sort_elements();
20+
21+
reset_parse();
22+
raw_ptr = Parse_text_raw;
23+
fred_parse_flag = 0;
24+
25+
pathname = cf_add_ext(pathname, FS_CAMPAIGN_FILE_EXT);
26+
fp = cfopen(pathname, "wt", CF_TYPE_MISSIONS);
27+
if (!fp) {
28+
nprintf(("Error", "Can't open campaign file to save.\n"));
29+
return -1;
30+
}
31+
32+
required_string_fred("$Name:");
33+
parse_comments(0);
34+
fout(" %s", Campaign.name);
35+
36+
Assert((Campaign.type >= 0) && (Campaign.type < MAX_CAMPAIGN_TYPES));
37+
required_string_fred("$Type:");
38+
parse_comments();
39+
fout(" %s", campaign_types[Campaign.type]);
40+
41+
// XSTR
42+
if (Campaign.desc) {
43+
required_string_fred("+Description:");
44+
parse_comments();
45+
fout_ext("\n", "%s", Campaign.desc);
46+
fout("\n$end_multi_text");
47+
}
48+
49+
if (Campaign.type != CAMPAIGN_TYPE_SINGLE) {
50+
required_string_fred("+Num Players:");
51+
parse_comments();
52+
fout(" %d", Campaign.num_players);
53+
}
54+
55+
// campaign flags - Goober5000
56+
if (save_config.save_format != MissionFormat::RETAIL) {
57+
optional_string_fred("$Flags:");
58+
parse_comments();
59+
fout(" %d\n", Campaign.flags);
60+
}
61+
62+
if (save_config.save_format != MissionFormat::RETAIL && !Campaign.custom_data.empty()) {
63+
if (optional_string_fred("$begin_custom_data_map")) {
64+
parse_comments(2);
65+
} else {
66+
fout("\n$begin_custom_data_map");
67+
}
68+
69+
for (const auto& pair : Campaign.custom_data) {
70+
fout("\n +Val: %s %s", pair.first.c_str(), pair.second.c_str());
71+
}
72+
73+
if (optional_string_fred("$end_custom_data_map")) {
74+
parse_comments();
75+
} else {
76+
fout("\n$end_custom_data_map");
77+
}
78+
}
79+
80+
// write out the ships and weapons which the player can start the campaign with
81+
optional_string_fred("+Starting Ships: (");
82+
parse_comments(2);
83+
for (int i = 0; i < ship_info_size(); i++) {
84+
if (Campaign.ships_allowed[i])
85+
fout(" \"%s\" ", Ship_info[i].name);
86+
}
87+
fout(")\n");
88+
89+
optional_string_fred("+Starting Weapons: (");
90+
parse_comments();
91+
for (int i = 0; i < weapon_info_size(); i++) {
92+
if (Campaign.weapons_allowed[i])
93+
fout(" \"%s\" ", Weapon_info[i].name);
94+
}
95+
fout(")\n");
96+
97+
fred_parse_flag = 0;
98+
for (int i = 0; i < Campaign.num_missions; i++) {
99+
// Expect to get Campaign.missions ordered from FRED
100+
cmission& cm = Campaign.missions[i];
101+
102+
required_string_either_fred("$Mission:", "#End");
103+
required_string_fred("$Mission:");
104+
parse_comments(2);
105+
fout(" %s", cm.name);
106+
107+
if (strlen(cm.briefing_cutscene)) {
108+
if (optional_string_fred("+Briefing Cutscene:", "$Mission"))
109+
parse_comments();
110+
else
111+
fout("\n+Briefing Cutscene:");
112+
113+
fout(" %s", cm.briefing_cutscene);
114+
}
115+
116+
required_string_fred("+Flags:", "$Mission:");
117+
parse_comments();
118+
119+
// don't save any internal flags
120+
auto flags_to_save = cm.flags & CMISSION_EXTERNAL_FLAG_MASK;
121+
122+
// Goober5000
123+
if (save_config.save_format != MissionFormat::RETAIL) {
124+
// don't save Bastion flag
125+
fout(" %d", flags_to_save & ~CMISSION_FLAG_BASTION);
126+
127+
// new main hall stuff
128+
if (optional_string_fred("+Main Hall:", "$Mission:"))
129+
parse_comments();
130+
else
131+
fout("\n+Main Hall:");
132+
133+
fout(" %s", cm.main_hall.c_str());
134+
} else {
135+
// save Bastion flag properly
136+
fout(" %d", flags_to_save | ((cm.main_hall != "") ? CMISSION_FLAG_BASTION : 0));
137+
}
138+
139+
if (!cm.substitute_main_hall.empty()) {
140+
fso_comment_push(";;FSO 3.7.2;;");
141+
if (optional_string_fred("+Substitute Main Hall:")) {
142+
parse_comments(1);
143+
fout(" %s", cm.substitute_main_hall.c_str());
144+
} else {
145+
fout_version("\n+Substitute Main Hall: %s", cm.substitute_main_hall.c_str());
146+
}
147+
fso_comment_pop();
148+
} else {
149+
bypass_comment(";;FSO 3.7.2;; +Substitute Main Hall:");
150+
}
151+
152+
if (cm.debrief_persona_index > 0) {
153+
fso_comment_push(";;FSO 3.6.8;;");
154+
if (optional_string_fred("+Debriefing Persona Index:")) {
155+
parse_comments(1);
156+
fout(" %d", cm.debrief_persona_index);
157+
} else {
158+
fout_version("\n+Debriefing Persona Index: %d", cm.debrief_persona_index);
159+
}
160+
fso_comment_pop();
161+
} else {
162+
bypass_comment(";;FSO 3.6.8;; +Debriefing Persona Index:");
163+
}
164+
165+
// new save cmission sexps
166+
if (optional_string_fred("+Formula:", "$Mission:")) {
167+
parse_comments();
168+
} else {
169+
fout("\n+Formula:");
170+
}
171+
172+
{
173+
SCP_string sexp_out{};
174+
convert_sexp_to_string(sexp_out, cm.formula, SEXP_SAVE_MODE);
175+
fout(" %s", sexp_out.c_str());
176+
}
177+
178+
bool mission_loop = cm.flags & CMISSION_FLAG_HAS_LOOP;
179+
180+
Assertion(cm.flags ^ CMISSION_FLAG_HAS_FORK,
181+
"scpFork campaigns cannot be saved, use axemFork.\n Should be detected on load.");
182+
183+
if (mission_loop) {
184+
required_string_fred("\n+Mission Loop:");
185+
parse_comments();
186+
187+
if (cm.mission_branch_desc) {
188+
required_string_fred("+Mission Loop Text:");
189+
parse_comments();
190+
fout_ext("\n", "%s", cm.mission_branch_desc);
191+
fout("\n$end_multi_text");
192+
}
193+
194+
if (cm.mission_branch_brief_anim) {
195+
required_string_fred("+Mission Loop Brief Anim:");
196+
parse_comments();
197+
fout_ext("\n", "%s", cm.mission_branch_brief_anim);
198+
fout("\n$end_multi_text");
199+
}
200+
201+
if (cm.mission_branch_brief_sound) {
202+
required_string_fred("+Mission Loop Brief Sound:");
203+
parse_comments();
204+
fout_ext("\n", "%s", cm.mission_branch_brief_sound);
205+
fout("\n$end_multi_text");
206+
}
207+
208+
// write out mission loop formula
209+
fout("\n+Formula:");
210+
{
211+
SCP_string sexp_out{};
212+
convert_sexp_to_string(sexp_out, cm.mission_loop_formula, SEXP_SAVE_MODE);
213+
fout(" %s", sexp_out.c_str());
214+
}
215+
}
216+
217+
if (optional_string_fred("+Level:", "$Mission:")) {
218+
parse_comments();
219+
} else {
220+
fout("\n\n+Level:");
221+
}
222+
223+
fout(" %d", cm.level);
224+
225+
if (optional_string_fred("+Position:", "$Mission:")) {
226+
parse_comments();
227+
} else {
228+
fout("\n+Position:");
229+
}
230+
231+
fout(" %d", cm.pos);
232+
233+
fso_comment_pop();
234+
}
235+
236+
required_string_fred("#End");
237+
parse_comments(2);
238+
token_found = NULL;
239+
parse_comments();
240+
fout("\n");
241+
242+
cfclose(fp);
243+
fso_comment_pop(true);
244+
245+
// Mission editor should run the error checker *before* calling the save function
246+
Assertion(!err, "Nothing in here should have a side effect to raise the mission error saving flag.");
247+
248+
return err;
249+
}
250+
251+
void Fred_campaign_save::save_campaign_sexp(int node, int link_num)
252+
{
253+
SCP_string sexp_out;
254+
Assert(node >= 0);
255+
256+
// if the link num is -1, then this is a end-of-campaign location
257+
if (link_num != -1) {
258+
if (build_sexp_string(sexp_out, node, 2, SEXP_SAVE_MODE)) {
259+
fout(" (\n %s\n ( next-mission \"%s\" )\n )\n",
260+
sexp_out.c_str(),
261+
Campaign.missions[link_num].name);
262+
} else {
263+
fout(" ( %s( next-mission \"%s\" ) )\n", sexp_out.c_str(), Campaign.missions[link_num].name);
264+
}
265+
} else {
266+
if (build_sexp_string(sexp_out, node, 2, SEXP_SAVE_MODE)) {
267+
fout(" (\n %s\n ( end-of-campaign )\n )\n", sexp_out.c_str());
268+
} else {
269+
fout(" ( %s( end-of-campaign ) )\n", sexp_out.c_str());
270+
}
271+
}
272+
}

code/missioneditor/campaignsave.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#pragma once
2+
#include "missionsave.h"
3+
4+
class Fred_campaign_save : public Fred_mission_save {
5+
public:
6+
/**
7+
* @brief Saves the campaign file to the given full pathname
8+
*
9+
* @param[in] pathname The full pathname to save to
10+
*
11+
* @details Returns the value of CFred_mission_save::err, which is:
12+
*
13+
* @returns 0 for no error, or
14+
* @returns A negative value if an error occurred
15+
*
16+
* @see save_mission_internal()
17+
*/
18+
int save_campaign_file(const char* pathname);
19+
20+
private:
21+
/**
22+
* @brief Save the campaign sexp to file
23+
*
24+
* @param[in] node Index of the sexp node
25+
* @param[in] link Mission index of the next mission. Is -1 if this is the last link
26+
*/
27+
void save_campaign_sexp(int node, int link_num);
28+
};

0 commit comments

Comments
 (0)