2 * $Logfile: /Freespace2/code/Fred2/MissionSave.cpp $
7 * Mission saving in Fred.
10 * Revision 1.1 2002/05/03 03:28:08 root
14 * 33 8/28/99 7:29p Dave
15 * Fixed wingmen persona messaging. Make sure locked turrets don't count
16 * towards the # attacking a player.
18 * 32 8/27/99 12:04a Dave
19 * Campaign loop screen.
21 * 31 8/26/99 8:52p Dave
22 * Gave multiplayer TvT messaging a heavy dose of sanity. Cheat codes.
24 * 30 8/17/99 5:20p Andsager
25 * Control campaign editor bug.
27 * 29 8/16/99 3:53p Andsager
28 * Add special warp in interface in Fred and saving / reading.
30 * 28 7/02/99 4:30p Dave
31 * Much more sophisticated lightning support.
33 * 27 6/10/99 11:06a Andsager
34 * Mission designed selection of asteroid types.
36 * 26 6/03/99 6:37p Dave
37 * More TNT fun. Made perspective bitmaps more flexible.
39 * 25 5/20/99 6:59p Dave
40 * Added alternate type names for ships. Changed swarm missile table
43 * 24 5/09/99 6:00p Dave
44 * Lots of cool new effects. E3 build tweaks.
46 * 23 4/26/99 8:47p Dave
47 * Made all pof related nebula stuff customizable through Fred.
49 * 22 4/26/99 12:49p Andsager
50 * Add protect object from beam support to Fred
52 * 21 4/16/99 2:34p Andsager
53 * Second pass on debris fields
55 * 20 4/15/99 5:00p Andsager
56 * Frist pass on Debris field
58 * 19 4/07/99 6:21p Dave
59 * Fred and Freespace support for multiple background bitmaps and suns.
60 * Fixed link errors on all subprojects. Moved encrypt_init() to
61 * cfile_init() and lcl_init(), since its safe to call twice.
63 * 18 3/31/99 9:50a Andsager
64 * Interface for generalization of asteroid field (debris field)
66 * 17 3/24/99 4:05p Dave
67 * Put in support for assigning the player to a specific squadron with a
68 * specific logo. Preliminary work for doing pos/orient checksumming in
69 * multiplayer to reduce bandwidth.
71 * 16 3/01/99 7:39p Dave
72 * Added prioritizing ship respawns. Also fixed respawns in TvT so teams
73 * don't mix respawn points.
75 * 15 2/26/99 6:01p Andsager
76 * Add sexp has-been-tagged-delay and cap-subsys-cargo-known-delay
78 * 14 2/17/99 2:11p Dave
79 * First full run of squad war. All freespace and tracker side stuff
82 * 13 2/11/99 2:15p Andsager
83 * Add ship explosion modification to FRED
85 * 12 2/07/99 8:51p Andsager
86 * Add inner bound to asteroid field. Inner bound tries to stay astroid
87 * free. Wrap when within and don't throw at ships inside.
89 * 11 2/03/99 12:42p Andsager
90 * Add escort priority. Modify ship_flags_dlg to include field. Save and
91 * Load. Add escort priority field to ship.
93 * 10 1/25/99 5:03a Dave
94 * First run of stealth, AWACS and TAG missile support. New mission type
97 * 9 1/19/99 3:57p Andsager
98 * Round 2 of variables
100 * 8 1/07/99 1:52p Andsager
101 * Initial check in of Sexp_variables
103 * 7 12/17/98 2:43p Andsager
104 * Modify fred campaign save file to include optional mission loops
106 * 6 11/14/98 5:37p Dave
107 * Put in support for full nebulas.
109 * 5 11/05/98 4:18p Dave
110 * Modified mission file format.
112 * 4 10/29/98 9:22p Dave
113 * Removed minor bug concering externalization of campaign files.
115 * 3 10/29/98 6:49p Dave
116 * Finished up Fred support for externalizing mission and campaign files.
118 * 2 10/07/98 6:28p Dave
119 * Initial checkin. Renamed all relevant stuff to be Fred2 instead of
120 * Fred. Globalized mission and campaign file extensions. Removed Silent
121 * Threat specific code.
123 * 1 10/07/98 3:00p Dave
125 * 207 9/16/98 10:42a Hoffoss
126 * Added sexp node counting to fsm files for end user designers.
128 * 206 9/15/98 7:24p Dave
129 * Minor UI changes. Localized bunch of new text.
131 * 205 9/15/98 11:44a Dave
132 * Renamed builtin ships and wepaons appropriately in FRED. Put in scoring
133 * scale factors. Fixed standalone filtering of MD missions to non-MD
136 * 204 9/10/98 1:17p Dave
137 * Put in code to flag missions and campaigns as being MD or not in Fred
138 * and Freespace. Put in multiplayer support for filtering out MD
139 * missions. Put in multiplayer popups for warning of non-valid missions.
141 * 203 5/21/98 12:58a Hoffoss
142 * Fixed warnings optimized build turned up.
144 * 202 5/20/98 1:04p Hoffoss
145 * Made credits screen use new artwork and removed rating field usage from
146 * Fred (a goal struct member).
148 * 201 5/19/98 1:19p Allender
149 * new low level reliable socket reading code. Make all missions/campaign
150 * load/save to data missions folder (i.e. we are rid of the player
153 * 200 5/13/98 5:13p Allender
154 * red alert support to go back to previous mission
161 #include "freespace.h"
162 #include "missionsave.h"
163 #include "missiongoals.h"
164 #include "missionmessage.h"
165 #include "missionparse.h"
166 #include "fredrender.h"
168 #include "starfield.h"
169 #include "lighting.h"
170 #include "linklist.h"
172 #include "missioncampaign.h"
173 #include "campaigntreewnd.h"
174 #include "campaigntreeview.h"
175 #include "campaigneditordlg.h"
177 #include "missionbriefcommon.h"
178 #include "management.h"
179 #include "eventmusic.h"
181 #include "asteroid.h"
182 #include "missioncmdbrief.h"
183 #include "jumpnode.h"
189 int CFred_mission_save::save_mission_file(char *pathname)
191 char backup_name[256], savepath[MAX_PATH_LEN], *p;
194 t = CTime::GetCurrentTime();
195 strcpy(The_mission.modified, t.Format("%x at %X"));
197 strcpy(savepath, "");
198 p = strrchr(pathname, '\\');
201 strcpy(savepath, pathname);
203 strcat(savepath, "\\");
205 strcat(savepath, "saving.xxx");
209 fp = cfopen(savepath, "wt", CFILE_NORMAL);
211 nprintf(("Error", "Can't open mission file to save.\n"));
215 if (save_mission_info())
217 else if (save_plot_info())
219 else if (save_variables())
221 // else if (save_briefing_info())
223 else if (save_cmd_briefs())
225 else if (save_briefing())
227 else if (save_debriefing())
229 else if (save_players())
231 else if (save_objects())
233 else if (save_wings())
235 else if (save_events())
237 else if (save_goals())
239 else if (save_waypoints())
241 else if (save_messages())
243 else if (save_reinforcements())
245 else if (save_bitmaps())
247 else if (save_asteroid_fields())
249 else if (save_music())
252 required_string_fred("#End");
261 mprintf(("Mission saving error code #%d", err));
264 strcpy(backup_name, pathname);
265 if (backup_name[strlen(backup_name) - 4] == '.')
266 backup_name[strlen(backup_name) - 4] = 0;
268 strcat(backup_name, ".bak");
269 cf_attrib(pathname, 0, FILE_ATTRIBUTE_READONLY, CF_TYPE_MISSIONS);
270 cf_delete(backup_name, CF_TYPE_MISSIONS);
271 cf_rename(pathname, backup_name, CF_TYPE_MISSIONS);
272 cf_rename(savepath, pathname, CF_TYPE_MISSIONS);
278 int CFred_mission_save::autosave_mission_file(char *pathname)
280 char backup_name[256], name2[256];
284 t = CTime::GetCurrentTime();
285 strcpy(The_mission.modified, t.Format("%x at %X"));
287 len = strlen(pathname);
288 strcpy(backup_name, pathname);
289 strcpy(name2, pathname);
290 sprintf(backup_name + len, ".%.3d", BACKUP_DEPTH);
291 cf_delete(backup_name, CF_TYPE_MISSIONS);
292 for (i=BACKUP_DEPTH; i>1; i--) {
293 sprintf(backup_name + len, ".%.3d", i - 1);
294 sprintf(name2 + len, ".%.3d", i);
295 cf_rename(backup_name, name2, CF_TYPE_MISSIONS);
298 strcpy(backup_name + len, ".001");
301 fp = cfopen(backup_name, "wt", CFILE_NORMAL, CF_TYPE_MISSIONS);
303 nprintf(("Error", "Can't open mission file to save.\n"));
307 if (save_mission_info())
309 else if (save_plot_info())
311 else if (save_variables())
313 // else if (save_briefing_info())
315 else if (save_cmd_briefs())
317 else if (save_briefing())
319 else if (save_debriefing())
321 else if (save_players())
323 else if (save_objects())
325 else if (save_wings())
327 else if (save_events())
329 else if (save_goals())
331 else if (save_waypoints())
333 else if (save_messages())
335 else if (save_reinforcements())
337 else if (save_bitmaps())
339 else if (save_asteroid_fields())
341 else if (save_music())
344 required_string_fred("#End");
353 mprintf(("Mission saving error code #%d", err));
358 int CFred_mission_save::save_mission_info()
360 // int f = count_free_sexp_nodes();
361 // fout("// Of %d total sexp nodes, %d free, %d used\n\n",MAX_SEXP_NODES, f, MAX_SEXP_NODES - f);
363 required_string_fred("#Mission Info");
366 required_string_fred("$Version:");
368 fout(" %.2f", FRED_MISSION_VERSION);
371 required_string_fred("$Name:");
374 fout_ext("%s", The_mission.name);
376 required_string_fred("$Author:");
378 fout(" %s", The_mission.author);
380 required_string_fred("$Created:");
382 fout(" %s", The_mission.created);
384 required_string_fred("$Modified:");
386 fout(" %s", The_mission.modified);
388 required_string_fred("$Notes:");
390 fout("\n%s", The_mission.notes);
392 required_string_fred("$End Notes:");
396 required_string_fred("$Mission Desc:");
399 fout_ext("%s", The_mission.mission_desc);
402 required_string_fred("$end_multi_text");
406 if (optional_string_fred("+Game Type:"))
409 fout("\n\n+Game Type:");
410 fout("\n%s", Game_types[The_mission.game_type]);
413 if ( optional_string_fred("+Game Type Flags:")){
416 fout("\n+Game Type Flags:");
419 fout(" %d", The_mission.game_type);
421 if (optional_string_fred("+Flags:")){
427 fout(" %d", The_mission.flags);
429 // maybe write out Nebula intensity
430 if(The_mission.flags & MISSION_FLAG_FULLNEB){
431 Assert(Neb2_awacs > 0.0f);
432 fout("\n+NebAwacs: %f\n", Neb2_awacs);
435 fout("\n+Storm: %s\n", Mission_parse_storm_name);
438 // For multiplayer missions -- write out the number of player starts and number of respawns
439 if (The_mission.game_type & MISSION_TYPE_MULTI) {
440 if (optional_string_fred("+Num Players:"))
443 fout("\n+Num Players:");
445 fout(" %d", Player_starts);
447 if (optional_string_fred("+Num Respawns:"))
450 fout("\n+Num Respawns:");
452 fout(" %d", The_mission.num_respawns);
455 if ( optional_string_fred("+Red Alert:")){
458 fout("\n+Red Alert:");
460 fout(" %d", The_mission.red_alert );
462 if ( optional_string_fred("+Scramble:")){
465 fout("\n+Scramble:");
467 fout(" %d", The_mission.scramble);
469 if ( optional_string_fred("+Disallow Support:")){
472 fout("\n+Disallow Support:");
475 fout(" %d", The_mission.disallow_support);
477 if (Mission_all_attack) {
478 if (optional_string_fred("+All Teams Attack")){
481 fout("\n+All Teams Attack");
485 if (Entry_delay_time) {
486 if (optional_string_fred("+Player Entry Delay:"))
489 fout("\n\n+Player Entry Delay:");
491 fout("\n%f", f2fl(Entry_delay_time));
494 if (optional_string_fred("+Viewer pos:")){
497 fout("\n\n+Viewer pos:");
500 save_vector(view_pos);
502 if (optional_string_fred("+Viewer orient:")){
505 fout("\n+Viewer orient:");
508 save_matrix(view_orient);
511 if(!(The_mission.game_type & MISSION_TYPE_MULTI) && (strlen(The_mission.squad_name) > 0)){
513 fout("\n+SquadReassignName: %s", The_mission.squad_name);
516 if(strlen(The_mission.squad_filename) > 0){
517 fout("\n+SquadReassignLogo: %s", The_mission.squad_filename);
524 int CFred_mission_save::save_plot_info()
526 required_string_fred("#Plot Info");
530 required_string_fred("$Tour:");
533 fout_ext("%s", The_mission.tour_name);
535 required_string_fred("$Pre-Briefing Cutscene:");
537 fout(" %s", The_mission.pre_briefing_cutscene);
539 required_string_fred("$Pre-Mission Cutscene:");
541 fout(" %s", The_mission.pre_mission_cutscene);
543 required_string_fred("$Next Mission Success:");
545 fout(" %s", The_mission.next_mission_success);
547 required_string_fred("$Next Mission Partial:");
549 fout(" %s", The_mission.next_mission_partial);
551 required_string_fred("$Next Mission Failure:");
553 fout(" %s", The_mission.next_mission_failure);
558 int CFred_mission_save::save_cmd_brief()
563 required_string_fred("#Command Briefing");
566 if (The_mission.game_type & MISSION_TYPE_MULTI)
567 return err; // no command briefings allowed in multiplayer missions.
569 for (stage=0; stage<Cur_cmd_brief->num_stages; stage++) {
570 required_string_fred("$Stage Text:");
575 fout_ext("%s", Cur_cmd_brief->stage[stage].text);
577 required_string_fred("$end_multi_text", "$Stage Text:");
580 required_string_fred("$Ani Filename:");
582 fout(" %s", Cur_cmd_brief->stage[stage].ani_filename);
584 required_string_fred("+Wave Filename:", "$Ani Filename:");
586 fout(" %s", Cur_cmd_brief->stage[stage].wave_filename);
592 int CFred_mission_save::save_cmd_briefs()
596 for (i=0; i<Num_teams; i++) {
597 Cur_cmd_brief = &Cmd_briefs[i];
604 int CFred_mission_save::save_briefing()
611 for ( nb = 0; nb < Num_teams; nb++ ) {
613 required_string_fred("#Briefing");
616 required_string_fred("$start_briefing");
619 Assert(Briefings[nb].num_stages <= MAX_BRIEF_STAGES);
620 required_string_fred("$num_stages:");
622 fout(" %d", Briefings[nb].num_stages);
624 for (i=0; i<Briefings[nb].num_stages; i++) {
625 bs = &Briefings[nb].stages[i];
627 required_string_fred("$start_stage");
630 required_string_fred("$multi_text");
635 sprintf(out,"%s", bs->new_text);
638 required_string_fred("$end_multi_text", "$start_stage");
641 if (!drop_white_space(bs->voice)[0]){
642 strcpy(bs->voice, "None");
645 required_string_fred("$voice:");
647 fout(" %s", bs->voice);
649 required_string_fred("$camera_pos:");
651 save_vector(bs->camera_pos);
653 required_string_fred("$camera_orient:");
655 save_matrix(bs->camera_orient);
657 required_string_fred("$camera_time:");
659 fout(" %d", bs->camera_time);
661 required_string_fred("$num_lines:");
663 fout(" %d", bs->num_lines);
665 for (k=0; k<bs->num_lines; k++) {
666 required_string_fred("$line_start:");
668 fout(" %d", bs->lines[k].start_icon);
670 required_string_fred("$line_end:");
672 fout(" %d", bs->lines[k].end_icon);
675 required_string_fred("$num_icons:");
677 Assert(bs->num_icons <= MAX_STAGE_ICONS );
678 fout(" %d", bs->num_icons);
680 required_string_fred("$Flags:");
682 fout(" %d", bs->flags);
684 required_string_fred("$Formula:");
686 convert_sexp_to_string(bs->formula, out, SEXP_SAVE_MODE);
689 for ( j = 0; j < bs->num_icons; j++ ) {
692 required_string_fred("$start_icon");
695 required_string_fred("$type:");
697 fout(" %d", bi->type);
699 required_string_fred("$team:");
701 fout(" %s", Team_names[bitmask_2_bitnum(bi->team)]);
703 required_string_fred("$class:");
705 if (bi->ship_class < 0)
708 fout(" %s", Ship_info[bi->ship_class].name);
710 required_string_fred("$pos:");
712 save_vector(bi->pos);
714 if (drop_white_space(bi->label)[0]) {
715 if (optional_string_fred("$label:"))
720 fout(" %s", bi->label);
723 if (optional_string_fred("+id:"))
730 required_string_fred("$hlight:");
732 fout(" %d", (bi->flags & BI_HIGHLIGHT)?1:0 );
734 required_string_fred("$multi_text");
737 // sprintf(out,"\n%s", bi->text);
740 required_string_fred("$end_multi_text");
743 required_string_fred("$end_icon");
747 required_string_fred("$end_stage");
750 required_string_fred("$end_briefing");
757 int CFred_mission_save::save_debriefing()
762 for ( j = 0; j < Num_teams; j++ ) {
764 Debriefing = &Debriefings[j];
766 required_string_fred("#Debriefing_info");
769 required_string_fred("$Num stages:");
771 fout(" %d", Debriefing->num_stages);
773 for (i=0; i<Debriefing->num_stages; i++) {
774 required_string_fred("$Formula:");
776 convert_sexp_to_string(Debriefing->stages[i].formula, out, SEXP_SAVE_MODE);
780 required_string_fred("$Multi text");
783 fout_ext("%s", Debriefing->stages[i].new_text);
785 required_string_fred("$end_multi_text");
788 if (!drop_white_space(Debriefing->stages[i].voice)[0]){
789 strcpy(Debriefing->stages[i].voice, "None");
792 required_string_fred("$Voice:");
794 fout(" %s", Debriefing->stages[i].voice);
797 required_string_fred("$Recommendation text:");
800 fout_ext("%s", Debriefing->stages[i].new_recommendation_text);
802 required_string_fred("$end_multi_text");
810 int sexp_variable_block_count();
812 int CFred_mission_save::save_variables()
815 char number[] = "number";
816 char string[] = "string";
817 char block[] = "block";
820 // sort sexp_variables
821 sexp_variable_sort();
824 int num_variables = sexp_variable_count();
825 int num_block_vars = sexp_variable_block_count();
826 int total_variables = num_variables + num_block_vars;
828 if (total_variables > 0) {
831 required_string_fred("#Sexp_variables");
834 required_string_fred("$Variables:");
840 for (i=0; i<num_variables; i++) {
841 if (Sexp_variables[i].type & SEXP_VARIABLE_NUMBER) {
846 // index "var name" "default" "type"
847 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);
851 for (i=MAX_SEXP_VARIABLES-num_block_vars; i<MAX_SEXP_VARIABLES; i++) {
853 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);
863 int CFred_mission_save::save_players()
866 int used_pool[MAX_WEAPON_TYPES];
868 // write out alternate name list
869 if(Mission_alt_type_count > 0){
870 fout("\n\n#Alternate Types:\n");
872 // write them all out
873 for(i=0; i<Mission_alt_type_count; i++){
874 fout("$Alt: %s\n", Mission_alt_types[i]);
881 required_string_fred("#Players");
883 fout("\t\t;! %d total\n", Player_starts);
885 for (i=0; i<Num_teams; i++) {
886 required_string_fred("$Starting Shipname:");
888 Assert(Player_start_shipnum >= 0);
889 fout(" %s", Ships[Player_start_shipnum].ship_name);
891 required_string_fred("$Ship Choices:");
895 for (j=0; j<Team_data[i].number_choices; j++)
896 fout("\t\"%s\"\t%d\n", Ship_info[Team_data[i].ship_list[j]].name,
897 Team_data[i].ship_count[j]);
901 if (optional_string_fred("+Weaponry Pool:", "$Starting Shipname:")){
904 fout("\n\n+Weaponry Pool:");
908 generate_weaponry_usage_list(used_pool);
909 for (j=0; j<Num_weapon_types; j++){
910 if (Team_data[i].weaponry_pool[j] + used_pool[j] > 0){
911 fout("\t\"%s\"\t%d\n", Weapon_info[j].name, Team_data[i].weaponry_pool[j] + used_pool[j]);
921 int CFred_mission_save::save_objects()
929 required_string_fred("#Objects");
931 fout("\t\t;! %d total\n", ship_get_num_ships() );
933 for (i=z=0; i<MAX_SHIPS; i++) {
934 if (Ships[i].objnum < 0){
938 j = Objects[Ships[i].objnum].type;
939 if ((j != OBJ_SHIP) && (j != OBJ_START)){
943 objp = &Objects[Ships[i].objnum];
944 sip = &Ship_info[Ships[i].ship_info_index];
945 required_string_either_fred("$Name:", "#Wings");
946 required_string_fred("$Name:");
947 parse_comments(z ? 2 : 1);
948 fout(" %s\t\t;! Object #%d\n", Ships[i].ship_name, i);
950 required_string_fred("$Class:");
952 fout(" %s", Ship_info[Ships[i].ship_info_index].name);
954 // optional alternate type name
955 if(strlen(Fred_alt_names[i])){
956 fout("\n$Alt: %s\n", Fred_alt_names[i]);
959 required_string_fred("$Team:");
961 fout(" %s", Team_names[bitmask_2_bitnum(Ships[i].team)]);
963 required_string_fred("$Location:");
965 save_vector(Objects[Ships[i].objnum].pos);
967 required_string_fred("$Orientation:");
969 save_matrix(Objects[Ships[i].objnum].orient);
971 required_string_fred("$IFF:");
973 fout(" %s", Iff_names[0]);
975 Assert(Ships[i].ai_index >= 0);
976 aip = &Ai_info[Ships[i].ai_index];
978 required_string_fred("$AI Behavior:");
980 fout(" %s", Ai_behavior_names[aip->behavior]);
982 if (Ships[i].weapons.ai_class != Ship_info[Ships[i].ship_info_index].ai_class) {
983 if (optional_string_fred("+AI Class:", "$Name:"))
986 fout("\n+AI Class:");
988 fout(" %s", Ai_class_names[Ships[i].weapons.ai_class]);
991 save_ai_goals(Ai_info[Ships[i].ai_index].goals, i);
994 required_string_fred("$Cargo 1:");
997 fout_ext("%s", Cargo_names[Ships[i].cargo1]);
999 save_common_object_data(&Objects[Ships[i].objnum], &Ships[i]);
1001 if (Ships[i].wingnum >= 0){
1002 Ships[i].arrival_location = ARRIVE_AT_LOCATION;
1005 required_string_fred("$Arrival Location:");
1007 fout(" %s", Arrival_location_names[Ships[i].arrival_location]);
1009 if (Ships[i].arrival_location != ARRIVE_AT_LOCATION) {
1010 if (optional_string_fred("+Arrival Distance:", "$Name:")){
1013 fout("\n+Arrival Distance:");
1016 fout(" %d", Ships[i].arrival_distance);
1017 if (optional_string_fred("$Arrival Anchor:", "$Name:")){
1020 fout("\n$Arrival Anchor:");
1023 z = Ships[i].arrival_anchor;
1024 if (z >= SPECIAL_ARRIVAL_ANCHORS_OFFSET){
1025 fout(" %s", Special_arrival_anchor_names[z - SPECIAL_ARRIVAL_ANCHORS_OFFSET]);
1026 } else if (z >= 0) {
1027 fout(" %s", Ships[z].ship_name);
1033 if (Ships[i].arrival_delay) {
1034 if (optional_string_fred("+Arrival Delay:", "$Name:")){
1037 fout("\n+Arrival Delay:");
1040 fout(" %d", Ships[i].arrival_delay);
1043 required_string_fred("$Arrival Cue:");
1045 convert_sexp_to_string(Ships[i].arrival_cue, out, SEXP_SAVE_MODE);
1048 required_string_fred("$Departure Location:");
1050 fout(" %s", Departure_location_names[Ships[i].departure_location]);
1053 if ( Ships[i].departure_location == DEPART_AT_DOCK_BAY ) {
1054 required_string_fred("$Departure Anchor:");
1057 if ( Ships[i].departure_anchor >= 0 ){
1058 fout(" %s", Ships[Ships[i].departure_anchor].ship_name );
1064 if (Ships[i].departure_delay) {
1065 if (optional_string_fred("+Departure delay:", "$Name:")){
1068 fout("\n+Departure delay:");
1071 fout(" %d", Ships[i].departure_delay);
1074 required_string_fred("$Departure Cue:");
1076 convert_sexp_to_string(Ships[i].departure_cue, out, SEXP_SAVE_MODE);
1079 required_string_fred("$Determination:");
1081 fout(" %d", Ships[i].determination);
1083 if (optional_string_fred("+Flags:", "$Name:")) {
1087 fout("\n+Flags: (");
1089 if (Ships[i].flags & SF_CARGO_REVEALED)
1090 fout(" \"cargo-known\"");
1091 if (Ships[i].flags & SF_IGNORE_COUNT)
1092 fout(" \"ignore-count\"");
1093 if (objp->flags & OF_PROTECTED)
1094 fout(" \"protect-ship\"");
1095 if (objp->flags & OF_BEAM_PROTECTED)
1096 fout(" \"beam-protect-ship\"");
1097 if (Ships[i].flags & SF_REINFORCEMENT)
1098 fout(" \"reinforcement\"");
1099 if (objp->flags & OF_NO_SHIELDS)
1100 fout(" \"no-shields\"");
1101 if (Ships[i].flags & SF_ESCORT)
1102 fout(" \"escort\"");
1103 if (objp->type == OBJ_START)
1104 fout(" \"player-start\"");
1105 if (Ships[i].flags & SF_NO_ARRIVAL_MUSIC)
1106 fout(" \"no-arrival-music\"");
1107 if (Ships[i].flags & SF_NO_ARRIVAL_WARP)
1108 fout(" \"no-arrival-warp\"");
1109 if (Ships[i].flags & SF_NO_DEPARTURE_WARP)
1110 fout(" \"no-departure-warp\"");
1111 if (Ships[i].flags & SF_LOCKED)
1112 fout(" \"locked\"");
1113 if (Ships[i].flags & SF_INVULNERABLE)
1114 fout(" \"invulnerable\"");
1115 if (Ships[i].flags & SF_HIDDEN_FROM_SENSORS)
1116 fout(" \"hidden-from-sensors\"");
1117 if ( Ships[i].flags & SF_SCANNABLE )
1118 fout(" \"scannable\"");
1119 if ( Ai_info[Ships[i].ai_index].ai_flags & AIF_KAMIKAZE )
1120 fout(" \"kamikaze\"");
1121 if ( Ai_info[Ships[i].ai_index].ai_flags & AIF_NO_DYNAMIC )
1122 fout(" \"no-dynamic\"");
1123 if ( Ships[i].flags & SF_RED_ALERT_STORE_STATUS )
1124 fout(" \"red-alert-carry\"");
1125 if ( objp->flags & OF_SPECIAL_WARP )
1126 fout(" \"special-warp\"");
1129 fout("+Respawn priority: %d\n", Ships[i].respawn_priority);
1131 if (Ships[i].flags & SF_ESCORT) {
1132 if (optional_string_fred("+Escort priority:", "$Name:")) {
1135 fout("\n+Escort priority:");
1138 fout(" %d", Ships[i].escort_priority);
1141 if (Ships[i].special_exp_index != -1) {
1142 if (optional_string_fred("+Special Exp index:", "$Name:")) {
1145 fout("+Special Exp index:");
1148 fout(" %d", Ships[i].special_exp_index);
1152 if ( Ai_info[Ships[i].ai_index].ai_flags & AIF_KAMIKAZE ) {
1153 if ( optional_string_fred("+Kamikaze Damage:", "$Name:")){
1156 fout("\n+Kamikaze Damage:");
1159 fout(" %d", (int)(Ai_info[Ships[i].ai_index].kamikaze_damage) );
1162 if (Ships[i].hotkey != -1) {
1163 if (optional_string_fred("+Hotkey:", "$Name:")){
1169 fout(" %d", Ships[i].hotkey);
1172 // mwa -- new code to save off information about initially docked ships.
1173 if ( Ships[i].flags & SF_INITIALLY_DOCKED ) {
1174 Assert ( aip->dock_objnum != -1 ); // ge allender if you hit this
1176 if (optional_string_fred("+Docked With:", "$Name:"))
1179 fout("\n+Docked With:");
1181 switch (Objects[aip->dock_objnum].type) {
1184 j = Objects[aip->dock_objnum].instance;
1191 fout(" %s", Ships[j].ship_name);
1193 required_string_fred("$Docker Point:", "$Name:");
1195 fout(" %s", model_get_dock_name(Ships[j].modelnum, aip->dockee_index));
1197 required_string_fred("$Dockee Point:", "$Name:");
1199 fout(" %s", model_get_dock_name(Ships[i].modelnum, aip->dock_index));
1203 // check the ship flag about killing off the ship before a missino starts. Write out the appropriate
1204 // variable if necessary
1205 if ( Ships[i].flags & SF_KILL_BEFORE_MISSION ) {
1206 if ( optional_string_fred("+Destroy At:", "$Name:"))
1209 fout ("\n+Destroy At: ");
1211 fout(" %d", Ships[i].final_death_time );
1214 // possibly write out the orders that this ship will accept. We'll only do it if the orders
1215 // are not the default set of orders
1216 if ( Ships[i].orders_accepted != ship_get_default_orders_accepted( &Ship_info[Ships[i].ship_info_index]) ) {
1217 if ( optional_string_fred("+Orders Accepted:", "$Name:") )
1220 fout("\n+Orders Accepted:");
1222 fout(" %d\t\t;! note that this is a bitfield!!!", Ships[i].orders_accepted);
1225 if (Ships[i].group >= 0) {
1226 if (optional_string_fred("+Group:", "$Name:"))
1231 fout(" %d", Ships[i].group);
1234 if (Ships[i].score) {
1235 if (optional_string_fred("+Score:", "$Name:"))
1240 fout(" %d", Ships[i].score);
1243 // deal with the persona for this ship as well.
1244 if ( Ships[i].persona_index != -1 ) {
1245 if (optional_string_fred("+Persona Index:", "$Name:"))
1248 fout("\n+Persona Index:");
1250 fout(" %d", Ships[i].persona_index);
1259 int CFred_mission_save::save_common_object_data(object *objp, ship *shipp)
1266 sip = &Ship_info[shipp->ship_info_index];
1267 if ((int) objp->phys_info.speed) {
1268 if (optional_string_fred("+Initial Velocity:", "$Name:", "+Subsystem:"))
1271 fout("\n+Initial Velocity:");
1273 fout(" %d", (int) objp->phys_info.speed);
1276 if ((int) objp->hull_strength != (int) sip->initial_hull_strength) {
1277 if (optional_string_fred("+Initial Hull:", "$Name:", "+Subsystem:"))
1280 fout("\n+Initial Hull:");
1282 fout(" %d", (int) objp->hull_strength);
1285 if ((int) get_shield_strength(objp) != 100) {
1286 if (optional_string_fred("+Initial Shields:", "$Name:", "+Subsystem:"))
1289 fout("\n+Initial Shields:");
1291 fout(" %d", (int) objp->shields[0]);
1294 // save normal ship weapons info
1295 required_string_fred("+Subsystem:", "$Name:");
1299 wp = &shipp->weapons;
1301 j = wp->num_primary_banks;
1303 if (wp->primary_bank_weapons[j] != sip->primary_bank_weapons[j])
1307 if (optional_string_fred("+Primary Banks:", "$Name:", "+Subsystem:"))
1310 fout("\n+Primary Banks:");
1313 for (j=0; j<wp->num_primary_banks; j++)
1314 fout("\"%s\" ", Weapon_info[wp->primary_bank_weapons[j]].name);
1320 j = wp->num_secondary_banks;
1322 if (wp->secondary_bank_weapons[j] != sip->secondary_bank_weapons[j])
1326 if (optional_string_fred("+Secondary Banks:", "$Name:", "+Subsystem:"))
1329 fout("\n+Secondary Banks:");
1332 for (j=0; j<wp->num_secondary_banks; j++)
1333 fout("\"%s\" ", Weapon_info[wp->secondary_bank_weapons[j]].name);
1339 j = wp->num_secondary_banks;
1341 if (wp->secondary_bank_ammo[j] != 100)
1345 if (optional_string_fred("+Sbank Ammo:", "$Name:", "+Subsystem:"))
1348 fout("\n+Sbank Ammo:");
1351 for (j=0; j<wp->num_secondary_banks; j++)
1352 fout("%d ", wp->secondary_bank_ammo[j]);
1357 ptr = GET_FIRST(&shipp->subsys_list);
1358 while (ptr != END_OF_LIST(&shipp->subsys_list)) {
1359 if ( (ptr->current_hits) || (ptr->system_info->type == SUBSYSTEM_TURRET) || (ptr->subsys_cargo_name != -1)) {
1360 if (optional_string_fred("+Subsystem:", "$Name:"))
1363 fout("\n+Subsystem:");
1365 fout(" %s", ptr->system_info->subobj_name);
1368 if (ptr->current_hits) {
1369 if (optional_string_fred("$Damage:", "$Name:", "+Subsystem:"))
1374 fout(" %d", (int) ptr->current_hits);
1377 if (ptr->subsys_cargo_name != -1) {
1378 if (optional_string_fred("+Cargo Name:", "$Name:", "+Subsystem:"))
1381 fout("\n+Cargo Name:");
1383 fout_ext("%s", Cargo_names[ptr->subsys_cargo_name]);
1386 if (ptr->system_info->type == SUBSYSTEM_TURRET)
1387 save_turret_info(ptr, shipp - Ships);
1389 ptr = GET_NEXT(ptr);
1392 /* for (j=0; j<shipp->status_count; j++) {
1393 required_string_fred("$Status Description:");
1395 fout(" %s", Status_desc_names[shipp->status_type[j]]);
1397 required_string_fred("$Status:");
1399 fout(" %s", Status_type_names[shipp->status[j]]);
1401 required_string_fred("$Target:");
1403 fout(" %s", Status_target_names[shipp->target[j]]);
1409 int CFred_mission_save::save_wings()
1412 int i, j, z, ship, count = 0;
1414 fred_parse_flag = 0;
1415 required_string_fred("#Wings");
1417 fout("\t\t;! %d total", num_wings);
1419 for (i=0; i<MAX_WINGS; i++) {
1420 if (!Wings[i].wave_count)
1424 required_string_either_fred("$Name:", "#Events");
1425 required_string_fred("$Name:");
1427 fout(" %s", Wings[i].name);
1429 required_string_fred("$Waves:");
1431 fout(" %d", Wings[i].num_waves);
1433 required_string_fred("$Wave Threshold:");
1435 fout(" %d", Wings[i].threshold);
1437 required_string_fred("$Special Ship:");
1439 fout(" %d\t\t;! %s\n", Wings[i].special_ship,
1440 Ships[Wings[i].ship_index[Wings[i].special_ship]].ship_name);
1442 required_string_fred("$Arrival Location:");
1444 fout(" %s", Arrival_location_names[Wings[i].arrival_location]);
1446 if (Wings[i].arrival_location != ARRIVE_AT_LOCATION) {
1447 if (optional_string_fred("+Arrival Distance:", "$Name:"))
1450 fout("\n+Arrival Distance:");
1452 fout(" %d", Wings[i].arrival_distance);
1453 if (optional_string_fred("$Arrival Anchor:", "$Name:"))
1456 fout("\n$Arrival Anchor:");
1458 z = Wings[i].arrival_anchor;
1459 if (z >= SPECIAL_ARRIVAL_ANCHORS_OFFSET)
1460 fout(" %s", Special_arrival_anchor_names[z - SPECIAL_ARRIVAL_ANCHORS_OFFSET]);
1462 fout(" %s", Ships[z].ship_name);
1467 if (Wings[i].arrival_delay) {
1468 if (optional_string_fred("+Arrival delay:", "$Name:"))
1471 fout("\n+Arrival delay:");
1473 fout(" %d", Wings[i].arrival_delay);
1476 required_string_fred("$Arrival Cue:");
1478 convert_sexp_to_string(Wings[i].arrival_cue, out, SEXP_SAVE_MODE);
1481 required_string_fred("$Departure Location:");
1483 fout(" %s", Departure_location_names[Wings[i].departure_location]);
1485 if ( Wings[i].departure_location == DEPART_AT_DOCK_BAY ) {
1486 required_string_fred("$Departure Anchor:");
1489 if ( Wings[i].departure_anchor >= 0 )
1490 fout(" %s", Ships[Wings[i].departure_anchor].ship_name );
1495 if (Wings[i].departure_delay) {
1496 if (optional_string_fred("+Departure delay:", "$Name:"))
1499 fout("\n+Departure delay:");
1501 fout(" %d", Wings[i].departure_delay);
1504 required_string_fred("$Departure Cue:");
1506 convert_sexp_to_string(Wings[i].departure_cue, out, SEXP_SAVE_MODE);
1509 required_string_fred("$Ships:");
1511 fout(" (\t\t;! %d total\n", Wings[i].wave_count);
1513 for (j=0; j<Wings[i].wave_count; j++) {
1514 ship = Wings[i].ship_index[j];
1515 // if (Objects[Ships[ship].objnum].type == OBJ_START)
1516 // fout("\t\"Player 1\"\n");
1518 fout("\t\"%s\"\n", Ships[Wings[i].ship_index[j]].ship_name);
1522 save_ai_goals(Wings[i].ai_goals, -1);
1524 if (Wings[i].hotkey != -1) {
1525 if (optional_string_fred("+Hotkey:", "$Name:"))
1530 fout(" %d", Wings[i].hotkey);
1533 if ( optional_string_fred("+Flags:", "$Name:")) {
1537 fout("\n+Flags: (");
1539 if ( Wings[i].flags & WF_IGNORE_COUNT )
1540 fout(" \"ignore-count\"");
1541 if ( Wings[i].flags & WF_REINFORCEMENT )
1542 fout(" \"reinforcement\"");
1543 if ( Wings[i].flags & WF_NO_ARRIVAL_MUSIC )
1544 fout(" \"no-arrival-music\"");
1545 if ( Wings[i].flags & WF_NO_ARRIVAL_MESSAGE )
1546 fout(" \"no-arrival-message\"");
1547 if ( Wings[i].flags & WF_NO_ARRIVAL_WARP )
1548 fout(" \"no-arrival-warp\"");
1549 if ( Wings[i].flags & WF_NO_DEPARTURE_WARP )
1550 fout(" \"no-departure-warp\"");
1551 if ( Wings[i].flags & WF_NO_DYNAMIC )
1552 fout( " \"no-dynamic\"" );
1556 if (Wings[i].wave_delay_min) {
1557 if (optional_string_fred("+Wave Delay Min:", "$Name:"))
1560 fout("\n+Wave Delay Min:");
1562 fout(" %d", Wings[i].wave_delay_min);
1565 if (Wings[i].wave_delay_max) {
1566 if (optional_string_fred("+Wave Delay Max:", "$Name:"))
1569 fout("\n+Wave Delay Max:");
1571 fout(" %d", Wings[i].wave_delay_max);
1575 Assert(count == num_wings);
1579 int CFred_mission_save::save_goals()
1584 fred_parse_flag = 0;
1585 required_string_fred("#Goals");
1587 fout("\t\t;! %d total\n", Num_goals);
1589 for (i=0; i<Num_goals; i++) {
1592 required_string_either_fred("$Type:", "#Waypoints");
1593 required_string_fred("$Type:");
1594 parse_comments(i ? 2 : 1);
1596 type = Mission_goals[i].type & GOAL_TYPE_MASK;
1597 fout(" %s", Goal_type_names[type]);
1599 if (*Mission_goals[i].name) {
1600 if (optional_string_fred("+Name:", "$Type:"))
1605 fout(" %s", Mission_goals[i].name);
1609 required_string_fred("$MessageNew:");
1612 fout_ext("%s", Mission_goals[i].message);
1614 required_string_fred("$end_multi_text");
1617 required_string_fred("$Formula:");
1619 convert_sexp_to_string(Mission_goals[i].formula, out, SEXP_SAVE_MODE);
1622 if ( Mission_goals[i].type & INVALID_GOAL ) {
1623 if (optional_string_fred("+Invalid", "$Type:"))
1629 if ( Mission_goals[i].flags & MGF_NO_MUSIC ) {
1630 if (optional_string_fred("+No music", "$Type:"))
1633 fout("\n+No music");
1636 if ( Mission_goals[i].score != 0 ) {
1637 if ( optional_string_fred("+Score:", "$Type:"))
1641 fout(" %d", Mission_goals[i].score );
1644 if ( The_mission.game_type & MISSION_TYPE_MULTI_TEAMS ) {
1645 if ( optional_string_fred("+Team:", "$Type:"))
1649 fout(" %d", Mission_goals[i].team );
1656 int CFred_mission_save::save_waypoints()
1661 fred_parse_flag = 0;
1662 required_string_fred("#Waypoints");
1664 fout("\t\t;! %d lists total\n", Num_waypoint_lists);
1666 for (i=0; i<Num_jump_nodes; i++) {
1667 ptr = GET_FIRST(&obj_used_list);
1668 while (ptr != END_OF_LIST(&obj_used_list)) {
1669 if ((ptr->type == OBJ_JUMP_NODE) && (ptr->instance == i))
1672 ptr = GET_NEXT(ptr);
1675 Assert(ptr != END_OF_LIST(&obj_used_list));
1677 required_string_fred("$Jump Node:", "$Jump Node Name:");
1679 save_vector(ptr->pos);
1681 required_string_fred("$Jump Node Name:", "$Jump Node:");
1683 fout(" %s", Jump_nodes[i].name);
1686 for (i=0; i<Num_waypoint_lists; i++) {
1687 required_string_either_fred("$Name:", "#Messages");
1688 required_string_fred("$Name:");
1689 parse_comments(i ? 2 : 1);
1690 fout(" %s", Waypoint_lists[i].name);
1692 required_string_fred("$List:");
1694 fout(" (\t\t;! %d points in list\n", Waypoint_lists[i].count);
1696 save_waypoint_list(Waypoint_lists[i]);
1703 int CFred_mission_save::save_waypoint_list(waypoint_list &w)
1707 for (i=0; i<w.count; i++)
1708 fout("\t( %f, %f, %f )\n", w.waypoints[i].x, w.waypoints[i].y, w.waypoints[i].z);
1713 int CFred_mission_save::save_messages()
1717 fred_parse_flag = 0;
1718 required_string_fred("#Messages");
1720 fout("\t\t;! %d total\n", Num_messages);
1722 for (i=Num_builtin_messages; i<Num_messages; i++) {
1723 required_string_either_fred("$Name:", "#Reinforcements");
1724 required_string_fred("$Name:");
1725 parse_comments(i ? 2 : 1);
1726 fout(" %s", Messages[i].name);
1729 required_string_fred("$Team:");
1730 parse_comments(i ? 2 : 1);
1731 if((Messages[i].multi_team < 0) || (Messages[i].multi_team >= 2)){
1734 fout(" %d", Messages[i].multi_team);
1738 required_string_fred("$MessageNew:");
1741 fout_ext("%s", Messages[i].message);
1743 required_string_fred("$end_multi_text");
1746 if ( Messages[i].persona_index != -1 ) {
1747 if ( optional_string_fred("+Persona:", "$Name:"))
1750 fout("\n+Persona:");
1752 fout(" %s", Personas[Messages[i].persona_index].name );
1755 if (Messages[i].avi_info.name) {
1756 if (optional_string_fred("+AVI Name:", "$Name:"))
1759 fout("\n+AVI Name:");
1761 fout(" %s", Messages[i].avi_info.name);
1764 if (Messages[i].wave_info.name) {
1765 if (optional_string_fred("+Wave Name:", "$Name:"))
1768 fout("\n+Wave Name:");
1770 fout(" %s", Messages[i].wave_info.name);
1777 int CFred_mission_save::save_vector(vector &v)
1779 fout(" %f, %f, %f", v.x, v.y, v.z);
1783 int CFred_mission_save::save_matrix(matrix &m)
1785 fout("\n\t%f, %f, %f,\n", m.rvec.x, m.rvec.y, m.rvec.z);
1786 fout("\t%f, %f, %f,\n", m.uvec.x, m.uvec.y, m.uvec.z);
1787 fout("\t%f, %f, %f", m.fvec.x, m.fvec.y, m.fvec.z);
1791 // saves comments from previous campaign/mission file
1792 void CFred_mission_save::parse_comments(int newlines)
1794 char *comment_start = NULL;
1795 int state = 0, same_line = 0, first_comment = 1, tab = 0, flag = 0;
1798 newlines = -newlines;
1805 if (fred_parse_flag || !Token_found_flag || !token_found || (token_found && (*Mission_text_raw == EOF_CHAR))) {
1806 while (newlines-- > 0)
1813 fout("%s", token_found);
1818 while (*raw_ptr != EOF_CHAR) {
1820 if (token_found && (*raw_ptr == *token_found))
1821 if (!strnicmp(raw_ptr, token_found, strlen(token_found))) {
1822 same_line = newlines - 1 + same_line;
1823 while (same_line-- > 0)
1829 fout("%s", token_found);
1833 if ((*raw_ptr == '/') && (raw_ptr[1] == '*')) {
1834 comment_start = raw_ptr;
1838 if ((*raw_ptr == ';') && (raw_ptr[1] != '!')) {
1839 comment_start = raw_ptr;
1843 if ((*raw_ptr == '/') && (raw_ptr[1] == '/')) {
1844 comment_start = raw_ptr;
1848 if (*raw_ptr == '\n')
1855 if ((*raw_ptr == '\n') && (state == 2)) {
1856 if (first_comment && !flag)
1860 fout("%s\n", comment_start);
1862 state = first_comment = same_line = flag = 0;
1865 if ((*raw_ptr == '*') && (raw_ptr[1] == '/') && (state == 1)) {
1866 if (first_comment && !flag)
1871 fout("%s", comment_start);
1872 raw_ptr[2] = (char)state;
1873 state = first_comment = flag = 0;
1883 int CFred_mission_save::fout(char *format, ...)
1892 va_start(args, format);
1893 vsprintf(str, format, args);
1895 Assert(strlen(str) < 16384);
1901 int CFred_mission_save::fout_ext(char *format, ...)
1904 char str_out[16384] = "";
1912 va_start(args, format);
1913 vsprintf(str, format, args);
1915 Assert(strlen(str) < 16384);
1917 // lookup the string in the hash table
1918 str_id = fhash_string_exists(str);
1919 // doesn't exist, so assign it an ID of -1 and stick it in the table
1921 sprintf(str_out, " XSTR(\"%s\", -1)", str);
1923 // add the string to the table
1924 fhash_add_str(str, -1);
1926 // _does_ exist, so just write it out as it is
1928 sprintf(str_out, " XSTR(\"%s\", %d)", str, str_id);
1931 cfputs(str_out, fp);
1935 void CFred_mission_save::save_ai_goals(ai_goals *goalp, int ship)
1937 char *str = NULL, buf[80];
1938 int i, valid, flag = 1;
1940 for (i=0; i<MAX_AI_GOALS; i++) {
1941 if (goalp[i].ai_mode == AI_GOAL_NONE)
1945 if (optional_string_fred("$AI Goals:", "$Name:"))
1948 fout("\n$AI Goals:");
1954 if (goalp[i].ai_mode == AI_GOAL_CHASE_ANY) {
1955 fout("( ai-chase-any %d ) ", goalp[i].priority);
1957 } else if (goalp[i].ai_mode == AI_GOAL_UNDOCK) {
1958 fout("( ai-undock %d ) ", goalp[i].priority);
1960 } else if (goalp[i].ai_mode == AI_GOAL_KEEP_SAFE_DISTANCE) {
1961 fout("( ai-keep-safe-distance %d ) ", goalp[i].priority);
1963 } else if (goalp[i].ai_mode == AI_GOAL_PLAY_DEAD) {
1964 fout("( ai-play-dead %d ) ", goalp[i].priority);
1966 } else if (goalp[i].ai_mode == AI_GOAL_WARP) {
1967 fout("( ai-warp-out %d ) ", goalp[i].priority);
1971 if (!goalp[i].ship_name) {
1972 Warning(LOCATION, "Ai goal has no target where one is required");
1975 sprintf(buf, "\"%s\"", goalp[i].ship_name);
1976 switch (goalp[i].ai_mode) {
1977 case AI_GOAL_WAYPOINTS:
1978 str = "ai-waypoints";
1981 case AI_GOAL_WAYPOINTS_ONCE:
1982 str = "ai-waypoints-once";
1985 case AI_GOAL_DESTROY_SUBSYSTEM:
1986 if (goalp[i].docker.index == -1 || !goalp[i].docker.index) {
1988 Warning(LOCATION, "AI destroy subsystem goal invalid subsystem name\n");
1991 sprintf(buf, "\"%s\" \"%s\"", goalp[i].ship_name, goalp[i].docker.name);
1992 str = "ai-destroy-subsystem";
2000 Warning(LOCATION, "Wings aren't allowed to have a docking goal\n");
2002 } else if (goalp[i].docker.index == -1 || !goalp[i].docker.index) {
2004 Warning(LOCATION, "AI dock goal for \"%s\" has invalid docker point "
2005 "(docking with \"%s\")\n", Ships[ship].ship_name, goalp[i].ship_name);
2007 } else if (goalp[i].dockee.index == -1 || !goalp[i].dockee.index) {
2009 Warning(LOCATION, "AI dock goal for \"%s\" has invalid dockee point "
2010 "(docking with \"%s\")\n", Ships[ship].ship_name, goalp[i].ship_name);
2013 sprintf(buf, "\"%s\" \"%s\" \"%s\"", goalp[i].ship_name,
2014 goalp[i].docker.name, goalp[i].dockee.name);
2024 case AI_GOAL_CHASE_WING:
2025 str = "ai-chase-wing";
2032 case AI_GOAL_GUARD_WING:
2033 str = "ai-guard-wing";
2036 case AI_GOAL_DISABLE_SHIP:
2037 str = "ai-disable-ship";
2040 case AI_GOAL_DISARM_SHIP:
2041 str = "ai-disarm-ship";
2044 case AI_GOAL_IGNORE:
2048 case AI_GOAL_EVADE_SHIP:
2049 str = "ai-evade-ship";
2052 case AI_GOAL_STAY_NEAR_SHIP:
2053 str = "ai-stay-near-ship";
2056 case AI_GOAL_STAY_STILL:
2057 str = "ai-stay-still";
2065 fout("( %s %s %d ) ", str, buf, goalp[i].priority);
2074 int CFred_mission_save::save_events()
2079 fred_parse_flag = 0;
2080 required_string_fred("#Events");
2082 fout("\t\t;! %d total\n", Num_mission_events);
2084 for (i=0; i<Num_mission_events; i++) {
2085 required_string_either_fred("$Formula:", "#Goals");
2086 required_string_fred("$Formula:");
2087 parse_comments(i ? 2 : 1);
2088 convert_sexp_to_string(Mission_events[i].formula, out, SEXP_SAVE_MODE);
2091 if (*Mission_events[i].name) {
2092 if (optional_string_fred("+Name:", "$Formula:")){
2098 fout(" %s", Mission_events[i].name);
2101 if ( optional_string_fred("+Repeat Count:", "$Formula:")){
2104 fout("\n+Repeat Count:");
2107 fout(" %d", Mission_events[i].repeat_count);
2109 if ( optional_string_fred("+Interval:", "$Formula:")){
2112 fout("\n+Interval:");
2115 fout(" %d", Mission_events[i].interval);
2117 if ( Mission_events[i].score != 0 ) {
2118 if ( optional_string_fred("+Score:", "$Formula:")){
2123 fout(" %d", Mission_events[i].score);
2126 if ( Mission_events[i].chain_delay >= 0 ) {
2127 if ( optional_string_fred("+Chained:", "$Formula:")){
2130 fout("\n+Chained:");
2133 fout(" %d", Mission_events[i].chain_delay);
2137 if (Mission_events[i].objective_text) {
2138 if (optional_string_fred("+Objective:", "$Formula:")){
2141 fout("\n+Objective:");
2145 fout_ext("%s", Mission_events[i].objective_text);
2149 if (Mission_events[i].objective_key_text) {
2150 if (optional_string_fred("+Objective key:", "$Formula:")){
2153 fout("\n+Objective key:");
2157 fout_ext("%s", Mission_events[i].objective_key_text);
2161 if (Mission_events[i].team >= 0){
2162 if (optional_string_fred("+Team:")){
2168 fout("%d", Mission_events[i].team);
2175 int CFred_mission_save::save_reinforcements()
2179 fred_parse_flag = 0;
2180 required_string_fred("#Reinforcements");
2182 fout("\t\t;! %d total\n", Num_reinforcements);
2184 for (i=0; i<Num_reinforcements; i++) {
2185 required_string_either_fred("$Name:", "#Background bitmaps");
2186 required_string_fred("$Name:");
2187 parse_comments(i ? 2 : 1);
2188 fout(" %s", Reinforcements[i].name);
2190 type = TYPE_ATTACK_PROTECT;
2191 for (j=0; j<MAX_SHIPS; j++)
2192 if ((Ships[j].objnum != -1) && !stricmp(Ships[j].ship_name, Reinforcements[i].name)) {
2193 if (Ship_info[Ships[j].ship_info_index].flags & SIF_SUPPORT)
2194 type = TYPE_REPAIR_REARM;
2198 required_string_fred("$Type:");
2200 fout(" %s", Reinforcement_type_names[type]);
2202 required_string_fred("$Num times:");
2204 fout(" %d", Reinforcements[i].uses);
2206 if ( optional_string_fred("+Arrival Delay:", "$Name:"))
2209 fout("\n+Arrival Delay:");
2210 fout(" %d", Reinforcements[i].arrival_delay );
2212 if (optional_string_fred("+No Messages:", "$Name:"))
2215 fout("\n+No Messages:");
2217 for (j = 0; j < MAX_REINFORCEMENT_MESSAGES; j++) {
2218 if ( strlen(Reinforcements[i].no_messages[j]) )
2219 fout(" \"%s\"", Reinforcements[i].no_messages[j]);
2223 if (optional_string_fred("+Yes Messages:", "$Name:"))
2226 fout("\n+Yes Messages:");
2228 for (j = 0; j < MAX_REINFORCEMENT_MESSAGES; j++) {
2229 if ( strlen(Reinforcements[i].yes_messages[j]) )
2230 fout(" \"%s\"", Reinforcements[i].yes_messages[j]);
2239 int CFred_mission_save::save_bitmaps()
2243 fred_parse_flag = 0;
2244 required_string_fred("#Background bitmaps");
2246 fout("\t\t;! %d total\n", Num_starfield_bitmaps);
2248 required_string_fred("$Num stars:");
2250 fout(" %d", Num_stars);
2252 float Ambient_light_level = 1.0f; // JAS: Should this be set to something?
2253 required_string_fred("$Ambient light level:");
2255 fout(" %d", Ambient_light_level);
2258 if(The_mission.flags & MISSION_FLAG_FULLNEB){
2259 required_string_fred("+Neb2:");
2261 fout(" %s\n", Neb2_texture_name);
2263 required_string_fred("+Neb2Flags:");
2265 fout(" %d\n", Neb2_poof_flags);
2269 if (Nebula_index >= 0) {
2270 if (optional_string_fred("+Nebula:")){
2275 fout(" %s", Nebula_filenames[Nebula_index]);
2277 required_string_fred("+Color:");
2279 fout(" %s", Nebula_colors[Mission_palette]);
2281 required_string_fred("+Pitch:");
2283 fout(" %d", Nebula_pitch);
2285 required_string_fred("+Bank:");
2287 fout(" %d", Nebula_bank);
2289 required_string_fred("+Heading:");
2291 fout(" %d", Nebula_heading);
2295 // save suns by sun bitmap filename
2296 for(idx=0; idx<Num_suns; idx++){
2297 // sun name, angles and scale
2298 required_string_fred("$Sun:");
2300 fout(" %s", Suns[idx].filename);
2302 required_string_fred("+Angles:");
2304 fout(" %f %f %f", Suns[idx].ang.p, Suns[idx].ang.b, Suns[idx].ang.h);
2306 required_string_fred("+Scale:");
2308 fout(" %f", Suns[idx].scale_x);
2311 // save background bitmaps by filename
2312 for(idx=0; idx<Num_starfield_bitmaps; idx++){
2313 // sun name, angles and scale
2314 required_string_fred("$Starbitmap:");
2316 fout(" %s", Starfield_bitmap_instance[idx].filename);
2318 required_string_fred("+Angles:");
2320 fout(" %f %f %f", Starfield_bitmap_instance[idx].ang.p, Starfield_bitmap_instance[idx].ang.b, Starfield_bitmap_instance[idx].ang.h);
2322 required_string_fred("+ScaleX:");
2324 fout(" %f", Starfield_bitmap_instance[idx].scale_x);
2326 required_string_fred("+ScaleY:");
2328 fout(" %f", Starfield_bitmap_instance[idx].scale_y);
2330 required_string_fred("+DivX:");
2332 fout(" %d", Starfield_bitmap_instance[idx].div_x);
2334 required_string_fred("+DivY:");
2336 fout(" %d", Starfield_bitmap_instance[idx].div_y);
2342 int CFred_mission_save::save_asteroid_fields()
2346 fred_parse_flag = 0;
2347 required_string_fred("#Asteroid Fields");
2350 for (i=0; i<1 /*MAX_ASTEROID_FIELDS*/; i++) {
2351 if (!Asteroid_field.num_initial_asteroids)
2354 required_string_fred("$Density:");
2356 fout(" %d", Asteroid_field.num_initial_asteroids);
2359 if (optional_string_fred("+Field Type:")){
2362 fout("\n+Field Type:");
2364 fout(" %d", Asteroid_field.field_type);
2367 if (optional_string_fred("+Debris Genre:")){
2370 fout("\n+Debris Genre:");
2372 fout(" %d", Asteroid_field.debris_genre);
2374 // field_debris_type (only if ship genre)
2375 if (Asteroid_field.debris_genre == DG_SHIP) {
2376 for (int idx=0; idx<3; idx++) {
2377 if (Asteroid_field.field_debris_type[idx] != -1) {
2378 if (optional_string_fred("+Field Debris Type:")){
2381 fout("\n+Field Debris Type:");
2383 fout(" %d", Asteroid_field.field_debris_type[idx]);
2387 // asteroid subtypes stored in field_debris_type as -1 or 1
2388 for (idx=0; idx<3; idx++) {
2389 if (Asteroid_field.field_debris_type[idx] != -1) {
2390 if (optional_string_fred("+Field Debris Type:")){
2393 fout("\n+Field Debris Type:");
2401 required_string_fred("$Average Speed:");
2403 fout(" %f", vm_vec_mag(&Asteroid_field.vel));
2405 required_string_fred("$Minimum:");
2407 save_vector(Asteroid_field.min_bound);
2409 required_string_fred("$Maximum:");
2411 save_vector(Asteroid_field.max_bound);
2413 if (Asteroid_field.has_inner_bound == 1) {
2414 if (optional_string_fred("+Inner Bound:")){
2417 fout("\n+Inner Bound:");
2420 required_string_fred("$Minimum:");
2422 save_vector(Asteroid_field.inner_min_bound);
2424 required_string_fred("$Maximum:");
2426 save_vector(Asteroid_field.inner_max_bound);
2433 int CFred_mission_save::save_music()
2435 required_string_fred("#Music");
2438 required_string_fred("$Event Music:");
2440 if (Current_soundtrack_num < 0)
2443 fout(" %s", Soundtracks[Current_soundtrack_num].name);
2445 required_string_fred("$Briefing Music:");
2447 if (Mission_music[SCORE_BRIEFING] < 0)
2450 fout(" %s", Spooled_music[Mission_music[SCORE_BRIEFING]].name);
2455 void CFred_mission_save::save_turret_info(ship_subsys *ptr, int ship)
2458 ship_weapon *wp = &ptr->weapons;
2460 if (wp->ai_class != Ship_info[Ships[ship].ship_info_index].ai_class) {
2461 if (optional_string_fred("+AI Class:", "$Name:", "+Subsystem:"))
2464 fout("\n+AI Class:");
2466 fout(" %s", Ai_class_names[wp->ai_class]);
2470 i = wp->num_primary_banks;
2472 if (wp->primary_bank_weapons[i] != ptr->system_info->primary_banks[i])
2476 if (optional_string_fred("+Primary Banks:", "$Name:", "+Subsystem:"))
2479 fout("\n+Primary Banks:");
2482 for (i=0; i<wp->num_primary_banks; i++)
2483 fout("\"%s\" ", Weapon_info[wp->primary_bank_weapons[i]].name);
2489 i = wp->num_secondary_banks;
2491 if (wp->secondary_bank_weapons[i] != ptr->system_info->secondary_banks[i])
2495 if (optional_string_fred("+Secondary Banks:", "$Name:", "+Subsystem:"))
2498 fout("\n+Secondary Banks:");
2501 for (i=0; i<wp->num_secondary_banks; i++)
2502 fout("\"%s\" ", Weapon_info[wp->secondary_bank_weapons[i]].name);
2508 i = wp->num_secondary_banks;
2510 if (wp->secondary_bank_ammo[i] != 100)
2514 if (optional_string_fred("+Sbank Ammo:", "$Name:", "+Subsystem:"))
2517 fout("\n+Sbank Ammo:");
2520 for (i=0; i<wp->num_secondary_banks; i++)
2521 fout("%d ", wp->secondary_bank_ammo[i]);
2527 int CFred_mission_save::save_campaign_file(char *pathname)
2531 Campaign_tree_formp->save_tree(); // flush all changes so they get saved.
2532 Campaign_tree_viewp->sort_elements();
2534 fred_parse_flag = 0;
2536 pathname = cf_add_ext(pathname, FS_CAMPAIGN_FILE_EXT);
2537 fp = cfopen(pathname, "wt", CFILE_NORMAL, CF_TYPE_MISSIONS);
2539 nprintf(("Error", "Can't open campaign file to save.\n"));
2543 required_string_fred("$Name:");
2545 fout(" %s", Campaign.name);
2547 Assert((Campaign.type >= 0) && (Campaign.type < MAX_CAMPAIGN_TYPES));
2548 required_string_fred("$Type:");
2550 fout(" %s", campaign_types[Campaign.type]);
2553 if (Campaign.desc) {
2554 required_string_fred("+Description:");
2557 fout_ext("%s", Campaign.desc);
2558 fout("\n$end_multi_text");
2561 if ( Campaign.type != CAMPAIGN_TYPE_SINGLE ) {
2562 required_string_fred("+Num Players:");
2564 fout(" %d", Campaign.num_players);
2567 // write out the ships and weapons which the player can start the campaign with
2568 optional_string_fred("+Starting Ships: (");
2570 for (i = 0; i < MAX_SHIP_TYPES; i++ ) {
2571 if ( Campaign.ships_allowed[i] )
2572 fout(" \"%s\" ", Ship_info[i].name );
2576 optional_string_fred("+Starting Weapons: (");
2578 for (i = 0; i < MAX_WEAPON_TYPES; i++ ) {
2579 if ( Campaign.weapons_allowed[i] )
2580 fout(" \"%s\" ", Weapon_info[i].name );
2584 fred_parse_flag = 0;
2585 for (i=0; i<Campaign.num_missions; i++) {
2587 required_string_either_fred("$Mission:", "#End");
2588 required_string_fred("$Mission:");
2590 fout(" %s", Campaign.missions[m].name);
2592 if ( strlen(Campaign.missions[i].briefing_cutscene) ) {
2593 if (optional_string_fred("+Briefing Cutscene:", "$Mission"))
2596 fout("\n+Briefing Cutscene:");
2598 fout( " %s", Campaign.missions[i].briefing_cutscene );
2601 required_string_fred("+Flags:", "$Mission:");
2603 fout(" %d", Campaign.missions[m].flags);
2605 // save campaign link sexp
2606 bool mission_loop = false;
2608 for (j=0; j<Total_links; j++) {
2609 if (Links[j].from == m) {
2611 if (optional_string_fred("+Formula:", "$Mission:"))
2614 fout("\n+Formula:");
2620 //save_campaign_sexp(Links[j].sexp, Campaign.missions[Links[j].to].name);
2621 if (Links[j].mission_loop) {
2622 mission_loop = true;
2624 save_campaign_sexp(Links[j].sexp, Links[j].to);
2633 // now save campaign loop sexp
2635 required_string_fred("\n+Mission Loop:");
2638 int num_mission_loop = 0;
2639 for (j=0; j<Total_links; j++) {
2640 if ( (Links[j].from == m) && (Links[j].mission_loop) ) {
2644 // maybe write out mission loop descript
2645 if ((num_mission_loop == 1) && Links[j].mission_loop_txt) {
2646 required_string_fred("+Mission Loop Text:");
2649 fout_ext("%s", Links[j].mission_loop_txt);
2650 fout("\n$end_multi_text");
2653 // maybe write out mission loop descript
2654 if ((num_mission_loop == 1) && Links[j].mission_loop_brief_anim) {
2655 required_string_fred("+Mission Loop Brief Anim:");
2658 fout_ext("%s", Links[j].mission_loop_brief_anim);
2659 fout("\n$end_multi_text");
2662 // maybe write out mission loop descript
2663 if ((num_mission_loop == 1) && Links[j].mission_loop_brief_sound) {
2664 required_string_fred("+Mission Loop Brief Sound:");
2667 fout_ext("%s", Links[j].mission_loop_brief_sound);
2668 fout("\n$end_multi_text");
2671 if (num_mission_loop == 1) {
2672 // write out mission loop formula
2673 fout("\n+Formula:");
2675 save_campaign_sexp(Links[j].sexp, Links[j].to);
2680 if (num_mission_loop > 1) {
2682 sprintf(buffer, "Multiple branching loop error from mission %s\nEdit campaign for *at most* 1 loop from each mission.", Campaign.missions[m].name);
2683 MessageBox((HWND)os_get_window(), buffer, "Error", MB_OK);
2687 if (optional_string_fred("+Level:", "$Mission:")){
2690 fout("\n\n+Level:");
2693 fout(" %d", Campaign.missions[m].level);
2695 if (optional_string_fred("+Position:", "$Mission:")){
2698 fout("\n+Position:");
2701 fout(" %d", Campaign.missions[m].pos);
2704 required_string_fred("#End");
2712 mprintf(("Campaign saving error code #%d", err));
2714 Campaign_wnd->error_checker();
2719 void CFred_mission_save::save_campaign_sexp(int node, int link_num)
2727 // if the link num is -1, then this is a end-of-campaign location
2728 if ( link_num != -1 ) {
2729 if (build_sexp_string(node, 2, SEXP_SAVE_MODE))
2730 fout(" (\n %s\n ( next-mission \"%s\" )\n )\n", out, Campaign.missions[link_num].name);
2732 fout(" ( %s( next-mission \"%s\" ) )\n", out, Campaign.missions[link_num].name);
2734 if (build_sexp_string(node, 2, SEXP_SAVE_MODE)){
2735 fout(" (\n %s\n ( end-of-campaign )\n )\n", out);
2737 fout(" ( %s( end-of-campaign ) )\n", out );