2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
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
10 * $Logfile: /Freespace2/code/Fred2/MissionSave.cpp $
15 * Mission saving in Fred.
18 * Revision 1.2 2002/06/09 04:41:16 relnev
19 * added copyright header
21 * Revision 1.1.1.1 2002/05/03 03:28:08 root
25 * 33 8/28/99 7:29p Dave
26 * Fixed wingmen persona messaging. Make sure locked turrets don't count
27 * towards the # attacking a player.
29 * 32 8/27/99 12:04a Dave
30 * Campaign loop screen.
32 * 31 8/26/99 8:52p Dave
33 * Gave multiplayer TvT messaging a heavy dose of sanity. Cheat codes.
35 * 30 8/17/99 5:20p Andsager
36 * Control campaign editor bug.
38 * 29 8/16/99 3:53p Andsager
39 * Add special warp in interface in Fred and saving / reading.
41 * 28 7/02/99 4:30p Dave
42 * Much more sophisticated lightning support.
44 * 27 6/10/99 11:06a Andsager
45 * Mission designed selection of asteroid types.
47 * 26 6/03/99 6:37p Dave
48 * More TNT fun. Made perspective bitmaps more flexible.
50 * 25 5/20/99 6:59p Dave
51 * Added alternate type names for ships. Changed swarm missile table
54 * 24 5/09/99 6:00p Dave
55 * Lots of cool new effects. E3 build tweaks.
57 * 23 4/26/99 8:47p Dave
58 * Made all pof related nebula stuff customizable through Fred.
60 * 22 4/26/99 12:49p Andsager
61 * Add protect object from beam support to Fred
63 * 21 4/16/99 2:34p Andsager
64 * Second pass on debris fields
66 * 20 4/15/99 5:00p Andsager
67 * Frist pass on Debris field
69 * 19 4/07/99 6:21p Dave
70 * Fred and Freespace support for multiple background bitmaps and suns.
71 * Fixed link errors on all subprojects. Moved encrypt_init() to
72 * cfile_init() and lcl_init(), since its safe to call twice.
74 * 18 3/31/99 9:50a Andsager
75 * Interface for generalization of asteroid field (debris field)
77 * 17 3/24/99 4:05p Dave
78 * Put in support for assigning the player to a specific squadron with a
79 * specific logo. Preliminary work for doing pos/orient checksumming in
80 * multiplayer to reduce bandwidth.
82 * 16 3/01/99 7:39p Dave
83 * Added prioritizing ship respawns. Also fixed respawns in TvT so teams
84 * don't mix respawn points.
86 * 15 2/26/99 6:01p Andsager
87 * Add sexp has-been-tagged-delay and cap-subsys-cargo-known-delay
89 * 14 2/17/99 2:11p Dave
90 * First full run of squad war. All freespace and tracker side stuff
93 * 13 2/11/99 2:15p Andsager
94 * Add ship explosion modification to FRED
96 * 12 2/07/99 8:51p Andsager
97 * Add inner bound to asteroid field. Inner bound tries to stay astroid
98 * free. Wrap when within and don't throw at ships inside.
100 * 11 2/03/99 12:42p Andsager
101 * Add escort priority. Modify ship_flags_dlg to include field. Save and
102 * Load. Add escort priority field to ship.
104 * 10 1/25/99 5:03a Dave
105 * First run of stealth, AWACS and TAG missile support. New mission type
108 * 9 1/19/99 3:57p Andsager
109 * Round 2 of variables
111 * 8 1/07/99 1:52p Andsager
112 * Initial check in of Sexp_variables
114 * 7 12/17/98 2:43p Andsager
115 * Modify fred campaign save file to include optional mission loops
117 * 6 11/14/98 5:37p Dave
118 * Put in support for full nebulas.
120 * 5 11/05/98 4:18p Dave
121 * Modified mission file format.
123 * 4 10/29/98 9:22p Dave
124 * Removed minor bug concering externalization of campaign files.
126 * 3 10/29/98 6:49p Dave
127 * Finished up Fred support for externalizing mission and campaign files.
129 * 2 10/07/98 6:28p Dave
130 * Initial checkin. Renamed all relevant stuff to be Fred2 instead of
131 * Fred. Globalized mission and campaign file extensions. Removed Silent
132 * Threat specific code.
134 * 1 10/07/98 3:00p Dave
136 * 207 9/16/98 10:42a Hoffoss
137 * Added sexp node counting to fsm files for end user designers.
139 * 206 9/15/98 7:24p Dave
140 * Minor UI changes. Localized bunch of new text.
142 * 205 9/15/98 11:44a Dave
143 * Renamed builtin ships and wepaons appropriately in FRED. Put in scoring
144 * scale factors. Fixed standalone filtering of MD missions to non-MD
147 * 204 9/10/98 1:17p Dave
148 * Put in code to flag missions and campaigns as being MD or not in Fred
149 * and Freespace. Put in multiplayer support for filtering out MD
150 * missions. Put in multiplayer popups for warning of non-valid missions.
152 * 203 5/21/98 12:58a Hoffoss
153 * Fixed warnings optimized build turned up.
155 * 202 5/20/98 1:04p Hoffoss
156 * Made credits screen use new artwork and removed rating field usage from
157 * Fred (a goal struct member).
159 * 201 5/19/98 1:19p Allender
160 * new low level reliable socket reading code. Make all missions/campaign
161 * load/save to data missions folder (i.e. we are rid of the player
164 * 200 5/13/98 5:13p Allender
165 * red alert support to go back to previous mission
172 #include "freespace.h"
173 #include "missionsave.h"
174 #include "missiongoals.h"
175 #include "missionmessage.h"
176 #include "missionparse.h"
177 #include "fredrender.h"
179 #include "starfield.h"
180 #include "lighting.h"
181 #include "linklist.h"
183 #include "missioncampaign.h"
184 #include "campaigntreewnd.h"
185 #include "campaigntreeview.h"
186 #include "campaigneditordlg.h"
188 #include "missionbriefcommon.h"
189 #include "management.h"
190 #include "eventmusic.h"
192 #include "asteroid.h"
193 #include "missioncmdbrief.h"
194 #include "jumpnode.h"
200 int CFred_mission_save::save_mission_file(char *pathname)
202 char backup_name[256], savepath[MAX_PATH_LEN], *p;
205 t = CTime::GetCurrentTime();
206 strcpy(The_mission.modified, t.Format("%x at %X"));
208 strcpy(savepath, "");
209 p = strrchr(pathname, '\\');
212 strcpy(savepath, pathname);
214 strcat(savepath, "\\");
216 strcat(savepath, "saving.xxx");
220 fp = cfopen(savepath, "wt", CFILE_NORMAL);
222 nprintf(("Error", "Can't open mission file to save.\n"));
226 if (save_mission_info())
228 else if (save_plot_info())
230 else if (save_variables())
232 // else if (save_briefing_info())
234 else if (save_cmd_briefs())
236 else if (save_briefing())
238 else if (save_debriefing())
240 else if (save_players())
242 else if (save_objects())
244 else if (save_wings())
246 else if (save_events())
248 else if (save_goals())
250 else if (save_waypoints())
252 else if (save_messages())
254 else if (save_reinforcements())
256 else if (save_bitmaps())
258 else if (save_asteroid_fields())
260 else if (save_music())
263 required_string_fred("#End");
272 mprintf(("Mission saving error code #%d", err));
275 strcpy(backup_name, pathname);
276 if (backup_name[strlen(backup_name) - 4] == '.')
277 backup_name[strlen(backup_name) - 4] = 0;
279 strcat(backup_name, ".bak");
280 cf_attrib(pathname, 0, FILE_ATTRIBUTE_READONLY, CF_TYPE_MISSIONS);
281 cf_delete(backup_name, CF_TYPE_MISSIONS);
282 cf_rename(pathname, backup_name, CF_TYPE_MISSIONS);
283 cf_rename(savepath, pathname, CF_TYPE_MISSIONS);
289 int CFred_mission_save::autosave_mission_file(char *pathname)
291 char backup_name[256], name2[256];
295 t = CTime::GetCurrentTime();
296 strcpy(The_mission.modified, t.Format("%x at %X"));
298 len = strlen(pathname);
299 strcpy(backup_name, pathname);
300 strcpy(name2, pathname);
301 sprintf(backup_name + len, ".%.3d", BACKUP_DEPTH);
302 cf_delete(backup_name, CF_TYPE_MISSIONS);
303 for (i=BACKUP_DEPTH; i>1; i--) {
304 sprintf(backup_name + len, ".%.3d", i - 1);
305 sprintf(name2 + len, ".%.3d", i);
306 cf_rename(backup_name, name2, CF_TYPE_MISSIONS);
309 strcpy(backup_name + len, ".001");
312 fp = cfopen(backup_name, "wt", CFILE_NORMAL, CF_TYPE_MISSIONS);
314 nprintf(("Error", "Can't open mission file to save.\n"));
318 if (save_mission_info())
320 else if (save_plot_info())
322 else if (save_variables())
324 // else if (save_briefing_info())
326 else if (save_cmd_briefs())
328 else if (save_briefing())
330 else if (save_debriefing())
332 else if (save_players())
334 else if (save_objects())
336 else if (save_wings())
338 else if (save_events())
340 else if (save_goals())
342 else if (save_waypoints())
344 else if (save_messages())
346 else if (save_reinforcements())
348 else if (save_bitmaps())
350 else if (save_asteroid_fields())
352 else if (save_music())
355 required_string_fred("#End");
364 mprintf(("Mission saving error code #%d", err));
369 int CFred_mission_save::save_mission_info()
371 // int f = count_free_sexp_nodes();
372 // fout("// Of %d total sexp nodes, %d free, %d used\n\n",MAX_SEXP_NODES, f, MAX_SEXP_NODES - f);
374 required_string_fred("#Mission Info");
377 required_string_fred("$Version:");
379 fout(" %.2f", FRED_MISSION_VERSION);
382 required_string_fred("$Name:");
385 fout_ext("%s", The_mission.name);
387 required_string_fred("$Author:");
389 fout(" %s", The_mission.author);
391 required_string_fred("$Created:");
393 fout(" %s", The_mission.created);
395 required_string_fred("$Modified:");
397 fout(" %s", The_mission.modified);
399 required_string_fred("$Notes:");
401 fout("\n%s", The_mission.notes);
403 required_string_fred("$End Notes:");
407 required_string_fred("$Mission Desc:");
410 fout_ext("%s", The_mission.mission_desc);
413 required_string_fred("$end_multi_text");
417 if (optional_string_fred("+Game Type:"))
420 fout("\n\n+Game Type:");
421 fout("\n%s", Game_types[The_mission.game_type]);
424 if ( optional_string_fred("+Game Type Flags:")){
427 fout("\n+Game Type Flags:");
430 fout(" %d", The_mission.game_type);
432 if (optional_string_fred("+Flags:")){
438 fout(" %d", The_mission.flags);
440 // maybe write out Nebula intensity
441 if(The_mission.flags & MISSION_FLAG_FULLNEB){
442 SDL_assert(Neb2_awacs > 0.0f);
443 fout("\n+NebAwacs: %f\n", Neb2_awacs);
446 fout("\n+Storm: %s\n", Mission_parse_storm_name);
449 // For multiplayer missions -- write out the number of player starts and number of respawns
450 if (The_mission.game_type & MISSION_TYPE_MULTI) {
451 if (optional_string_fred("+Num Players:"))
454 fout("\n+Num Players:");
456 fout(" %d", Player_starts);
458 if (optional_string_fred("+Num Respawns:"))
461 fout("\n+Num Respawns:");
463 fout(" %d", The_mission.num_respawns);
466 if ( optional_string_fred("+Red Alert:")){
469 fout("\n+Red Alert:");
471 fout(" %d", The_mission.red_alert );
473 if ( optional_string_fred("+Scramble:")){
476 fout("\n+Scramble:");
478 fout(" %d", The_mission.scramble);
480 if ( optional_string_fred("+Disallow Support:")){
483 fout("\n+Disallow Support:");
486 fout(" %d", The_mission.disallow_support);
488 if (Mission_all_attack) {
489 if (optional_string_fred("+All Teams Attack")){
492 fout("\n+All Teams Attack");
496 if (Entry_delay_time) {
497 if (optional_string_fred("+Player Entry Delay:"))
500 fout("\n\n+Player Entry Delay:");
502 fout("\n%f", f2fl(Entry_delay_time));
505 if (optional_string_fred("+Viewer pos:")){
508 fout("\n\n+Viewer pos:");
511 save_vector(view_pos);
513 if (optional_string_fred("+Viewer orient:")){
516 fout("\n+Viewer orient:");
519 save_matrix(view_orient);
522 if(!(The_mission.game_type & MISSION_TYPE_MULTI) && (strlen(The_mission.squad_name) > 0)){
524 fout("\n+SquadReassignName: %s", The_mission.squad_name);
527 if(strlen(The_mission.squad_filename) > 0){
528 fout("\n+SquadReassignLogo: %s", The_mission.squad_filename);
535 int CFred_mission_save::save_plot_info()
537 required_string_fred("#Plot Info");
541 required_string_fred("$Tour:");
544 fout_ext("%s", The_mission.tour_name);
546 required_string_fred("$Pre-Briefing Cutscene:");
548 fout(" %s", The_mission.pre_briefing_cutscene);
550 required_string_fred("$Pre-Mission Cutscene:");
552 fout(" %s", The_mission.pre_mission_cutscene);
554 required_string_fred("$Next Mission Success:");
556 fout(" %s", The_mission.next_mission_success);
558 required_string_fred("$Next Mission Partial:");
560 fout(" %s", The_mission.next_mission_partial);
562 required_string_fred("$Next Mission Failure:");
564 fout(" %s", The_mission.next_mission_failure);
569 int CFred_mission_save::save_cmd_brief()
574 required_string_fred("#Command Briefing");
577 if (The_mission.game_type & MISSION_TYPE_MULTI)
578 return err; // no command briefings allowed in multiplayer missions.
580 for (stage=0; stage<Cur_cmd_brief->num_stages; stage++) {
581 required_string_fred("$Stage Text:");
586 fout_ext("%s", Cur_cmd_brief->stage[stage].text);
588 required_string_fred("$end_multi_text", "$Stage Text:");
591 required_string_fred("$Ani Filename:");
593 fout(" %s", Cur_cmd_brief->stage[stage].ani_filename);
595 required_string_fred("+Wave Filename:", "$Ani Filename:");
597 fout(" %s", Cur_cmd_brief->stage[stage].wave_filename);
603 int CFred_mission_save::save_cmd_briefs()
607 for (i=0; i<Num_teams; i++) {
608 Cur_cmd_brief = &Cmd_briefs[i];
615 int CFred_mission_save::save_briefing()
622 for ( nb = 0; nb < Num_teams; nb++ ) {
624 required_string_fred("#Briefing");
627 required_string_fred("$start_briefing");
630 SDL_assert(Briefings[nb].num_stages <= MAX_BRIEF_STAGES);
631 required_string_fred("$num_stages:");
633 fout(" %d", Briefings[nb].num_stages);
635 for (i=0; i<Briefings[nb].num_stages; i++) {
636 bs = &Briefings[nb].stages[i];
638 required_string_fred("$start_stage");
641 required_string_fred("$multi_text");
646 sprintf(out,"%s", bs->new_text);
649 required_string_fred("$end_multi_text", "$start_stage");
652 if (!drop_white_space(bs->voice)[0]){
653 strcpy(bs->voice, "None");
656 required_string_fred("$voice:");
658 fout(" %s", bs->voice);
660 required_string_fred("$camera_pos:");
662 save_vector(bs->camera_pos);
664 required_string_fred("$camera_orient:");
666 save_matrix(bs->camera_orient);
668 required_string_fred("$camera_time:");
670 fout(" %d", bs->camera_time);
672 required_string_fred("$num_lines:");
674 fout(" %d", bs->num_lines);
676 for (k=0; k<bs->num_lines; k++) {
677 required_string_fred("$line_start:");
679 fout(" %d", bs->lines[k].start_icon);
681 required_string_fred("$line_end:");
683 fout(" %d", bs->lines[k].end_icon);
686 required_string_fred("$num_icons:");
688 SDL_assert(bs->num_icons <= MAX_STAGE_ICONS );
689 fout(" %d", bs->num_icons);
691 required_string_fred("$Flags:");
693 fout(" %d", bs->flags);
695 required_string_fred("$Formula:");
697 convert_sexp_to_string(bs->formula, out, SEXP_SAVE_MODE);
700 for ( j = 0; j < bs->num_icons; j++ ) {
703 required_string_fred("$start_icon");
706 required_string_fred("$type:");
708 fout(" %d", bi->type);
710 required_string_fred("$team:");
712 fout(" %s", Team_names[bitmask_2_bitnum(bi->team)]);
714 required_string_fred("$class:");
716 if (bi->ship_class < 0)
719 fout(" %s", Ship_info[bi->ship_class].name);
721 required_string_fred("$pos:");
723 save_vector(bi->pos);
725 if (drop_white_space(bi->label)[0]) {
726 if (optional_string_fred("$label:"))
731 fout(" %s", bi->label);
734 if (optional_string_fred("+id:"))
741 required_string_fred("$hlight:");
743 fout(" %d", (bi->flags & BI_HIGHLIGHT)?1:0 );
745 required_string_fred("$multi_text");
748 // sprintf(out,"\n%s", bi->text);
751 required_string_fred("$end_multi_text");
754 required_string_fred("$end_icon");
758 required_string_fred("$end_stage");
761 required_string_fred("$end_briefing");
768 int CFred_mission_save::save_debriefing()
773 for ( j = 0; j < Num_teams; j++ ) {
775 Debriefing = &Debriefings[j];
777 required_string_fred("#Debriefing_info");
780 required_string_fred("$Num stages:");
782 fout(" %d", Debriefing->num_stages);
784 for (i=0; i<Debriefing->num_stages; i++) {
785 required_string_fred("$Formula:");
787 convert_sexp_to_string(Debriefing->stages[i].formula, out, SEXP_SAVE_MODE);
791 required_string_fred("$Multi text");
794 fout_ext("%s", Debriefing->stages[i].new_text);
796 required_string_fred("$end_multi_text");
799 if (!drop_white_space(Debriefing->stages[i].voice)[0]){
800 strcpy(Debriefing->stages[i].voice, "None");
803 required_string_fred("$Voice:");
805 fout(" %s", Debriefing->stages[i].voice);
808 required_string_fred("$Recommendation text:");
811 fout_ext("%s", Debriefing->stages[i].new_recommendation_text);
813 required_string_fred("$end_multi_text");
821 int sexp_variable_block_count();
823 int CFred_mission_save::save_variables()
826 char number[] = "number";
827 char string[] = "string";
828 char block[] = "block";
831 // sort sexp_variables
832 sexp_variable_sort();
835 int num_variables = sexp_variable_count();
836 int num_block_vars = sexp_variable_block_count();
837 int total_variables = num_variables + num_block_vars;
839 if (total_variables > 0) {
842 required_string_fred("#Sexp_variables");
845 required_string_fred("$Variables:");
851 for (i=0; i<num_variables; i++) {
852 if (Sexp_variables[i].type & SEXP_VARIABLE_NUMBER) {
857 // index "var name" "default" "type"
858 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);
862 for (i=MAX_SEXP_VARIABLES-num_block_vars; i<MAX_SEXP_VARIABLES; i++) {
864 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);
874 int CFred_mission_save::save_players()
877 int used_pool[MAX_WEAPON_TYPES];
879 // write out alternate name list
880 if(Mission_alt_type_count > 0){
881 fout("\n\n#Alternate Types:\n");
883 // write them all out
884 for(i=0; i<Mission_alt_type_count; i++){
885 fout("$Alt: %s\n", Mission_alt_types[i]);
892 required_string_fred("#Players");
894 fout("\t\t;! %d total\n", Player_starts);
896 for (i=0; i<Num_teams; i++) {
897 required_string_fred("$Starting Shipname:");
899 SDL_assert(Player_start_shipnum >= 0);
900 fout(" %s", Ships[Player_start_shipnum].ship_name);
902 required_string_fred("$Ship Choices:");
906 for (j=0; j<Team_data[i].number_choices; j++)
907 fout("\t\"%s\"\t%d\n", Ship_info[Team_data[i].ship_list[j]].name,
908 Team_data[i].ship_count[j]);
912 if (optional_string_fred("+Weaponry Pool:", "$Starting Shipname:")){
915 fout("\n\n+Weaponry Pool:");
919 generate_weaponry_usage_list(used_pool);
920 for (j=0; j<Num_weapon_types; j++){
921 if (Team_data[i].weaponry_pool[j] + used_pool[j] > 0){
922 fout("\t\"%s\"\t%d\n", Weapon_info[j].name, Team_data[i].weaponry_pool[j] + used_pool[j]);
932 int CFred_mission_save::save_objects()
940 required_string_fred("#Objects");
942 fout("\t\t;! %d total\n", ship_get_num_ships() );
944 for (i=z=0; i<MAX_SHIPS; i++) {
945 if (Ships[i].objnum < 0){
949 j = Objects[Ships[i].objnum].type;
950 if ((j != OBJ_SHIP) && (j != OBJ_START)){
954 objp = &Objects[Ships[i].objnum];
955 sip = &Ship_info[Ships[i].ship_info_index];
956 required_string_either_fred("$Name:", "#Wings");
957 required_string_fred("$Name:");
958 parse_comments(z ? 2 : 1);
959 fout(" %s\t\t;! Object #%d\n", Ships[i].ship_name, i);
961 required_string_fred("$Class:");
963 fout(" %s", Ship_info[Ships[i].ship_info_index].name);
965 // optional alternate type name
966 if(strlen(Fred_alt_names[i])){
967 fout("\n$Alt: %s\n", Fred_alt_names[i]);
970 required_string_fred("$Team:");
972 fout(" %s", Team_names[bitmask_2_bitnum(Ships[i].team)]);
974 required_string_fred("$Location:");
976 save_vector(Objects[Ships[i].objnum].pos);
978 required_string_fred("$Orientation:");
980 save_matrix(Objects[Ships[i].objnum].orient);
982 required_string_fred("$IFF:");
984 fout(" %s", Iff_names[0]);
986 SDL_assert(Ships[i].ai_index >= 0);
987 aip = &Ai_info[Ships[i].ai_index];
989 required_string_fred("$AI Behavior:");
991 fout(" %s", Ai_behavior_names[aip->behavior]);
993 if (Ships[i].weapons.ai_class != Ship_info[Ships[i].ship_info_index].ai_class) {
994 if (optional_string_fred("+AI Class:", "$Name:"))
997 fout("\n+AI Class:");
999 fout(" %s", Ai_class_names[Ships[i].weapons.ai_class]);
1002 save_ai_goals(Ai_info[Ships[i].ai_index].goals, i);
1005 required_string_fred("$Cargo 1:");
1008 fout_ext("%s", Cargo_names[Ships[i].cargo1]);
1010 save_common_object_data(&Objects[Ships[i].objnum], &Ships[i]);
1012 if (Ships[i].wingnum >= 0){
1013 Ships[i].arrival_location = ARRIVE_AT_LOCATION;
1016 required_string_fred("$Arrival Location:");
1018 fout(" %s", Arrival_location_names[Ships[i].arrival_location]);
1020 if (Ships[i].arrival_location != ARRIVE_AT_LOCATION) {
1021 if (optional_string_fred("+Arrival Distance:", "$Name:")){
1024 fout("\n+Arrival Distance:");
1027 fout(" %d", Ships[i].arrival_distance);
1028 if (optional_string_fred("$Arrival Anchor:", "$Name:")){
1031 fout("\n$Arrival Anchor:");
1034 z = Ships[i].arrival_anchor;
1035 if (z >= SPECIAL_ARRIVAL_ANCHORS_OFFSET){
1036 fout(" %s", Special_arrival_anchor_names[z - SPECIAL_ARRIVAL_ANCHORS_OFFSET]);
1037 } else if (z >= 0) {
1038 fout(" %s", Ships[z].ship_name);
1044 if (Ships[i].arrival_delay) {
1045 if (optional_string_fred("+Arrival Delay:", "$Name:")){
1048 fout("\n+Arrival Delay:");
1051 fout(" %d", Ships[i].arrival_delay);
1054 required_string_fred("$Arrival Cue:");
1056 convert_sexp_to_string(Ships[i].arrival_cue, out, SEXP_SAVE_MODE);
1059 required_string_fred("$Departure Location:");
1061 fout(" %s", Departure_location_names[Ships[i].departure_location]);
1064 if ( Ships[i].departure_location == DEPART_AT_DOCK_BAY ) {
1065 required_string_fred("$Departure Anchor:");
1068 if ( Ships[i].departure_anchor >= 0 ){
1069 fout(" %s", Ships[Ships[i].departure_anchor].ship_name );
1075 if (Ships[i].departure_delay) {
1076 if (optional_string_fred("+Departure delay:", "$Name:")){
1079 fout("\n+Departure delay:");
1082 fout(" %d", Ships[i].departure_delay);
1085 required_string_fred("$Departure Cue:");
1087 convert_sexp_to_string(Ships[i].departure_cue, out, SEXP_SAVE_MODE);
1090 required_string_fred("$Determination:");
1092 fout(" %d", Ships[i].determination);
1094 if (optional_string_fred("+Flags:", "$Name:")) {
1098 fout("\n+Flags: (");
1100 if (Ships[i].flags & SF_CARGO_REVEALED)
1101 fout(" \"cargo-known\"");
1102 if (Ships[i].flags & SF_IGNORE_COUNT)
1103 fout(" \"ignore-count\"");
1104 if (objp->flags & OF_PROTECTED)
1105 fout(" \"protect-ship\"");
1106 if (objp->flags & OF_BEAM_PROTECTED)
1107 fout(" \"beam-protect-ship\"");
1108 if (Ships[i].flags & SF_REINFORCEMENT)
1109 fout(" \"reinforcement\"");
1110 if (objp->flags & OF_NO_SHIELDS)
1111 fout(" \"no-shields\"");
1112 if (Ships[i].flags & SF_ESCORT)
1113 fout(" \"escort\"");
1114 if (objp->type == OBJ_START)
1115 fout(" \"player-start\"");
1116 if (Ships[i].flags & SF_NO_ARRIVAL_MUSIC)
1117 fout(" \"no-arrival-music\"");
1118 if (Ships[i].flags & SF_NO_ARRIVAL_WARP)
1119 fout(" \"no-arrival-warp\"");
1120 if (Ships[i].flags & SF_NO_DEPARTURE_WARP)
1121 fout(" \"no-departure-warp\"");
1122 if (Ships[i].flags & SF_LOCKED)
1123 fout(" \"locked\"");
1124 if (Ships[i].flags & SF_INVULNERABLE)
1125 fout(" \"invulnerable\"");
1126 if (Ships[i].flags & SF_HIDDEN_FROM_SENSORS)
1127 fout(" \"hidden-from-sensors\"");
1128 if ( Ships[i].flags & SF_SCANNABLE )
1129 fout(" \"scannable\"");
1130 if ( Ai_info[Ships[i].ai_index].ai_flags & AIF_KAMIKAZE )
1131 fout(" \"kamikaze\"");
1132 if ( Ai_info[Ships[i].ai_index].ai_flags & AIF_NO_DYNAMIC )
1133 fout(" \"no-dynamic\"");
1134 if ( Ships[i].flags & SF_RED_ALERT_STORE_STATUS )
1135 fout(" \"red-alert-carry\"");
1136 if ( objp->flags & OF_SPECIAL_WARP )
1137 fout(" \"special-warp\"");
1140 fout("+Respawn priority: %d\n", Ships[i].respawn_priority);
1142 if (Ships[i].flags & SF_ESCORT) {
1143 if (optional_string_fred("+Escort priority:", "$Name:")) {
1146 fout("\n+Escort priority:");
1149 fout(" %d", Ships[i].escort_priority);
1152 if (Ships[i].special_exp_index != -1) {
1153 if (optional_string_fred("+Special Exp index:", "$Name:")) {
1156 fout("+Special Exp index:");
1159 fout(" %d", Ships[i].special_exp_index);
1163 if ( Ai_info[Ships[i].ai_index].ai_flags & AIF_KAMIKAZE ) {
1164 if ( optional_string_fred("+Kamikaze Damage:", "$Name:")){
1167 fout("\n+Kamikaze Damage:");
1170 fout(" %d", (int)(Ai_info[Ships[i].ai_index].kamikaze_damage) );
1173 if (Ships[i].hotkey != -1) {
1174 if (optional_string_fred("+Hotkey:", "$Name:")){
1180 fout(" %d", Ships[i].hotkey);
1183 // mwa -- new code to save off information about initially docked ships.
1184 if ( Ships[i].flags & SF_INITIALLY_DOCKED ) {
1185 SDL_assert ( aip->dock_objnum != -1 ); // ge allender if you hit this
1187 if (optional_string_fred("+Docked With:", "$Name:"))
1190 fout("\n+Docked With:");
1192 switch (Objects[aip->dock_objnum].type) {
1195 j = Objects[aip->dock_objnum].instance;
1202 fout(" %s", Ships[j].ship_name);
1204 required_string_fred("$Docker Point:", "$Name:");
1206 fout(" %s", model_get_dock_name(Ships[j].modelnum, aip->dockee_index));
1208 required_string_fred("$Dockee Point:", "$Name:");
1210 fout(" %s", model_get_dock_name(Ships[i].modelnum, aip->dock_index));
1214 // check the ship flag about killing off the ship before a missino starts. Write out the appropriate
1215 // variable if necessary
1216 if ( Ships[i].flags & SF_KILL_BEFORE_MISSION ) {
1217 if ( optional_string_fred("+Destroy At:", "$Name:"))
1220 fout ("\n+Destroy At: ");
1222 fout(" %d", Ships[i].final_death_time );
1225 // possibly write out the orders that this ship will accept. We'll only do it if the orders
1226 // are not the default set of orders
1227 if ( Ships[i].orders_accepted != ship_get_default_orders_accepted( &Ship_info[Ships[i].ship_info_index]) ) {
1228 if ( optional_string_fred("+Orders Accepted:", "$Name:") )
1231 fout("\n+Orders Accepted:");
1233 fout(" %d\t\t;! note that this is a bitfield!!!", Ships[i].orders_accepted);
1236 if (Ships[i].group >= 0) {
1237 if (optional_string_fred("+Group:", "$Name:"))
1242 fout(" %d", Ships[i].group);
1245 if (Ships[i].score) {
1246 if (optional_string_fred("+Score:", "$Name:"))
1251 fout(" %d", Ships[i].score);
1254 // deal with the persona for this ship as well.
1255 if ( Ships[i].persona_index != -1 ) {
1256 if (optional_string_fred("+Persona Index:", "$Name:"))
1259 fout("\n+Persona Index:");
1261 fout(" %d", Ships[i].persona_index);
1270 int CFred_mission_save::save_common_object_data(object *objp, ship *shipp)
1277 sip = &Ship_info[shipp->ship_info_index];
1278 if ((int) objp->phys_info.speed) {
1279 if (optional_string_fred("+Initial Velocity:", "$Name:", "+Subsystem:"))
1282 fout("\n+Initial Velocity:");
1284 fout(" %d", (int) objp->phys_info.speed);
1287 if ((int) objp->hull_strength != (int) sip->initial_hull_strength) {
1288 if (optional_string_fred("+Initial Hull:", "$Name:", "+Subsystem:"))
1291 fout("\n+Initial Hull:");
1293 fout(" %d", (int) objp->hull_strength);
1296 if ((int) get_shield_strength(objp) != 100) {
1297 if (optional_string_fred("+Initial Shields:", "$Name:", "+Subsystem:"))
1300 fout("\n+Initial Shields:");
1302 fout(" %d", (int) objp->shields[0]);
1305 // save normal ship weapons info
1306 required_string_fred("+Subsystem:", "$Name:");
1310 wp = &shipp->weapons;
1312 j = wp->num_primary_banks;
1314 if (wp->primary_bank_weapons[j] != sip->primary_bank_weapons[j])
1318 if (optional_string_fred("+Primary Banks:", "$Name:", "+Subsystem:"))
1321 fout("\n+Primary Banks:");
1324 for (j=0; j<wp->num_primary_banks; j++)
1325 fout("\"%s\" ", Weapon_info[wp->primary_bank_weapons[j]].name);
1331 j = wp->num_secondary_banks;
1333 if (wp->secondary_bank_weapons[j] != sip->secondary_bank_weapons[j])
1337 if (optional_string_fred("+Secondary Banks:", "$Name:", "+Subsystem:"))
1340 fout("\n+Secondary Banks:");
1343 for (j=0; j<wp->num_secondary_banks; j++)
1344 fout("\"%s\" ", Weapon_info[wp->secondary_bank_weapons[j]].name);
1350 j = wp->num_secondary_banks;
1352 if (wp->secondary_bank_ammo[j] != 100)
1356 if (optional_string_fred("+Sbank Ammo:", "$Name:", "+Subsystem:"))
1359 fout("\n+Sbank Ammo:");
1362 for (j=0; j<wp->num_secondary_banks; j++)
1363 fout("%d ", wp->secondary_bank_ammo[j]);
1368 ptr = GET_FIRST(&shipp->subsys_list);
1369 while (ptr != END_OF_LIST(&shipp->subsys_list)) {
1370 if ( (ptr->current_hits) || (ptr->system_info->type == SUBSYSTEM_TURRET) || (ptr->subsys_cargo_name != -1)) {
1371 if (optional_string_fred("+Subsystem:", "$Name:"))
1374 fout("\n+Subsystem:");
1376 fout(" %s", ptr->system_info->subobj_name);
1379 if (ptr->current_hits) {
1380 if (optional_string_fred("$Damage:", "$Name:", "+Subsystem:"))
1385 fout(" %d", (int) ptr->current_hits);
1388 if (ptr->subsys_cargo_name != -1) {
1389 if (optional_string_fred("+Cargo Name:", "$Name:", "+Subsystem:"))
1392 fout("\n+Cargo Name:");
1394 fout_ext("%s", Cargo_names[ptr->subsys_cargo_name]);
1397 if (ptr->system_info->type == SUBSYSTEM_TURRET)
1398 save_turret_info(ptr, shipp - Ships);
1400 ptr = GET_NEXT(ptr);
1403 /* for (j=0; j<shipp->status_count; j++) {
1404 required_string_fred("$Status Description:");
1406 fout(" %s", Status_desc_names[shipp->status_type[j]]);
1408 required_string_fred("$Status:");
1410 fout(" %s", Status_type_names[shipp->status[j]]);
1412 required_string_fred("$Target:");
1414 fout(" %s", Status_target_names[shipp->target[j]]);
1420 int CFred_mission_save::save_wings()
1423 int i, j, z, ship, count = 0;
1425 fred_parse_flag = 0;
1426 required_string_fred("#Wings");
1428 fout("\t\t;! %d total", num_wings);
1430 for (i=0; i<MAX_WINGS; i++) {
1431 if (!Wings[i].wave_count)
1435 required_string_either_fred("$Name:", "#Events");
1436 required_string_fred("$Name:");
1438 fout(" %s", Wings[i].name);
1440 required_string_fred("$Waves:");
1442 fout(" %d", Wings[i].num_waves);
1444 required_string_fred("$Wave Threshold:");
1446 fout(" %d", Wings[i].threshold);
1448 required_string_fred("$Special Ship:");
1450 fout(" %d\t\t;! %s\n", Wings[i].special_ship,
1451 Ships[Wings[i].ship_index[Wings[i].special_ship]].ship_name);
1453 required_string_fred("$Arrival Location:");
1455 fout(" %s", Arrival_location_names[Wings[i].arrival_location]);
1457 if (Wings[i].arrival_location != ARRIVE_AT_LOCATION) {
1458 if (optional_string_fred("+Arrival Distance:", "$Name:"))
1461 fout("\n+Arrival Distance:");
1463 fout(" %d", Wings[i].arrival_distance);
1464 if (optional_string_fred("$Arrival Anchor:", "$Name:"))
1467 fout("\n$Arrival Anchor:");
1469 z = Wings[i].arrival_anchor;
1470 if (z >= SPECIAL_ARRIVAL_ANCHORS_OFFSET)
1471 fout(" %s", Special_arrival_anchor_names[z - SPECIAL_ARRIVAL_ANCHORS_OFFSET]);
1473 fout(" %s", Ships[z].ship_name);
1478 if (Wings[i].arrival_delay) {
1479 if (optional_string_fred("+Arrival delay:", "$Name:"))
1482 fout("\n+Arrival delay:");
1484 fout(" %d", Wings[i].arrival_delay);
1487 required_string_fred("$Arrival Cue:");
1489 convert_sexp_to_string(Wings[i].arrival_cue, out, SEXP_SAVE_MODE);
1492 required_string_fred("$Departure Location:");
1494 fout(" %s", Departure_location_names[Wings[i].departure_location]);
1496 if ( Wings[i].departure_location == DEPART_AT_DOCK_BAY ) {
1497 required_string_fred("$Departure Anchor:");
1500 if ( Wings[i].departure_anchor >= 0 )
1501 fout(" %s", Ships[Wings[i].departure_anchor].ship_name );
1506 if (Wings[i].departure_delay) {
1507 if (optional_string_fred("+Departure delay:", "$Name:"))
1510 fout("\n+Departure delay:");
1512 fout(" %d", Wings[i].departure_delay);
1515 required_string_fred("$Departure Cue:");
1517 convert_sexp_to_string(Wings[i].departure_cue, out, SEXP_SAVE_MODE);
1520 required_string_fred("$Ships:");
1522 fout(" (\t\t;! %d total\n", Wings[i].wave_count);
1524 for (j=0; j<Wings[i].wave_count; j++) {
1525 ship = Wings[i].ship_index[j];
1526 // if (Objects[Ships[ship].objnum].type == OBJ_START)
1527 // fout("\t\"Player 1\"\n");
1529 fout("\t\"%s\"\n", Ships[Wings[i].ship_index[j]].ship_name);
1533 save_ai_goals(Wings[i].ai_goals, -1);
1535 if (Wings[i].hotkey != -1) {
1536 if (optional_string_fred("+Hotkey:", "$Name:"))
1541 fout(" %d", Wings[i].hotkey);
1544 if ( optional_string_fred("+Flags:", "$Name:")) {
1548 fout("\n+Flags: (");
1550 if ( Wings[i].flags & WF_IGNORE_COUNT )
1551 fout(" \"ignore-count\"");
1552 if ( Wings[i].flags & WF_REINFORCEMENT )
1553 fout(" \"reinforcement\"");
1554 if ( Wings[i].flags & WF_NO_ARRIVAL_MUSIC )
1555 fout(" \"no-arrival-music\"");
1556 if ( Wings[i].flags & WF_NO_ARRIVAL_MESSAGE )
1557 fout(" \"no-arrival-message\"");
1558 if ( Wings[i].flags & WF_NO_ARRIVAL_WARP )
1559 fout(" \"no-arrival-warp\"");
1560 if ( Wings[i].flags & WF_NO_DEPARTURE_WARP )
1561 fout(" \"no-departure-warp\"");
1562 if ( Wings[i].flags & WF_NO_DYNAMIC )
1563 fout( " \"no-dynamic\"" );
1567 if (Wings[i].wave_delay_min) {
1568 if (optional_string_fred("+Wave Delay Min:", "$Name:"))
1571 fout("\n+Wave Delay Min:");
1573 fout(" %d", Wings[i].wave_delay_min);
1576 if (Wings[i].wave_delay_max) {
1577 if (optional_string_fred("+Wave Delay Max:", "$Name:"))
1580 fout("\n+Wave Delay Max:");
1582 fout(" %d", Wings[i].wave_delay_max);
1586 SDL_assert(count == num_wings);
1590 int CFred_mission_save::save_goals()
1595 fred_parse_flag = 0;
1596 required_string_fred("#Goals");
1598 fout("\t\t;! %d total\n", Num_goals);
1600 for (i=0; i<Num_goals; i++) {
1603 required_string_either_fred("$Type:", "#Waypoints");
1604 required_string_fred("$Type:");
1605 parse_comments(i ? 2 : 1);
1607 type = Mission_goals[i].type & GOAL_TYPE_MASK;
1608 fout(" %s", Goal_type_names[type]);
1610 if (*Mission_goals[i].name) {
1611 if (optional_string_fred("+Name:", "$Type:"))
1616 fout(" %s", Mission_goals[i].name);
1620 required_string_fred("$MessageNew:");
1623 fout_ext("%s", Mission_goals[i].message);
1625 required_string_fred("$end_multi_text");
1628 required_string_fred("$Formula:");
1630 convert_sexp_to_string(Mission_goals[i].formula, out, SEXP_SAVE_MODE);
1633 if ( Mission_goals[i].type & INVALID_GOAL ) {
1634 if (optional_string_fred("+Invalid", "$Type:"))
1640 if ( Mission_goals[i].flags & MGF_NO_MUSIC ) {
1641 if (optional_string_fred("+No music", "$Type:"))
1644 fout("\n+No music");
1647 if ( Mission_goals[i].score != 0 ) {
1648 if ( optional_string_fred("+Score:", "$Type:"))
1652 fout(" %d", Mission_goals[i].score );
1655 if ( The_mission.game_type & MISSION_TYPE_MULTI_TEAMS ) {
1656 if ( optional_string_fred("+Team:", "$Type:"))
1660 fout(" %d", Mission_goals[i].team );
1667 int CFred_mission_save::save_waypoints()
1672 fred_parse_flag = 0;
1673 required_string_fred("#Waypoints");
1675 fout("\t\t;! %d lists total\n", Num_waypoint_lists);
1677 for (i=0; i<Num_jump_nodes; i++) {
1678 ptr = GET_FIRST(&obj_used_list);
1679 while (ptr != END_OF_LIST(&obj_used_list)) {
1680 if ((ptr->type == OBJ_JUMP_NODE) && (ptr->instance == i))
1683 ptr = GET_NEXT(ptr);
1686 SDL_assert(ptr != END_OF_LIST(&obj_used_list));
1688 required_string_fred("$Jump Node:", "$Jump Node Name:");
1690 save_vector(ptr->pos);
1692 required_string_fred("$Jump Node Name:", "$Jump Node:");
1694 fout(" %s", Jump_nodes[i].name);
1697 for (i=0; i<Num_waypoint_lists; i++) {
1698 required_string_either_fred("$Name:", "#Messages");
1699 required_string_fred("$Name:");
1700 parse_comments(i ? 2 : 1);
1701 fout(" %s", Waypoint_lists[i].name);
1703 required_string_fred("$List:");
1705 fout(" (\t\t;! %d points in list\n", Waypoint_lists[i].count);
1707 save_waypoint_list(Waypoint_lists[i]);
1714 int CFred_mission_save::save_waypoint_list(waypoint_list &w)
1718 for (i=0; i<w.count; i++)
1719 fout("\t( %f, %f, %f )\n", w.waypoints[i].x, w.waypoints[i].y, w.waypoints[i].z);
1724 int CFred_mission_save::save_messages()
1728 fred_parse_flag = 0;
1729 required_string_fred("#Messages");
1731 fout("\t\t;! %d total\n", Num_messages);
1733 for (i=Num_builtin_messages; i<Num_messages; i++) {
1734 required_string_either_fred("$Name:", "#Reinforcements");
1735 required_string_fred("$Name:");
1736 parse_comments(i ? 2 : 1);
1737 fout(" %s", Messages[i].name);
1740 required_string_fred("$Team:");
1741 parse_comments(i ? 2 : 1);
1742 if((Messages[i].multi_team < 0) || (Messages[i].multi_team >= 2)){
1745 fout(" %d", Messages[i].multi_team);
1749 required_string_fred("$MessageNew:");
1752 fout_ext("%s", Messages[i].message);
1754 required_string_fred("$end_multi_text");
1757 if ( Messages[i].persona_index != -1 ) {
1758 if ( optional_string_fred("+Persona:", "$Name:"))
1761 fout("\n+Persona:");
1763 fout(" %s", Personas[Messages[i].persona_index].name );
1766 if (Messages[i].avi_info.name) {
1767 if (optional_string_fred("+AVI Name:", "$Name:"))
1770 fout("\n+AVI Name:");
1772 fout(" %s", Messages[i].avi_info.name);
1775 if (Messages[i].wave_info.name) {
1776 if (optional_string_fred("+Wave Name:", "$Name:"))
1779 fout("\n+Wave Name:");
1781 fout(" %s", Messages[i].wave_info.name);
1788 int CFred_mission_save::save_vector(vector &v)
1790 fout(" %f, %f, %f", v.x, v.y, v.z);
1794 int CFred_mission_save::save_matrix(matrix &m)
1796 fout("\n\t%f, %f, %f,\n", m.rvec.x, m.rvec.y, m.rvec.z);
1797 fout("\t%f, %f, %f,\n", m.uvec.x, m.uvec.y, m.uvec.z);
1798 fout("\t%f, %f, %f", m.fvec.x, m.fvec.y, m.fvec.z);
1802 // saves comments from previous campaign/mission file
1803 void CFred_mission_save::parse_comments(int newlines)
1805 char *comment_start = NULL;
1806 int state = 0, same_line = 0, first_comment = 1, tab = 0, flag = 0;
1809 newlines = -newlines;
1816 if (fred_parse_flag || !Token_found_flag || !token_found || (token_found && (*Mission_text_raw == EOF_CHAR))) {
1817 while (newlines-- > 0)
1824 fout("%s", token_found);
1829 while (*raw_ptr != EOF_CHAR) {
1831 if (token_found && (*raw_ptr == *token_found))
1832 if (!strnicmp(raw_ptr, token_found, strlen(token_found))) {
1833 same_line = newlines - 1 + same_line;
1834 while (same_line-- > 0)
1840 fout("%s", token_found);
1844 if ((*raw_ptr == '/') && (raw_ptr[1] == '*')) {
1845 comment_start = raw_ptr;
1849 if ((*raw_ptr == ';') && (raw_ptr[1] != '!')) {
1850 comment_start = raw_ptr;
1854 if ((*raw_ptr == '/') && (raw_ptr[1] == '/')) {
1855 comment_start = raw_ptr;
1859 if (*raw_ptr == '\n')
1866 if ((*raw_ptr == '\n') && (state == 2)) {
1867 if (first_comment && !flag)
1871 fout("%s\n", comment_start);
1873 state = first_comment = same_line = flag = 0;
1876 if ((*raw_ptr == '*') && (raw_ptr[1] == '/') && (state == 1)) {
1877 if (first_comment && !flag)
1882 fout("%s", comment_start);
1883 raw_ptr[2] = (char)state;
1884 state = first_comment = flag = 0;
1894 int CFred_mission_save::fout(char *format, ...)
1903 va_start(args, format);
1904 vsprintf(str, format, args);
1906 SDL_assert(strlen(str) < 16384);
1912 int CFred_mission_save::fout_ext(char *format, ...)
1915 char str_out[16384] = "";
1923 va_start(args, format);
1924 vsprintf(str, format, args);
1926 SDL_assert(strlen(str) < 16384);
1928 // lookup the string in the hash table
1929 str_id = fhash_string_exists(str);
1930 // doesn't exist, so assign it an ID of -1 and stick it in the table
1932 sprintf(str_out, " XSTR(\"%s\", -1)", str);
1934 // add the string to the table
1935 fhash_add_str(str, -1);
1937 // _does_ exist, so just write it out as it is
1939 sprintf(str_out, " XSTR(\"%s\", %d)", str, str_id);
1942 cfputs(str_out, fp);
1946 void CFred_mission_save::save_ai_goals(ai_goals *goalp, int ship)
1948 char *str = NULL, buf[80];
1949 int i, valid, flag = 1;
1951 for (i=0; i<MAX_AI_GOALS; i++) {
1952 if (goalp[i].ai_mode == AI_GOAL_NONE)
1956 if (optional_string_fred("$AI Goals:", "$Name:"))
1959 fout("\n$AI Goals:");
1965 if (goalp[i].ai_mode == AI_GOAL_CHASE_ANY) {
1966 fout("( ai-chase-any %d ) ", goalp[i].priority);
1968 } else if (goalp[i].ai_mode == AI_GOAL_UNDOCK) {
1969 fout("( ai-undock %d ) ", goalp[i].priority);
1971 } else if (goalp[i].ai_mode == AI_GOAL_KEEP_SAFE_DISTANCE) {
1972 fout("( ai-keep-safe-distance %d ) ", goalp[i].priority);
1974 } else if (goalp[i].ai_mode == AI_GOAL_PLAY_DEAD) {
1975 fout("( ai-play-dead %d ) ", goalp[i].priority);
1977 } else if (goalp[i].ai_mode == AI_GOAL_WARP) {
1978 fout("( ai-warp-out %d ) ", goalp[i].priority);
1982 if (!goalp[i].ship_name) {
1983 Warning(LOCATION, "Ai goal has no target where one is required");
1986 sprintf(buf, "\"%s\"", goalp[i].ship_name);
1987 switch (goalp[i].ai_mode) {
1988 case AI_GOAL_WAYPOINTS:
1989 str = "ai-waypoints";
1992 case AI_GOAL_WAYPOINTS_ONCE:
1993 str = "ai-waypoints-once";
1996 case AI_GOAL_DESTROY_SUBSYSTEM:
1997 if (goalp[i].docker.index == -1 || !goalp[i].docker.index) {
1999 Warning(LOCATION, "AI destroy subsystem goal invalid subsystem name\n");
2002 sprintf(buf, "\"%s\" \"%s\"", goalp[i].ship_name, goalp[i].docker.name);
2003 str = "ai-destroy-subsystem";
2011 Warning(LOCATION, "Wings aren't allowed to have a docking goal\n");
2013 } else if (goalp[i].docker.index == -1 || !goalp[i].docker.index) {
2015 Warning(LOCATION, "AI dock goal for \"%s\" has invalid docker point "
2016 "(docking with \"%s\")\n", Ships[ship].ship_name, goalp[i].ship_name);
2018 } else if (goalp[i].dockee.index == -1 || !goalp[i].dockee.index) {
2020 Warning(LOCATION, "AI dock goal for \"%s\" has invalid dockee point "
2021 "(docking with \"%s\")\n", Ships[ship].ship_name, goalp[i].ship_name);
2024 sprintf(buf, "\"%s\" \"%s\" \"%s\"", goalp[i].ship_name,
2025 goalp[i].docker.name, goalp[i].dockee.name);
2035 case AI_GOAL_CHASE_WING:
2036 str = "ai-chase-wing";
2043 case AI_GOAL_GUARD_WING:
2044 str = "ai-guard-wing";
2047 case AI_GOAL_DISABLE_SHIP:
2048 str = "ai-disable-ship";
2051 case AI_GOAL_DISARM_SHIP:
2052 str = "ai-disarm-ship";
2055 case AI_GOAL_IGNORE:
2059 case AI_GOAL_EVADE_SHIP:
2060 str = "ai-evade-ship";
2063 case AI_GOAL_STAY_NEAR_SHIP:
2064 str = "ai-stay-near-ship";
2067 case AI_GOAL_STAY_STILL:
2068 str = "ai-stay-still";
2076 fout("( %s %s %d ) ", str, buf, goalp[i].priority);
2085 int CFred_mission_save::save_events()
2090 fred_parse_flag = 0;
2091 required_string_fred("#Events");
2093 fout("\t\t;! %d total\n", Num_mission_events);
2095 for (i=0; i<Num_mission_events; i++) {
2096 required_string_either_fred("$Formula:", "#Goals");
2097 required_string_fred("$Formula:");
2098 parse_comments(i ? 2 : 1);
2099 convert_sexp_to_string(Mission_events[i].formula, out, SEXP_SAVE_MODE);
2102 if (*Mission_events[i].name) {
2103 if (optional_string_fred("+Name:", "$Formula:")){
2109 fout(" %s", Mission_events[i].name);
2112 if ( optional_string_fred("+Repeat Count:", "$Formula:")){
2115 fout("\n+Repeat Count:");
2118 fout(" %d", Mission_events[i].repeat_count);
2120 if ( optional_string_fred("+Interval:", "$Formula:")){
2123 fout("\n+Interval:");
2126 fout(" %d", Mission_events[i].interval);
2128 if ( Mission_events[i].score != 0 ) {
2129 if ( optional_string_fred("+Score:", "$Formula:")){
2134 fout(" %d", Mission_events[i].score);
2137 if ( Mission_events[i].chain_delay >= 0 ) {
2138 if ( optional_string_fred("+Chained:", "$Formula:")){
2141 fout("\n+Chained:");
2144 fout(" %d", Mission_events[i].chain_delay);
2148 if (Mission_events[i].objective_text) {
2149 if (optional_string_fred("+Objective:", "$Formula:")){
2152 fout("\n+Objective:");
2156 fout_ext("%s", Mission_events[i].objective_text);
2160 if (Mission_events[i].objective_key_text) {
2161 if (optional_string_fred("+Objective key:", "$Formula:")){
2164 fout("\n+Objective key:");
2168 fout_ext("%s", Mission_events[i].objective_key_text);
2172 if (Mission_events[i].team >= 0){
2173 if (optional_string_fred("+Team:")){
2179 fout("%d", Mission_events[i].team);
2186 int CFred_mission_save::save_reinforcements()
2190 fred_parse_flag = 0;
2191 required_string_fred("#Reinforcements");
2193 fout("\t\t;! %d total\n", Num_reinforcements);
2195 for (i=0; i<Num_reinforcements; i++) {
2196 required_string_either_fred("$Name:", "#Background bitmaps");
2197 required_string_fred("$Name:");
2198 parse_comments(i ? 2 : 1);
2199 fout(" %s", Reinforcements[i].name);
2201 type = TYPE_ATTACK_PROTECT;
2202 for (j=0; j<MAX_SHIPS; j++)
2203 if ((Ships[j].objnum != -1) && !stricmp(Ships[j].ship_name, Reinforcements[i].name)) {
2204 if (Ship_info[Ships[j].ship_info_index].flags & SIF_SUPPORT)
2205 type = TYPE_REPAIR_REARM;
2209 required_string_fred("$Type:");
2211 fout(" %s", Reinforcement_type_names[type]);
2213 required_string_fred("$Num times:");
2215 fout(" %d", Reinforcements[i].uses);
2217 if ( optional_string_fred("+Arrival Delay:", "$Name:"))
2220 fout("\n+Arrival Delay:");
2221 fout(" %d", Reinforcements[i].arrival_delay );
2223 if (optional_string_fred("+No Messages:", "$Name:"))
2226 fout("\n+No Messages:");
2228 for (j = 0; j < MAX_REINFORCEMENT_MESSAGES; j++) {
2229 if ( strlen(Reinforcements[i].no_messages[j]) )
2230 fout(" \"%s\"", Reinforcements[i].no_messages[j]);
2234 if (optional_string_fred("+Yes Messages:", "$Name:"))
2237 fout("\n+Yes Messages:");
2239 for (j = 0; j < MAX_REINFORCEMENT_MESSAGES; j++) {
2240 if ( strlen(Reinforcements[i].yes_messages[j]) )
2241 fout(" \"%s\"", Reinforcements[i].yes_messages[j]);
2250 int CFred_mission_save::save_bitmaps()
2254 fred_parse_flag = 0;
2255 required_string_fred("#Background bitmaps");
2257 fout("\t\t;! %d total\n", Num_starfield_bitmaps);
2259 required_string_fred("$Num stars:");
2261 fout(" %d", Num_stars);
2263 float Ambient_light_level = 1.0f; // JAS: Should this be set to something?
2264 required_string_fred("$Ambient light level:");
2266 fout(" %d", Ambient_light_level);
2269 if(The_mission.flags & MISSION_FLAG_FULLNEB){
2270 required_string_fred("+Neb2:");
2272 fout(" %s\n", Neb2_texture_name);
2274 required_string_fred("+Neb2Flags:");
2276 fout(" %d\n", Neb2_poof_flags);
2280 if (Nebula_index >= 0) {
2281 if (optional_string_fred("+Nebula:")){
2286 fout(" %s", Nebula_filenames[Nebula_index]);
2288 required_string_fred("+Color:");
2290 fout(" %s", Nebula_colors[Mission_palette]);
2292 required_string_fred("+Pitch:");
2294 fout(" %d", Nebula_pitch);
2296 required_string_fred("+Bank:");
2298 fout(" %d", Nebula_bank);
2300 required_string_fred("+Heading:");
2302 fout(" %d", Nebula_heading);
2306 // save suns by sun bitmap filename
2307 for(idx=0; idx<Num_suns; idx++){
2308 // sun name, angles and scale
2309 required_string_fred("$Sun:");
2311 fout(" %s", Suns[idx].filename);
2313 required_string_fred("+Angles:");
2315 fout(" %f %f %f", Suns[idx].ang.p, Suns[idx].ang.b, Suns[idx].ang.h);
2317 required_string_fred("+Scale:");
2319 fout(" %f", Suns[idx].scale_x);
2322 // save background bitmaps by filename
2323 for(idx=0; idx<Num_starfield_bitmaps; idx++){
2324 // sun name, angles and scale
2325 required_string_fred("$Starbitmap:");
2327 fout(" %s", Starfield_bitmap_instance[idx].filename);
2329 required_string_fred("+Angles:");
2331 fout(" %f %f %f", Starfield_bitmap_instance[idx].ang.p, Starfield_bitmap_instance[idx].ang.b, Starfield_bitmap_instance[idx].ang.h);
2333 required_string_fred("+ScaleX:");
2335 fout(" %f", Starfield_bitmap_instance[idx].scale_x);
2337 required_string_fred("+ScaleY:");
2339 fout(" %f", Starfield_bitmap_instance[idx].scale_y);
2341 required_string_fred("+DivX:");
2343 fout(" %d", Starfield_bitmap_instance[idx].div_x);
2345 required_string_fred("+DivY:");
2347 fout(" %d", Starfield_bitmap_instance[idx].div_y);
2353 int CFred_mission_save::save_asteroid_fields()
2357 fred_parse_flag = 0;
2358 required_string_fred("#Asteroid Fields");
2361 for (i=0; i<1 /*MAX_ASTEROID_FIELDS*/; i++) {
2362 if (!Asteroid_field.num_initial_asteroids)
2365 required_string_fred("$Density:");
2367 fout(" %d", Asteroid_field.num_initial_asteroids);
2370 if (optional_string_fred("+Field Type:")){
2373 fout("\n+Field Type:");
2375 fout(" %d", Asteroid_field.field_type);
2378 if (optional_string_fred("+Debris Genre:")){
2381 fout("\n+Debris Genre:");
2383 fout(" %d", Asteroid_field.debris_genre);
2385 // field_debris_type (only if ship genre)
2386 if (Asteroid_field.debris_genre == DG_SHIP) {
2387 for (int idx=0; idx<3; idx++) {
2388 if (Asteroid_field.field_debris_type[idx] != -1) {
2389 if (optional_string_fred("+Field Debris Type:")){
2392 fout("\n+Field Debris Type:");
2394 fout(" %d", Asteroid_field.field_debris_type[idx]);
2398 // asteroid subtypes stored in field_debris_type as -1 or 1
2399 for (idx=0; idx<3; idx++) {
2400 if (Asteroid_field.field_debris_type[idx] != -1) {
2401 if (optional_string_fred("+Field Debris Type:")){
2404 fout("\n+Field Debris Type:");
2412 required_string_fred("$Average Speed:");
2414 fout(" %f", vm_vec_mag(&Asteroid_field.vel));
2416 required_string_fred("$Minimum:");
2418 save_vector(Asteroid_field.min_bound);
2420 required_string_fred("$Maximum:");
2422 save_vector(Asteroid_field.max_bound);
2424 if (Asteroid_field.has_inner_bound == 1) {
2425 if (optional_string_fred("+Inner Bound:")){
2428 fout("\n+Inner Bound:");
2431 required_string_fred("$Minimum:");
2433 save_vector(Asteroid_field.inner_min_bound);
2435 required_string_fred("$Maximum:");
2437 save_vector(Asteroid_field.inner_max_bound);
2444 int CFred_mission_save::save_music()
2446 required_string_fred("#Music");
2449 required_string_fred("$Event Music:");
2451 if (Current_soundtrack_num < 0)
2454 fout(" %s", Soundtracks[Current_soundtrack_num].name);
2456 required_string_fred("$Briefing Music:");
2458 if (Mission_music[SCORE_BRIEFING] < 0)
2461 fout(" %s", Spooled_music[Mission_music[SCORE_BRIEFING]].name);
2466 void CFred_mission_save::save_turret_info(ship_subsys *ptr, int ship)
2469 ship_weapon *wp = &ptr->weapons;
2471 if (wp->ai_class != Ship_info[Ships[ship].ship_info_index].ai_class) {
2472 if (optional_string_fred("+AI Class:", "$Name:", "+Subsystem:"))
2475 fout("\n+AI Class:");
2477 fout(" %s", Ai_class_names[wp->ai_class]);
2481 i = wp->num_primary_banks;
2483 if (wp->primary_bank_weapons[i] != ptr->system_info->primary_banks[i])
2487 if (optional_string_fred("+Primary Banks:", "$Name:", "+Subsystem:"))
2490 fout("\n+Primary Banks:");
2493 for (i=0; i<wp->num_primary_banks; i++)
2494 fout("\"%s\" ", Weapon_info[wp->primary_bank_weapons[i]].name);
2500 i = wp->num_secondary_banks;
2502 if (wp->secondary_bank_weapons[i] != ptr->system_info->secondary_banks[i])
2506 if (optional_string_fred("+Secondary Banks:", "$Name:", "+Subsystem:"))
2509 fout("\n+Secondary Banks:");
2512 for (i=0; i<wp->num_secondary_banks; i++)
2513 fout("\"%s\" ", Weapon_info[wp->secondary_bank_weapons[i]].name);
2519 i = wp->num_secondary_banks;
2521 if (wp->secondary_bank_ammo[i] != 100)
2525 if (optional_string_fred("+Sbank Ammo:", "$Name:", "+Subsystem:"))
2528 fout("\n+Sbank Ammo:");
2531 for (i=0; i<wp->num_secondary_banks; i++)
2532 fout("%d ", wp->secondary_bank_ammo[i]);
2538 int CFred_mission_save::save_campaign_file(char *pathname)
2542 Campaign_tree_formp->save_tree(); // flush all changes so they get saved.
2543 Campaign_tree_viewp->sort_elements();
2545 fred_parse_flag = 0;
2547 pathname = cf_add_ext(pathname, FS_CAMPAIGN_FILE_EXT);
2548 fp = cfopen(pathname, "wt", CFILE_NORMAL, CF_TYPE_MISSIONS);
2550 nprintf(("Error", "Can't open campaign file to save.\n"));
2554 required_string_fred("$Name:");
2556 fout(" %s", Campaign.name);
2558 SDL_assert((Campaign.type >= 0) && (Campaign.type < MAX_CAMPAIGN_TYPES));
2559 required_string_fred("$Type:");
2561 fout(" %s", campaign_types[Campaign.type]);
2564 if (Campaign.desc) {
2565 required_string_fred("+Description:");
2568 fout_ext("%s", Campaign.desc);
2569 fout("\n$end_multi_text");
2572 if ( Campaign.type != CAMPAIGN_TYPE_SINGLE ) {
2573 required_string_fred("+Num Players:");
2575 fout(" %d", Campaign.num_players);
2578 // write out the ships and weapons which the player can start the campaign with
2579 optional_string_fred("+Starting Ships: (");
2581 for (i = 0; i < MAX_SHIP_TYPES; i++ ) {
2582 if ( Campaign.ships_allowed[i] )
2583 fout(" \"%s\" ", Ship_info[i].name );
2587 optional_string_fred("+Starting Weapons: (");
2589 for (i = 0; i < MAX_WEAPON_TYPES; i++ ) {
2590 if ( Campaign.weapons_allowed[i] )
2591 fout(" \"%s\" ", Weapon_info[i].name );
2595 fred_parse_flag = 0;
2596 for (i=0; i<Campaign.num_missions; i++) {
2598 required_string_either_fred("$Mission:", "#End");
2599 required_string_fred("$Mission:");
2601 fout(" %s", Campaign.missions[m].name);
2603 if ( strlen(Campaign.missions[i].briefing_cutscene) ) {
2604 if (optional_string_fred("+Briefing Cutscene:", "$Mission"))
2607 fout("\n+Briefing Cutscene:");
2609 fout( " %s", Campaign.missions[i].briefing_cutscene );
2612 required_string_fred("+Flags:", "$Mission:");
2614 fout(" %d", Campaign.missions[m].flags);
2616 // save campaign link sexp
2617 bool mission_loop = false;
2619 for (j=0; j<Total_links; j++) {
2620 if (Links[j].from == m) {
2622 if (optional_string_fred("+Formula:", "$Mission:"))
2625 fout("\n+Formula:");
2631 //save_campaign_sexp(Links[j].sexp, Campaign.missions[Links[j].to].name);
2632 if (Links[j].mission_loop) {
2633 mission_loop = true;
2635 save_campaign_sexp(Links[j].sexp, Links[j].to);
2644 // now save campaign loop sexp
2646 required_string_fred("\n+Mission Loop:");
2649 int num_mission_loop = 0;
2650 for (j=0; j<Total_links; j++) {
2651 if ( (Links[j].from == m) && (Links[j].mission_loop) ) {
2655 // maybe write out mission loop descript
2656 if ((num_mission_loop == 1) && Links[j].mission_loop_txt) {
2657 required_string_fred("+Mission Loop Text:");
2660 fout_ext("%s", Links[j].mission_loop_txt);
2661 fout("\n$end_multi_text");
2664 // maybe write out mission loop descript
2665 if ((num_mission_loop == 1) && Links[j].mission_loop_brief_anim) {
2666 required_string_fred("+Mission Loop Brief Anim:");
2669 fout_ext("%s", Links[j].mission_loop_brief_anim);
2670 fout("\n$end_multi_text");
2673 // maybe write out mission loop descript
2674 if ((num_mission_loop == 1) && Links[j].mission_loop_brief_sound) {
2675 required_string_fred("+Mission Loop Brief Sound:");
2678 fout_ext("%s", Links[j].mission_loop_brief_sound);
2679 fout("\n$end_multi_text");
2682 if (num_mission_loop == 1) {
2683 // write out mission loop formula
2684 fout("\n+Formula:");
2686 save_campaign_sexp(Links[j].sexp, Links[j].to);
2691 if (num_mission_loop > 1) {
2693 sprintf(buffer, "Multiple branching loop error from mission %s\nEdit campaign for *at most* 1 loop from each mission.", Campaign.missions[m].name);
2694 MessageBox((HWND)os_get_window(), buffer, "Error", MB_OK);
2698 if (optional_string_fred("+Level:", "$Mission:")){
2701 fout("\n\n+Level:");
2704 fout(" %d", Campaign.missions[m].level);
2706 if (optional_string_fred("+Position:", "$Mission:")){
2709 fout("\n+Position:");
2712 fout(" %d", Campaign.missions[m].pos);
2715 required_string_fred("#End");
2723 mprintf(("Campaign saving error code #%d", err));
2725 Campaign_wnd->error_checker();
2730 void CFred_mission_save::save_campaign_sexp(int node, int link_num)
2736 SDL_assert(node >= 0);
2738 // if the link num is -1, then this is a end-of-campaign location
2739 if ( link_num != -1 ) {
2740 if (build_sexp_string(node, 2, SEXP_SAVE_MODE))
2741 fout(" (\n %s\n ( next-mission \"%s\" )\n )\n", out, Campaign.missions[link_num].name);
2743 fout(" ( %s( next-mission \"%s\" ) )\n", out, Campaign.missions[link_num].name);
2745 if (build_sexp_string(node, 2, SEXP_SAVE_MODE)){
2746 fout(" (\n %s\n ( end-of-campaign )\n )\n", out);
2748 fout(" ( %s( end-of-campaign ) )\n", out );