FS2_Open
Open source remastering of the Freespace 2 engine
missionsave.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) Volition, Inc. 1999. All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell
5  * or otherwise commercially exploit the source or things you created based on the
6  * source.
7  *
8 */
9 
10 
11 
12 #include "stdafx.h"
13 #include "freespace2/freespace.h"
14 #include "MissionSave.h"
15 #include "mission/missiongoals.h"
16 #include "mission/missionmessage.h"
17 #include "mission/missionparse.h"
18 #include "FredRender.h"
19 #include "ai/aigoals.h"
20 #include "starfield/starfield.h"
21 #include "lighting/lighting.h"
22 #include "globalincs/linklist.h"
23 #include "weapon/weapon.h"
25 #include "CampaignTreeWnd.h"
26 #include "CampaignTreeView.h"
27 #include "CampaignEditorDlg.h"
28 #include "parse/sexp.h"
30 #include "Management.h"
31 #include "gamesnd/eventmusic.h"
32 #include "starfield/nebula.h"
33 #include "asteroid/asteroid.h"
35 #include "jumpnode/jumpnode.h"
36 #include "MainFrm.h"
37 #include "localization/fhash.h"
38 #include "nebula/neb.h"
39 #include "osapi/osapi.h"
40 #include "FredView.h"
41 #include "cfile/cfile.h"
42 #include "object/objectdock.h"
43 #include "object/objectshield.h"
44 #include "iff_defs/iff_defs.h"
46 #include "globalincs/version.h"
47 #include "sound/sound.h"
48 #include "sound/ds.h"
49 #include "math/vecmat.h"
50 
51 
52 void CFred_mission_save::convert_special_tags_to_retail(char *text, int max_len)
53 {
54  replace_all(text, "$quote", "''", max_len);
55  replace_all(text, "$semicolon", ",", max_len);
56 }
57 
58 void CFred_mission_save::convert_special_tags_to_retail(SCP_string &text)
59 {
60  replace_all(text, "$quote", "''");
61  replace_all(text, "$semicolon", ",");
62 }
63 
64 // Goober5000 - convert $quote and $semicolon to '' and ,
65 void CFred_mission_save::convert_special_tags_to_retail()
66 {
67  int i, team, stage;
68 
70  return;
71 
72  for (team = 0; team < Num_teams; team++)
73  {
74  // command briefing
75  for (stage = 0; stage < Cmd_briefs[team].num_stages; stage++)
76  {
77  convert_special_tags_to_retail(Cmd_briefs[team].stage[stage].text);
78  }
79 
80  // briefing
81  for (stage = 0; stage < Briefings[team].num_stages; stage++)
82  {
83  convert_special_tags_to_retail(Briefings[team].stages[stage].text);
84  }
85 
86  // debriefing
87  for (stage = 0; stage < Debriefings[team].num_stages; stage++)
88  {
89  convert_special_tags_to_retail(Debriefings[team].stages[stage].text);
90  }
91  }
92 
93  for (i = Num_builtin_messages; i < Num_messages; i++)
94  {
95  convert_special_tags_to_retail(Messages[i].message, MESSAGE_LENGTH);
96  }
97 }
98 
99 void CFred_mission_save::save_mission_internal(const char *pathname)
100 {
101  CTime t;
102 
103  t = CTime::GetCurrentTime();
104  strcpy_s(The_mission.modified, t.Format("%x at %X"));
105 
106  reset_parse();
107  fred_parse_flag = 0;
108  fp = cfopen(pathname, "wt", CFILE_NORMAL, CF_TYPE_MISSIONS);
109  if (!fp) {
110  nprintf(("Error", "Can't open mission file to save.\n"));
111  err = -1;
112  return;
113  }
114 
115  // Goober5000
116  convert_special_tags_to_retail();
117 
118  if (save_mission_info())
119  err = -2;
120  else if (save_plot_info())
121  err = -3;
122  else if (save_variables())
123  err = -3;
124 // else if (save_briefing_info())
125 // err = -4;
126  else if (save_cutscenes())
127  err = -4;
128  else if (save_fiction())
129  err = -3;
130  else if (save_cmd_briefs())
131  err = -4;
132  else if (save_briefing())
133  err = -4;
134  else if (save_debriefing())
135  err = -5;
136  else if (save_players())
137  err = -6;
138  else if (save_objects())
139  err = -7;
140  else if (save_wings())
141  err = -8;
142  else if (save_events())
143  err = -9;
144  else if (save_goals())
145  err = -10;
146  else if (save_waypoints())
147  err = -11;
148  else if (save_messages())
149  err = -12;
150  else if (save_reinforcements())
151  err = -13;
152  else if (save_bitmaps())
153  err = -14;
154  else if (save_asteroid_fields())
155  err = -15;
156  else if (save_music())
157  err = -16;
158  else {
159  required_string_fred("#End");
160  parse_comments(2);
161  token_found = NULL;
162  parse_comments();
163  fout("\n");
164  }
165 
166  cfclose(fp);
167  if (err)
168  mprintf(("Mission saving error code #%d\n", err));
169 }
170 
172 {
173  char backup_name[256], savepath[MAX_PATH_LEN], *p;
174 
175  strcpy_s(savepath, "");
176  p = strrchr(pathname, '\\');
177  if ( p ) {
178  *p = '\0';
179  strcpy_s(savepath, pathname);
180  *p = '\\';
181  strcat_s(savepath, "\\");
182  }
183  strcat_s(savepath, "saving.xxx");
184 
185  save_mission_internal(savepath);
186 
187  if (!err) {
188  strcpy_s(backup_name, pathname);
189  if (backup_name[strlen(backup_name) - 4] == '.')
190  backup_name[strlen(backup_name) - 4] = 0;
191 
192  strcat_s(backup_name, ".bak");
193  cf_attrib(pathname, 0, FILE_ATTRIBUTE_READONLY, CF_TYPE_MISSIONS);
194  cf_delete(backup_name, CF_TYPE_MISSIONS);
195  cf_rename(pathname, backup_name, CF_TYPE_MISSIONS);
196  cf_rename(savepath, pathname, CF_TYPE_MISSIONS);
197  }
198 
199  return err;
200 }
201 
203 {
204  char backup_name[256], name2[256];
205  int i, len;
206 
207  len = strlen(pathname);
208  strcpy_s(backup_name, pathname);
209  strcpy_s(name2, pathname);
210  sprintf(backup_name + len, ".%.3d", BACKUP_DEPTH);
211  cf_delete(backup_name, CF_TYPE_MISSIONS);
212  for (i=BACKUP_DEPTH; i>1; i--) {
213  sprintf(backup_name + len, ".%.3d", i - 1);
214  sprintf(name2 + len, ".%.3d", i);
215  cf_rename(backup_name, name2, CF_TYPE_MISSIONS);
216  }
217 
218  strcpy(backup_name + len, ".001");
219 
220  save_mission_internal(backup_name);
221 
222  return err;
223 }
224 
225 int CFred_mission_save::save_mission_info()
226 {
227  required_string_fred("#Mission Info");
228  parse_comments(0);
229 
230  required_string_fred("$Version:");
231  parse_comments(2);
232  fout(" %.2f", FRED_MISSION_VERSION);
233 
234  // XSTR
235  required_string_fred("$Name:");
236  parse_comments();
237  fout_ext(" ", "%s", The_mission.name);
238 
239  required_string_fred("$Author:");
240  parse_comments();
241  fout(" %s", The_mission.author);
242 
243  required_string_fred("$Created:");
244  parse_comments();
245  fout(" %s", The_mission.created);
246 
247  required_string_fred("$Modified:");
248  parse_comments();
249  fout(" %s", The_mission.modified);
250 
251  required_string_fred("$Notes:");
252  parse_comments();
253  fout("\n%s", The_mission.notes);
254 
255  required_string_fred("$End Notes:");
256  parse_comments(0);
257 
258  // XSTR
259  required_string_fred("$Mission Desc:");
260  parse_comments(2);
261  fout_ext("\n", "%s", The_mission.mission_desc);
262  fout("\n");
263 
264  required_string_fred("$end_multi_text");
265  parse_comments(0);
266 
267 #if 0
268  if (optional_string_fred("+Game Type:"))
269  parse_comments(2);
270  else
271  fout("\n\n+Game Type:");
272  fout("\n%s", Game_types[The_mission.game_type]);
273 #endif
274 
275  if ( optional_string_fred("+Game Type Flags:")){
276  parse_comments(1);
277  } else {
278  fout("\n+Game Type Flags:");
279  }
280 
281  fout(" %d", The_mission.game_type);
282 
283  if (optional_string_fred("+Flags:")){
284  parse_comments(1);
285  } else {
286  fout("\n+Flags:");
287  }
288 
289  fout(" %d", The_mission.flags);
290 
291  // maybe write out Nebula intensity
293  Assert(Neb2_awacs > 0.0f);
294  fout("\n+NebAwacs: %f\n", Neb2_awacs);
295 
296  // storm name
297  fout("\n+Storm: %s\n", Mission_parse_storm_name);
298  }
299 
300  // Goober5000
302  {
303  // write out the nebula clipping multipliers
304  fout("\n+Fog Near Mult: %f\n", Neb2_fog_near_mult);
305  fout("\n+Fog Far Mult: %f\n", Neb2_fog_far_mult);
306 
308  {
309  fout("\n$Contrail Speed Threshold: %d\n", The_mission.contrail_threshold);
310  }
311  }
312 
313  // For multiplayer missions -- write out the number of player starts and number of respawns
315  if (optional_string_fred("+Num Players:"))
316  parse_comments(2);
317  else
318  fout("\n+Num Players:");
319 
320  fout(" %d", Player_starts);
321 
322  if (optional_string_fred("+Num Respawns:"))
323  parse_comments(2);
324  else
325  fout("\n+Num Respawns:");
326 
327  fout(" %d", The_mission.num_respawns);
328 
330  fso_comment_push(";;FSO 3.6.11;;");
331  if (optional_string_fred("+Max Respawn Time:"))
332  parse_comments(2);
333  else {
334  fout_version("\n+Max Respawn Time:");
335  }
336  fso_comment_pop();
337 
339  }
340  else {
341  bypass_comment(";;FSO 3.6.11;; +Max Respawn Time:");
342  }
343  }
344 
346  {
347  if ( optional_string_fred("+Red Alert:"))
348  parse_comments(2);
349  else
350  fout("\n+Red Alert:");
351 
352  fout(" %d", (The_mission.flags & MISSION_FLAG_RED_ALERT) ? 1 : 0);
353  }
354 
355  if ( Format_fs2_open == FSO_FORMAT_RETAIL ) //-V581
356  {
357  if ( optional_string_fred("+Scramble:"))
358  parse_comments(2);
359  else
360  fout("\n+Scramble:");
361 
362  fout(" %d", (The_mission.flags & MISSION_FLAG_SCRAMBLE) ? 1 : 0);
363  }
364 
365  if ( optional_string_fred("+Disallow Support:")){
366  parse_comments(1);
367  } else {
368  fout("\n+Disallow Support:");
369  }
370  // this is compatible with non-SCP variants - Goober5000
371  fout(" %d", (The_mission.support_ships.max_support_ships == 0)?1:0 );
372 
373  // here be WMCoolmon's hull and subsys repair stuff
375  {
376  if ( optional_string_fred("+Hull Repair Ceiling:")) {
377  parse_comments(1);
378  } else {
379  fout("\n+Hull Repair Ceiling:");
380  }
382 
383  if ( optional_string_fred("+Subsystem Repair Ceiling:")) {
384  parse_comments(1);
385  } else {
386  fout("\n+Subsystem Repair Ceiling:");
387  }
389  }
390 
391  if (Mission_all_attack) {
392  if (optional_string_fred("+All Teams Attack")){
393  parse_comments();
394  } else {
395  fout("\n+All Teams Attack");
396  }
397  }
398 
399  if (Entry_delay_time) {
400  if (optional_string_fred("+Player Entry Delay:"))
401  parse_comments(2);
402  else
403  fout("\n\n+Player Entry Delay:");
404 
405  fout("\n%f", f2fl(Entry_delay_time));
406  }
407 
408  if (optional_string_fred("+Viewer pos:")){
409  parse_comments(2);
410  } else {
411  fout("\n\n+Viewer pos:");
412  }
413 
414  save_vector(view_pos);
415 
416  if (optional_string_fred("+Viewer orient:")){
417  parse_comments();
418  } else {
419  fout("\n+Viewer orient:");
420  }
421 
422  save_matrix(view_orient);
423 
424  // squadron info
425  if(!(The_mission.game_type & MISSION_TYPE_MULTI) && (strlen(The_mission.squad_name) > 0)){ //-V805
426  // squad name
427  fout("\n+SquadReassignName: %s", The_mission.squad_name);
428 
429  // maybe squad logo
430  if(strlen(The_mission.squad_filename) > 0){ //-V805
431  fout("\n+SquadReassignLogo: %s", The_mission.squad_filename);
432  }
433  }
434 
435  // Goober5000 - special wing info
437  {
438  int i;
439  fout("\n");
440 
441  // starting wings
442  if (strcmp(Starting_wing_names[0], "Alpha") || strcmp(Starting_wing_names[1], "Beta") || strcmp(Starting_wing_names[2], "Gamma"))
443  {
444  fout("\n$Starting wing names: ( ");
445 
446  for (i=0; i<MAX_STARTING_WINGS; i++)
447  {
448  fout ("\"%s\" ", Starting_wing_names[i]);
449  }
450 
451  fout (")");
452  }
453 
454  // squadron wings
455  if (strcmp(Squadron_wing_names[0], "Alpha") || strcmp(Squadron_wing_names[1], "Beta") || strcmp(Squadron_wing_names[2], "Gamma") || strcmp(Squadron_wing_names[3], "Delta") || strcmp(Squadron_wing_names[4], "Epsilon"))
456  {
457  fout("\n$Squadron wing names: ( ");
458 
459  for (i=0; i<MAX_SQUADRON_WINGS; i++)
460  {
461  fout("\"%s\" ", Squadron_wing_names[i]);
462  }
463 
464  fout(")");
465  }
466 
467  // tvt wings
468  if (strcmp(TVT_wing_names[0], "Alpha") || strcmp(TVT_wing_names[1], "Zeta"))
469  {
470  fout("\n$Team-versus-team wing names: ( ");
471 
472  for (i=0; i<MAX_TVT_WINGS; i++)
473  {
474  fout("\"%s\" ", TVT_wing_names[i]);
475  }
476 
477  fout(")");
478  }
479  }
480 
481  save_custom_bitmap("$Load Screen 640:", "$Load Screen 1024:", The_mission.loading_screen[GR_640], The_mission.loading_screen[GR_1024], 1);
482 
483  // Phreak's skybox stuff
484  if (strlen(The_mission.skybox_model) > 0) //-V805
485  {
486  char out_str[NAME_LENGTH];
487  char *period;
488 
489  // kill off any extension, we will add one here
491  period = strrchr(out_str, '.');
492  if (period != NULL)
493  *period = 0;
494 
495  fso_comment_push(";;FSO 3.6.0;;");
496  if (optional_string_fred("$Skybox Model:")) {
497  parse_comments(2);
498  fout(" %s.pof", out_str);
499  } else {
500  fout_version("\n\n$Skybox Model: %s.pof", out_str);
501  }
502  fso_comment_pop();
503  } else {
504  bypass_comment(";;FSO 3.6.0;; $Skybox Model:");
505  }
506 
507  // orientation?
509  fso_comment_push(";;FSO 3.6.14;;");
510  if (optional_string_fred("+Skybox Orientation:")) {
511  parse_comments(1);
512  save_matrix(The_mission.skybox_orientation);
513  } else {
514  fout_version("\n+Skybox Orientation:");
515  save_matrix(The_mission.skybox_orientation);
516  }
517  fso_comment_pop();
518  } else {
519  bypass_comment(";;FSO 3.6.14;; +Skybox Orientation:");
520  }
521 
522  // are skybox flags in use?
524  //char out_str[4096];
525  fso_comment_push(";;FSO 3.6.11;;");
526  if (optional_string_fred("+Skybox Flags:")) {
527  parse_comments(1);
528  fout( " %d", The_mission.skybox_flags);
529  } else {
530  fout_version("\n+Skybox Flags: %d", The_mission.skybox_flags);
531  }
532  fso_comment_pop();
533  } else {
534  bypass_comment(";;FSO 3.6.11;; +Skybox Flags:");
535  }
536 
537  // Goober5000's AI profile stuff
538  int profile_index = (The_mission.ai_profile - Ai_profiles);
539  Assert(profile_index >= 0 && profile_index < MAX_AI_PROFILES);
540 
541  fso_comment_push(";;FSO 3.6.9;;");
542  if (optional_string_fred("$AI Profile:")) {
543  parse_comments(2);
545  } else {
546  fout_version("\n\n$AI Profile: %s", The_mission.ai_profile->profile_name);
547  }
548  fso_comment_pop();
549 
550  // sound environment (EFX/EAX) - taylor
552  if ( (m_env->id >= 0) && (m_env->id < (int)EFX_presets.size()) ) {
553  EFXREVERBPROPERTIES *prop = &EFX_presets[m_env->id];
554 
555  fso_comment_push(";;FSO 3.6.12;;");
556 
557  fout_version("\n\n$Sound Environment: %s", prop->name.c_str());
558 
559  if (m_env->volume != prop->flGain) {
560  fout_version("\n+Volume: %f", m_env->volume);
561  }
562 
563  if (m_env->damping != prop->flDecayHFRatio) {
564  fout_version("\n+Damping: %f", m_env->damping);
565  }
566 
567  if (m_env->decay != prop->flDecayTime) {
568  fout_version("\n+Decay Time: %f", m_env->decay);
569  }
570 
571  fso_comment_pop();
572  }
573 
574  return err;
575 }
576 
577 int CFred_mission_save::save_plot_info()
578 {
580  {
581  if (optional_string_fred("#Plot Info"))
582  {
583  parse_comments(2);
584 
585  // XSTR
586  required_string_fred("$Tour:");
587  parse_comments(2);
588  fout_ext(" ", "Blah");
589 
590  required_string_fred("$Pre-Briefing Cutscene:");
591  parse_comments();
592  fout(" Blah");
593 
594  required_string_fred("$Pre-Mission Cutscene:");
595  parse_comments();
596  fout(" Blah");
597 
598  required_string_fred("$Next Mission Success:");
599  parse_comments();
600  fout(" Blah");
601 
602  required_string_fred("$Next Mission Partial:");
603  parse_comments();
604  fout(" Blah");
605 
606  required_string_fred("$Next Mission Failure:");
607  parse_comments();
608  fout(" Blah");
609  }
610  else
611  {
612  fout("\n\n#Plot Info\n\n");
613 
614  fout("$Tour: ");
615  fout_ext(NULL, "Blah");
616  fout("\n");
617  fout("$Pre-Briefing Cutscene: Blah\n");
618  fout("$Pre-Mission Cutscene: Blah\n");
619  fout("$Next Mission Success: Blah\n");
620  fout("$Next Mission Partial: Blah\n");
621  fout("$Next Mission Failure: Blah\n");
622 
623  fout("\n");
624  }
625  }
626 
627  fso_comment_pop(true);
628 
629  return err;
630 }
631 
632 int CFred_mission_save::save_cutscenes()
633 {
634  char type[NAME_LENGTH];
635  SCP_string sexp_out;
636 
637  // Let's just assume it has them for now -
638  if (!(The_mission.cutscenes.empty()) ) {
640  if (optional_string_fred("#Cutscenes")) {
641  parse_comments(2);
642  }
643  else {
644  fout_version("\n\n#Cutscenes\n\n");
645  }
646 
647  for (uint i = 0; i < The_mission.cutscenes.size(); i++) {
648  if ( strlen(The_mission.cutscenes[i].filename) ) {
649  // determine the name of this cutscene type
650  switch (The_mission.cutscenes[i].type) {
651  case MOVIE_PRE_FICTION:
652  strcpy_s(type, "$Fiction Viewer Cutscene:");
653  break;
654  case MOVIE_PRE_CMD_BRIEF:
655  strcpy_s(type, "$Command Brief Cutscene:");
656  break;
657  case MOVIE_PRE_BRIEF:
658  strcpy_s(type, "$Briefing Cutscene:");
659  break;
660  case MOVIE_PRE_GAME:
661  strcpy_s(type, "$Pre-game Cutscene:");
662  break;
663  case MOVIE_PRE_DEBRIEF:
664  strcpy_s(type, "$Debriefing Cutscene:");
665  break;
666  case MOVIE_END_CAMPAIGN:
667  strcpy_s(type, "$Campaign End Cutscene:");
668  break;
669  default:
670  Int3();
671  continue;
672  }
673 
674  if (optional_string_fred(type)) {
675  parse_comments();
676  fout(" %s", The_mission.cutscenes[i].filename);
677  }
678  else {
679  fout_version("%s %s\n", type, The_mission.cutscenes[i].filename);
680  }
681 
682  required_string_fred("+formula:");
683  parse_comments();
685  fout(" %s", sexp_out.c_str());
686  }
687  }
688  required_string_fred("#end");
689  parse_comments();
690  }
691  else {
692  MessageBox(NULL, "Warning: This mission contains cutscene data, but you are saving in the retail mission format. This information will be lost", "Incompatibility with retail mission format", MB_OK);
693  }
694  }
695 
696  fso_comment_pop(true);
697  return err;
698 }
699 
700 int CFred_mission_save::save_fiction()
701 {
702  if (mission_has_fiction())
703  {
705  {
706  if (optional_string_fred("#Fiction Viewer"))
707  parse_comments(2);
708  else
709  fout("\n\n#Fiction Viewer");
710 
711  // we have multiple stages now, so save them all
712  for (SCP_vector<fiction_viewer_stage>::iterator stage = Fiction_viewer_stages.begin(); stage != Fiction_viewer_stages.end(); ++stage)
713  {
714  fout("\n");
715 
716  // save file
717  required_string_fred("$File:");
718  parse_comments();
719  fout(" %s", stage->story_filename);
720 
721  // save font
722  if (strlen(stage->font_filename) > 0) //-V805
723  {
724  if (optional_string_fred("$Font:"))
725  parse_comments();
726  else
727  fout("\n$Font:");
728  fout(" %s", stage->font_filename);
729  }
730  else
731  optional_string_fred("$Font:");
732 
733  // save voice
734  if (strlen(stage->voice_filename) > 0) //-V805
735  {
736  if (optional_string_fred("$Voice:"))
737  parse_comments();
738  else
739  fout("\n$Voice:");
740  fout(" %s", stage->voice_filename);
741  }
742  else
743  optional_string_fred("$Voice:");
744 
745  // save UI
746  if (strlen(stage->ui_name) > 0)
747  {
748  if (optional_string_fred("$UI:"))
749  parse_comments();
750  else
751  fout("\n$UI:");
752  fout(" %s", stage->ui_name);
753  }
754  else
755  optional_string_fred("$UI:");
756 
757  // save background
758  save_custom_bitmap("$Background 640:", "$Background 1024:", stage->background[GR_640], stage->background[GR_1024]);
759 
760  // save sexp formula if we have one
761  if (stage->formula >= 0 && stage->formula != Locked_sexp_true)
762  {
763  SCP_string sexp_out;
764  convert_sexp_to_string(sexp_out, stage->formula, SEXP_SAVE_MODE);
765 
766  if (optional_string_fred("$Formula:"))
767  parse_comments();
768  else
769  fout("\n$Formula:");
770  fout(" %s", sexp_out.c_str());
771  }
772  else
773  optional_string_fred("$Formula:");
774  }
775  }
776  else
777  {
778  MessageBox(NULL, "Warning: This mission contains fiction viewer data, but you are saving in the retail mission format.", "Incompatibility with retail mission format", MB_OK);
779  }
780  }
781 
782  fso_comment_pop(true);
783 
784  return err;
785 }
786 
787 int CFred_mission_save::save_cmd_brief()
788 {
789  int stage;
790 
791  stage = 0;
792  required_string_fred("#Command Briefing");
793  parse_comments(2);
794 
796  return err; // no command briefings allowed in multiplayer missions.
797 
798  save_custom_bitmap("$Background 640:", "$Background 1024:", Cur_cmd_brief->background[GR_640], Cur_cmd_brief->background[GR_1024], 1);
799 
800  for (stage=0; stage<Cur_cmd_brief->num_stages; stage++) {
801  required_string_fred("$Stage Text:");
802  parse_comments(2);
803 
804  // XSTR
805  fout_ext("\n", "%s", Cur_cmd_brief->stage[stage].text.c_str());
806 
807  required_string_fred("$end_multi_text", "$Stage Text:");
808  parse_comments();
809 
810  required_string_fred("$Ani Filename:");
811  parse_comments();
812  fout(" %s", Cur_cmd_brief->stage[stage].ani_filename);
813 
814  required_string_fred("+Wave Filename:", "$Ani Filename:");
815  parse_comments();
816  fout(" %s", Cur_cmd_brief->stage[stage].wave_filename);
817 
818  fso_comment_pop();
819  }
820 
821  fso_comment_pop(true);
822 
823  return err;
824 }
825 
826 int CFred_mission_save::save_cmd_briefs()
827 {
828  int i;
829 
830  for (i=0; i<Num_teams; i++) {
832  save_cmd_brief();
833  }
834 
835  return err;
836 }
837 
838 int CFred_mission_save::save_briefing()
839 {
840  int i,j,k, nb;
841  SCP_string sexp_out;
842  brief_stage *bs;
843  brief_icon *bi;
844 
845  for ( nb = 0; nb < Num_teams; nb++ ) {
846 
847  required_string_fred("#Briefing");
848  parse_comments(2);
849 
850  required_string_fred("$start_briefing");
851  parse_comments();
852 
853  save_custom_bitmap("$briefing_background_640:", "$briefing_background_1024:", Briefings[nb].background[GR_640], Briefings[nb].background[GR_1024]);
854  save_custom_bitmap("$ship_select_background_640:", "$ship_select_background_1024:", Briefings[nb].ship_select_background[GR_640], Briefings[nb].ship_select_background[GR_1024]);
855  save_custom_bitmap("$weapon_select_background_640:", "$weapon_select_background_1024:", Briefings[nb].weapon_select_background[GR_640], Briefings[nb].weapon_select_background[GR_1024]);
856 
857  Assert(Briefings[nb].num_stages <= MAX_BRIEF_STAGES);
858  required_string_fred("$num_stages:");
859  parse_comments();
860  fout(" %d", Briefings[nb].num_stages);
861 
862  for (i=0; i<Briefings[nb].num_stages; i++) {
863  bs = &Briefings[nb].stages[i];
864 
865  required_string_fred("$start_stage");
866  parse_comments();
867 
868  required_string_fred("$multi_text");
869  parse_comments();
870 
871  // XSTR
872  fout_ext("\n", "%s", bs->text.c_str());
873 
874  required_string_fred("$end_multi_text", "$start_stage");
875  parse_comments();
876 
877  if (!drop_white_space(bs->voice)[0]){
878  strcpy_s(bs->voice, "None");
879  }
880 
881  required_string_fred("$voice:");
882  parse_comments();
883  fout(" %s", bs->voice);
884 
885  required_string_fred("$camera_pos:");
886  parse_comments();
887  save_vector(bs->camera_pos);
888 
889  required_string_fred("$camera_orient:");
890  parse_comments();
891  save_matrix(bs->camera_orient);
892 
893  required_string_fred("$camera_time:");
894  parse_comments();
895  fout(" %d", bs->camera_time);
896 
897  required_string_fred("$num_lines:");
898  parse_comments();
899  fout(" %d", bs->num_lines);
900 
901  for (k=0; k<bs->num_lines; k++) {
902  required_string_fred("$line_start:");
903  parse_comments();
904  fout(" %d", bs->lines[k].start_icon);
905 
906  required_string_fred("$line_end:");
907  parse_comments();
908  fout(" %d", bs->lines[k].end_icon);
909 
910  fso_comment_pop();
911  }
912 
913  required_string_fred("$num_icons:");
914  parse_comments();
916  fout(" %d", bs->num_icons);
917 
918  required_string_fred("$Flags:");
919  parse_comments();
920  fout(" %d", bs->flags);
921 
922  required_string_fred("$Formula:");
923  parse_comments();
925  fout(" %s", sexp_out.c_str());
926 
927  for ( j = 0; j < bs->num_icons; j++ ) {
928  bi = &bs->icons[j];
929 
930  required_string_fred("$start_icon");
931  parse_comments();
932 
933  required_string_fred("$type:");
934  parse_comments();
935  fout(" %d", bi->type);
936 
937  required_string_fred("$team:");
938  parse_comments();
939  fout(" %s", Iff_info[bi->team].iff_name);
940 
941  required_string_fred("$class:");
942  parse_comments();
943  if (bi->ship_class < 0)
944  bi->ship_class = 0;
945 
946  fout(" %s", Ship_info[bi->ship_class].name);
947 
948  required_string_fred("$pos:");
949  parse_comments();
950  save_vector(bi->pos);
951 
952  if (drop_white_space(bi->label)[0]) {
953  if (optional_string_fred("$label:"))
954  parse_comments();
955  else
956  fout("\n$label:");
957 
958  fout(" %s", bi->label);
959  }
960 
961  if (optional_string_fred("+id:"))
962  parse_comments();
963  else
964  fout("\n+id:");
965 
966  fout(" %d", bi->id);
967 
968  required_string_fred("$hlight:");
969  parse_comments();
970  fout(" %d", (bi->flags & BI_HIGHLIGHT)?1:0 );
971 
973  {
974  required_string_fred("$mirror:");
975  parse_comments();
976  fout(" %d", (bi->flags & BI_MIRROR_ICON)?1:0 );
977  }
978 
980  {
981  required_string_fred("$use wing icon:");
982  parse_comments();
983  fout(" %d", (bi->flags & BI_USE_WING_ICON)?1:0 );
984  }
985 
986  required_string_fred("$multi_text");
987  parse_comments();
988 
989 // sprintf(out,"\n%s", bi->text);
990 // fout(out);
991 
992  required_string_fred("$end_multi_text");
993  parse_comments();
994 
995  required_string_fred("$end_icon");
996  parse_comments();
997 
998  fso_comment_pop();
999  }
1000 
1001  required_string_fred("$end_stage");
1002  parse_comments();
1003 
1004  fso_comment_pop();
1005  }
1006  required_string_fred("$end_briefing");
1007  parse_comments();
1008 
1009  fso_comment_pop();
1010  }
1011 
1012  fso_comment_pop(true);
1013 
1014  return err;
1015 }
1016 
1017 int CFred_mission_save::save_debriefing()
1018 {
1019  int j, i;
1020  SCP_string sexp_out;
1021 
1022  for ( j = 0; j < Num_teams; j++ ) {
1023 
1024  Debriefing = &Debriefings[j];
1025 
1026  required_string_fred("#Debriefing_info");
1027  parse_comments(2);
1028 
1029  save_custom_bitmap("$Background 640:", "$Background 1024:", Debriefing->background[GR_640], Debriefing->background[GR_1024], 1);
1030 
1031  required_string_fred("$Num stages:");
1032  parse_comments(2);
1033  fout(" %d", Debriefing->num_stages);
1034 
1035  for (i=0; i<Debriefing->num_stages; i++) {
1036  required_string_fred("$Formula:");
1037  parse_comments(2);
1039  fout(" %s", sexp_out.c_str());
1040 
1041  // XSTR
1042  required_string_fred("$Multi text");
1043  parse_comments();
1044  fout_ext("\n ", "%s", Debriefing->stages[i].text.c_str());
1045 
1046  required_string_fred("$end_multi_text");
1047  parse_comments();
1048 
1049  if (!drop_white_space(Debriefing->stages[i].voice)[0]){
1050  strcpy_s(Debriefing->stages[i].voice, "None");
1051  }
1052 
1053  required_string_fred("$Voice:");
1054  parse_comments();
1055  fout(" %s", Debriefing->stages[i].voice);
1056 
1057  // XSTR
1058  required_string_fred("$Recommendation text:");
1059  parse_comments();
1060  fout_ext("\n ", "%s", Debriefing->stages[i].recommendation_text.c_str());
1061 
1062  required_string_fred("$end_multi_text");
1063  parse_comments();
1064 
1065  fso_comment_pop();
1066  }
1067  }
1068 
1069  fso_comment_pop(true);
1070 
1071  return err;
1072 }
1073 
1075 // save variables
1076 int CFred_mission_save::save_variables()
1077 {
1078  char *type;
1079  char number[] = "number";
1080  char string[] = "string";
1081  char block[] = "block";
1082  int i;
1083  int num_block_vars = 0;
1084 
1085  // sort sexp_variables
1087 
1088  // get count
1089  int num_variables = sexp_variable_count();
1090 
1093  num_block_vars = num_block_variables();
1094  }
1095  int total_variables = num_variables + num_block_vars;
1096 
1097  if (total_variables > 0) {
1098 
1099  // write 'em out
1100  required_string_fred("#Sexp_variables");
1101  parse_comments(2);
1102 
1103  required_string_fred("$Variables:");
1104  parse_comments(2);
1105 
1106  fout("\n(");
1107 // parse_comments();
1108 
1109  for (i=0; i<num_variables; i++) {
1110  if (Sexp_variables[i].type & SEXP_VARIABLE_NUMBER) {
1111  type = number;
1112  } else {
1113  type = string;
1114  }
1115  // index "var name" "default" "type"
1116  fout("\n\t\t%d\t\t\"%s\"\t\t\"%s\"\t\t\"%s\"", i, Sexp_variables[i].variable_name, Sexp_variables[i].text, type);
1117 
1118  // persistent and network variables
1120  {
1121  // Network variable - Karajorma
1122  if (Sexp_variables[i].type & SEXP_VARIABLE_NETWORK) {
1123  fout("\t\t\"%s\"", "network-variable");
1124  }
1125 
1126  // player-persistent - Goober5000
1128  fout("\t\t\"%s\"", "player-persistent");
1129  // campaign-persistent - Goober5000
1130  } else if (Sexp_variables[i].type & SEXP_VARIABLE_CAMPAIGN_PERSISTENT) {
1131  fout("\t\t\"%s\"", "campaign-persistent");
1132  }
1133  }
1134 
1135 // parse_comments();
1136  }
1137 
1138  for (i=MAX_SEXP_VARIABLES-num_block_vars; i<MAX_SEXP_VARIABLES; i++) {
1139  type = block;
1140  fout("\n\t\t%d\t\t\"%s\"\t\t\"%s\"\t\t\"%s\"", i, Block_variables[i].variable_name, Block_variables[i].text, type);
1141  }
1142 
1143  fout("\n)");
1144 
1145  fso_comment_pop();
1146  }
1147 
1148  fso_comment_pop(true);
1149 
1150  return err;
1151 }
1152 
1153 
1154 int CFred_mission_save::save_players()
1155 {
1156  int i, j;
1157  int var_idx;
1158  int used_pool[MAX_WEAPON_TYPES];
1159 
1160  if (optional_string_fred("#Alternate Types:")) { // Make sure the parser doesn't get out of sync
1161  required_string_fred("#end");
1162  }
1163  if (optional_string_fred("#Callsigns:")) {
1164  required_string_fred("#end");
1165  }
1166 
1167  // write out alternate name list
1168  if(Mission_alt_type_count > 0){
1169  fout("\n\n#Alternate Types:\n");
1170 
1171  // write them all out
1172  for(i=0; i<Mission_alt_type_count; i++){
1173  fout("$Alt: %s\n", Mission_alt_types[i]);
1174  }
1175 
1176  // end
1177  fout("\n#end\n");
1178  }
1179 
1180  // write out callsign list
1181  if(Mission_callsign_count > 0){
1182  fout("\n\n#Callsigns:\n");
1183 
1184  // write them all out
1185  for(i=0; i<Mission_callsign_count; i++){
1186  fout("$Callsign: %s\n", Mission_callsigns[i]);
1187  }
1188 
1189  // end
1190  fout("\n#end\n");
1191  }
1192 
1193  required_string_fred("#Players");
1194  parse_comments(2);
1195  fout("\t\t;! %d total\n", Player_starts);
1196 
1197  for (i=0; i<Num_teams; i++) {
1198  required_string_fred("$Starting Shipname:");
1199  parse_comments();
1201  fout(" %s", Ships[Player_start_shipnum].ship_name);
1202 
1203  required_string_fred("$Ship Choices:");
1204  parse_comments();
1205  fout(" (\n");
1206 
1207  for (j=0; j<Team_data[i].num_ship_choices; j++) {
1208  // Check to see if a variable name should be written for the class rather than a number
1209  if (strlen(Team_data[i].ship_list_variables[j])) {
1210  var_idx = get_index_sexp_variable_name(Team_data[i].ship_list_variables[j]);
1211  Assert (var_idx > -1 && var_idx < MAX_SEXP_VARIABLES);
1212 
1213  fout("\t@%s\t", Sexp_variables[var_idx].variable_name);
1214  }
1215  else {
1216  fout("\t\"%s\"\t", Ship_info[Team_data[i].ship_list[j]].name);
1217  }
1218 
1219  // Now check if we should write a variable or a number for the amount of ships available
1220  if (strlen(Team_data[i].ship_count_variables[j])) {
1221  var_idx = get_index_sexp_variable_name(Team_data[i].ship_count_variables[j]);
1222  Assert (var_idx > -1 && var_idx < MAX_SEXP_VARIABLES);
1223 
1224  fout("@%s\n", Sexp_variables[var_idx].variable_name);
1225  }
1226  else {
1227  fout("%d\n", Team_data[i].ship_count[j]);
1228  }
1229  }
1230 
1231  fout(")");
1232 
1233  if (optional_string_fred("+Weaponry Pool:", "$Starting Shipname:")){
1234  parse_comments(2);
1235  } else {
1236  fout("\n\n+Weaponry Pool:");
1237  }
1238 
1239  fout(" (\n");
1240  generate_weaponry_usage_list(i, used_pool);
1241  for (j=0; j<Team_data[i].num_weapon_choices; j++) {
1242  // first output the weapon name or a variable that sets it
1243  if (strlen(Team_data[i].weaponry_pool_variable[j])) {
1244  var_idx = get_index_sexp_variable_name(Team_data[i].weaponry_pool_variable[j]);
1245  Assert (var_idx > -1 && var_idx < MAX_SEXP_VARIABLES);
1246 
1247  fout("\t@%s\t", Sexp_variables[var_idx].variable_name);
1248  }
1249  else {
1250  fout("\t\"%s\"\t", Weapon_info[Team_data[i].weaponry_pool[j]].name);
1251  }
1252 
1253  // now output the amount of this weapon or a variable that sets it. If this weapon is in the used pool and isn't
1254  // set by a variable we should add the amount of weapons used by the wings to it and zero the entry so we know
1255  // that we have dealt with it
1256  if (strlen(Team_data[i].weaponry_amount_variable[j])) {
1257  var_idx = get_index_sexp_variable_name(Team_data[i].weaponry_amount_variable[j]);
1258  Assert (var_idx > -1 && var_idx < MAX_SEXP_VARIABLES);
1259 
1260  fout ("@%s\n", Sexp_variables[var_idx].variable_name);
1261  }
1262  else {
1263  if (strlen(Team_data[i].weaponry_pool_variable[j])) {
1264  fout ("%d\n", Team_data[i].weaponry_count[j]);
1265  }
1266  else {
1267  fout ("%d\n", Team_data[i].weaponry_count[j] + used_pool[Team_data[i].weaponry_pool[j]]);
1268  used_pool[Team_data[i].weaponry_pool[j]] = 0;
1269  }
1270  }
1271  }
1272 
1273  // now we add anything left in the used pool as a static entry
1274  for (j=0; j<Num_weapon_types; j++){
1275  if (used_pool[j] > 0){
1276  fout("\t\"%s\"\t%d\n", Weapon_info[j].name, used_pool[j]);
1277  }
1278  }
1279 
1280  fout(")");
1281 
1282  // Goober5000 - mjn.mixael's required weapon feature
1283  bool uses_required_weapon = false;
1284  for (j=0; j<MAX_WEAPON_TYPES; j++)
1285  {
1286  if (Team_data[i].weapon_required[j])
1287  {
1288  uses_required_weapon = true;
1289  break;
1290  }
1291  }
1292  if (uses_required_weapon)
1293  {
1294  if (optional_string_fred("+Required for mission:", "$Starting Shipname:"))
1295  parse_comments(2);
1296  else
1297  fout("\n+Required for mission:");
1298 
1299  fout(" (");
1300  for (j=0; j<MAX_WEAPON_TYPES; j++)
1301  {
1302  if (Team_data[i].weapon_required[j])
1303  fout(" \"%s\"", Weapon_info[j].name);
1304  }
1305  fout(" )");
1306  }
1307 
1308  fso_comment_pop();
1309  }
1310 
1311  fso_comment_pop(true);
1312 
1313  return err;
1314 }
1315 
1316 // Goober5000
1317 void CFred_mission_save::save_single_dock_instance(ship *shipp, dock_instance *dock_ptr)
1318 {
1319  Assert(shipp && dock_ptr);
1320  Assert(dock_ptr->docked_objp->type == OBJ_SHIP || dock_ptr->docked_objp->type == OBJ_START);
1321 
1322  // get ships and objects
1323  object *objp = &Objects[shipp->objnum];
1324  object *other_objp = dock_ptr->docked_objp;
1325  ship *other_shipp = &Ships[other_objp->instance];
1326 
1327  // write other ship
1328  if (optional_string_fred("+Docked With:", "$Name:"))
1329  parse_comments();
1330  else
1331  fout("\n+Docked With:");
1332  fout(" %s", other_shipp->ship_name);
1333 
1334 
1335  // Goober5000 - hm, Volition seems to have reversed docker and dockee here
1336 
1337  // write docker (actually dockee) point
1338  required_string_fred("$Docker Point:", "$Name:");
1339  parse_comments();
1340  fout(" %s", model_get_dock_name(Ship_info[other_shipp->ship_info_index].model_num, dock_find_dockpoint_used_by_object(other_objp, objp)));
1341 
1342  // write dockee (actually docker) point
1343  required_string_fred("$Dockee Point:", "$Name:");
1344  parse_comments();
1345  fout(" %s", model_get_dock_name(Ship_info[shipp->ship_info_index].model_num, dock_find_dockpoint_used_by_object(objp, other_objp)));
1346 
1347  fso_comment_pop(true);
1348 }
1349 
1350 int CFred_mission_save::save_objects()
1351 {
1352  SCP_string sexp_out;
1353  int i, j, z;
1354  ai_info *aip;
1355  object *objp;
1356  ship *shipp;
1357  ship_info *sip;
1358 
1359  required_string_fred("#Objects");
1360  parse_comments(2);
1361  fout("\t\t;! %d total\n", ship_get_num_ships() );
1362 
1363  for (i=z=0; i<MAX_SHIPS; i++) {
1364  if (Ships[i].objnum < 0){
1365  continue;
1366  }
1367 
1368  j = Objects[Ships[i].objnum].type;
1369  if ((j != OBJ_SHIP) && (j != OBJ_START)){
1370  continue;
1371  }
1372 
1373  shipp = &Ships[i];
1374  objp = &Objects[shipp->objnum];
1375  sip = &Ship_info[shipp->ship_info_index];
1376  required_string_either_fred("$Name:", "#Wings");
1377  required_string_fred("$Name:");
1378  parse_comments(z ? 2 : 1);
1379  fout(" %s\t\t;! Object #%d\n", shipp->ship_name, i);
1380 
1381  required_string_fred("$Class:");
1382  parse_comments(0);
1383  fout(" %s", Ship_info[shipp->ship_info_index].name);
1384 
1385  //alt classes stuff
1387  for (SCP_vector<alt_class>::iterator ii = shipp->s_alt_classes.begin(); ii != shipp->s_alt_classes.end(); ++ii) {
1388  // is this a variable?
1389  if (ii->variable_index != -1) {
1390  fout("\n$Alt Ship Class: @%s", Sexp_variables[ii->variable_index].variable_name);
1391  }
1392  else {
1393  fout("\n$Alt Ship Class: \"%s\"", Ship_info[ii->ship_class].name);
1394  }
1395 
1396  // default class?
1397  if (ii->default_to_this_class) {
1398  fout("\n+Default Class:");
1399  }
1400  }
1401  }
1402 
1403  // optional alternate type name
1404  if(strlen(Fred_alt_names[i])){
1405  fout("\n$Alt: %s\n", Fred_alt_names[i]);
1406  }
1407 
1408  // optional callsign
1409  if(strlen(Fred_callsigns[i])){
1410  fout("\n$Callsign: %s\n", Fred_callsigns[i]);
1411  }
1412 
1413  required_string_fred("$Team:");
1414  parse_comments();
1415  fout(" %s", Iff_info[shipp->team].iff_name);
1416 
1417  if (Ship_info[shipp->ship_info_index].uses_team_colors) {
1418  required_string_fred("$Team Color Setting:");
1419  parse_comments();
1420  fout(" %s", shipp->team_name.c_str());
1421  }
1422 
1423  required_string_fred("$Location:");
1424  parse_comments();
1425  save_vector(Objects[shipp->objnum].pos);
1426 
1427  required_string_fred("$Orientation:");
1428  parse_comments();
1429  save_matrix(Objects[shipp->objnum].orient);
1430 
1431  if (Format_fs2_retail)
1432  {
1433  required_string_fred("$IFF:");
1434  parse_comments();
1435  fout(" %s", "IFF 1");
1436  }
1437 
1438  Assert(shipp->ai_index >= 0);
1439  aip = &Ai_info[shipp->ai_index];
1440 
1441  required_string_fred("$AI Behavior:");
1442  parse_comments();
1443  fout(" %s", Ai_behavior_names[aip->behavior]);
1444 
1445  if (shipp->weapons.ai_class != Ship_info[shipp->ship_info_index].ai_class) {
1446  if (optional_string_fred("+AI Class:", "$Name:"))
1447  parse_comments();
1448  else
1449  fout("\n+AI Class:");
1450 
1451  fout(" %s", Ai_class_names[shipp->weapons.ai_class]);
1452  }
1453 
1454  save_ai_goals(Ai_info[shipp->ai_index].goals, i);
1455 
1456  // XSTR
1457  required_string_fred("$Cargo 1:");
1458  parse_comments();
1459  fout_ext(" ", "%s", Cargo_names[shipp->cargo1]);
1460 
1461  save_common_object_data(&Objects[shipp->objnum], &Ships[i]);
1462 
1463  if (shipp->wingnum >= 0){
1465  }
1466 
1467  required_string_fred("$Arrival Location:");
1468  parse_comments();
1470 
1471  if (shipp->arrival_location != ARRIVE_AT_LOCATION)
1472  {
1473  if (optional_string_fred("+Arrival Distance:", "$Name:")){
1474  parse_comments();
1475  } else {
1476  fout("\n+Arrival Distance:");
1477  }
1478 
1479  fout(" %d", shipp->arrival_distance);
1480  if (optional_string_fred("$Arrival Anchor:", "$Name:")){
1481  parse_comments();
1482  } else {
1483  fout("\n$Arrival Anchor:");
1484  }
1485 
1486  z = shipp->arrival_anchor;
1488  {
1489  // get name
1490  char tmp[NAME_LENGTH + 15];
1492 
1493  // save it
1494  fout(" %s", tmp);
1495  }
1496  else if (z >= 0)
1497  {
1498  fout(" %s", Ships[z].ship_name);
1499  }
1500  else
1501  {
1502  fout(" <error>");
1503  }
1504  }
1505 
1506  // Goober5000
1508  {
1509  if ((shipp->arrival_location == ARRIVE_FROM_DOCK_BAY) && (shipp->arrival_path_mask > 0))
1510  {
1511  int j, anchor_shipnum;
1512  polymodel *pm;
1513 
1514  anchor_shipnum = shipp->arrival_anchor;
1515  Assert(anchor_shipnum >= 0 && anchor_shipnum < MAX_SHIPS);
1516 
1517  fout("\n+Arrival Paths: ( ");
1518 
1519  pm = model_get(Ship_info[Ships[anchor_shipnum].ship_info_index].model_num);
1520  for (j = 0; j < pm->ship_bay->num_paths; j++)
1521  {
1522  if (shipp->arrival_path_mask & (1 << j))
1523  {
1524  fout("\"%s\" ", pm->paths[pm->ship_bay->path_indexes[j]].name);
1525  }
1526  }
1527 
1528  fout(")");
1529  }
1530  }
1531 
1532  if (shipp->arrival_delay)
1533  {
1534  if (optional_string_fred("+Arrival Delay:", "$Name:"))
1535  parse_comments();
1536  else
1537  fout("\n+Arrival Delay:");
1538 
1539  fout(" %d", shipp->arrival_delay);
1540  }
1541 
1542  required_string_fred("$Arrival Cue:");
1543  parse_comments();
1545  fout(" %s", sexp_out.c_str());
1546 
1547  required_string_fred("$Departure Location:");
1548  parse_comments();
1550 
1551  if ( shipp->departure_location != DEPART_AT_LOCATION )
1552  {
1553  required_string_fred("$Departure Anchor:");
1554  parse_comments();
1555 
1556  if ( shipp->departure_anchor >= 0 )
1557  fout(" %s", Ships[shipp->departure_anchor].ship_name );
1558  else
1559  fout(" <error>");
1560  }
1561 
1562  // Goober5000
1564  {
1565  if ((shipp->departure_location == DEPART_AT_DOCK_BAY) && (shipp->departure_path_mask > 0))
1566  {
1567  int j, anchor_shipnum;
1568  polymodel *pm;
1569 
1570  anchor_shipnum = shipp->departure_anchor;
1571  Assert(anchor_shipnum >= 0 && anchor_shipnum < MAX_SHIPS);
1572 
1573  fout("\n+Departure Paths: ( ");
1574 
1575  pm = model_get(Ship_info[Ships[anchor_shipnum].ship_info_index].model_num);
1576  for (j = 0; j < pm->ship_bay->num_paths; j++)
1577  {
1578  if (shipp->departure_path_mask & (1 << j))
1579  {
1580  fout("\"%s\" ", pm->paths[pm->ship_bay->path_indexes[j]].name);
1581  }
1582  }
1583 
1584  fout(")");
1585  }
1586  }
1587 
1588  if (shipp->departure_delay)
1589  {
1590  if (optional_string_fred("+Departure delay:", "$Name:"))
1591  parse_comments();
1592  else
1593  fout("\n+Departure delay:");
1594 
1595  fout(" %d", shipp->departure_delay);
1596  }
1597 
1598  required_string_fred("$Departure Cue:");
1599  parse_comments();
1601  fout(" %s", sexp_out.c_str());
1602 
1603  required_string_fred("$Determination:");
1604  parse_comments();
1605  fout(" 10"); // dummy value for backwards compatibility
1606 
1607  if (optional_string_fred("+Flags:", "$Name:")) {
1608  parse_comments();
1609  fout (" (");
1610  } else
1611  fout("\n+Flags: (");
1612 
1613  if (shipp->flags & SF_CARGO_REVEALED)
1614  fout(" \"cargo-known\"");
1615  if (shipp->flags & SF_IGNORE_COUNT)
1616  fout(" \"ignore-count\"");
1617  if (objp->flags & OF_PROTECTED)
1618  fout(" \"protect-ship\"");
1619  if (shipp->flags & SF_REINFORCEMENT)
1620  fout(" \"reinforcement\"");
1621  if (objp->flags & OF_NO_SHIELDS)
1622  fout(" \"no-shields\"");
1623  if (shipp->flags & SF_ESCORT)
1624  fout(" \"escort\"");
1625  if (objp->type == OBJ_START)
1626  fout(" \"player-start\"");
1627  if (shipp->flags & SF_NO_ARRIVAL_MUSIC)
1628  fout(" \"no-arrival-music\"");
1629  if (shipp->flags & SF_NO_ARRIVAL_WARP)
1630  fout(" \"no-arrival-warp\"");
1631  if (shipp->flags & SF_NO_DEPARTURE_WARP)
1632  fout(" \"no-departure-warp\"");
1633  if (Objects[shipp->objnum].flags & OF_INVULNERABLE)
1634  fout(" \"invulnerable\"");
1635  if (shipp->flags & SF_HIDDEN_FROM_SENSORS)
1636  fout(" \"hidden-from-sensors\"");
1637  if (shipp->flags & SF_SCANNABLE)
1638  fout(" \"scannable\"");
1639  if (Ai_info[shipp->ai_index].ai_flags & AIF_KAMIKAZE)
1640  fout(" \"kamikaze\"");
1641  if (Ai_info[shipp->ai_index].ai_flags & AIF_NO_DYNAMIC)
1642  fout(" \"no-dynamic\"");
1643  if (shipp->flags & SF_RED_ALERT_STORE_STATUS)
1644  fout(" \"red-alert-carry\"");
1645  if (objp->flags & OF_BEAM_PROTECTED)
1646  fout(" \"beam-protect-ship\"");
1647  if (objp->flags & OF_FLAK_PROTECTED)
1648  fout(" \"flak-protect-ship\"");
1649  if (objp->flags & OF_LASER_PROTECTED)
1650  fout(" \"laser-protect-ship\"");
1651  if (objp->flags & OF_MISSILE_PROTECTED)
1652  fout(" \"missile-protect-ship\"");
1653  if (shipp->ship_guardian_threshold != 0)
1654  fout(" \"guardian\"");
1655  if (objp->flags & OF_SPECIAL_WARPIN)
1656  fout(" \"special-warp\"");
1657  if (shipp->flags & SF_VAPORIZE)
1658  fout(" \"vaporize\"");
1659  if (shipp->flags2 & SF2_STEALTH)
1660  fout(" \"stealth\"");
1661  if (shipp->flags2 & SF2_FRIENDLY_STEALTH_INVIS)
1662  fout(" \"friendly-stealth-invisible\"");
1663  if (shipp->flags2 & SF2_DONT_COLLIDE_INVIS)
1664  fout(" \"don't-collide-invisible\"");
1665  //for compatibility reasons ship locked or weapons locked are saved as both locked in retail mode
1666  if ((Format_fs2_open == FSO_FORMAT_RETAIL) && ((shipp->flags2 & SF2_SHIP_LOCKED) || (shipp->flags2 & SF2_WEAPONS_LOCKED)))
1667  fout(" \"locked\"");
1668  fout(" )");
1669 
1670  // flags2 added by Goober5000 --------------------------------
1672  {
1673  if (optional_string_fred("+Flags2:", "$Name:")) {
1674  parse_comments();
1675  fout (" (");
1676  } else
1677  fout("\n+Flags2: (");
1678 
1679  if (shipp->flags2 & SF2_PRIMITIVE_SENSORS)
1680  fout(" \"primitive-sensors\"");
1681  if (shipp->flags2 & SF2_NO_SUBSPACE_DRIVE)
1682  fout(" \"no-subspace-drive\"");
1683  if (shipp->flags2 & SF2_NAVPOINT_CARRY)
1684  fout(" \"nav-carry-status\"");
1685  if (shipp->flags2 & SF2_AFFECTED_BY_GRAVITY)
1686  fout(" \"affected-by-gravity\"");
1688  fout(" \"toggle-subsystem-scanning\"");
1689  if (objp->flags & OF_TARGETABLE_AS_BOMB)
1690  fout(" \"targetable-as-bomb\"");
1691  if (shipp->flags2 & SF2_NO_BUILTIN_MESSAGES)
1692  fout(" \"no-builtin-messages\"");
1693  if (shipp->flags2 & SF2_PRIMARIES_LOCKED)
1694  fout(" \"primaries-locked\"");
1695  if (shipp->flags2 & SF2_SECONDARIES_LOCKED)
1696  fout(" \"secondaries-locked\"");
1697  if (shipp->flags2 & SF2_NO_DEATH_SCREAM)
1698  fout(" \"no-death-scream\"");
1699  if (shipp->flags2 & SF2_ALWAYS_DEATH_SCREAM)
1700  fout(" \"always-death-scream\"");
1701  if (shipp->flags2 & SF2_NAVPOINT_NEEDSLINK)
1702  fout(" \"nav-needslink\"");
1703  if (shipp->flags2 & SF2_HIDE_SHIP_NAME)
1704  fout(" \"hide-ship-name\"");
1705  if (shipp->flags2 & SF2_SET_CLASS_DYNAMICALLY)
1706  fout(" \"set-class-dynamically\"");
1708  fout(" \"lock-all-turrets\"");
1709  if (shipp->flags2 & SF2_AFTERBURNER_LOCKED)
1710  fout(" \"afterburners-locked\"");
1711  if (shipp->flags2 & SF2_FORCE_SHIELDS_ON)
1712  fout(" \"force-shields-on\"");
1713  if (objp->flags & OF_IMMOBILE)
1714  fout(" \"immobile\"");
1715  if (shipp->flags2 & SF2_NO_ETS)
1716  fout(" \"no-ets\"");
1717  if (shipp->flags2 & SF2_CLOAKED)
1718  fout(" \"cloaked\"");
1719  if (shipp->flags2 & SF2_SHIP_LOCKED)
1720  fout(" \"ship-locked\"");
1721  if (shipp->flags2 & SF2_WEAPONS_LOCKED)
1722  fout(" \"weapons-locked\"");
1723  if (shipp->flags2 & SF2_SCRAMBLE_MESSAGES)
1724  fout(" \"scramble-messages\"");
1725  if (!(objp->flags & OF_COLLIDES))
1726  fout(" \"no-collide\"");
1728  fout(" \"no-disabled-self-destruct\"");
1729  fout(" )");
1730  }
1731  // -----------------------------------------------------------
1732 
1733  fout("\n+Respawn priority: %d", shipp->respawn_priority); // HA! Newline added by Goober5000
1734 
1735  if (shipp->flags & SF_ESCORT) {
1736  if (optional_string_fred("+Escort priority:", "$Name:")) {
1737  parse_comments();
1738  } else {
1739  fout("\n+Escort priority:");
1740  }
1741 
1742  fout(" %d", shipp->escort_priority);
1743  }
1744 
1745  // special explosions
1747  if (shipp->use_special_explosion) {
1748  fso_comment_push(";;FSO 3.6.13;;");
1749  if (optional_string_fred("$Special Explosion:", "$Name:")) {
1750  parse_comments();
1751 
1752  required_string_fred("+Special Exp Damage:");
1753  parse_comments();
1754  fout(" %d", shipp->special_exp_damage);
1755 
1756  required_string_fred("+Special Exp Blast:");
1757  parse_comments();
1758  fout(" %d", shipp->special_exp_blast);
1759 
1760  required_string_fred("+Special Exp Inner Radius:");
1761  parse_comments();
1762  fout(" %d", shipp->special_exp_inner);
1763 
1764  required_string_fred("+Special Exp Outer Radius:");
1765  parse_comments();
1766  fout(" %d", shipp->special_exp_outer);
1767 
1768  if (shipp->use_shockwave && (shipp->special_exp_shockwave_speed > 0)) {
1769  optional_string_fred("+Special Exp Shockwave Speed:");
1770  parse_comments();
1771  fout(" %d", shipp->special_exp_shockwave_speed);
1772  }
1773  else {
1774  bypass_comment(";;FSO 3.6.13;; +Special Exp Shockwave Speed:", "$Name:");
1775  }
1776 
1777  if (shipp->special_exp_deathroll_time > 0) {
1778  optional_string_fred("+Special Exp Death Roll Time:");
1779  parse_comments();
1780  fout(" %d", shipp->special_exp_deathroll_time);
1781  }
1782  else {
1783  bypass_comment(";;FSO 3.6.13;; +Special Exp Death Roll Time:", "$Name:");
1784  }
1785  }
1786  else {
1787  fout_version("\n$Special Explosion:");
1788 
1789  fout_version("\n+Special Exp Damage:");
1790  fout(" %d", shipp->special_exp_damage);
1791 
1792  fout_version("\n+Special Exp Blast:");
1793  fout(" %d", shipp->special_exp_blast);
1794 
1795  fout_version("\n+Special Exp Inner Radius:");
1796  fout(" %d", shipp->special_exp_inner);
1797 
1798  fout_version("\n+Special Exp Outer Radius:");
1799  fout(" %d", shipp->special_exp_outer);
1800 
1801  if (shipp->use_shockwave && (shipp->special_exp_shockwave_speed > 0)) {
1802  fout_version("\n+Special Exp Shockwave Speed:");
1803  fout(" %d", shipp->special_exp_shockwave_speed);
1804  }
1805 
1806  if (shipp->special_exp_deathroll_time > 0) {
1807  fout_version("\n+Special Exp Death Roll Time:");
1808  fout(" %d", shipp->special_exp_deathroll_time);
1809  }
1810 
1811  }
1812  fso_comment_pop();
1813  }
1814  else {
1815  bypass_comment(";;FSO 3.6.13;; +Special Exp Shockwave Speed:", "$Name:");
1816  bypass_comment(";;FSO 3.6.13;; +Special Exp Death Roll Time:", "$Name:");
1817  }
1818  }
1819  // retail format special explosions
1820  else {
1821  if (shipp->use_special_explosion) {
1822  int special_exp_index;
1823 
1824  if (has_special_explosion_block_index(&Ships[i], &special_exp_index)) {
1825  fout("\n+Special Exp index:");
1826  fout(" %d", special_exp_index);
1827  }
1828  else {
1829  CString text = "You are saving in the retail mission format, but ";
1830  text += "the mission has too many special explosions defined. \"";
1831  text += shipp->ship_name;
1832  text += "\" has therefore lost any special explosion data that was defined for it. ";
1833  text += "\" Either remove special explosions or SEXP variables if you need it to have one ";
1834  MessageBox(NULL, text, "Too many variables!", MB_OK);
1835 
1836  }
1837  }
1838  }
1839 
1840  // Goober5000 ------------------------------------------------
1842  {
1843  if (shipp->special_hitpoints) {
1844  fso_comment_push(";;FSO 3.6.13;;");
1845  if (optional_string_fred("+Special Hitpoints:", "$Name:")) {
1846  parse_comments();
1847  } else {
1848  fout_version("\n+Special Hitpoints:");
1849  }
1850  fso_comment_pop();
1851 
1852  fout(" %d", shipp->special_hitpoints);
1853  }
1854  else {
1855  bypass_comment(";;FSO 3.6.13;; +Special Hitpoints:", "$Name:");
1856  }
1857 
1858  if (shipp->special_shield >= 0) {
1859  fso_comment_push(";;FSO 3.6.13;;");
1860  if (optional_string_fred("+Special Shield Points:", "$Name:")) {
1861  parse_comments();
1862  } else {
1863  fout_version("\n+Special Shield Points:");
1864  }
1865  fso_comment_pop();
1866 
1867  fout(" %d", shipp->special_shield);
1868  }
1869  else {
1870  bypass_comment(";;FSO 3.6.13;; +Special Shield Points:", "$Name:");
1871  }
1872  }
1873  // -----------------------------------------------------------
1874 
1875  if ( Ai_info[shipp->ai_index].ai_flags & AIF_KAMIKAZE ) {
1876  if ( optional_string_fred("+Kamikaze Damage:", "$Name:")){
1877  parse_comments();
1878  } else {
1879  fout("\n+Kamikaze Damage:");
1880  }
1881 
1882  fout(" %d", Ai_info[shipp->ai_index].kamikaze_damage);
1883  }
1884 
1885  if (shipp->hotkey != -1) {
1886  if (optional_string_fred("+Hotkey:", "$Name:")){
1887  parse_comments();
1888  } else {
1889  fout("\n+Hotkey:");
1890  }
1891 
1892  fout(" %d", shipp->hotkey);
1893  }
1894 
1895  // mwa -- new code to save off information about initially docked ships.
1896  // Goober5000 - newer code to save off information about initially docked ships. ;)
1897  if (object_is_docked(&Objects[shipp->objnum]))
1898  {
1899  // possible incompatibility
1901  {
1902  static bool warned = false;
1903  if (!warned)
1904  {
1905  CString text = "You are saving in the retail mission format, but \"";
1906  text += shipp->ship_name;
1907  text += "\" is docked to more than one ship. If you wish to run this mission in retail, ";
1908  text += "you should remove the additional ships and save the mission again.";
1909  MessageBox(NULL, text, "Incompatibility with retail mission format", MB_OK);
1910 
1911  warned = true; // to avoid zillions of boxes
1912  }
1913  }
1914 
1915  // save one-on-one groups as if they were retail
1917  {
1918  // retail format only saved information for non-leaders
1919  if (!(shipp->flags & SF_DOCK_LEADER))
1920  {
1921  save_single_dock_instance(&Ships[i], Objects[shipp->objnum].dock_list);
1922  }
1923  }
1924  // multiply docked
1925  else
1926  {
1927  // save all instances for all ships
1928  for (dock_instance *dock_ptr = Objects[shipp->objnum].dock_list; dock_ptr != NULL; dock_ptr = dock_ptr->next)
1929  {
1930  save_single_dock_instance(&Ships[i], dock_ptr);
1931  }
1932  }
1933  }
1934 
1935  // check the ship flag about killing off the ship before a missino starts. Write out the appropriate
1936  // variable if necessary
1937  if ( shipp->flags & SF_KILL_BEFORE_MISSION ) {
1938  if ( optional_string_fred("+Destroy At:", "$Name:"))
1939  parse_comments();
1940  else
1941  fout("\n+Destroy At: ");
1942 
1943  fout(" %d", shipp->final_death_time );
1944  }
1945 
1946  // possibly write out the orders that this ship will accept. We'll only do it if the orders
1947  // are not the default set of orders
1949  if ( optional_string_fred("+Orders Accepted:", "$Name:") )
1950  parse_comments();
1951  else
1952  fout("\n+Orders Accepted:");
1953 
1954  fout(" %d\t\t;! note that this is a bitfield!!!", shipp->orders_accepted);
1955  }
1956 
1957  if (shipp->group >= 0) {
1958  if (optional_string_fred("+Group:", "$Name:"))
1959  parse_comments();
1960  else
1961  fout("\n+Group:");
1962 
1963  fout(" %d", shipp->group);
1964  }
1965 
1966  // always write out the score to ensure backwards compatibility. If the score is the same as the value
1967  // in the table write out a flag to tell the game to simply use whatever is in the table instead
1968  if (Ship_info[shipp->ship_info_index].score == shipp->score ) {
1969  fso_comment_push(";;FSO 3.6.10;;");
1970  if ( optional_string_fred("+Use Table Score:", "$Name:") ) {
1971  parse_comments();
1972  } else {
1973  fout_version("\n+Use Table Score:");
1974  }
1975  fso_comment_pop();
1976  }
1977  else {
1978  bypass_comment(";;FSO 3.6.10;; +Use Table Score:", "$Name:");
1979  }
1980 
1981  if (optional_string_fred("+Score:", "$Name:"))
1982  parse_comments();
1983  else
1984  fout("\n+Score:");
1985 
1986  fout(" %d", shipp->score);
1987 
1988 
1989  if (Format_fs2_open != FSO_FORMAT_RETAIL && shipp->assist_score_pct != 0) {
1990  fso_comment_push(";;FSO 3.6.10;;");
1991  if ( optional_string_fred("+Assist Score Percentage:") ) {
1992  parse_comments();
1993  } else {
1994  fout_version("\n+Assist Score Percentage:");
1995  }
1996  fso_comment_pop();
1997 
1998  fout(" %f", shipp->assist_score_pct);
1999  }
2000  else {
2001  bypass_comment(";;FSO 3.6.10;; +Assist Score Percentage:", "$Name:");
2002  }
2003 
2004  // deal with the persona for this ship as well.
2005  if ( shipp->persona_index != -1 ) {
2006  if (optional_string_fred("+Persona Index:", "$Name:"))
2007  parse_comments();
2008  else
2009  fout("\n+Persona Index:");
2010 
2011  fout(" %d", shipp->persona_index);
2012  }
2013 
2014  // Goober5000 - deal with texture replacement ----------------
2015  if (!Fred_texture_replacements.empty()) {
2016  bool needs_header = true;
2017  fso_comment_push(";;FSO 3.6.8;;");
2018 
2020  if ( !stricmp(shipp->ship_name, ii->ship_name) ) {
2021  if (needs_header) {
2022  if (optional_string_fred("$Texture Replace:")) {
2023  parse_comments(1);
2024  } else {
2025  fout_version("\n$Texture Replace:");
2026  }
2027 
2028  needs_header = false;
2029  }
2030 
2031  // write out this entry
2032  if (optional_string_fred("+old:")) {
2033  parse_comments(1);
2034  fout(" %s", ii->old_texture);
2035  } else {
2036  fout_version("\n+old: %s", ii->old_texture);
2037  }
2038 
2039  if (optional_string_fred("+new:")) {
2040  parse_comments(1);
2041  fout(" %s", ii->new_texture);
2042  } else {
2043  fout_version("\n+new: %s", ii->new_texture);
2044  }
2045  }
2046  }
2047 
2048  fso_comment_pop();
2049  } else {
2050  bypass_comment(";;FSO 3.6.8;; $Texture Replace:", "$Name:");
2051  }
2052 
2053  // end of texture replacement -------------------------------
2054 
2055  z++;
2056 
2057  fso_comment_pop();
2058  }
2059 
2060  fso_comment_pop(true);
2061 
2062  return err;
2063 }
2064 
2065 int CFred_mission_save::save_common_object_data(object *objp, ship *shipp)
2066 {
2067  int j, z;
2068  ship_subsys *ptr = NULL;
2069  ship_info *sip = NULL;
2070  ship_weapon *wp = NULL;
2071  float temp_max_hull_strength;
2072 
2073  sip = &Ship_info[shipp->ship_info_index];
2074 
2075  if ((int) objp->phys_info.speed) {
2076  if (optional_string_fred("+Initial Velocity:", "$Name:", "+Subsystem:"))
2077  parse_comments();
2078  else
2079  fout("\n+Initial Velocity:");
2080 
2081  fout(" %d", (int) objp->phys_info.speed);
2082  }
2083 
2084  // Goober5000
2086  {
2087  temp_max_hull_strength = (float)shipp->special_hitpoints;
2088  }
2089  else
2090  {
2091  temp_max_hull_strength = sip->max_hull_strength;
2092  }
2093 
2094  if ((int) objp->hull_strength != temp_max_hull_strength) {
2095  if (optional_string_fred("+Initial Hull:", "$Name:", "+Subsystem:"))
2096  parse_comments();
2097  else
2098  fout("\n+Initial Hull:");
2099 
2100  fout(" %d", (int) objp->hull_strength);
2101  }
2102 
2103  if ((int) shield_get_strength(objp) != 100) {
2104  if (optional_string_fred("+Initial Shields:", "$Name:", "+Subsystem:"))
2105  parse_comments();
2106  else
2107  fout("\n+Initial Shields:");
2108 
2109  fout(" %d", (int) objp->shield_quadrant[0]);
2110  }
2111 
2112  // save normal ship weapons info
2113  required_string_fred("+Subsystem:", "$Name:");
2114  parse_comments();
2115  fout(" Pilot");
2116 
2117  wp = &shipp->weapons;
2118  z = 0;
2119  j = wp->num_primary_banks;
2120  while (j-- && (j >= 0))
2121  if (wp->primary_bank_weapons[j] != sip->primary_bank_weapons[j])
2122  z = 1;
2123 
2124  if (z) {
2125  if (optional_string_fred("+Primary Banks:", "$Name:", "+Subsystem:"))
2126  parse_comments();
2127  else
2128  fout("\n+Primary Banks:");
2129 
2130  fout(" ( ");
2131  for (j=0; j<wp->num_primary_banks; j++) {
2132  if (wp->primary_bank_weapons[j] != -1) { // Just in case someone has set a weapon bank to empty
2133  fout("\"%s\" ", Weapon_info[wp->primary_bank_weapons[j]].name);
2134  } else {
2135  fout("\"\" ");
2136  }
2137  }
2138 
2139  fout(")");
2140  }
2141 
2142  z = 0;
2143  j = wp->num_secondary_banks;
2144  while (j-- && (j >= 0))
2145  if (wp->secondary_bank_weapons[j] != sip->secondary_bank_weapons[j])
2146  z = 1;
2147 
2148  if (z) {
2149  if (optional_string_fred("+Secondary Banks:", "$Name:", "+Subsystem:"))
2150  parse_comments();
2151  else
2152  fout("\n+Secondary Banks:");
2153 
2154  fout(" ( ");
2155  for (j=0; j<wp->num_secondary_banks; j++) {
2156  if (wp->secondary_bank_weapons[j] != -1) {
2157  fout("\"%s\" ", Weapon_info[wp->secondary_bank_weapons[j]].name);
2158  } else {
2159  fout("\"\" ");
2160  }
2161  }
2162 
2163  fout(")");
2164  }
2165 
2166  z = 0;
2167  j = wp->num_secondary_banks;
2168  while (j-- && (j >= 0))
2169  if (wp->secondary_bank_ammo[j] != 100)
2170  z = 1;
2171 
2172  if (z) {
2173  if (optional_string_fred("+Sbank Ammo:", "$Name:", "+Subsystem:"))
2174  parse_comments();
2175  else
2176  fout("\n+Sbank Ammo:");
2177 
2178  fout(" ( ");
2179  for (j=0; j<wp->num_secondary_banks; j++)
2180  fout("%d ", wp->secondary_bank_ammo[j]);
2181 
2182  fout(")");
2183  }
2184 
2185  ptr = GET_FIRST(&shipp->subsys_list);
2186  Assert(ptr);
2187 
2188  while (ptr != END_OF_LIST(&shipp->subsys_list) && ptr) {
2189  // Crashing here!
2190  if ( (ptr->current_hits) || (ptr->system_info && ptr->system_info->type == SUBSYSTEM_TURRET) || (ptr->subsys_cargo_name > 0)) {
2191  if (optional_string_fred("+Subsystem:", "$Name:"))
2192  parse_comments();
2193  else
2194  fout("\n+Subsystem:");
2195 
2196  fout(" %s", ptr->system_info->subobj_name);
2197  }
2198 
2199  if (ptr->current_hits) {
2200  if (optional_string_fred("$Damage:", "$Name:", "+Subsystem:"))
2201  parse_comments();
2202  else
2203  fout("\n$Damage:");
2204 
2205  fout(" %d", (int) ptr->current_hits);
2206  }
2207 
2208  if (ptr->subsys_cargo_name > 0) {
2209  if (optional_string_fred("+Cargo Name:", "$Name:", "+Subsystem:"))
2210  parse_comments();
2211  else
2212  fout("\n+Cargo Name:");
2213 
2214  fout_ext(NULL, "%s", Cargo_names[ptr->subsys_cargo_name]);
2215  }
2216 
2217  if (ptr->system_info->type == SUBSYSTEM_TURRET)
2218  save_turret_info(ptr, shipp - Ships);
2219 
2220  ptr = GET_NEXT(ptr);
2221 
2222  fso_comment_pop();
2223  }
2224 
2225 /* for (j=0; j<shipp->status_count; j++) {
2226  required_string_fred("$Status Description:");
2227  parse_comments(-1);
2228  fout(" %s", Status_desc_names[shipp->status_type[j]]);
2229 
2230  required_string_fred("$Status:");
2231  parse_comments(-1);
2232  fout(" %s", Status_type_names[shipp->status[j]]);
2233 
2234  required_string_fred("$Target:");
2235  parse_comments(-1);
2236  fout(" %s", Status_target_names[shipp->target[j]]);
2237  }*/
2238 
2239  fso_comment_pop(true);
2240 
2241  return err;
2242 }
2243 
2244 int CFred_mission_save::save_wings()
2245 {
2246  SCP_string sexp_out;
2247  int i, j, z, ship, count = 0;
2248 
2249  fred_parse_flag = 0;
2250  required_string_fred("#Wings");
2251  parse_comments(2);
2252  fout("\t\t;! %d total", Num_wings);
2253 
2254  for (i=0; i<MAX_WINGS; i++) {
2255  if (!Wings[i].wave_count)
2256  continue;
2257 
2258  count++;
2259  required_string_either_fred("$Name:", "#Events");
2260  required_string_fred("$Name:");
2261  parse_comments(2);
2262  fout(" %s", Wings[i].name);
2263 
2264  // squad logo - Goober5000
2266  {
2267  if (strlen(Wings[i].wing_squad_filename) > 0) //-V805
2268  {
2269  if (optional_string_fred("+Squad Logo:", "$Name:"))
2270  parse_comments();
2271  else
2272  fout("\n+Squad Logo:");
2273 
2274  fout(" %s", Wings[i].wing_squad_filename);
2275  }
2276  }
2277 
2278  required_string_fred("$Waves:");
2279  parse_comments();
2280  fout(" %d", Wings[i].num_waves);
2281 
2282  required_string_fred("$Wave Threshold:");
2283  parse_comments();
2284  fout(" %d", Wings[i].threshold);
2285 
2286  required_string_fred("$Special Ship:");
2287  parse_comments();
2288  fout(" %d\t\t;! %s\n", Wings[i].special_ship,
2289  Ships[Wings[i].ship_index[Wings[i].special_ship]].ship_name);
2290 
2291  required_string_fred("$Arrival Location:");
2292  parse_comments();
2293  fout(" %s", Arrival_location_names[Wings[i].arrival_location]);
2294 
2295  if (Wings[i].arrival_location != ARRIVE_AT_LOCATION)
2296  {
2297  if (optional_string_fred("+Arrival Distance:", "$Name:"))
2298  parse_comments();
2299  else
2300  fout("\n+Arrival Distance:");
2301 
2302  fout(" %d", Wings[i].arrival_distance);
2303  if (optional_string_fred("$Arrival Anchor:", "$Name:"))
2304  parse_comments();
2305  else
2306  fout("\n$Arrival Anchor:");
2307 
2308  z = Wings[i].arrival_anchor;
2309  if (z & SPECIAL_ARRIVAL_ANCHOR_FLAG)
2310  {
2311  // get name
2312  char tmp[NAME_LENGTH + 15];
2314 
2315  // save it
2316  fout(" %s", tmp);
2317  }
2318  else if (z >= 0)
2319  {
2320  fout(" %s", Ships[z].ship_name);
2321  }
2322  else
2323  {
2324  fout(" <error>");
2325  }
2326  }
2327 
2328  // Goober5000
2330  {
2331  if ((Wings[i].arrival_location == ARRIVE_FROM_DOCK_BAY) && (Wings[i].arrival_path_mask > 0))
2332  {
2333  int j, anchor_shipnum;
2334  polymodel *pm;
2335 
2336  anchor_shipnum = Wings[i].arrival_anchor;
2337  Assert(anchor_shipnum >= 0 && anchor_shipnum < MAX_SHIPS);
2338 
2339  fout("\n+Arrival Paths: ( ");
2340 
2341  pm = model_get(Ship_info[Ships[anchor_shipnum].ship_info_index].model_num);
2342  for (j = 0; j < pm->ship_bay->num_paths; j++)
2343  {
2344  if (Wings[i].arrival_path_mask & (1 << j))
2345  {
2346  fout("\"%s\" ", pm->paths[pm->ship_bay->path_indexes[j]].name);
2347  }
2348  }
2349 
2350  fout(")");
2351  }
2352  }
2353 
2354  if (Wings[i].arrival_delay)
2355  {
2356  if (optional_string_fred("+Arrival delay:", "$Name:"))
2357  parse_comments();
2358  else
2359  fout("\n+Arrival delay:");
2360 
2361  fout(" %d", Wings[i].arrival_delay);
2362  }
2363 
2364  required_string_fred("$Arrival Cue:");
2365  parse_comments();
2366  convert_sexp_to_string(sexp_out, Wings[i].arrival_cue, SEXP_SAVE_MODE);
2367  fout(" %s", sexp_out.c_str());
2368 
2369  required_string_fred("$Departure Location:");
2370  parse_comments();
2371  fout(" %s", Departure_location_names[Wings[i].departure_location]);
2372 
2373  if ( Wings[i].departure_location != DEPART_AT_LOCATION )
2374  {
2375  required_string_fred("$Departure Anchor:");
2376  parse_comments();
2377 
2378  if ( Wings[i].departure_anchor >= 0 )
2379  fout(" %s", Ships[Wings[i].departure_anchor].ship_name );
2380  else
2381  fout(" <error>");
2382  }
2383 
2384  // Goober5000
2386  {
2387  if ((Wings[i].departure_location == DEPART_AT_DOCK_BAY) && (Wings[i].departure_path_mask > 0))
2388  {
2389  int j, anchor_shipnum;
2390  polymodel *pm;
2391 
2392  anchor_shipnum = Wings[i].departure_anchor;
2393  Assert(anchor_shipnum >= 0 && anchor_shipnum < MAX_SHIPS);
2394 
2395  fout("\n+Departure Paths: ( ");
2396 
2397  pm = model_get(Ship_info[Ships[anchor_shipnum].ship_info_index].model_num);
2398  for (j = 0; j < pm->ship_bay->num_paths; j++)
2399  {
2400  if (Wings[i].departure_path_mask & (1 << j))
2401  {
2402  fout("\"%s\" ", pm->paths[pm->ship_bay->path_indexes[j]].name);
2403  }
2404  }
2405 
2406  fout(")");
2407  }
2408  }
2409 
2410  if (Wings[i].departure_delay)
2411  {
2412  if (optional_string_fred("+Departure delay:", "$Name:"))
2413  parse_comments();
2414  else
2415  fout("\n+Departure delay:");
2416 
2417  fout(" %d", Wings[i].departure_delay);
2418  }
2419 
2420  required_string_fred("$Departure Cue:");
2421  parse_comments();
2422  convert_sexp_to_string(sexp_out, Wings[i].departure_cue, SEXP_SAVE_MODE);
2423  fout(" %s", sexp_out.c_str());
2424 
2425  required_string_fred("$Ships:");
2426  parse_comments();
2427  fout(" (\t\t;! %d total\n", Wings[i].wave_count);
2428 
2429  for (j=0; j<Wings[i].wave_count; j++) {
2430  ship = Wings[i].ship_index[j];
2431 // if (Objects[Ships[ship].objnum].type == OBJ_START)
2432 // fout("\t\"Player 1\"\n");
2433 // else
2434  fout("\t\"%s\"\n", Ships[Wings[i].ship_index[j]].ship_name);
2435  }
2436 
2437  fout(")");
2438 
2439  save_ai_goals(Wings[i].ai_goals, -1);
2440 
2441  if (Wings[i].hotkey != -1) {
2442  if (optional_string_fred("+Hotkey:", "$Name:"))
2443  parse_comments();
2444  else
2445  fout("\n+Hotkey:");
2446 
2447  fout(" %d", Wings[i].hotkey);
2448  }
2449 
2450  if ( optional_string_fred("+Flags:", "$Name:")) {
2451  parse_comments();
2452  fout( " (" );
2453  } else
2454  fout("\n+Flags: (");
2455 
2456  if ( Wings[i].flags & WF_IGNORE_COUNT )
2457  fout(" \"ignore-count\"");
2458  if ( Wings[i].flags & WF_REINFORCEMENT )
2459  fout(" \"reinforcement\"");
2460  if ( Wings[i].flags & WF_NO_ARRIVAL_MUSIC )
2461  fout(" \"no-arrival-music\"");
2462  if ( Wings[i].flags & WF_NO_ARRIVAL_MESSAGE )
2463  fout(" \"no-arrival-message\"");
2464  if ( Wings[i].flags & WF_NO_ARRIVAL_WARP )
2465  fout(" \"no-arrival-warp\"");
2466  if ( Wings[i].flags & WF_NO_DEPARTURE_WARP )
2467  fout(" \"no-departure-warp\"");
2468  if ( Wings[i].flags & WF_NO_DYNAMIC )
2469  fout( " \"no-dynamic\"" );
2470 
2471  fout(" )");
2472 
2473  if (Wings[i].wave_delay_min) {
2474  if (optional_string_fred("+Wave Delay Min:", "$Name:"))
2475  parse_comments();
2476  else
2477  fout("\n+Wave Delay Min:");
2478 
2479  fout(" %d", Wings[i].wave_delay_min);
2480  }
2481 
2482  if (Wings[i].wave_delay_max) {
2483  if (optional_string_fred("+Wave Delay Max:", "$Name:"))
2484  parse_comments();
2485  else
2486  fout("\n+Wave Delay Max:");
2487 
2488  fout(" %d", Wings[i].wave_delay_max);
2489  }
2490 
2491  fso_comment_pop();
2492  }
2493 
2494  fso_comment_pop(true);
2495 
2496  Assert(count == Num_wings);
2497  return err;
2498 }
2499 
2500 int CFred_mission_save::save_goals()
2501 {
2502  SCP_string sexp_out;
2503  int i;
2504 
2505  fred_parse_flag = 0;
2506  required_string_fred("#Goals");
2507  parse_comments(2);
2508  fout("\t\t;! %d total\n", Num_goals);
2509 
2510  for (i=0; i<Num_goals; i++) {
2511  int type;
2512 
2513  required_string_either_fred("$Type:", "#Waypoints");
2514  required_string_fred("$Type:");
2515  parse_comments(i ? 2 : 1);
2516 
2517  type = Mission_goals[i].type & GOAL_TYPE_MASK;
2518  fout(" %s", Goal_type_names[type]);
2519 
2520  if (*Mission_goals[i].name) {
2521  if (optional_string_fred("+Name:", "$Type:"))
2522  parse_comments();
2523  else
2524  fout("\n+Name:");
2525 
2526  fout(" %s", Mission_goals[i].name);
2527  }
2528 
2529  // XSTR
2530  required_string_fred("$MessageNew:");
2531  parse_comments();
2532  fout_ext(" ", "%s", Mission_goals[i].message);
2533  fout("\n");
2534  required_string_fred("$end_multi_text");
2535  parse_comments(0);
2536 
2537  required_string_fred("$Formula:");
2538  parse_comments();
2539  convert_sexp_to_string(sexp_out, Mission_goals[i].formula, SEXP_SAVE_MODE);
2540  fout(" %s", sexp_out.c_str());
2541 
2542  if ( Mission_goals[i].type & INVALID_GOAL ) {
2543  if (optional_string_fred("+Invalid", "$Type:"))
2544  parse_comments();
2545  else
2546  fout("\n+Invalid");
2547  }
2548 
2549  if ( Mission_goals[i].flags & MGF_NO_MUSIC ) {
2550  if (optional_string_fred("+No music", "$Type:"))
2551  parse_comments();
2552  else
2553  fout("\n+No music");
2554  }
2555 
2556  if ( Mission_goals[i].score != 0 ) {
2557  if ( optional_string_fred("+Score:", "$Type:"))
2558  parse_comments();
2559  else
2560  fout("\n+Score:");
2561  fout(" %d", Mission_goals[i].score );
2562  }
2563 
2565  if ( optional_string_fred("+Team:", "$Type:"))
2566  parse_comments();
2567  else
2568  fout("\n+Team:");
2569  fout(" %d", Mission_goals[i].team );
2570  }
2571 
2572  fso_comment_pop();
2573  }
2574 
2575  fso_comment_pop(true);
2576 
2577  return err;
2578 }
2579 
2580 int CFred_mission_save::save_waypoints()
2581 {
2582  //object *ptr;
2583 
2584  fred_parse_flag = 0;
2585  required_string_fred("#Waypoints");
2586  parse_comments(2);
2587  fout("\t\t;! %d lists total\n", Waypoint_lists.size());
2588 
2590  for (jnp = Jump_nodes.begin(); jnp != Jump_nodes.end(); ++jnp)
2591  {
2592  required_string_fred("$Jump Node:", "$Jump Node Name:");
2593  parse_comments(2);
2594  save_vector(jnp->GetSCPObject()->pos);
2595 
2596  required_string_fred("$Jump Node Name:", "$Jump Node:");
2597  parse_comments();
2598  fout(" %s", jnp->GetName());
2599 
2600  if(jnp->IsSpecialModel())
2601  {
2602  if ( optional_string_fred("+Model File:", "$Jump Node:"))
2603  parse_comments();
2604  else
2605  fout("\n+Model File:");
2606 
2607  int model = jnp->GetModelNumber();
2608  polymodel *pm = model_get(model);
2609  fout(" %s", pm->filename );
2610  }
2611 
2612  if(jnp->IsColored())
2613  {
2614  if ( optional_string_fred("+Alphacolor:", "$Jump Node:"))
2615  parse_comments();
2616  else
2617  fout("\n+Alphacolor:");
2618 
2619  color jn_color = jnp->GetColor();
2620  fout(" %u %u %u %u", jn_color.red, jn_color.green, jn_color.blue, jn_color.alpha );
2621  }
2622 
2623  int hidden_is_there = optional_string_fred("+Hidden:", "$Jump Node:");
2624  if(hidden_is_there)
2625  parse_comments();
2626 
2627  if(hidden_is_there || jnp->IsHidden())
2628  {
2629  if(!hidden_is_there)
2630  fout("\n+Hidden:");
2631 
2632  if(jnp->IsHidden())
2633  fout(" %s", "true");
2634  else
2635  fout(" %s", "false");
2636  }
2637 
2638  fso_comment_pop();
2639  }
2640 
2642  for (ii = Waypoint_lists.begin(); ii != Waypoint_lists.end(); ++ii)
2643  {
2644  required_string_either_fred("$Name:", "#Messages");
2645  required_string_fred("$Name:");
2646  parse_comments((ii == Waypoint_lists.begin()) ? 1 : 2);
2647  fout(" %s", ii->get_name());
2648 
2649  required_string_fred("$List:");
2650  parse_comments();
2651  fout(" (\t\t;! %d points in list\n", ii->get_waypoints().size());
2652 
2653  save_waypoint_list(&(*ii));
2654  fout(")");
2655 
2656  fso_comment_pop();
2657  }
2658 
2659  fso_comment_pop(true);
2660 
2661  return err;
2662 }
2663 
2664 int CFred_mission_save::save_waypoint_list(waypoint_list *wp_list)
2665 {
2666  Assert(wp_list != NULL);
2668 
2669  for (ii = wp_list->get_waypoints().begin(); ii != wp_list->get_waypoints().end(); ++ii)
2670  {
2671  vec3d *pos = ii->get_pos();
2672  fout("\t( %f, %f, %f )\n", pos->xyz.x, pos->xyz.y, pos->xyz.z);
2673  }
2674 
2675  return 0;
2676 }
2677 
2678 int CFred_mission_save::save_messages()
2679 {
2680  int i;
2681 
2682  fred_parse_flag = 0;
2683  required_string_fred("#Messages");
2684  parse_comments(2);
2685  fout("\t\t;! %d total\n", Num_messages-Num_builtin_messages);
2686 
2687  // Goober5000 - special Command info
2689  {
2691  fout("\n$Command Sender: %s", The_mission.command_sender);
2692 
2694  fout("\n$Command Persona: %s", Personas[The_mission.command_persona].name);
2695  }
2696 
2697  for (i=Num_builtin_messages; i<Num_messages; i++) {
2698  required_string_either_fred("$Name:", "#Reinforcements");
2699  required_string_fred("$Name:");
2700  parse_comments(2);
2701  fout(" %s", Messages[i].name);
2702 
2703  // team
2704  required_string_fred("$Team:");
2705  parse_comments(1);
2706  if((Messages[i].multi_team < 0) || (Messages[i].multi_team >= 2)){
2707  fout(" %d", -1);
2708  } else {
2709  fout(" %d", Messages[i].multi_team);
2710  }
2711 
2712  // XSTR
2713  required_string_fred("$MessageNew:");
2714  parse_comments();
2715  fout_ext(" ", "%s", Messages[i].message);
2716  fout("\n");
2717  required_string_fred("$end_multi_text");
2718  parse_comments(0);
2719 
2720  if ( Messages[i].persona_index != -1 ) {
2721  if ( optional_string_fred("+Persona:", "$Name:"))
2722  parse_comments();
2723  else
2724  fout("\n+Persona:");
2725 
2726  fout(" %s", Personas[Messages[i].persona_index].name );
2727  }
2728 
2729  if (Messages[i].avi_info.name) {
2730  if (optional_string_fred("+AVI Name:", "$Name:"))
2731  parse_comments();
2732  else
2733  fout("\n+AVI Name:");
2734 
2735  fout(" %s", Messages[i].avi_info.name);
2736  }
2737 
2738  if (Messages[i].wave_info.name) {
2739  if (optional_string_fred("+Wave Name:", "$Name:"))
2740  parse_comments();
2741  else
2742  fout("\n+Wave Name:");
2743 
2744  fout(" %s", Messages[i].wave_info.name);
2745  }
2746 
2747  fso_comment_pop();
2748  }
2749 
2750  fso_comment_pop(true);
2751 
2752  return err;
2753 }
2754 
2755 int CFred_mission_save::save_vector(vec3d &v)
2756 {
2757  fout(" %f, %f, %f", v.xyz.x, v.xyz.y, v.xyz.z);
2758  return 0;
2759 }
2760 
2761 int CFred_mission_save::save_matrix(matrix &m)
2762 {
2763  fout("\n\t%f, %f, %f,\n", m.vec.rvec.xyz.x, m.vec.rvec.xyz.y, m.vec.rvec.xyz.z);
2764  fout("\t%f, %f, %f,\n", m.vec.uvec.xyz.x, m.vec.uvec.xyz.y, m.vec.uvec.xyz.z);
2765  fout("\t%f, %f, %f", m.vec.fvec.xyz.x, m.vec.fvec.xyz.y, m.vec.fvec.xyz.z);
2766  return 0;
2767 }
2768 
2769 // Goober5000 - move past the comment without copying it to the output file
2770 // (used for special FSO comment tags)
2771 void CFred_mission_save::bypass_comment(const char *comment, const char *end)
2772 {
2773  char *ch = strstr(raw_ptr, comment);
2774  if (ch != NULL)
2775  {
2776  if (end != NULL) {
2777  char *ep = strstr(raw_ptr, end);
2778  if (ep != NULL && ep < ch)
2779  return;
2780  }
2781  char *writep = ch;
2782  char *readp = strchr(writep, '\n');
2783 
2784  // copy all characters past it
2785  while (*readp != '\0')
2786  {
2787  *writep = *readp;
2788 
2789  writep++;
2790  readp++;
2791  }
2792 
2793  *writep = '\0';
2794  }
2795 }
2796 
2797 // saves comments from previous campaign/mission file
2799 {
2800  char *comment_start = NULL;
2801  int state = 0, same_line = 0, first_comment = 1, tab = 0, flag = 0;
2802  bool version_added = false;
2803 
2804  if (newlines < 0) {
2805  newlines = -newlines;
2806  tab = 1;
2807  }
2808 
2809  if (newlines)
2810  same_line = 1;
2811 
2813  while (newlines-- > 0)
2814  fout("\n");
2815 
2816  if (tab && token_found)
2817  fout_version("\t%s", token_found);
2818  else if (token_found)
2819  fout_version("%s", token_found);
2820  else if (tab)
2821  fout("\t");
2822 
2823  return;
2824  }
2825 
2826  while (*raw_ptr != EOF_CHAR) {
2827  // state values (as far as I could figure out):
2828  // 0 - raw_ptr not inside comment
2829  // 1 - raw_ptr inside /**/ comment
2830  // 2 - raw_ptr inside ; (newline-delimited) comment
2831  // 3,4 - raw_ptr inside ;; (FSO version) comment
2832  if (!state) {
2833  if (token_found && (*raw_ptr == *token_found))
2834  if (!strnicmp(raw_ptr, token_found, strlen(token_found))) {
2835  same_line = newlines - 1 + same_line;
2836  while (same_line-- > 0)
2837  fout("\n");
2838 
2839  if (tab) {
2840  fout_version("\t");
2841  fout("%s", token_found);
2842  } else {
2843  fout_version("%s", token_found);
2844  }
2845 
2846  // If you have a bunch of lines that all start with the same token (like, say, "+Subsystem:"),
2847  // this makes it so it won't just repeatedly match the first one. -MageKing17
2848  raw_ptr++;
2849 
2850  if (version_added)
2851  fso_comment_pop();
2852 
2853  return;
2854  }
2855 
2856  if ((*raw_ptr == '/') && (raw_ptr[1] == '*')) {
2857  comment_start = raw_ptr;
2858  state = 1;
2859  }
2860 
2861  if ((*raw_ptr == ';') && (raw_ptr[1] != '!')) {
2862  comment_start = raw_ptr;
2863  state = 2;
2864 
2865  // check for a FSO version comment, but if we can't understand it then
2866  // just handle it as a regular comment
2867  if ( (raw_ptr[1] == ';') && (raw_ptr[2] == 'F') && (raw_ptr[3] == 'S') && (raw_ptr[4] == 'O') ) {
2868  int major, minor, build, revis;
2869  int s_num = scan_fso_version_string(raw_ptr, &major, &minor, &build, &revis);
2870 
2871  // hack for releases
2872  if (FS_VERSION_REVIS < 1000) {
2873  s_num = 3;
2874  }
2875 
2876  if ( (s_num == 3) && ((major < FS_VERSION_MAJOR) || ((major == FS_VERSION_MAJOR) && ((minor < FS_VERSION_MINOR) || ((minor == FS_VERSION_MINOR) && (build <= FS_VERSION_BUILD))))) ) {
2877  state = 3;
2878  } else if ( (s_num == 4) && ((major < FS_VERSION_MAJOR) || ((major == FS_VERSION_MAJOR) && ((minor < FS_VERSION_MINOR) || ((minor == FS_VERSION_MINOR) && ((build < FS_VERSION_BUILD) || ((build == FS_VERSION_BUILD) && (revis <= FS_VERSION_REVIS))))))) ) {
2879  state = 3;
2880  } else {
2881  state = 4;
2882  }
2883  }
2884  }
2885 
2886  if (*raw_ptr == '\n')
2887  flag = 1;
2888 
2889  if (flag && state && !(state == 3) )
2890  fout("\n");
2891 
2892  } else {
2893  if (*raw_ptr == '\n') {
2894  if (state == 2) {
2895  if (first_comment && !flag)
2896  fout("\t\t");
2897 
2898  *raw_ptr = 0;
2899  fout("%s\n", comment_start);
2900  *raw_ptr = '\n';
2901  state = first_comment = same_line = flag = 0;
2902  } else if (state == 4) {
2903  same_line = newlines - 2 + same_line;
2904  while (same_line-- > 0)
2905  fout("\n");
2906 
2907  if (*(raw_ptr-1) == '\r') {
2908  *(raw_ptr-1) = '\0';
2909  } else {
2910  *raw_ptr = 0;
2911  }
2912 
2913  fout("%s\n", comment_start);
2914 
2915  if (*(raw_ptr-1) == '\0') {
2916  *(raw_ptr-1) = '\r';
2917  } else {
2918  *raw_ptr = '\n';
2919  }
2920 
2921  state = first_comment = same_line = flag = 0;
2922  }
2923  }
2924 
2925  if ((*raw_ptr == '*') && (raw_ptr[1] == '/') && (state == 1)) {
2926  if (first_comment && !flag)
2927  fout("\t\t");
2928 
2929  const char tmp = raw_ptr[2];
2930  raw_ptr[2] = 0;
2931  fout("%s", comment_start);
2932  raw_ptr[2] = tmp;
2933  state = first_comment = flag = 0;
2934  }
2935 
2936  if ( (*raw_ptr == ';') && (raw_ptr[1] == ';') && (state == 3) ) {
2937  const char tmp = raw_ptr[2];
2938  raw_ptr[2] = 0;
2939  if (version_added)
2940  fso_comment_pop();
2941  else
2942  version_added = true;
2943  fso_comment_push(comment_start);
2944  raw_ptr[2] = tmp;
2945  state = first_comment = flag = 0;
2946  raw_ptr++;
2947  }
2948  }
2949 
2950  raw_ptr++;
2951  }
2952 
2953  if (version_added)
2954  fso_comment_pop();
2955 
2956  return;
2957 }
2958 
2960 {
2961  SCP_string str_scp;
2962  char *ch = NULL;
2963  va_list args;
2964 
2965  if (err) {
2966  return err;
2967  }
2968 
2969  // output the version first thing, but skip the special case where we use
2970  // fout_version() for multiline value strings (typically indicated by an initial space)
2971  if ( (Format_fs2_open == FSO_FORMAT_COMPATIBILITY_MODE) && (*format != ' ') && !fso_ver_comment.empty() ) {
2972  while (*format == '\n') {
2973  str_scp.append(1, *format);
2974  format++;
2975  }
2976 
2977  str_scp.append(fso_ver_comment.back().c_str());
2978  str_scp.append(" ");
2979 
2980  cfputs(str_scp.c_str(), fp);
2981 
2982  str_scp = "";
2983  }
2984 
2985  va_start(args, format);
2986  vsprintf(str_scp, format, args);
2987  va_end(args);
2988 
2989  char *str_c = vm_strdup(str_scp.c_str());
2990 
2991  // this could be a multi-line string, so we've got to handle it all properly
2992  if ( (Format_fs2_open == FSO_FORMAT_COMPATIBILITY_MODE) && !fso_ver_comment.empty() ) {
2993  bool first_line = true;
2994  char *str_p = str_c;
2995 
2996  ch = strchr(str_c, '\n');
2997 
2998  // if we have something, and it's not just at the end, then process it specially
2999  if ( (ch != NULL) && (*(ch+1) != '\0') ) {
3000  do {
3001  if ( *(ch+1) != '\0' ) {
3002  *ch = '\0';
3003 
3004  if (first_line) {
3005  first_line = false;
3006  } else {
3007  cfputs(fso_ver_comment.back().c_str(), fp);
3008  cfputs(" ", fp);
3009  }
3010 
3011  cfputs(str_p, fp);
3012  cfputc('\n', fp);
3013 
3014  str_p = ch+1;
3015  } else {
3016  if (first_line) {
3017  first_line = false;
3018  } else {
3019  cfputs(fso_ver_comment.back().c_str(), fp);
3020  cfputs(" ", fp);
3021  }
3022 
3023  cfputs(str_p, fp);
3024 
3025  str_p = ch+1;
3026 
3027  break;
3028  }
3029  } while ( (ch = strchr(str_p, '\n')) != NULL );
3030 
3031  // be sure to account for any ending elements too
3032  if ( strlen(str_p) ) {
3033  cfputs(fso_ver_comment.back().c_str(), fp);
3034  cfputs(" ", fp);
3035  cfputs(str_p, fp);
3036  }
3037 
3038  vm_free(str_c);
3039  return 0;
3040  }
3041  }
3042 
3043  cfputs(str_c, fp);
3044 
3045  vm_free(str_c);
3046  return 0;
3047 }
3048 
3050 {
3051  SCP_string str;
3052  va_list args;
3053 
3054  if (err) {
3055  return err;
3056  }
3057 
3058  va_start(args, format);
3059  vsprintf(str, format, args);
3060  va_end(args);
3061 
3062  cfputs(str.c_str(), fp);
3063  return 0;
3064 }
3065 
3066 int CFred_mission_save::fout_ext(char *pre_str, char *format, ...)
3067 {
3068  SCP_string str_scp;
3069  SCP_string str_out_scp;
3070  va_list args;
3071  int str_id;
3072 
3073  if (err){
3074  return err;
3075  }
3076 
3077  va_start(args, format);
3078  vsprintf(str_scp, format, args);
3079  va_end(args);
3080 
3081  if (pre_str) {
3082  str_out_scp = pre_str;
3083  }
3084 
3085  // lookup the string in the hash table
3086  str_id = fhash_string_exists(str_scp.c_str());
3087 
3088  // doesn't exist, so assign it an ID of -1 and stick it in the table
3089  if (str_id <= -2) {
3090  str_out_scp += " XSTR(\"";
3091  str_out_scp += str_scp;
3092  str_out_scp += "\", -1)";
3093 
3094  // add the string to the table
3095  fhash_add_str(str_scp.c_str(), -1);
3096  }
3097  // _does_ exist, so just write it out as it is
3098  else {
3099  char buf[10];
3100  sprintf(buf, "%d", str_id);
3101 
3102  str_out_scp += " XSTR(\"";
3103  str_out_scp += str_scp;
3104  str_out_scp += "\", ";
3105  str_out_scp += buf;
3106  str_out_scp += ")";
3107  }
3108 
3109  char *str_out_c = vm_strdup(str_out_scp.c_str());
3110 
3111  // this could be a multi-line string, so we've got to handle it all properly
3112  if ( !fso_ver_comment.empty() ) {
3113  bool first_line = true;
3114  char *str_p = str_out_c;
3115 
3116  char *ch = strchr(str_out_c, '\n');
3117 
3118  // if we have something, and it's not just at the end, then process it specially
3119  if ( (ch != NULL) && (*(ch+1) != '\0') ) {
3120  do {
3121  if ( *(ch+1) != '\0' ) {
3122  *ch = '\0';
3123 
3124  if (first_line) {
3125  first_line = false;
3126  } else {
3127  cfputs(fso_ver_comment.back().c_str(), fp);
3128  cfputs(" ", fp);
3129  }
3130 
3131  cfputs(str_p, fp);
3132  cfputc('\n', fp);
3133 
3134  str_p = ch+1;
3135  } else {
3136  if (first_line) {
3137  first_line = false;
3138  } else {
3139  cfputs(fso_ver_comment.back().c_str(), fp);
3140  cfputs(" ", fp);
3141  }
3142 
3143  cfputs(str_p, fp);
3144 
3145  str_p = ch+1;
3146 
3147  break;
3148  }
3149  } while ( (ch = strchr(str_p, '\n')) != NULL );
3150 
3151  // be sure to account for any ending elements too
3152  if ( strlen(str_p) ) {
3153  cfputs(fso_ver_comment.back().c_str(), fp);
3154  cfputs(" ", fp);
3155  cfputs(str_p, fp);
3156  }
3157 
3158  vm_free(str_out_c);
3159  return 0;
3160  }
3161  }
3162 
3163  cfputs(str_out_c, fp);
3164 
3165  vm_free(str_out_c);
3166  return 0;
3167 }
3168 
3170 {
3171  char *str = NULL, buf[80];
3172  int i, valid, flag = 1;
3173 
3174  for (i=0; i<MAX_AI_GOALS; i++) {
3175  if (goalp[i].ai_mode == AI_GOAL_NONE)
3176  continue;
3177 
3178  if (flag) {
3179  if (optional_string_fred("$AI Goals:", "$Name:"))
3180  parse_comments();
3181  else
3182  fout("\n$AI Goals:");
3183 
3184  fout(" ( goals ");
3185  flag = 0;
3186  }
3187 
3188  if (goalp[i].ai_mode == AI_GOAL_CHASE_ANY) {
3189  fout("( ai-chase-any %d ) ", goalp[i].priority);
3190 
3191  } else if (goalp[i].ai_mode == AI_GOAL_UNDOCK) {
3192  fout("( ai-undock %d ) ", goalp[i].priority);
3193 
3194  } else if (goalp[i].ai_mode == AI_GOAL_KEEP_SAFE_DISTANCE) {
3195  fout("( ai-keep-safe-distance %d ) ", goalp[i].priority);
3196 
3197  } else if (goalp[i].ai_mode == AI_GOAL_PLAY_DEAD) {
3198  fout("( ai-play-dead %d ) ", goalp[i].priority);
3199 
3200  } else if (goalp[i].ai_mode == AI_GOAL_WARP) {
3201  fout("( ai-warp-out %d ) ", goalp[i].priority);
3202 
3203  } else {
3204  valid = 1;
3205  if (!goalp[i].target_name) {
3206  Warning(LOCATION, "Ai goal has no target where one is required");
3207 
3208  } else {
3209  sprintf(buf, "\"%s\"", goalp[i].target_name);
3210  switch (goalp[i].ai_mode) {
3211  case AI_GOAL_WAYPOINTS:
3212  str = "ai-waypoints";
3213  break;
3214 
3216  str = "ai-waypoints-once";
3217  break;
3218 
3220  if (goalp[i].docker.index == -1 || !goalp[i].docker.index) {
3221  valid = 0;
3222  Warning(LOCATION, "AI destroy subsystem goal invalid subsystem name\n");
3223 
3224  } else {
3225  sprintf(buf, "\"%s\" \"%s\"", goalp[i].target_name, goalp[i].docker.name);
3226  str = "ai-destroy-subsystem";
3227  }
3228 
3229  break;
3230 
3231  case AI_GOAL_DOCK:
3232  if (ship < 0) {
3233  valid = 0;
3234  Warning(LOCATION, "Wings aren't allowed to have a docking goal\n");
3235 
3236  } else if (goalp[i].docker.index == -1 || !goalp[i].docker.index) {
3237  valid = 0;
3238  Warning(LOCATION, "AI dock goal for \"%s\" has invalid docker point "
3239  "(docking with \"%s\")\n", Ships[ship].ship_name, goalp[i].target_name);
3240 
3241  } else if (goalp[i].dockee.index == -1 || !goalp[i].dockee.index) {
3242  valid = 0;
3243  Warning(LOCATION, "AI dock goal for \"%s\" has invalid dockee point "
3244  "(docking with \"%s\")\n", Ships[ship].ship_name, goalp[i].target_name);
3245 
3246  } else {
3247  sprintf(buf, "\"%s\" \"%s\" \"%s\"", goalp[i].target_name,
3248  goalp[i].docker.name, goalp[i].dockee.name);
3249 
3250  str = "ai-dock";
3251  }
3252  break;
3253 
3254  case AI_GOAL_CHASE:
3255  str = "ai-chase";
3256  break;
3257 
3258  case AI_GOAL_CHASE_WING:
3259  str = "ai-chase-wing";
3260  break;
3261 
3262  case AI_GOAL_GUARD:
3263  str = "ai-guard";
3264  break;
3265 
3266  case AI_GOAL_GUARD_WING:
3267  str = "ai-guard-wing";
3268  break;
3269 
3270  case AI_GOAL_DISABLE_SHIP:
3271  str = "ai-disable-ship";
3272  break;
3273 
3274  case AI_GOAL_DISARM_SHIP:
3275  str = "ai-disarm-ship";
3276  break;
3277 
3278  case AI_GOAL_IGNORE:
3279  str = "ai-ignore";
3280  break;
3281 
3282  case AI_GOAL_IGNORE_NEW:
3283  str = "ai-ignore-new";
3284  break;
3285 
3286  case AI_GOAL_EVADE_SHIP:
3287  str = "ai-evade-ship";
3288  break;
3289 
3291  str = "ai-stay-near-ship";
3292  break;
3293 
3294  case AI_GOAL_STAY_STILL:
3295  str = "ai-stay-still";
3296  break;
3297 
3298  default:
3299  Assert(0);
3300  }
3301 
3302  if (valid)
3303  fout("( %s %s %d ) ", str, buf, goalp[i].priority);
3304  }
3305  }
3306 
3307  fso_comment_pop();
3308  }
3309 
3310  if (!flag)
3311  fout(")");
3312 
3313  fso_comment_pop(true);
3314 }
3315 
3316 int CFred_mission_save::save_events()
3317 {
3318  SCP_string sexp_out;
3319  int i, j, add_flag;
3320 
3321  fred_parse_flag = 0;
3322  required_string_fred("#Events");
3323  parse_comments(2);
3324  fout("\t\t;! %d total\n", Num_mission_events);
3325 
3326  for (i=0; i<Num_mission_events; i++) {
3327  required_string_either_fred("$Formula:", "#Goals");
3328  required_string_fred("$Formula:");
3329  parse_comments(i ? 2 : 1);
3330  convert_sexp_to_string(sexp_out, Mission_events[i].formula, SEXP_SAVE_MODE);
3331  fout(" %s", sexp_out.c_str());
3332 
3333  if (*Mission_events[i].name) {
3334  if (optional_string_fred("+Name:", "$Formula:")){
3335  parse_comments();
3336  } else {
3337  fout("\n+Name:");
3338  }
3339 
3340  fout(" %s", Mission_events[i].name);
3341  }
3342 
3343  if ( optional_string_fred("+Repeat Count:", "$Formula:")){
3344  parse_comments();
3345  } else {
3346  fout("\n+Repeat Count:");
3347  }
3348 
3349  // if we have a trigger count but no repeat count, we want the event to loop until it has triggered enough times
3350  if ( Mission_events[i].repeat_count == 1 && Mission_events[i].trigger_count != 1) {
3351  fout(" -1");
3352  }
3353  else {
3354  fout(" %d", Mission_events[i].repeat_count);
3355  }
3356 
3357  if (Format_fs2_open != FSO_FORMAT_RETAIL && Mission_events[i].trigger_count != 1 ) {
3358  fso_comment_push(";;FSO 3.6.11;;");
3359  if ( optional_string_fred("+Trigger Count:", "$Formula:")){
3360  parse_comments();
3361  } else {
3362  fout_version("\n+Trigger Count:");
3363  }
3364  fso_comment_pop();
3365 
3366  fout(" %d", Mission_events[i].trigger_count);
3367  }
3368 
3369  if ( optional_string_fred("+Interval:", "$Formula:")){
3370  parse_comments();
3371  } else {
3372  fout("\n+Interval:");
3373  }
3374 
3375  fout(" %d", Mission_events[i].interval);
3376 
3377  if ( Mission_events[i].score != 0 ) {
3378  if ( optional_string_fred("+Score:", "$Formula:")){
3379  parse_comments();
3380  } else {
3381  fout("\n+Score:");
3382  }
3383  fout(" %d", Mission_events[i].score);
3384  }
3385 
3386  if ( Mission_events[i].chain_delay >= 0 ) {
3387  if ( optional_string_fred("+Chained:", "$Formula:")){
3388  parse_comments();
3389  } else {
3390  fout("\n+Chained:");
3391  }
3392 
3393  fout(" %d", Mission_events[i].chain_delay);
3394  }
3395 
3396  //XSTR
3397  if (Mission_events[i].objective_text) {
3398  if (optional_string_fred("+Objective:", "$Formula:")){
3399  parse_comments();
3400  } else {
3401  fout("\n+Objective:");
3402  }
3403 
3404  fout_ext(" ", "%s", Mission_events[i].objective_text);
3405  }
3406 
3407  //XSTR
3408  if (Mission_events[i].objective_key_text) {
3409  if (optional_string_fred("+Objective key:", "$Formula:")){
3410  parse_comments();
3411  } else {
3412  fout("\n+Objective key:");
3413  }
3414 
3415  fout_ext(" ", "%s", Mission_events[i].objective_key_text);
3416  }
3417 
3418  // save team
3419  if (Mission_events[i].team >= 0){
3420  if (optional_string_fred("+Team:")){
3421  parse_comments();
3422  } else {
3423  fout("\n+Team:");
3424  }
3425  fout(" %d", Mission_events[i].team);
3426  }
3427 
3428  if (Format_fs2_open != FSO_FORMAT_RETAIL && Mission_events[i].mission_log_flags != 0 ) {
3429  fso_comment_push(";;FSO 3.6.11;;");
3430  if ( optional_string_fred("+Event Log Flags: (", "$Formula:")){
3431  parse_comments();
3432  } else {
3433  fout_version("\n+Event Log Flags: (");
3434  }
3435  fso_comment_pop();
3436 
3437  for (j = 0; j < MAX_MISSION_EVENT_LOG_FLAGS ; j++) {
3438  add_flag = 1 << j;
3439  if (Mission_events[i].mission_log_flags & add_flag ) {
3440  fout(" \"%s\"", Mission_event_log_flags[j]);
3441  }
3442  }
3443  fout(" )");
3444  }
3445 
3446  fso_comment_pop();
3447  }
3448 
3449  fso_comment_pop(true);
3450 
3451  return err;
3452 }
3453 
3455 {
3456  int i, j, type;
3457 
3458  fred_parse_flag = 0;
3459  required_string_fred("#Reinforcements");
3460  parse_comments(2);
3461  fout("\t\t;! %d total\n", Num_reinforcements);
3462 
3463  for (i=0; i<Num_reinforcements; i++) {
3464  required_string_either_fred("$Name:", "#Background bitmaps");
3465  required_string_fred("$Name:");
3466  parse_comments(i ? 2 : 1);
3467  fout(" %s", Reinforcements[i].name);
3468 
3469  type = TYPE_ATTACK_PROTECT;
3470  for (j=0; j<MAX_SHIPS; j++)
3471  if ((Ships[j].objnum != -1) && !stricmp(Ships[j].ship_name, Reinforcements[i].name)) {
3472  if (Ship_info[Ships[j].ship_info_index].flags & SIF_SUPPORT)
3473  type = TYPE_REPAIR_REARM;
3474  break;
3475  }
3476 
3477  required_string_fred("$Type:");
3478  parse_comments();
3479  fout(" %s", Reinforcement_type_names[type]);
3480 
3481  required_string_fred("$Num times:");
3482  parse_comments();
3483  fout(" %d", Reinforcements[i].uses);
3484 
3485  if ( optional_string_fred("+Arrival Delay:", "$Name:"))
3486  parse_comments();
3487  else
3488  fout("\n+Arrival Delay:");
3489  fout(" %d", Reinforcements[i].arrival_delay );
3490 
3491  if (optional_string_fred("+No Messages:", "$Name:"))
3492  parse_comments();
3493  else
3494  fout("\n+No Messages:");
3495  fout(" (");
3496  for (j = 0; j < MAX_REINFORCEMENT_MESSAGES; j++) {
3497  if ( strlen(Reinforcements[i].no_messages[j]) )
3498  fout(" \"%s\"", Reinforcements[i].no_messages[j]);
3499  }
3500  fout(" )");
3501 
3502  if (optional_string_fred("+Yes Messages:", "$Name:"))
3503  parse_comments();
3504  else
3505  fout("\n+Yes Messages:");
3506  fout(" (");
3507  for (j = 0; j < MAX_REINFORCEMENT_MESSAGES; j++) {
3508  if ( strlen(Reinforcements[i].yes_messages[j]) )
3509  fout(" \"%s\"", Reinforcements[i].yes_messages[j]);
3510  }
3511  fout(" )");
3512 
3513  fso_comment_pop();
3514  }
3515 
3516  fso_comment_pop(true);
3517 
3518  return err;
3519 }
3520 
3522 {
3523  int i;
3524  uint j;
3525 
3526  fred_parse_flag = 0;
3527  required_string_fred("#Background bitmaps");
3528  parse_comments(2);
3529  fout("\t\t;! %d total\n", stars_get_num_bitmaps());
3530 
3531  required_string_fred("$Num stars:");
3532  parse_comments();
3533  fout(" %d", Num_stars);
3534 
3535  required_string_fred("$Ambient light level:");
3536  parse_comments();
3538 
3539  // neb2 stuff
3541  required_string_fred("+Neb2:");
3542  parse_comments();
3543  fout(" %s\n", Neb2_texture_name);
3544 
3545  required_string_fred("+Neb2Flags:");
3546  parse_comments();
3547  fout(" %d\n", Neb2_poof_flags);
3548  }
3549  // neb 1 stuff
3550  else {
3551  if (Nebula_index >= 0) {
3552  if (optional_string_fred("+Nebula:")){
3553  parse_comments();
3554  } else {
3555  fout("\n+Nebula:");
3556  }
3558 
3559  required_string_fred("+Color:");
3560  parse_comments();
3562 
3563  required_string_fred("+Pitch:");
3564  parse_comments();
3565  fout(" %d", Nebula_pitch);
3566 
3567  required_string_fred("+Bank:");
3568  parse_comments();
3569  fout(" %d", Nebula_bank);
3570 
3571  required_string_fred("+Heading:");
3572  parse_comments();
3573  fout(" %d", Nebula_heading);
3574  }
3575  }
3576 
3577  fso_comment_pop();
3578 
3579  // Goober5000 - save all but the lowest priority using the special comment tag
3580  for (i = 0; i < Num_backgrounds; i++)
3581  {
3582  bool tag = (i < Num_backgrounds - 1);
3583  background_t *background = &Backgrounds[i];
3584 
3585  fso_comment_push(";;FSO 3.6.9;;");
3586  if (optional_string_fred("$Bitmap List:")) {
3587  parse_comments(2);
3588  } else {
3589  fout_version("\n\n$Bitmap List:");
3590  }
3591 
3592  if ( !tag ) {
3593  fso_comment_pop(true);
3594  }
3595 
3596  // save suns by filename
3597  for (j = 0; j < background->suns.size(); j++)
3598  {
3599  starfield_list_entry *sle = &background->suns[j];
3600 
3601  // filename
3602  required_string_fred("$Sun:");
3603  parse_comments();
3604  fout(" %s", sle->filename);
3605 
3606  // angles
3607  required_string_fred("+Angles:");
3608  parse_comments();
3609  fout(" %f %f %f", sle->ang.p, sle->ang.b, sle->ang.h);
3610 
3611  // scale
3612  required_string_fred("+Scale:");
3613  parse_comments();
3614  fout(" %f", sle->scale_x);
3615  }
3616 
3617  // save background bitmaps by filename
3618  for (j = 0; j < background->bitmaps.size(); j++)
3619  {
3620  starfield_list_entry *sle = &background->bitmaps[j];
3621 
3622  // filename
3623  required_string_fred("$Starbitmap:");
3624  parse_comments();
3625  fout(" %s", sle->filename);
3626 
3627  // angles
3628  required_string_fred("+Angles:");
3629  parse_comments();
3630  fout(" %f %f %f", sle->ang.p, sle->ang.b, sle->ang.h);
3631 
3632  // scale
3633  required_string_fred("+ScaleX:");
3634  parse_comments();
3635  fout(" %f", sle->scale_x);
3636  required_string_fred("+ScaleY:");
3637  parse_comments();
3638  fout(" %f", sle->scale_y);
3639 
3640  // div
3641  required_string_fred("+DivX:");
3642  parse_comments();
3643  fout(" %d", sle->div_x);
3644  required_string_fred("+DivY:");
3645  parse_comments();
3646  fout(" %d", sle->div_y);
3647  }
3648 
3649  fso_comment_pop();
3650  }
3651 
3652  // taylor's environment map thingy
3653  if (strlen(The_mission.envmap_name) > 0) { //-V805
3654  fso_comment_push(";;FSO 3.6.9;;");
3655  if (optional_string_fred("$Environment Map:")) {
3656  parse_comments(2);
3657  fout(" %s", The_mission.envmap_name);
3658  } else {
3659  fout_version("\n\n$Environment Map: %s", The_mission.envmap_name);
3660  }
3661  fso_comment_pop();
3662  } else {
3663  bypass_comment(";;FSO 3.6.9;; $Environment Map:");
3664  }
3665 
3666  fso_comment_pop(true);
3667 
3668  return err;
3669 }
3670 
3671 int CFred_mission_save::save_asteroid_fields()
3672 {
3673  int i, idx;
3674 
3675  fred_parse_flag = 0;
3676  required_string_fred("#Asteroid Fields");
3677  parse_comments(2);
3678 
3679  for (i=0; i<1 /*MAX_ASTEROID_FIELDS*/; i++) {
3681  continue;
3682 
3683  required_string_fred("$Density:");
3684  parse_comments(2);
3686 
3687  // field type
3688  if (optional_string_fred("+Field Type:")){
3689  parse_comments();
3690  } else {
3691  fout("\n+Field Type:");
3692  }
3693  fout(" %d", Asteroid_field.field_type);
3694 
3695  // debris type
3696  if (optional_string_fred("+Debris Genre:")){
3697  parse_comments();
3698  } else {
3699  fout("\n+Debris Genre:");
3700  }
3702 
3703  // field_debris_type (only if ship genre)
3705  for (int idx=0; idx<3; idx++) {
3706  if (Asteroid_field.field_debris_type[idx] != -1) {
3707  if (optional_string_fred("+Field Debris Type:")){
3708  parse_comments();
3709  } else {
3710  fout("\n+Field Debris Type:");
3711  }
3712  fout(" %d", Asteroid_field.field_debris_type[idx]);
3713  }
3714  }
3715  } else {
3716  // asteroid subtypes stored in field_debris_type as -1 or 1
3717  for (idx=0; idx<3; idx++) {
3718  if (Asteroid_field.field_debris_type[idx] != -1) {
3719  if (optional_string_fred("+Field Debris Type:")){
3720  parse_comments();
3721  } else {
3722  fout("\n+Field Debris Type:");
3723  }
3724  fout(" %d", idx);
3725  }
3726  }
3727  }
3728 
3729 
3730  required_string_fred("$Average Speed:");
3731  parse_comments();
3732  fout(" %f", vm_vec_mag(&Asteroid_field.vel));
3733 
3734  required_string_fred("$Minimum:");
3735  parse_comments();
3736  save_vector(Asteroid_field.min_bound);
3737 
3738  required_string_fred("$Maximum:");
3739  parse_comments();
3740  save_vector(Asteroid_field.max_bound);
3741 
3742  if (Asteroid_field.has_inner_bound == 1) {
3743  if (optional_string_fred("+Inner Bound:")){
3744  parse_comments();
3745  } else {
3746  fout("\n+Inner Bound:");
3747  }
3748 
3749  required_string_fred("$Minimum:");
3750  parse_comments();
3751  save_vector(Asteroid_field.inner_min_bound);
3752 
3753  required_string_fred("$Maximum:");
3754  parse_comments();
3755  save_vector(Asteroid_field.inner_max_bound);
3756  }
3757 
3758  fso_comment_pop();
3759  }
3760 
3761  fso_comment_pop(true);
3762 
3763  return err;
3764 }
3765 
3766 int CFred_mission_save::save_music()
3767 {
3768  required_string_fred("#Music");
3769  parse_comments(2);
3770 
3771  required_string_fred("$Event Music:");
3772  parse_comments(2);
3773  if (Current_soundtrack_num < 0)
3774  fout(" None");
3775  else
3776  fout(" %s", Soundtracks[Current_soundtrack_num].name);
3777 
3778  // Goober5000 - save using the special comment prefix
3780  fso_comment_push(";;FSO 3.6.9;;");
3781  if (optional_string_fred("$Substitute Event Music:")) {
3782  parse_comments(1);
3784  } else {
3785  fout_version("\n$Substitute Event Music: %s", The_mission.substitute_event_music_name);
3786  }
3787  fso_comment_pop();
3788  } else {
3789  bypass_comment(";;FSO 3.6.9;; $Substitute Event Music:");
3790  }
3791 
3792  required_string_fred("$Briefing Music:");
3793  parse_comments();
3794  if (Mission_music[SCORE_BRIEFING] < 0)
3795  fout(" None");
3796  else
3798 
3799  // Goober5000 - save using the special comment prefix
3801  fso_comment_push(";;FSO 3.6.9;;");
3802  if (optional_string_fred("$Substitute Briefing Music:")) {
3803  parse_comments(1);
3805  } else {
3806  fout_version("\n$Substitute Briefing Music: %s", The_mission.substitute_briefing_music_name);
3807  }
3808  fso_comment_pop();
3809  } else {
3810  bypass_comment(";;FSO 3.6.9;; $Substitute Briefing Music:");
3811  }
3812 
3813  // avoid keeping the old one around
3814  bypass_comment(";;FSO 3.6.8;; $Substitute Music:");
3815 
3816  // old stuff
3818  if (optional_string_fred("$Debriefing Success Music:")) {
3819  parse_comments(1);
3820  } else {
3821  fout("\n$Debriefing Success Music:");
3822  }
3824  }
3826  if (optional_string_fred("$Debriefing Average Music:")) {
3827  parse_comments(1);
3828  } else {
3829  fout("\n$Debriefing Average Music:");
3830  }
3832  }
3834  if (optional_string_fred("$Debriefing Fail Music:")) {
3835  parse_comments(1);
3836  } else {
3837  fout("\n$Debriefing Fail Music:");
3838  }
3840  }
3841 
3842  // Goober5000 - save using the special comment prefix
3844  fso_comment_push(";;FSO 3.6.11;;");
3845  if (optional_string_fred("$Fiction Viewer Music:")) {
3846  parse_comments(1);
3848  } else {
3849  fout_version("\n$Fiction Viewer Music: %s", Spooled_music[Mission_music[SCORE_FICTION_VIEWER]].name);
3850  }
3851  fso_comment_pop();
3852  } else {
3853  bypass_comment(";;FSO 3.6.11;; $Fiction Viewer Music:");
3854  }
3855 
3856  fso_comment_pop(true);
3857 
3858  return err;
3859 }
3860 
3861 void CFred_mission_save::save_custom_bitmap(const char *expected_string_640, const char *expected_string_1024, const char *string_field_640, const char *string_field_1024, int blank_lines)
3862 {
3864  {
3865  if ((*string_field_640 != '\0') || (*string_field_1024 != '\0'))
3866  {
3867  while (blank_lines-- > 0)
3868  fout("\n");
3869  }
3870 
3871  if (*string_field_640 != '\0')
3872  {
3873  fout("\n%s %s", expected_string_640, string_field_640);
3874  }
3875 
3876  if (*string_field_1024 != '\0')
3877  {
3878  fout("\n%s %s", expected_string_1024, string_field_1024);
3879  }
3880  }
3881 }
3882 
3884 {
3885  int i, z;
3886  ship_weapon *wp = &ptr->weapons;
3887 
3888  if (wp->ai_class != Ship_info[Ships[ship].ship_info_index].ai_class) {
3889  if (optional_string_fred("+AI Class:", "$Name:", "+Subsystem:"))
3890  parse_comments();
3891  else
3892  fout("\n+AI Class:");
3893 
3894  fout(" %s", Ai_class_names[wp->ai_class]);
3895  }
3896 
3897  z = 0;
3898  i = wp->num_primary_banks;
3899  while (i--)
3900  if (wp->primary_bank_weapons[i] != ptr->system_info->primary_banks[i])
3901  z = 1;
3902 
3903  if (z) {
3904  if (optional_string_fred("+Primary Banks:", "$Name:", "+Subsystem:"))
3905  parse_comments();
3906  else
3907  fout("\n+Primary Banks:");
3908 
3909  fout(" ( ");
3910  for (i=0; i<wp->num_primary_banks; i++) {
3911  if (wp->primary_bank_weapons[i] != -1) { // Just in case someone has set a weapon bank to empty
3912  fout("\"%s\" ", Weapon_info[wp->primary_bank_weapons[i]].name);
3913  } else {
3914  fout("\"\" ");
3915  }
3916  }
3917  fout(")");
3918  }
3919 
3920  z = 0;
3921  i = wp->num_secondary_banks;
3922  while (i--)
3923  if (wp->secondary_bank_weapons[i] != ptr->system_info->secondary_banks[i])
3924  z = 1;
3925 
3926  if (z) {
3927  if (optional_string_fred("+Secondary Banks:", "$Name:", "+Subsystem:"))
3928  parse_comments();
3929  else
3930  fout("\n+Secondary Banks:");
3931 
3932  fout(" ( ");
3933  for (i=0; i<wp->num_secondary_banks; i++) {
3934  if (wp->secondary_bank_weapons[i] != -1) {
3935  fout("\"%s\" ", Weapon_info[wp->secondary_bank_weapons[i]].name);
3936  } else {
3937  fout("\"\" ");
3938  }
3939  }
3940  fout(")");
3941  }
3942 
3943  z = 0;
3944  i = wp->num_secondary_banks;
3945  while (i--)
3946  if (wp->secondary_bank_ammo[i] != 100)
3947  z = 1;
3948 
3949  if (z) {
3950  if (optional_string_fred("+Sbank Ammo:", "$Name:", "+Subsystem:"))
3951  parse_comments();
3952  else
3953  fout("\n+Sbank Ammo:");
3954 
3955  fout(" ( ");
3956  for (i=0; i<wp->num_secondary_banks; i++)
3957  fout("%d ", wp->secondary_bank_ammo[i]);
3958 
3959  fout(")");
3960  }
3961 
3962  fso_comment_pop(true);
3963 }
3964 
3966 {
3967  int i, j, m, flag;
3968 
3969  Campaign_tree_formp->save_tree(); // flush all changes so they get saved.
3971  reset_parse();
3972  fred_parse_flag = 0;
3973 
3974  pathname = cf_add_ext(pathname, FS_CAMPAIGN_FILE_EXT);
3975  fp = cfopen(pathname, "wt", CFILE_NORMAL, CF_TYPE_MISSIONS);
3976  if (!fp) {
3977  nprintf(("Error", "Can't open campaign file to save.\n"));
3978  return -1;
3979  }
3980 
3981  required_string_fred("$Name:");
3982  parse_comments(0);
3983  fout(" %s", Campaign.name);
3984 
3986  required_string_fred("$Type:");
3987  parse_comments();
3988  fout(" %s", campaign_types[Campaign.type]);
3989 
3990  // XSTR
3991  if (Campaign.desc) {
3992  required_string_fred("+Description:");
3993  parse_comments();
3994  fout_ext("\n", "%s", Campaign.desc);
3995  fout("\n$end_multi_text");
3996  }
3997 
3998  if ( Campaign.type != CAMPAIGN_TYPE_SINGLE ) {
3999  required_string_fred("+Num Players:");
4000  parse_comments();
4001  fout(" %d", Campaign.num_players);
4002  }
4003 
4004  // campaign flags - Goober5000
4006  {
4007  optional_string_fred("$Flags:");
4008  parse_comments();
4009  fout(" %d\n", Campaign.flags);
4010  }
4011 
4012  // write out the ships and weapons which the player can start the campaign with
4013  optional_string_fred("+Starting Ships: (");
4014  parse_comments(2);
4015  for (i = 0; i < MAX_SHIP_CLASSES; i++ ) {
4016  if ( Campaign.ships_allowed[i] )
4017  fout(" \"%s\" ", Ship_info[i].name );
4018  }
4019  fout( ")\n" );
4020 
4021  optional_string_fred("+Starting Weapons: (");
4022  parse_comments();
4023  for (i = 0; i < MAX_WEAPON_TYPES; i++ ) {
4024  if ( Campaign.weapons_allowed[i] )
4025  fout(" \"%s\" ", Weapon_info[i].name );
4026  }
4027  fout( ")\n" );
4028 
4029  fred_parse_flag = 0;
4030  for (i=0; i<Campaign.num_missions; i++) {
4031  m = Sorted[i];
4032  required_string_either_fred("$Mission:", "#End");
4033  required_string_fred("$Mission:");
4034  parse_comments(2);
4035  fout(" %s", Campaign.missions[m].name);
4036 
4037  if ( strlen(Campaign.missions[i].briefing_cutscene) ) {
4038  if (optional_string_fred("+Briefing Cutscene:", "$Mission"))
4039  parse_comments();
4040  else
4041  fout("\n+Briefing Cutscene:");
4042 
4043  fout( " %s", Campaign.missions[i].briefing_cutscene );
4044  }
4045 
4046  required_string_fred("+Flags:", "$Mission:");
4047  parse_comments();
4048 
4049  // Goober5000
4051  {
4052  // don't save Bastion flag
4054 
4055  // new main hall stuff
4056  if (optional_string_fred("+Main Hall:", "$Mission:"))
4057  parse_comments();
4058  else
4059  fout("\n+Main Hall:");
4060 
4061  fout(" %s", Campaign.missions[m].main_hall.c_str());
4062  }
4063  else
4064  {
4065  // save Bastion flag properly
4066  fout(" %d", Campaign.missions[m].flags | ((Campaign.missions[m].main_hall != "") ? CMISSION_FLAG_BASTION : 0));
4067  }
4068 
4069  if ( Campaign.missions[m].debrief_persona_index > 0 ) {
4070  fso_comment_push(";;FSO 3.6.8;;");
4071  if (optional_string_fred("+Debriefing Persona Index:")) {
4072  parse_comments(1);
4074  } else {
4075  fout_version("\n+Debriefing Persona Index: %d", Campaign.missions[m].debrief_persona_index);
4076  }
4077  fso_comment_pop();
4078  } else {
4079  bypass_comment(";;FSO 3.6.8;; +Debriefing Persona Index:");
4080  }
4081 
4082  // save campaign link sexp
4083  bool mission_loop = false;
4084  bool mission_fork = false;
4085  flag = 0;
4086  for (j=0; j<Total_links; j++) {
4087  if (Links[j].from == m) {
4088  if (!flag) {
4089  if (optional_string_fred("+Formula:", "$Mission:"))
4090  parse_comments();
4091  else
4092  fout("\n+Formula:");
4093 
4094  fout(" ( cond\n");
4095  flag = 1;
4096  }
4097 
4098  //save_campaign_sexp(Links[j].sexp, Campaign.missions[Links[j].to].name);
4099  if (Links[j].is_mission_loop) {
4100  mission_loop = true;
4101  } else if (Links[j].is_mission_fork) {
4102  mission_fork = true;
4103  } else {
4104  save_campaign_sexp(Links[j].sexp, Links[j].to);
4105  }
4106  }
4107  }
4108 
4109  if (flag) {
4110  fout(")");
4111  }
4112 
4113  // now save campaign loop sexp
4114  if (mission_loop || mission_fork) {
4115  if (mission_loop)
4116  required_string_fred("\n+Mission Loop:");
4117  else
4118  required_string_fred("\n+Mission Fork:");
4119  parse_comments();
4120 
4121  int num_mission_special = 0;
4122  for (j=0; j<Total_links; j++) {
4123  if ( (Links[j].from == m) && (Links[j].is_mission_loop || Links[j].is_mission_fork) ) {
4124 
4125  num_mission_special++;
4126 
4127  if ((num_mission_special == 1) && Links[j].mission_branch_txt) {
4128  if (mission_loop)
4129  required_string_fred("+Mission Loop Text:");
4130  else
4131  required_string_fred("+Mission Fork Text:");
4132  parse_comments();
4133  fout_ext("\n", "%s", Links[j].mission_branch_txt);
4134  fout("\n$end_multi_text");
4135  }
4136 
4137  if ((num_mission_special == 1) && Links[j].mission_branch_brief_anim) {
4138  if (mission_loop)
4139  required_string_fred("+Mission Loop Brief Anim:");
4140  else
4141  required_string_fred("+Mission Fork Brief Anim:");
4142  parse_comments();
4143  fout_ext("\n", "%s", Links[j].mission_branch_brief_anim);
4144  fout("\n$end_multi_text");
4145  }
4146 
4147  if ((num_mission_special == 1) && Links[j].mission_branch_brief_sound) {
4148  if (mission_loop)
4149  required_string_fred("+Mission Loop Brief Sound:");
4150  else
4151  required_string_fred("+Mission Fork Brief Sound:");
4152  parse_comments();
4153  fout_ext("\n", "%s", Links[j].mission_branch_brief_sound);
4154  fout("\n$end_multi_text");
4155  }
4156 
4157  if (num_mission_special == 1) {
4158  // write out mission loop formula
4159  fout("\n+Formula:");
4160  fout(" ( cond\n");
4161  save_campaign_sexp(Links[j].sexp, Links[j].to);
4162  fout(")");
4163  }
4164  if (mission_fork) {
4165  fout("Option: ", Campaign.missions[Links[j].to].name);
4166  }
4167  }
4168  }
4169  if (mission_loop && num_mission_special > 1) {
4170  char buffer[1024];
4171  sprintf(buffer, "Multiple branching loop error from mission %s\nEdit campaign for *at most* 1 loop from each mission.", Campaign.missions[m].name);
4172  MessageBox((HWND)os_get_window(), buffer, "Error", MB_OK);
4173  }
4174  }
4175 
4176  if (optional_string_fred("+Level:", "$Mission:")){
4177  parse_comments();
4178  } else {
4179  fout("\n\n+Level:");
4180  }
4181 
4182  fout(" %d", Campaign.missions[m].level);
4183 
4184  if (optional_string_fred("+Position:", "$Mission:")){
4185  parse_comments();
4186  } else {
4187  fout("\n+Position:");
4188  }
4189 
4190  fout(" %d", Campaign.missions[m].pos);
4191 
4192  fso_comment_pop();
4193  }
4194 
4195  required_string_fred("#End");
4196  parse_comments(2);
4197  token_found = NULL;
4198  parse_comments();
4199  fout("\n");
4200 
4201  cfclose(fp);
4202  if (err)
4203  mprintf(("Campaign saving error code #%d\n", err));
4204  else
4206 
4207  fso_comment_pop(true);
4208 
4209  return err;
4210 }
4211 
4212 void CFred_mission_save::save_campaign_sexp(int node, int link_num)
4213 {
4214  SCP_string sexp_out;
4215  Assert(node >= 0);
4216 
4217  // if the link num is -1, then this is a end-of-campaign location
4218  if ( link_num != -1 ) {
4219  if (build_sexp_string(sexp_out, node, 2, SEXP_SAVE_MODE)) {
4220  fout(" (\n %s\n ( next-mission \"%s\" )\n )\n", sexp_out.c_str(), Campaign.missions[link_num].name);
4221  } else {
4222  fout(" ( %s( next-mission \"%s\" ) )\n", sexp_out.c_str(), Campaign.missions[link_num].name);
4223  }
4224  } else {
4225  if (build_sexp_string(sexp_out, node, 2, SEXP_SAVE_MODE)) {
4226  fout(" (\n %s\n ( end-of-campaign )\n )\n", sexp_out.c_str());
4227  } else {
4228  fout(" ( %s( end-of-campaign ) )\n", sexp_out.c_str());
4229  }
4230  }
4231 }
4232 
4234 {
4235  if ( fso_ver_comment.empty() ) {
4236  fso_ver_comment.push_back( SCP_string(ver) );
4237  return;
4238  }
4239 
4240  SCP_string before = fso_ver_comment.back();
4241 
4242  int major, minor, build, revis;
4243  int in_major, in_minor, in_build, in_revis;
4244  int elem1, elem2;
4245 
4246  elem1 = scan_fso_version_string( fso_ver_comment.back().c_str(), &major, &minor, &build, &revis );
4247  elem2 = scan_fso_version_string( ver, &in_major, &in_minor, &in_build, &in_revis );
4248 
4249  // check consistency
4250  if (elem1 == 3 && elem2 == 4 || elem1 == 4 && elem2 == 3) {
4251  elem1 = elem2 = 3;
4252  } else if ((elem1 >= 3 && elem2 >= 3) && (revis < 1000 || in_revis < 1000)) {
4253  elem1 = elem2 = 3;
4254  }
4255 
4256  if ( (elem1 == 3) && ((major > in_major) || ((major == in_major) && ((minor > in_minor) || ((minor == in_minor) && (build > in_build))))) ) {
4257  // the push'd version is older than our current version, so just push a copy of the previous version
4258  fso_ver_comment.push_back( before );
4259  } else if ( (elem1 == 4) && ((major > in_major) || ((major == in_major) && ((minor > in_minor) || ((minor == in_minor) && ((build > in_build) || ((build == in_build) || (revis > in_revis))))))) ) {
4260  // the push'd version is older than our current version, so just push a copy of the previous version
4261  fso_ver_comment.push_back( before );
4262  } else {
4263  fso_ver_comment.push_back( SCP_string(ver) );
4264  }
4265 }
4266 
4268 {
4269  if ( fso_ver_comment.empty() ) {
4270  return;
4271  }
4272 
4273  if (pop_all) {
4274  fso_ver_comment.clear();
4275  return;
4276  }
4277 
4278  fso_ver_comment.pop_back();
4279 }
4280 
#define SF2_TOGGLE_SUBSYSTEM_SCANNING
Definition: ship.h:490
#define SF2_WEAPONS_LOCKED
Definition: ship.h:507
GLenum GLsizei GLenum format
Definition: Gl.h:1509
SCP_string sexp
Definition: sexp.cpp:25556
field_type_t field_type
Definition: asteroid.h:136
int Neb2_poof_flags
Definition: neb.cpp:64
#define OF_INVULNERABLE
Definition: object.h:107
matrix skybox_orientation
Definition: missionparse.h:148
#define MOVIE_PRE_GAME
Definition: missionparse.h:119
char Starting_wing_names[MAX_STARTING_WINGS][NAME_LENGTH]
Definition: ship.cpp:139
char Neb2_texture_name[MAX_FILENAME_LEN]
Definition: neb.cpp:75
wing Wings[MAX_WINGS]
Definition: ship.cpp:128
#define CFILE_NORMAL
Definition: cfile.h:89
void * HWND
Definition: config.h:104
int i
Definition: multi_pxo.cpp:466
#define vm_free(ptr)
Definition: pstypes.h:548
int Total_links
int Nebula_heading
Definition: nebula.cpp:40
uint num_respawns
Definition: missionparse.h:141
#define AI_GOAL_UNDOCK
Definition: aigoals.h:36
#define CAMPAIGN_TYPE_SINGLE
float p
Definition: pstypes.h:111
#define SF2_CLOAKED
Definition: ship.h:504
SCP_vector< starfield_list_entry > suns
Definition: starfield.h:40
model_subsystem * system_info
Definition: ship.h:314
int primary_bank_weapons[MAX_SHIP_PRIMARY_BANKS]
Definition: ship.h:103
union ai_goal::@1 dockee
#define BI_HIGHLIGHT
int cf_delete(const char *filename, int path_type)
Delete the specified file.
Definition: cfile.cpp:483
void generate_weaponry_usage_list(int *arr, int wing)
int team
Definition: ship.h:606
#define SEXP_VARIABLE_NETWORK
Definition: sexp.h:888
#define WF_IGNORE_COUNT
Definition: ship.h:1503
GLbitfield stages
Definition: Glext.h:7177
#define DEPART_AT_LOCATION
Definition: missionparse.h:245
char Mission_callsigns[MAX_CALLSIGNS][NAME_LENGTH]
int Num_mission_events
int secondary_bank_weapons[MAX_SHIP_SECONDARY_BANKS]
Definition: ship.h:1299
char name[NAME_LENGTH]
Definition: weapon.h:322
int ai_class
Definition: ship.h:148
void sexp_variable_sort()
Definition: sexp.cpp:29577
int num_primary_banks
Definition: ship.h:99
int arrival_anchor
Definition: ship.h:1541
int objnum
Definition: ship.h:537
int score
Definition: ship.h:542
int game_type
Definition: missionparse.h:138
int Player_start_shipnum
int Mission_music[NUM_SCORES]
Definition: eventmusic.cpp:253
#define OF_LASER_PROTECTED
Definition: object.h:120
#define MISSION_FLAG_FULLNEB
Definition: missionparse.h:70
int Current_soundtrack_num
Definition: eventmusic.cpp:59
void fso_comment_pop(bool pop_all=false)
int div_y
Definition: starfield.h:33
polymodel * model_get(int model_num)
Definition: modelread.cpp:3134
weapon_info Weapon_info[MAX_WEAPON_TYPES]
Definition: weapons.cpp:79
float shield_get_strength(object *objp)
ship_weapon weapons
Definition: ship.h:658
SCP_string recommendation_text
#define MAX_CAMPAIGN_TYPES
#define MISSION_FLAG_SCRAMBLE
Definition: missionparse.h:85
float vm_vec_mag(const vec3d *v)
Definition: vecmat.cpp:325
int scan_fso_version_string(const char *text, int *major, int *minor, int *build, int *revis)
Definition: parselo.cpp:4126
char * name
Definition: ai.h:153
#define SF2_NO_DISABLED_SELF_DESTRUCT
Definition: ship.h:511
background_t Backgrounds[MAX_BACKGROUNDS]
Definition: starfield.cpp:125
int ambient_light_level
Definition: missionparse.h:152
int escort_priority
Definition: ship.h:541
char name[NAME_LENGTH]
Definition: missionparse.h:131
char name[NAME_LENGTH]
#define SF2_NO_SUBSPACE_DRIVE
Definition: ship.h:487
#define SF_IGNORE_COUNT
Definition: ship.h:436
char ** Ai_class_names
Definition: aicode.cpp:268
void cf_attrib(const char *name, int set, int clear, int type)
#define AI_GOAL_CHASE_WING
Definition: aigoals.h:37
void save_tree(int clear=1)
int sexp_variable_block_count()
char * model_get_dock_name(int modelnum, int index)
Definition: modelread.cpp:5103
#define AI_GOAL_DOCK
Definition: aigoals.h:30
campaign_editor * Campaign_tree_formp
physics_info phys_info
Definition: object.h:157
#define MAX_WINGS
Definition: globals.h:50
#define AI_GOAL_WAYPOINTS
Definition: aigoals.h:31
#define MAX_SHIPS
Definition: globals.h:37
int save_campaign_file(char *pathname)
int replace_all(char *str, char *oldstr, char *newstr, uint max_len, int range)
Definition: parselo.cpp:3941
#define AI_GOAL_GUARD
Definition: aigoals.h:38
int departure_anchor
Definition: ship.h:618
#define DEFAULT_NMODEL_FLAGS
Definition: starfield.h:20
brief_stage stages[MAX_BRIEF_STAGES]
#define SF2_FORCE_SHIELDS_ON
Definition: ship.h:502
int arrival_location
Definition: ship.h:610
#define SF_DOCK_LEADER
Definition: ship.h:455
void _cdecl void void _cdecl void _cdecl Warning(char *filename, int line, SCP_FORMAT_STRING const char *format,...) SCP_FORMAT_STRING_ARGS(3
int vm_matrix_same(matrix *m1, matrix *m2)
Definition: vecmat.cpp:1535
Assert(pm!=NULL)
int special_exp_blast
Definition: ship.h:586
void stuff_special_arrival_anchor_name(char *buf, int iff_index, int restrict_to_players, int retail_format)
int departure_location
Definition: ship.h:617
Definition: pstypes.h:88
char Fred_alt_names[MAX_SHIPS][NAME_LENGTH+1]
Definition: management.cpp:93
#define mprintf(args)
Definition: pstypes.h:238
int departure_anchor
Definition: ship.h:1547
int ai_index
Definition: ship.h:538
ship_weapon weapons
Definition: ship.h:362
SCP_vector< fiction_viewer_stage > Fiction_viewer_stages
int special_exp_inner
Definition: ship.h:587
int fhash_string_exists(const char *str)
Definition: fhash.cpp:130
ubyte debrief_persona_index
#define AI_GOAL_STAY_NEAR_SHIP
Definition: aigoals.h:47
Definition: 2d.h:95
float flDecayTime
Definition: ds.h:146
char * drop_white_space(char *str)
Definition: parselo.cpp:159
#define AI_GOAL_CHASE_ANY
Definition: aigoals.h:41
struct vec3d::@225::@227 xyz
CButton * team
int skybox_flags
Definition: missionparse.h:150
int Nebula_index
#define FS_VERSION_MAJOR
Definition: version.h:37
#define SF2_DONT_COLLIDE_INVIS
Definition: ship.h:486
GLclampf f
Definition: Glext.h:7097
int num_block_variables()
Definition: sexp.cpp:3777
char briefing_cutscene[NAME_LENGTH]
dock_instance * next
Definition: objectdock.h:19
#define AI_GOAL_WARP
Definition: aigoals.h:33
#define WF_NO_ARRIVAL_MUSIC
Definition: ship.h:1506
#define MISSION_TYPE_MULTI
Definition: missionparse.h:62
#define OF_NO_SHIELDS
Definition: object.h:110
#define SF2_FRIENDLY_STEALTH_INVIS
Definition: ship.h:484
int special_exp_outer
Definition: ship.h:588
#define SF2_AFFECTED_BY_GRAVITY
Definition: ship.h:489
#define AI_GOAL_IGNORE
Definition: aigoals.h:42
int max_respawn_delay
Definition: missionparse.h:142
ai_info Ai_info[MAX_AI_INFO]
Definition: ai.cpp:23
#define WF_NO_DYNAMIC
Definition: ship.h:1511
int dock_find_dockpoint_used_by_object(object *objp, object *other_objp)
Definition: objectdock.cpp:116
#define SF2_NAVPOINT_CARRY
Definition: ship.h:488
#define f2fl(fx)
Definition: floating.h:37
#define OF_SPECIAL_WARPIN
Definition: object.h:116
#define SF2_SET_CLASS_DYNAMICALLY
Definition: ship.h:500
char ani_filename[MAX_FILENAME_LEN]
#define AI_GOAL_DISARM_SHIP
Definition: aigoals.h:40
#define SCORE_FICTION_VIEWER
Definition: eventmusic.h:55
float flDecayHFRatio
Definition: ds.h:147
sexp_variable Sexp_variables[MAX_SEXP_VARIABLES]
Definition: sexp.cpp:846
GLenum GLuint GLenum GLsizei const GLchar * message
Definition: Glext.h:5156
#define OF_COLLIDES
Definition: object.h:104
Definition: ai.h:329
#define MAX_STAGE_ICONS
uint flags
Definition: ship.h:644
#define SCORE_BRIEFING
Definition: eventmusic.h:51
int primary_bank_weapons[MAX_SHIP_PRIMARY_BANKS]
Definition: ship.h:1294
ubyte blue
Definition: 2d.h:102
#define MAX_MISSION_EVENT_LOG_FLAGS
Definition: missiongoals.h:87
std::basic_string< char, std::char_traits< char >, std::allocator< char > > SCP_string
Definition: vmallocator.h:21
#define CMISSION_FLAG_BASTION
object * objp
Definition: lua.cpp:3105
cmd_brief Cmd_briefs[MAX_TVT_TEAMS]
ubyte ships_allowed[MAX_SHIP_CLASSES]
vec3d inner_min_bound
Definition: asteroid.h:131
#define Int3()
Definition: pstypes.h:292
int get_index_sexp_variable_name(const char *text)
Definition: sexp.cpp:29324
SOUNDTRACK_INFO Soundtracks[MAX_SOUNDTRACKS]
Definition: eventmusic.cpp:57
void convert_sexp_to_string(SCP_string &dest, int cur_node, int mode)
Definition: sexp.cpp:3904
briefing Briefings[MAX_TVT_TEAMS]
vec3d min_bound
Definition: asteroid.h:127
#define AIF_KAMIKAZE
Definition: ai.h:55
ship_bay_t * ship_bay
Definition: model.h:776
#define MAX_SQUADRON_WINGS
Definition: globals.h:55
#define AI_GOAL_NONE
Definition: ai.h:194
ship * shipp
Definition: lua.cpp:9162
vec3d pos
Definition: object.h:152
GLenum GLuint GLenum GLsizei const GLchar * buf
Definition: Glext.h:7308
#define MAX_REINFORCEMENT_MESSAGES
Definition: ship.h:78
#define MAX_TVT_WINGS
Definition: globals.h:59
vec3d max_bound
Definition: asteroid.h:128
vec3d view_pos
Definition: fredrender.cpp:103
mission_goal Mission_goals[MAX_GOALS]
int ai_flags
Definition: ai.h:330
#define CONTRAIL_THRESHOLD_DEFAULT
Definition: missionparse.h:226
SCP_list< waypoint_list > Waypoint_lists
Definition: waypoint.cpp:9
float volume
Definition: sound.h:93
char * name
#define SPECIAL_ARRIVAL_ANCHOR_FLAG
Definition: missionparse.h:37
int div_x
Definition: starfield.h:33
SCP_list< CJumpNode > Jump_nodes
Definition: jumpnode.cpp:16
GLenum type
Definition: Gl.h:1492
#define BI_USE_WING_ICON
int primary_banks[MAX_SHIP_PRIMARY_BANKS]
Definition: model.h:218
#define BI_MIRROR_ICON
int hotkey
Definition: ship.h:540
iff_info Iff_info[MAX_IFFS]
Definition: iff_defs.cpp:20
int Num_backgrounds
Definition: starfield.cpp:123
ubyte green
Definition: 2d.h:101
int Sorted[MAX_CAMPAIGN_MISSIONS]
#define SF2_SCRAMBLE_MESSAGES
Definition: ship.h:509
char * Reinforcement_type_names[]
reinforcements Reinforcements[MAX_REINFORCEMENTS]
Definition: ship.cpp:165
fix Entry_delay_time
SCP_vector< waypoint > & get_waypoints()
Definition: waypoint.cpp:89
int num_initial_asteroids
Definition: asteroid.h:135
float Neb2_awacs
Definition: neb.cpp:156
int ship_get_num_ships()
Definition: ship.cpp:522
#define OF_TARGETABLE_AS_BOMB
Definition: object.h:118
ship_subsys subsys_list
Definition: ship.h:630
campaign_tree_view * Campaign_tree_viewp
sound_env sound_environment
Definition: missionparse.h:155
#define SCORE_DEBRIEF_FAIL
Definition: eventmusic.h:54
#define FS_CAMPAIGN_FILE_EXT
Definition: freespace.h:26
int object_is_docked(object *objp)
Definition: object.cpp:2019
#define INVALID_GOAL
Definition: missiongoals.h:33
uint os_get_window()
Definition: osapi.cpp:208
SCP_vector< MMessage > Messages
float scale_x
Definition: starfield.h:32
brief_icon * icons
int orders_accepted
Definition: ship.h:624
int Locked_sexp_true
Definition: sexp.cpp:828
bool use_special_explosion
Definition: ship.h:584
int instance
Definition: object.h:150
#define SCORE_DEBRIEF_AVERAGE
Definition: eventmusic.h:53
#define DEPART_AT_DOCK_BAY
Definition: missionparse.h:246
int Num_stars
Definition: starfield.cpp:58
angles ang
Definition: starfield.h:34
int Token_found_flag
Definition: parselo.cpp:44
char squad_filename[MAX_FILENAME_LEN]
Definition: missionparse.h:144
#define OBJ_START
Definition: object.h:35
char voice[MAX_FILENAME_LEN]
#define OF_IMMOBILE
Definition: object.h:122
char * Departure_location_names[MAX_DEPARTURE_NAMES]
int group
Definition: ship.h:674
dock_instance * dock_list
Definition: object.h:166
bool dock_check_docked_one_on_one(object *objp)
Definition: objectdock.cpp:56
SCP_vector< alt_class > s_alt_classes
Definition: ship.h:791
struct matrix::@228::@230 vec
#define WF_NO_DEPARTURE_WARP
Definition: ship.h:1510
char created[DATE_TIME_LENGTH]
Definition: missionparse.h:134
unsigned int uint
Definition: pstypes.h:64
char dockee[NAME_LENGTH]
matrix view_orient
Definition: fredrender.cpp:112
void vsprintf(SCP_string &dest, const char *format, va_list ap)
Definition: parselo.cpp:3800
#define cfopen(...)
Definition: cfile.h:134
#define nprintf(args)
Definition: pstypes.h:239
int optional_string_fred(char *pstr, char *end, char *end2)
Definition: parselo.cpp:630
void save_turret_info(ship_subsys *ptr, int ship)
ubyte weapons_allowed[MAX_WEAPON_TYPES]
int fred_parse_flag
Definition: parselo.cpp:43
int build_sexp_string(SCP_string &accumulator, int cur_node, int level, int mode)
Definition: sexp.cpp:3839
#define MAX_SHIP_CLASSES
Definition: globals.h:48
char Mission_alt_types[MAX_ALT_TYPE_NAMES][NAME_LENGTH]
#define SF2_NO_DEATH_SCREAM
Definition: ship.h:495
char * Nebula_filenames[NUM_NEBULAS]
int num_paths
Definition: model.h:540
ai_profile_t * ai_profile
Definition: missionparse.h:168
#define MISSION_FLAG_RED_ALERT
Definition: missionparse.h:84
#define strnicmp(s1, s2, n)
Definition: config.h:272
int wingnum
Definition: ship.h:623
char Squadron_wing_names[MAX_SQUADRON_WINGS][NAME_LENGTH]
Definition: ship.cpp:140
float speed
Definition: physics.h:79
#define DEFAULT_COMMAND
#define MAX_STARTING_WINGS
Definition: globals.h:54
int num_secondary_banks
Definition: ship.h:100
ai_goal goals[MAX_AI_GOALS]
Definition: ai.h:412
mission_event Mission_events[MAX_MISSION_EVENTS]
Persona * Personas
#define SF2_HIDE_SHIP_NAME
Definition: ship.h:498
#define FRED_MISSION_VERSION
Definition: missionparse.h:45
int Mission_callsign_count
#define MAX_WEAPON_TYPES
Definition: globals.h:73
int num_weapon_choices
Definition: missionparse.h:539
int secondary_banks[MAX_SHIP_SECONDARY_BANKS]
Definition: model.h:220
float hull_strength
Definition: object.h:160
ai_profile_t Ai_profiles[MAX_AI_PROFILES]
Definition: ai_profiles.cpp:22
int persona_index
Definition: ship.h:696
char * Mission_event_log_flags[MAX_MISSION_EVENT_LOG_FLAGS]
sprintf(buf,"(%f,%f,%f)", v3->xyz.x, v3->xyz.y, v3->xyz.z)
char * Goal_type_names[MAX_GOAL_TYPE_NAMES]
#define WF_NO_ARRIVAL_WARP
Definition: ship.h:1509
int weaponry_pool[MAX_WEAPON_TYPES]
Definition: missionparse.h:540
#define MAX_AI_GOALS
Definition: ai.h:91
GLdouble GLdouble z
Definition: Glext.h:5451
#define SF_HIDDEN_FROM_SENSORS
Definition: ship.h:464
ubyte red
Definition: 2d.h:100
#define OF_PROTECTED
Definition: object.h:108
bool generate_special_explosion_block_variables()
Definition: sexp.cpp:3726
#define SUBSYSTEM_TURRET
Definition: model.h:54
char squad_name[NAME_LENGTH]
Definition: missionparse.h:145
int special_exp_deathroll_time
Definition: ship.h:591
#define GR_640
Definition: 2d.h:652
#define vm_strdup(ptr)
Definition: pstypes.h:549
int fout_ext(char *pre_str, char *format,...)
int Nebula_bank
Definition: nebula.cpp:39
char cargo1
Definition: ship.h:549
char notes[NOTES_LENGTH]
Definition: missionparse.h:136
int autosave_mission_file(char *pathname)
#define AI_GOAL_STAY_STILL
Definition: aigoals.h:52
GLuint buffer
Definition: Glext.h:5492
int Num_wings
Definition: ship.cpp:120
#define MB_OK
Definition: config.h:179
#define FS_VERSION_MINOR
Definition: version.h:38
int secondary_bank_weapons[MAX_SHIP_SECONDARY_BANKS]
Definition: ship.h:104
int Num_teams
int cfputc(int c, CFILE *cfile)
Definition: cfile.cpp:1460
int cf_rename(const char *old_name, const char *name, int dir_type)
Definition: cfile.cpp:567
GLsizei const GLchar ** string
Definition: Glext.h:5636
char skybox_model[MAX_FILENAME_LEN]
Definition: missionparse.h:147
char name[MAX_NAME_LEN]
Definition: model.h:415
char * Ai_behavior_names[MAX_AI_BEHAVIORS]
int subsys_cargo_name
Definition: ship.h:373
float current_hits
Definition: ship.h:319
Definition: ship.h:534
#define WF_NO_ARRIVAL_MESSAGE
Definition: ship.h:1508
#define AI_GOAL_EVADE_SHIP
Definition: aigoals.h:44
#define MAX_AI_PROFILES
Definition: ai_profiles.h:76
cmd_brief_stage stage[CMD_BRIEF_STAGES_MAX]
int idx
Definition: multiui.cpp:761
#define ARRIVE_AT_LOCATION
Definition: missionparse.h:237
int secondary_bank_ammo[MAX_SHIP_SECONDARY_BANKS]
Definition: ship.h:134
GLdouble GLdouble t
Definition: Glext.h:5329
#define SF_NO_DEPARTURE_WARP
Definition: ship.h:441
float assist_score_pct
Definition: ship.h:543
#define MGF_NO_MUSIC
Definition: missiongoals.h:52
char loading_screen[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN]
Definition: missionparse.h:146
char profile_name[NAME_LENGTH]
Definition: ai_profiles.h:80
char voice[MAX_FILENAME_LEN]
int wave_count
Definition: ship.h:1527
#define AI_GOAL_IGNORE_NEW
Definition: aigoals.h:57
campaign_tree_wnd * Campaign_wnd
object Objects[MAX_OBJECTS]
Definition: object.cpp:62
ubyte alpha
Definition: 2d.h:103
#define MESSAGE_LENGTH
Definition: globals.h:23
const char * token_found
Definition: parselo.cpp:49
#define AI_GOAL_PLAY_DEAD
Definition: aigoals.h:53
#define stars_get_num_bitmaps()
Definition: starfield.h:68
int sexp_variable_count()
Definition: sexp.cpp:29490
#define GOAL_TYPE_MASK
Definition: missiongoals.h:34
SCP_vector< mission_cutscene > cutscenes
Definition: missionparse.h:170
#define AI_GOAL_WAYPOINTS_ONCE
Definition: aigoals.h:32
#define SF_NO_ARRIVAL_WARP
Definition: ship.h:440
bool mission_has_fiction()
SCP_string team_name
Definition: ship.h:812
char modified[DATE_TIME_LENGTH]
Definition: missionparse.h:135
matrix orient
Definition: object.h:153
int ship_guardian_threshold
Definition: ship.h:601
bool use_shockwave
Definition: ship.h:589
sexp_variable Block_variables[MAX_SEXP_VARIABLES]
Definition: sexp.cpp:847
#define OBJ_SHIP
Definition: object.h:32
char name[NAME_LENGTH]
float decay
Definition: sound.h:95
#define AI_GOAL_GUARD_WING
Definition: aigoals.h:43
GLbitfield flags
Definition: Glext.h:6722
menu_music Spooled_music[MAX_SPOOLED_MUSIC]
Definition: eventmusic.cpp:249
int Num_weapon_types
Definition: weapons.cpp:105
#define SF2_NO_BUILTIN_MESSAGES
Definition: ship.h:491
void reset_parse(char *text)
Definition: parselo.cpp:3305
#define OF_MISSILE_PROTECTED
Definition: object.h:121
GLuint const GLchar * name
Definition: Glext.h:5608
int Num_builtin_messages
char background[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN]
int final_death_time
Definition: ship.h:569
char background[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN]
#define MAX_PATH_LEN
Definition: pstypes.h:325
#define SF_RED_ALERT_STORE_STATUS
Definition: ship.h:469
char * cf_add_ext(const char *filename, const char *ext)
Definition: cfile.cpp:458
char command_sender[NAME_LENGTH]
Definition: missionparse.h:159
#define SF_SCANNABLE
Definition: ship.h:465
#define SIF_SUPPORT
Definition: ship.h:878
#define SF2_PRIMARIES_LOCKED
Definition: ship.h:492