1 //
2 //
3 
4 #if defined(_MSC_VER) && _MSC_VER <= 1920
5 	// work around MSVC 2015 and 2017 compiler bug
6 	// https://bugreports.qt.io/browse/QTBUG-72073
7 	#define QT_NO_FLOAT16_OPERATORS
8 #endif
9 
10 #include "MissionSpecDialogModel.h"
11 #include "ui/dialogs/MissionSpecDialog.h"
12 
13 #include "cfile/cfile.h"
14 #include "mission/missionmessage.h"
15 
16 #include <QtWidgets>
17 
18 namespace fso {
19 namespace fred {
20 namespace dialogs {
21 
MissionSpecDialogModel(QObject * parent,EditorViewport * viewport)22 MissionSpecDialogModel::MissionSpecDialogModel(QObject* parent, EditorViewport* viewport) :
23 	AbstractDialogModel(parent, viewport) {
24 	initializeData();
25 }
26 
initializeData()27 void MissionSpecDialogModel::initializeData() {
28 	_m_mission_title = The_mission.name;
29 	_m_designer_name = The_mission.author;
30 	_m_created = The_mission.created;
31 	_m_modified = The_mission.modified;
32 	_m_mission_notes = The_mission.notes;
33 	_m_mission_desc = The_mission.mission_desc;
34 
35 	_m_full_war = Mission_all_attack;
36 	_m_disallow_support = (The_mission.support_ships.max_support_ships == 0);
37 
38 	_m_flags = The_mission.flags;
39 
40 	_m_loading_640 = The_mission.loading_screen[GR_640];
41 	_m_loading_1024 = The_mission.loading_screen[GR_1024];
42 
43 	// squad info
44 	if (strlen(The_mission.squad_name) > 0) { //-V805
45 		_m_squad_name = The_mission.squad_name;
46 		_m_squad_filename = The_mission.squad_filename;
47 	}
48 	else {
49 		_m_squad_name = NO_SQUAD;
50 		_m_squad_filename = "";
51 	}
52 
53 	_m_type = The_mission.game_type;
54 	_m_ai_profile = AI_PROFILES_INDEX(The_mission.ai_profile);
55 
56 	_m_event_music = Current_soundtrack_num + 1;
57 	_m_substitute_event_music = The_mission.substitute_event_music_name;
58 
59 	_m_command_persona = The_mission.command_persona;
60 	_m_command_sender = The_mission.command_sender;
61 
62 	_m_num_respawns = The_mission.num_respawns;
63 	_m_max_respawn_delay = The_mission.max_respawn_delay;
64 	_m_max_hull_repair_val = The_mission.support_ships.max_hull_repair_val;
65 	_m_max_subsys_repair_val = The_mission.support_ships.max_subsys_repair_val;
66 
67 	_m_contrail_threshold = The_mission.contrail_threshold;
68 	_m_contrail_threshold_flag = (_m_contrail_threshold != CONTRAIL_THRESHOLD_DEFAULT);
69 
70 	modelChanged();
71 }
72 
apply()73 bool MissionSpecDialogModel::apply() {
74 	int new_m_type;
75 
76 	// deal with changing the mission type.  Code is done this way since training missions
77 	// just override anything else.
78 	new_m_type = _m_type;
79 
80 	if (!new_m_type) {
81 		auto button = _viewport->dialogProvider->showButtonDialog(DialogType::Error, "Invalid mission type", "You must select the game type: training, single, or multiplayer",
82 		{ DialogButton::Ok });
83 		if (button == DialogButton::Ok) {
84 			return false;
85 		}
86 	}
87 
88 	The_mission.game_type = new_m_type;
89 	The_mission.num_respawns = _m_num_respawns;
90 	The_mission.max_respawn_delay = _m_max_respawn_delay;
91 	The_mission.support_ships.max_support_ships = (_m_disallow_support) ? 0 : -1;
92 	The_mission.support_ships.max_hull_repair_val = _m_max_hull_repair_val;
93 	The_mission.support_ships.max_subsys_repair_val = _m_max_subsys_repair_val;
94 
95 	// Copy mission flags
96 	The_mission.flags = _m_flags;
97 
98 	// set ship trail threshold
99 	if (_m_contrail_threshold_flag) {
100 		The_mission.contrail_threshold = _m_contrail_threshold;
101 	} else {
102 		The_mission.contrail_threshold = CONTRAIL_THRESHOLD_DEFAULT;
103 	}
104 
105 	//if there's a odd number of quotation marks, the mission won't parse
106 	//If there are an even number, nothing after the first one appears
107 	//So just get rid of them
108 	Editor::strip_quotation_marks(_m_mission_title);
109 	Editor::strip_quotation_marks(_m_designer_name);
110 	Editor::strip_quotation_marks(_m_mission_notes);
111 	Editor::strip_quotation_marks(_m_mission_desc);
112 	Editor::strip_quotation_marks(_m_squad_name);
113 
114 	// puts "$End Notes:" on a different line to ensure it's not interpreted as part of a comment
115 	Editor::pad_with_newline(_m_mission_notes, NOTES_LENGTH - 1);
116 
117 	strncpy(The_mission.name, _m_mission_title.c_str(), NAME_LENGTH-1);
118 	strncpy(The_mission.author, _m_designer_name.c_str(), NAME_LENGTH-1);
119 	strncpy(The_mission.loading_screen[GR_640], _m_loading_640.c_str(), NAME_LENGTH-1);
120 	strncpy(The_mission.loading_screen[GR_1024], _m_loading_1024.c_str(), NAME_LENGTH-1);
121 	strncpy(The_mission.notes, _m_mission_notes.c_str(), NOTES_LENGTH);
122 	strncpy(The_mission.mission_desc, _m_mission_desc.c_str(), MISSION_DESC_LENGTH);
123 
124 	// copy squad stuff
125 	if (_m_squad_name == NO_SQUAD) {
126 		strcpy_s(The_mission.squad_name, "");
127 		strcpy_s(The_mission.squad_filename, "");
128 	} else {
129 		strncpy(The_mission.squad_name, _m_squad_name.c_str(), NAME_LENGTH);
130 		strncpy(The_mission.squad_filename, _m_squad_filename.c_str(), MAX_FILENAME_LEN);
131 	}
132 
133 	The_mission.ai_profile = &Ai_profiles[_m_ai_profile];
134 
135 	Current_soundtrack_num = _m_event_music - 1;
136 	strcpy_s(The_mission.substitute_event_music_name, _m_substitute_event_music.c_str());
137 
138 	The_mission.command_persona = _m_command_persona;
139 	if (_m_command_sender.at(0) == '#')
140 		strcpy_s(The_mission.command_sender, _m_command_sender.substr(1).c_str());
141 	else
142 		strcpy_s(The_mission.command_sender, _m_command_sender.c_str());
143 
144 	Mission_all_attack = (int)_m_full_war;
145 
146 	// update the Num_teams variable accoriding to mission types
147 	Num_teams = 1;
148 	if ((The_mission.game_type & MISSION_TYPE_MULTI) && (The_mission.game_type & MISSION_TYPE_MULTI_TEAMS)) {
149 		Num_teams = 2;
150 	}
151 
152 	return true;
153 }
154 
reject()155 void MissionSpecDialogModel::reject() {
156 
157 }
158 
setMissionTitle(const SCP_string & m_mission_title)159 void MissionSpecDialogModel::setMissionTitle(const SCP_string& m_mission_title) {
160 	modify(_m_mission_title, m_mission_title);
161 }
162 
getMissionTitle()163 SCP_string MissionSpecDialogModel::getMissionTitle() {
164 	return _m_mission_title;
165 }
166 
setDesigner(const SCP_string & m_designer_name)167 void MissionSpecDialogModel::setDesigner(const SCP_string& m_designer_name) {
168 	modify(_m_designer_name, m_designer_name);
169 }
170 
getDesigner()171 SCP_string MissionSpecDialogModel::getDesigner() {
172 	return _m_designer_name;
173 }
174 
getCreatedTime()175 SCP_string MissionSpecDialogModel::getCreatedTime() {
176 	return _m_created;
177 }
178 
getModifiedTime()179 SCP_string MissionSpecDialogModel::getModifiedTime() {
180 	return _m_modified;
181 }
182 
setMissionType(int m_type)183 void MissionSpecDialogModel::setMissionType(int m_type) {
184 	if (m_type & (MISSION_TYPE_SINGLE |
185 		MISSION_TYPE_MULTI |
186 		MISSION_TYPE_TRAINING |
187 		MISSION_TYPE_MULTI_COOP |
188 		MISSION_TYPE_MULTI_DOGFIGHT |
189 		MISSION_TYPE_MULTI_TEAMS)) {
190 		modify(_m_type, m_type);
191 		modelChanged();
192 	} else {
193 		auto button = _viewport->dialogProvider->showButtonDialog(DialogType::Error, "Invalid mission type", "You must select the game type: training, single, or multiplayer",
194 		{ DialogButton::Ok });
195 		if (button == DialogButton::Ok) {
196 			return;
197 		}
198 	}
199 }
200 
getMissionType()201 int MissionSpecDialogModel::getMissionType() {
202 	return _m_type;
203 }
204 
setNumRespawns(uint m_num_respawns)205 void MissionSpecDialogModel::setNumRespawns(uint m_num_respawns) {
206 	modify(_m_num_respawns, m_num_respawns);
207 }
208 
getNumRespawns()209 uint MissionSpecDialogModel::getNumRespawns() {
210 	return _m_num_respawns;
211 }
212 
setMaxRespawnDelay(int m_max_respawn_delay)213 void MissionSpecDialogModel::setMaxRespawnDelay(int m_max_respawn_delay) {
214 	modify(_m_max_respawn_delay, m_max_respawn_delay);
215 }
216 
getMaxRespawnDelay()217 int MissionSpecDialogModel::getMaxRespawnDelay() {
218 	return _m_max_respawn_delay;
219 }
220 
setSquadronName(const SCP_string & m_squad_name)221 void MissionSpecDialogModel::setSquadronName(const SCP_string& m_squad_name) {
222 	modify(_m_squad_name, m_squad_name);
223 }
224 
getSquadronName()225 SCP_string MissionSpecDialogModel::getSquadronName() {
226 	return _m_squad_name;
227 }
228 
setSquadronLogo(const SCP_string & m_squad_filename)229 void MissionSpecDialogModel::setSquadronLogo(const SCP_string& m_squad_filename) {
230 	modify(_m_squad_filename, m_squad_filename);
231 }
232 
getSquadronLogo()233 SCP_string MissionSpecDialogModel::getSquadronLogo() {
234 	return _m_squad_filename;
235 }
236 
setLowResLoadingScreen(const SCP_string & m_loading_640)237 void MissionSpecDialogModel::setLowResLoadingScreen(const SCP_string& m_loading_640) {
238 	modify(_m_loading_640, m_loading_640);
239 }
240 
getLowResLoadingScren()241 SCP_string MissionSpecDialogModel::getLowResLoadingScren() {
242 	return _m_loading_640;
243 }
244 
setHighResLoadingScreen(const SCP_string & m_loading_1024)245 void MissionSpecDialogModel::setHighResLoadingScreen(const SCP_string& m_loading_1024) {
246 	modify(_m_loading_1024, m_loading_1024);
247 }
248 
getHighResLoadingScren()249 SCP_string MissionSpecDialogModel::getHighResLoadingScren() {
250 	return _m_loading_1024;
251 }
252 
setDisallowSupport(bool m_disallow_support)253 void MissionSpecDialogModel::setDisallowSupport(bool m_disallow_support) {
254 	modify(_m_disallow_support, m_disallow_support);
255 }
256 
getDisallowSupport()257 bool MissionSpecDialogModel::getDisallowSupport() {
258 	return _m_disallow_support;
259 }
260 
setHullRepairMax(float m_max_hull_repair_val)261 void MissionSpecDialogModel::setHullRepairMax(float m_max_hull_repair_val) {
262 	modify(_m_max_hull_repair_val, m_max_hull_repair_val);
263 }
264 
getHullRepairMax()265 int MissionSpecDialogModel::getHullRepairMax() {
266 	return _m_max_hull_repair_val;
267 }
268 
setSubsysRepairMax(float m_max_subsys_repair_val)269 void MissionSpecDialogModel::setSubsysRepairMax(float m_max_subsys_repair_val){
270 	modify(_m_max_subsys_repair_val, m_max_subsys_repair_val);
271 }
272 
getSubsysRepairMax()273 int MissionSpecDialogModel::getSubsysRepairMax() {
274 	return _m_max_subsys_repair_val;
275 }
276 
setTrailThresholdFlag(bool m_contrail_threshold_flag)277 void MissionSpecDialogModel::setTrailThresholdFlag(bool m_contrail_threshold_flag) {
278 	modify(_m_contrail_threshold_flag, m_contrail_threshold_flag);
279 }
280 
getTrailThresholdFlag()281 bool MissionSpecDialogModel::getTrailThresholdFlag() {
282 	return _m_contrail_threshold_flag;
283 }
284 
setTrailDisplaySpeed(int m_contrail_threshold)285 void MissionSpecDialogModel::setTrailDisplaySpeed(int m_contrail_threshold) {
286 	modify(_m_contrail_threshold, m_contrail_threshold);
287 }
288 
getTrailDisplaySpeed()289 int MissionSpecDialogModel::getTrailDisplaySpeed() {
290 	return _m_contrail_threshold;
291 }
292 
setCommandSender(const SCP_string & m_command_sender)293 void MissionSpecDialogModel::setCommandSender(const SCP_string& m_command_sender) {
294 	modify(_m_command_sender, m_command_sender);
295 }
296 
getCommandSender()297 SCP_string MissionSpecDialogModel::getCommandSender() {
298 	return _m_command_sender;
299 }
300 
setCommandPersona(int m_command_persona)301 void MissionSpecDialogModel::setCommandPersona(int m_command_persona) {
302 	modify(_m_command_persona, m_command_persona);
303 }
304 
getCommandPersona()305 int MissionSpecDialogModel::getCommandPersona() {
306 	return _m_command_persona;
307 }
308 
setEventMusic(int m_event_music)309 void MissionSpecDialogModel::setEventMusic(int m_event_music) {
310 	modify(_m_event_music, m_event_music);
311 }
312 
getEventMusic()313 int MissionSpecDialogModel::getEventMusic() {
314 	return _m_event_music;
315 }
316 
setSubEventMusic(const SCP_string & m_substitute_event_music)317 void MissionSpecDialogModel::setSubEventMusic(const SCP_string& m_substitute_event_music) {
318 	modify(_m_substitute_event_music, m_substitute_event_music);
319 }
320 
getSubEventMusic()321 SCP_string MissionSpecDialogModel::getSubEventMusic() {
322 	return _m_substitute_event_music;
323 }
324 
setMissionFlag(Mission::Mission_Flags flag,bool enabled)325 void MissionSpecDialogModel::setMissionFlag(Mission::Mission_Flags flag, bool enabled) {
326 	if (_m_flags[flag] != enabled) {
327 		_m_flags.set(flag, enabled);
328 		set_modified();
329 		modelChanged();
330 	}
331 }
332 
getMissionFlags() const333 const flagset<Mission::Mission_Flags>& MissionSpecDialogModel::getMissionFlags() const {
334 	return _m_flags;
335 }
336 
setMissionFullWar(bool enabled)337 void MissionSpecDialogModel::setMissionFullWar(bool enabled) {
338 	if (_m_full_war != enabled) {
339 		_m_full_war = enabled;
340 		_m_flags.set(Mission::Mission_Flags::All_attack, enabled);
341 		set_modified();
342 		modelChanged();
343 	}
344 }
345 
setAIProfileIndex(int m_ai_profile)346 void MissionSpecDialogModel::setAIProfileIndex(int m_ai_profile) {
347 	modify(_m_ai_profile, m_ai_profile);
348 }
349 
getAIProfileIndex() const350 int MissionSpecDialogModel::getAIProfileIndex() const {
351 	return _m_ai_profile;
352 }
353 
setMissionDescText(const SCP_string & m_mission_desc)354 void MissionSpecDialogModel::setMissionDescText(const SCP_string& m_mission_desc) {
355 	modify(_m_mission_desc, m_mission_desc.substr(0, MIN(MISSION_DESC_LENGTH, m_mission_desc.length())));
356 }
357 
getMissionDescText()358 SCP_string MissionSpecDialogModel::getMissionDescText() {
359 	return _m_mission_desc;
360 }
361 
setDesignerNoteText(const SCP_string & m_mission_notes)362 void MissionSpecDialogModel::setDesignerNoteText(const SCP_string& m_mission_notes) {
363 	modify(_m_mission_notes, m_mission_notes.substr(0, MIN(NOTES_LENGTH, m_mission_notes.length())));
364 }
365 
getDesignerNoteText()366 SCP_string MissionSpecDialogModel::getDesignerNoteText() {
367 	return _m_mission_notes;
368 }
369 
set_modified()370 void MissionSpecDialogModel::set_modified() {
371 	if (!_modified) {
372 		_modified = true;
373 	}
374 }
375 
query_modified()376 bool MissionSpecDialogModel::query_modified() {
377 	return _modified;
378 }
379 
380 }
381 }
382 }
383