]> icculus.org git repositories - taylor/freespace2.git/blob - src/mission/missioncampaign.cpp
warning cleanup
[taylor/freespace2.git] / src / mission / missioncampaign.cpp
1 /*
2  * $Logfile: /Freespace2/code/Mission/MissionCampaign.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  *
7  * source for dealing with campaigns
8  *
9  * $Log$
10  * Revision 1.2  2002/06/02 04:26:34  relnev
11  * warning cleanup
12  *
13  * Revision 1.1.1.1  2002/05/03 03:28:09  root
14  * Initial import.
15  *
16  * 
17  * 23    9/14/99 4:35a Dave
18  * Argh. Added all kinds of code to handle potential crashes in debriefing
19  * code.
20  * 
21  * 22    9/09/99 11:40p Dave
22  * Handle an Assert() in beam code. Added supernova sounds. Play the right
23  * 2 end movies properly, based upon what the player did in the mission.
24  * 
25  * 21    9/09/99 9:34a Jefff
26  * fixed a potential exit-loop bug
27  * 
28  * 20    9/07/99 6:55p Jefff
29  * functionality to break out of a loop.  hacked functionality to jump to
30  * a specific mission in a campaign -- doesnt grant ships/weapons from
31  * skipped missions tho.
32  * 
33  * 19    9/07/99 2:19p Jefff
34  * clear skip mission player vars on mission skip
35  * 
36  * 18    9/06/99 9:45p Jefff
37  * break out of loop and skip mission support
38  * 
39  * 17    9/06/99 6:38p Dave
40  * Improved CD detection code.
41  * 
42  * 16    9/03/99 1:32a Dave
43  * CD checking by act. Added support to play 2 cutscenes in a row
44  * seamlessly. Fixed super low level cfile bug related to files in the
45  * root directory of a CD. Added cheat code to set campaign mission # in
46  * main hall.
47  * 
48  * 15    8/27/99 12:04a Dave
49  * Campaign loop screen.
50  * 
51  * 14    8/04/99 5:36p Andsager
52  * Show upsell screens at end of demo campaign before returning to main
53  * hall.
54  * 
55  * 13    2/05/99 3:50p Anoop
56  * Removed dumb campaign mission stats saving from multiplayer.
57  * 
58  * 12    12/17/98 2:43p Andsager
59  * Modify fred campaign save file to include optional mission loops
60  * 
61  * 11    12/12/98 3:17p Andsager
62  * Clean up mission eval, goal, event and mission scoring.
63  * 
64  * 10    12/10/98 9:59a Andsager
65  * Fix some bugs with mission loops
66  * 
67  * 9     12/09/98 1:56p Andsager
68  * Initial checkin of mission loop
69  * 
70  * 8     11/05/98 5:55p Dave
71  * Big pass at reducing #includes
72  * 
73  * 7     11/03/98 4:48p Johnson
74  * Fixed campaign file versioning bug left over from Silent Threat port.
75  * 
76  * 6     10/23/98 3:51p Dave
77  * Full support for tstrings.tbl and foreign languages. All that remains
78  * is to make it active in Fred.
79  * 
80  * 5     10/13/98 2:47p Andsager
81  * Remove reference to Tech_shivan_species_avail
82  * 
83  * 4     10/13/98 9:28a Dave
84  * Started neatening up freespace.h. Many variables renamed and
85  * reorganized. Added AlphaColors.[h,cpp]
86  * 
87  * 3     10/07/98 6:27p Dave
88  * Globalized mission and campaign file extensions. Removed Silent Threat
89  * special code. Moved \cache \players and \multidata into the \data
90  * directory.
91  * 
92  * 2     10/07/98 10:53a Dave
93  * Initial checkin.
94  * 
95  * 1     10/07/98 10:49a Dave
96  * 
97  * 95    9/10/98 1:17p Dave
98  * Put in code to flag missions and campaigns as being MD or not in Fred
99  * and Freespace. Put in multiplayer support for filtering out MD
100  * missions. Put in multiplayer popups for warning of non-valid missions.
101  * 
102  * 94    9/01/98 4:25p Dave
103  * Put in total (I think) backwards compatibility between mission disk
104  * freespace and non mission disk freespace, including pilot files and
105  * campaign savefiles.
106  * 
107  * 93    7/06/98 4:10p Hoffoss
108  * Fixed some bugs that presented themselves when trying to use a pilot
109  * that has a no longer existent campaign active.  Also expanded the
110  * campaign load code to actually return a proper error code, instead of
111  * always trapping errors internally and crashing, and always returning 0.
112  * 
113  * 92    6/17/98 9:30a Allender
114  * fixed red alert replay stats clearing problem
115  * 
116  * 91    6/01/98 11:43a John
117  * JAS & MK:  Classified all strings for localization.
118  * 
119  * 90    5/25/98 1:29p Allender
120  * end mission sequencing
121  * 
122  * 89    5/21/98 9:25p Allender
123  * endgame movie always viewable at end of campaign
124  * 
125  * 88    5/13/98 5:14p Allender
126  * red alert support to go back to previous mission
127  * 
128  * 87    5/12/98 4:16p Hoffoss
129  * Fixed bug where not all missions in all campaigns were being filtered
130  * out of stand alone mission listing in simulator room.
131  * 
132  * 86    5/05/98 3:29p Hoffoss
133  * Changed code so description is BEFORE num players in campaign file.
134  * Other code is relying on this ordering.
135  * 
136  * 85    5/05/98 12:19p Dave
137  * campaign description goes *after* num players
138  * 
139  * 84    5/04/98 5:52p Comet
140  * Fixed bug with Galatea/Bastion selection when finishing missions.
141  * 
142  * 83    5/01/98 2:46p Duncan
143  * fix a cfile problem with campaigns related to the new cfile stuff
144  * 
145  * 82    5/01/98 12:34p John
146  * Added code to force FreeSpace to run in the same dir as exe and made
147  * all the parse error messages a little nicer.
148  * 
149  * 81    4/30/98 7:01p Duncan
150  * AL: don't allow deletion of campaign files in multiplayer
151  * 
152  * 80    4/30/98 4:53p John
153  * Restructured and cleaned up cfile code.  Added capability to read off
154  * of CD-ROM drive and out of multiple pack files.
155  *
156  * $NoKeywords: $
157  */
158
159 #include <stdio.h>
160 #ifndef PLAT_UNIX
161 #include <direct.h>
162 #include <io.h>
163 #endif
164 #include <string.h>
165 #include <setjmp.h>
166 #include <errno.h>
167
168 #include "key.h"
169 #include "ui.h"
170 #include "missioncampaign.h"
171 #include "gamesequence.h"
172 #include "2d.h"
173 #include "parselo.h"
174 #include "missionload.h"
175 #include "freespace.h"
176 #include "sexp.h"
177 #include "cfile.h"
178 #include "player.h"
179 #include "missiongoals.h"
180 // #include "movie.h"
181 #include "multi.h"
182 #include "techmenu.h"
183 #include "eventmusic.h"
184 #include "alphacolors.h"
185 #include "localize.h"
186 #include "supernova.h"
187
188 // mission disk stuff
189 #define CAMPAIGN_SAVEFILE_MAX_SHIPS_OLD                                         75
190 #define CAMPAIGN_SAVEFILE_MAX_WEAPONS_OLD                                               44
191
192 #define CAMPAIGN_INITIAL_RELEASE_FILE_VERSION                           6
193
194 // campaign wasn't ended
195 int Campaign_ended_in_mission = 0;
196
197 // stuff for selecting campaigns.  We need to keep both arrays around since we display the
198 // list of campaigns by name, but must load campaigns by filename
199 char *Campaign_names[MAX_CAMPAIGNS];
200 char *Campaign_file_names[MAX_CAMPAIGNS];
201 int     Num_campaigns;
202
203 char *campaign_types[MAX_CAMPAIGN_TYPES] = 
204 {
205 //XSTR:OFF
206         "single",
207         "multi coop",
208         "multi teams"
209 //XSTR:ON
210 };
211
212 // modules local variables to deal with getting new ships/weapons available to the player
213 int Num_granted_ships, Num_granted_weapons;             // per mission counts of new ships and weapons
214 int Granted_ships[MAX_SHIP_TYPES];
215 int Granted_weapons[MAX_WEAPON_TYPES];
216
217 // variables to control the UI stuff for loading campaigns
218 LOCAL UI_WINDOW Campaign_window;
219 LOCAL UI_LISTBOX Campaign_listbox;
220 LOCAL UI_BUTTON Campaign_okb, Campaign_cancelb;
221
222 // the campaign!!!!!
223 campaign Campaign;
224
225 // variables with deal with the campaign save file
226 #define CAMPAIGN_FILE_VERSION                                                   12
227 //#define CAMPAIGN_FILE_COMPATIBLE_VERSION              CAMPAIGN_INITIAL_RELEASE_FILE_VERSION
228 #define CAMPAIGN_FILE_COMPATIBLE_VERSION                        CAMPAIGN_FILE_VERSION
229 #define CAMPAIGN_FILE_ID                                                                0xbeefcafe
230
231 // variables with deal with the campaign stats save file
232 #define CAMPAIGN_STATS_FILE_VERSION                                     1
233 #define CAMPAIGN_STATS_FILE_COMPATIBLE_VERSION  1
234 #define CAMPAIGN_STATS_FILE_ID                                          0xabbadaad
235
236 // mission_campaign_get_name returns a string (which is malloced in this routine) of the name
237 // of the given freespace campaign file.  In the type field, we return if the campaign is a single
238 // player or multiplayer campaign.  The type field will only be valid if the name returned is non-NULL
239 int mission_campaign_get_info(char *filename, char *name, int *type, int *max_players, char **desc)
240 {
241         int rval, i;
242         char campaign_type[NAME_LENGTH], fname[MAX_FILENAME_LEN];
243
244         Assert( name != NULL );
245         Assert( type != NULL );
246
247         // open localization
248         lcl_ext_open();
249
250         strcpy(fname, filename);
251         if ((strlen(fname) < 4) || stricmp(fname + strlen(fname) - 4, FS_CAMPAIGN_FILE_EXT)){
252                 strcat(fname, FS_CAMPAIGN_FILE_EXT);
253         }
254
255         Assert(strlen(fname) < MAX_FILENAME_LEN);
256
257         if ((rval = setjmp(parse_abort)) != 0) {
258                 if (rval == 5){
259                         // close localization
260                         lcl_ext_close();
261
262                         return 0;
263                 }
264
265                 Error(LOCATION, "Error parsing '%s'\r\nError code = %i.\r\n", fname, rval);
266
267         } else {
268                 read_file_text( fname );
269                 reset_parse();
270                 required_string("$Name:");
271
272                 stuff_string( name, F_NAME, NULL );
273                 if ( name == NULL ) {
274                         Int3();
275                         nprintf(("Warning", "No name found for campaign file %s\n", filename));
276
277                         // close localization
278                         lcl_ext_close();
279
280                         return 0;
281                 }
282
283                 required_string( "$Type:" );
284                 stuff_string( campaign_type, F_NAME, NULL );
285
286                 *type = -1;
287                 for (i=0; i<MAX_CAMPAIGN_TYPES; i++) {
288                         if ( !stricmp(campaign_type, campaign_types[i]) ) {
289                                 *type = i;
290                         }
291                 }
292
293                 if (desc) {
294                         *desc = NULL;
295                         if (optional_string("+Description:")) {
296                                 *desc = stuff_and_malloc_string(F_MULTITEXT, NULL, MISSION_DESC_LENGTH);
297                         }
298                 }
299
300                 // if this is a multiplayer campaign, get the max players
301                 if ((*type) > 0) {
302                         skip_to_string("+Num Players:");
303                         stuff_int(max_players);
304                 }               
305
306                 // if we found a valid campaign type
307                 if ((*type) >= 0) {
308                         // close localization
309                         lcl_ext_close();
310
311                         return 1;
312                 }
313         }
314
315         Int3();         // get Allender -- incorrect type found
316
317         // close localization
318         lcl_ext_close();
319
320         return 0;
321 }
322
323 // parses campaign and returns a list of missions in it.  Returns number of missions added to
324 // the 'list', and up to 'max' missions may be added to 'list'.  Returns negative on error.
325 //
326 int mission_campaign_get_mission_list(char *filename, char **list, int max)
327 {
328         int rval, i, num = 0;
329         char name[NAME_LENGTH];
330
331         filename = cf_add_ext(filename, FS_CAMPAIGN_FILE_EXT);
332
333         // read the mission file and get the list of mission filenames
334         if ((rval = setjmp(parse_abort)) != 0) {
335                 // since we can't return count of allocated elements, free them instead
336                 for (i=0; i<num; i++)
337                         free(list[i]);
338
339                 return -1;
340
341         } else {
342                 read_file_text(filename);
343                 reset_parse();
344
345                 while (skip_to_string("$Mission:") > 0) {
346                         stuff_string(name, F_NAME, NULL);
347                         if (num < max)
348                                 list[num++] = strdup(name);
349                 }
350         }
351
352         return num;
353 }
354
355 void mission_campaign_maybe_add( char *filename, int multiplayer )
356 {
357         char name[NAME_LENGTH];
358         int type,max_players;
359
360         if ( mission_campaign_get_info( filename, name, &type, &max_players) ) {
361                 if ( !multiplayer && ( type == CAMPAIGN_TYPE_SINGLE) ) {
362                         Campaign_names[Num_campaigns] = strdup(name);
363                         Campaign_file_names[Num_campaigns] = strdup(filename);
364                         Num_campaigns++;
365                 }
366         }
367 }
368
369 // mission_campaign_build_list() builds up the list of campaigns that the user might
370 // be able to pick from.  It uses the multiplayer flag to tell if we should display a list
371 // of single or multiplayer campaigns.  This routine sets the Num_campaigns and Campaign_names
372 // global variables
373 void mission_campaign_build_list( int multiplayer )
374 {
375 #ifdef PLAT_UNIX
376         STUB_FUNCTION;
377 #else
378         int find_handle;
379         _finddata_t find;
380         char wild_card[256];
381
382         Num_campaigns = 0;
383         mission_campaign_maybe_add( BUILTIN_CAMPAIGN, multiplayer);
384
385         memset(wild_card, 0, 256);
386         strcpy(wild_card, NOX("data\\missions\\*"));
387         strcat(wild_card, FS_CAMPAIGN_FILE_EXT);
388         find_handle = _findfirst( wild_card, &find );
389         if( find_handle != -1 ) {
390                 if ( !(find.attrib & _A_SUBDIR) && stricmp(find.name, BUILTIN_CAMPAIGN) ){
391                         mission_campaign_maybe_add( find.name, multiplayer);
392                 }
393
394                 while( !_findnext( find_handle, &find ) )       {
395                         if ( !(find.attrib & _A_SUBDIR) && stricmp(find.name, BUILTIN_CAMPAIGN) )       {
396                                 if ( Num_campaigns >= MAX_CAMPAIGNS ){
397                                         //MessageBox( -2,-2, 1, "Only the first 300 files will be displayed.", "Ok" );
398                                         break;
399                                 } else {
400                                         mission_campaign_maybe_add( find.name, multiplayer);
401                                 }
402                         }
403                 }
404         }
405 #endif
406 }
407
408 // gets optional ship/weapon information
409 void mission_campaign_get_sw_info()
410 {
411         int i, count, ship_list[MAX_SHIP_TYPES], weapon_list[MAX_WEAPON_TYPES];
412
413         // set allowable ships to the SIF_PLAYER_SHIPs
414         memset( Campaign.ships_allowed, 0, sizeof(Campaign.ships_allowed) );
415         for (i = 0; i < MAX_SHIP_TYPES; i++ ) {
416                 if ( Ship_info[i].flags & SIF_PLAYER_SHIP )
417                         Campaign.ships_allowed[i] = 1;
418         }
419
420         for (i = 0; i < MAX_WEAPON_TYPES; i++ )
421                 Campaign.weapons_allowed[i] = 1;
422
423         if ( optional_string("+Starting Ships:") ) {
424                 for (i = 0; i < MAX_SHIP_TYPES; i++ )
425                         Campaign.ships_allowed[i] = 0;
426
427                 count = stuff_int_list(ship_list, MAX_SHIP_TYPES, SHIP_INFO_TYPE);
428
429                 // now set the array elements stating which ships we are allowed
430                 for (i = 0; i < count; i++ ) {
431                         if ( Ship_info[ship_list[i]].flags & SIF_PLAYER_SHIP )
432                                 Campaign.ships_allowed[ship_list[i]] = 1;
433                 }
434         }
435
436         if ( optional_string("+Starting Weapons:") ) {
437                 for (i = 0; i < MAX_WEAPON_TYPES; i++ )
438                         Campaign.weapons_allowed[i] = 0;
439
440                 count = stuff_int_list(weapon_list, MAX_WEAPON_TYPES, WEAPON_POOL_TYPE);
441
442                 // now set the array elements stating which ships we are allowed
443                 for (i = 0; i < count; i++ )
444                         Campaign.weapons_allowed[weapon_list[i]] = 1;
445         }
446 }
447
448 // mission_campaign_load starts a new campaign.  It reads in the mission information in the campaign file
449 // It also sets up all variables needed inside of the game to deal with starting mission numbers, etc
450 //
451 // Note: Due to difficulties in generalizing this function, parts of it are duplicated throughout
452 // this file.  If you change the format of the campaign file, you should be sure these related
453 // functions work properly and update them if it breaks them.
454 int mission_campaign_load( char *filename, int load_savefile )
455 {
456         int len, rval, i;
457         char name[NAME_LENGTH], type[NAME_LENGTH];
458
459         filename = cf_add_ext(filename, FS_CAMPAIGN_FILE_EXT);
460
461         // open localization
462         lcl_ext_open(); 
463
464         // read the mission file and get the list of mission filenames
465         if ((rval = setjmp(parse_abort)) != 0) {
466                 mprintf(("Error parsing '%s'\r\nError code = %i.\r\n", filename, rval));
467
468                 // close localization
469                 lcl_ext_close();
470
471                 return CAMPAIGN_ERROR_CORRUPT;
472
473         } else {
474                 // be sure to remove all old malloced strings of Mission_names
475                 // we must also free any goal stuff that was from a previous campaign
476                 // this also frees sexpressions so the next call to init_sexp will be able to reclaim
477                 // nodes previously used by another campaign.
478                 mission_campaign_close();
479
480                 strcpy( Campaign.filename, filename );
481
482                 // only initialize the sexpression stuff when Fred isn't running.  It'll screw things up major
483                 // if it does
484                 if ( !Fred_running ){
485                         init_sexp();            // must initialize the sexpression stuff
486                 }
487
488                 read_file_text( filename );
489                 reset_parse();
490                 memset( &Campaign, 0, sizeof(Campaign) );
491
492                 // copy filename to campaign structure minus the extension
493                 len = strlen(filename) - 4;
494                 Assert(len < MAX_FILENAME_LEN);
495                 strncpy(Campaign.filename, filename, len);
496                 Campaign.filename[len] = 0;
497
498                 required_string("$Name:");
499                 stuff_string( name, F_NAME, NULL );
500                 
501                 //Store campaign name in the global struct
502                 strcpy( Campaign.name, name );
503
504                 required_string( "$Type:" );
505                 stuff_string( type, F_NAME, NULL );
506
507                 for (i = 0; i < MAX_CAMPAIGN_TYPES; i++ ) {
508                         if ( !stricmp(type, campaign_types[i]) ) {
509                                 Campaign.type = i;
510                                 break;
511                         }
512                 }
513
514                 if ( i == MAX_CAMPAIGN_TYPES )
515                         Error(LOCATION, "Unknown campaign type %s!", type);
516
517                 Campaign.desc = NULL;
518                 if (optional_string("+Description:"))
519                         Campaign.desc = stuff_and_malloc_string(F_MULTITEXT, NULL, MISSION_DESC_LENGTH);
520
521                 // if the type is multiplayer -- get the number of players
522                 Campaign.num_players = 0;
523                 if ( Campaign.type != CAMPAIGN_TYPE_SINGLE) {
524                         required_string("+Num players:");
525                         stuff_int( &(Campaign.num_players) );
526                 }               
527
528                 // parse the optional ship/weapon information
529                 mission_campaign_get_sw_info();
530
531                 // parse the mission file and actually read in the mission stuff
532                 Campaign.num_missions = 0;
533                 while ( required_string_either("#End", "$Mission:") ) {
534                         cmission *cm;
535
536                         required_string("$Mission:");
537                         stuff_string(name, F_NAME, NULL);
538                         cm = &Campaign.missions[Campaign.num_missions];
539                         cm->name = strdup(name);
540
541                         cm->briefing_cutscene[0] = 0;
542                         if ( optional_string("+Briefing Cutscene:") )
543                                 stuff_string( cm->briefing_cutscene, F_NAME, NULL );
544
545                         cm->flags = 0;
546                         if (optional_string("+Flags:"))
547                                 stuff_int(&cm->flags);
548
549                         cm->formula = -1;
550                         if ( optional_string("+Formula:") ) {
551                                 cm->formula = get_sexp_main();
552                                 if ( !Fred_running ) {
553                                         Assert ( cm->formula != -1 );
554                                         sexp_mark_persistent( cm->formula );
555
556                                 } else {
557                                         if ( cm->formula == -1 ){
558                                                 // close localization
559                                                 lcl_ext_close();
560
561                                                 return CAMPAIGN_ERROR_SEXP_EXHAUSTED;
562                                         }
563                                 }
564                         }
565
566                         // Do misison looping stuff
567                         cm->has_mission_loop = 0;
568                         if ( optional_string("+Mission Loop:") ) {
569                                 cm->has_mission_loop = 1;
570                         }
571
572                         cm->mission_loop_desc = NULL;
573                         if ( optional_string("+Mission Loop Text:")) {
574                                 cm->mission_loop_desc = stuff_and_malloc_string(F_MULTITEXT, NULL, MISSION_DESC_LENGTH);
575                         }
576
577                         cm->mission_loop_brief_anim = NULL;
578                         if ( optional_string("+Mission Loop Brief Anim:")) {
579                                 cm->mission_loop_brief_anim = stuff_and_malloc_string(F_MULTITEXT, NULL, MAX_FILENAME_LEN);
580                         }
581
582                         cm->mission_loop_brief_sound = NULL;
583                         if ( optional_string("+Mission Loop Brief Sound:")) {
584                                 cm->mission_loop_brief_sound = stuff_and_malloc_string(F_MULTITEXT, NULL, MAX_FILENAME_LEN);
585                         }
586
587                         cm->mission_loop_formula = -1;
588                         if ( optional_string("+Formula:") ) {
589                                 cm->mission_loop_formula = get_sexp_main();
590                                 if ( !Fred_running ) {
591                                         Assert ( cm->mission_loop_formula != -1 );
592                                         sexp_mark_persistent( cm->mission_loop_formula );
593
594                                 } else {
595                                         if ( cm->mission_loop_formula == -1 ){
596                                                 // close localization
597                                                 lcl_ext_close();
598
599                                                 return CAMPAIGN_ERROR_SEXP_EXHAUSTED;
600                                         }
601                                 }
602                         }
603
604                         if (optional_string("+Level:")) {
605                                 stuff_int( &cm->level );
606                                 if ( cm->level == 0 )  // check if the top (root) of the whole tree
607                                         Campaign.next_mission = Campaign.num_missions;
608
609                         } else
610                                 Campaign.realign_required = 1;
611
612                         if (optional_string("+Position:"))
613                                 stuff_int( &cm->pos );
614                         else
615                                 Campaign.realign_required = 1;
616
617                         if (Fred_running) {
618                                 cm->num_goals = -1;
619                                 cm->num_events = -1;
620                                 cm->notes = NULL;
621
622                         } else {
623                                 cm->num_goals = 0;
624                                 cm->num_events = 0;
625                         }
626
627                         cm->goals = NULL;
628                         cm->events = NULL;
629                         Campaign.num_missions++;
630                 }
631         }
632
633         // set up the other variables for the campaign stuff.  After initializing, we must try and load
634         // the campaign save file for this player.  Since all campaign loads go through this routine, I
635         // think this place should be the only necessary place to load the campaign save stuff.  The campaign
636         // save file will get written when a mission has ended by player choice.
637         Campaign.next_mission = 0;
638         Campaign.prev_mission = -1;
639         Campaign.current_mission = -1;  
640
641         // loading the campaign will get us to the current and next mission that the player must fly
642         // plus load all of the old goals that future missions might rely on.
643         if (!Fred_running && load_savefile && (Campaign.type == CAMPAIGN_TYPE_SINGLE)) {
644                 mission_campaign_savefile_load(Campaign.filename);
645         }
646
647         // close localization
648         lcl_ext_close();
649
650         return 0;
651 }
652
653 // mission_campaign_load_by_name() loads up a freespace campaign given the filename.  This routine
654 // is used to load up campaigns when a pilot file is loaded.  Generally, the
655 // filename will probably be the freespace campaign file, but not necessarily.
656 int mission_campaign_load_by_name( char *filename )
657 {
658         char name[NAME_LENGTH],test[5];
659         int type,max_players;
660
661         // make sure to tack on .fsc on the end if its not there already
662         if(strlen(filename) > 0){
663                 if(strlen(filename) > 4){
664                         strcpy(test,filename+(strlen(filename)-4));
665                         if(strcmp(test, FS_CAMPAIGN_FILE_EXT)!=0){
666                                 strcat(filename, FS_CAMPAIGN_FILE_EXT);
667                         }
668                 } else {
669                         strcat(filename, FS_CAMPAIGN_FILE_EXT);
670                 }
671         } else {
672                 Error(LOCATION,"Tried to load campaign file with illegal length/extension!");
673         }
674
675         if (!mission_campaign_get_info(filename, name, &type, &max_players)){
676                 return -1;      
677         }
678
679         Num_campaigns = 0;
680         Campaign_file_names[Num_campaigns] = filename;
681         Campaign_names[Num_campaigns] = name;
682         Num_campaigns++;
683         mission_campaign_load(filename);                
684         return 0;
685 }
686
687 int mission_campaign_load_by_name_csfe( char *filename, char *callsign )
688 {
689         Game_mode |= GM_NORMAL;
690         strcpy(Player->callsign, callsign);
691         return mission_campaign_load_by_name( filename);
692 }
693
694
695 // mission_campaign_init initializes some variables then loads the default Freespace single player campaign.
696 void mission_campaign_init()
697 {
698         memset(&Campaign, 0, sizeof(Campaign) );
699 }
700
701 // Fill in the root of the campaign save filename
702 void mission_campaign_savefile_generate_root(char *filename)
703 {
704 #ifdef PLAT_UNIX
705         STUB_FUNCTION;
706 #else
707         char base[_MAX_FNAME];
708
709         Assert ( strlen(Campaign.filename) != 0 );
710
711         // build up the filename for the save file.  There could be a problem with filename length,
712         // but this problem can get fixed in several ways -- ignore the problem for now though.
713         _splitpath( Campaign.filename, NULL, NULL, base, NULL );
714         Assert ( (strlen(base) + strlen(Player->callsign) + 1) < _MAX_FNAME );
715
716         sprintf( filename, NOX("%s.%s."), Player->callsign, base );
717 #endif
718 }
719
720 // mission_campaign_savefile_save saves the state of the campaign.  This function will probably always be called
721 // then the player is done flying a mission in the campaign path.  It will save the missions played, the
722 // state of the goals, etc.
723
724 int mission_campaign_savefile_save()
725 {
726         char filename[_MAX_FNAME];
727         CFILE *fp;
728         int i,j, mission_count;
729
730         memset(filename, 0, _MAX_FNAME);
731         mission_campaign_savefile_generate_root(filename);
732
733         // name the file differently depending on whether we're in single player or multiplayer mode
734         // single player : *.csg
735         strcat( filename, NOX("csg"));  
736
737         fp = cfopen(filename,"wb", CFILE_NORMAL, CF_TYPE_SINGLE_PLAYERS);
738
739         if (!fp)
740                 return errno;
741
742         // Write out campaign file info
743         cfwrite_int( CAMPAIGN_FILE_ID,fp );
744         cfwrite_int( CAMPAIGN_FILE_VERSION,fp );
745
746         // put in the file signature (single or multiplayer campaign) - see MissionCampaign.h for the #defines
747         cfwrite_int( CAMPAIGN_SINGLE_PLAYER_SIG, fp );
748
749         // do we need to write out the filename of the campaign?
750         cfwrite_string_len( Campaign.filename, fp );
751         cfwrite_int( Campaign.prev_mission, fp );
752         cfwrite_int( Campaign.next_mission, fp );
753         cfwrite_int( Campaign.loop_reentry, fp );
754         cfwrite_int( Campaign.loop_enabled, fp );
755
756         // write out the information for ships/weapons which this player is allowed to use
757         cfwrite_int(Num_ship_types, fp);
758         cfwrite_int(Num_weapon_types, fp);
759         for ( i = 0; i < Num_ship_types; i++ ){
760                 cfwrite_char( Campaign.ships_allowed[i], fp );
761         }
762
763         for ( i = 0; i < Num_weapon_types; i++ ){
764                 cfwrite_char( Campaign.weapons_allowed[i], fp );
765         }
766
767         // write out the completed mission matrix.  Used to tell which missions the player
768         // can replay in the simulator.  Also, each completed mission contains a list of the goals
769         // that were in the mission along with the goal completion status.
770         cfwrite_int( Campaign.num_missions_completed, fp );
771         for (i = 0; i < MAX_CAMPAIGN_MISSIONS; i++ ) {
772                 if ( Campaign.missions[i].completed ) {
773                         cfwrite_int( i, fp );
774                         cfwrite_int( Campaign.missions[i].num_goals, fp );
775                         for ( j = 0; j < Campaign.missions[i].num_goals; j++ ) {
776                                 cfwrite_string_len( Campaign.missions[i].goals[j].name, fp );
777                                 cfwrite_char( Campaign.missions[i].goals[j].status, fp );
778                         }
779                         cfwrite_int( Campaign.missions[i].num_events, fp );
780                         for ( j = 0; j < Campaign.missions[i].num_events; j++ ) {
781                                 cfwrite_string_len( Campaign.missions[i].events[j].name, fp );
782                                 cfwrite_char( Campaign.missions[i].events[j].status, fp );
783                         }
784
785                         // write flags
786                         cfwrite_int(Campaign.missions[i].flags, fp);
787                 }
788         }
789
790         cfclose( fp );
791
792         // 6/17/98
793         // ugh!  due to horrible bug, the stats saved at the end of every level were not written
794         // out to disk.  Write out a seperate file to do this.  We will only read it in if we actually
795         // find the file.
796         memset(filename, 0, _MAX_FNAME);
797         mission_campaign_savefile_generate_root(filename);
798
799         // name the file differently depending on whether we're in single player or multiplayer mode
800         // single player : *.csg
801         strcat( filename, NOX("css"));
802
803         fp = cfopen(filename,"wb", CFILE_NORMAL, CF_TYPE_SINGLE_PLAYERS);
804
805         if (!fp)
806                 return errno;
807
808         // Write out campaign file info
809         cfwrite_int( CAMPAIGN_STATS_FILE_ID,fp );
810         cfwrite_int( CAMPAIGN_STATS_FILE_VERSION,fp );
811
812         // determine how many missions we are saving -- I think that this method is safer than the method
813         // I used for release
814         mission_count = 0;
815         for ( i = 0; i < Campaign.num_missions; i++ ) {
816                 if ( Campaign.missions[i].completed ) {
817                         mission_count++;
818                 }
819         }
820
821         // write out the stats information to disk.     
822         cfwrite_int( mission_count, fp );
823         for (i = 0; i < Campaign.num_missions; i++ ) {
824                 if ( Campaign.missions[i].completed ) {
825                         cfwrite_int( i, fp );
826                         cfwrite( &Campaign.missions[i].stats, sizeof(scoring_struct), 1, fp );
827                 }
828         }
829
830         cfclose( fp );
831         
832         return 0;
833 }
834
835 // The following function always only ever ever ever called by CSFE!!!!!
836 int campaign_savefile_save(char *pname)
837 {
838         if (Campaign.type == CAMPAIGN_TYPE_SINGLE)
839                 Game_mode &= ~GM_MULTIPLAYER;
840         else
841                 Game_mode |= GM_MULTIPLAYER;
842
843         strcpy(Player->callsign, pname);
844         //memcpy(&Campaign, camp, sizeof(campaign));
845         return mission_campaign_savefile_save();
846 }
847
848
849 // the below two functions is internal to this module.  It is here so that I can group the save/load
850 // functions together.
851 //
852
853 // mission_campaign_savefile_delete deletes any save file in the players directory for the given
854 // campaign filename
855 void mission_campaign_savefile_delete( char *cfilename, int is_multi )
856 {
857 #ifdef PLAT_UNIX
858         STUB_FUNCTION;
859 #else
860         char filename[_MAX_FNAME], base[_MAX_FNAME];
861
862         _splitpath( cfilename, NULL, NULL, base, NULL );
863
864         if ( Player->flags & PLAYER_FLAGS_IS_MULTI ) {
865                 return; // no such thing as a multiplayer campaign savefile
866         }
867
868         sprintf( filename, NOX("%s.%s.csg"), Player->callsign, base );
869
870         cf_delete( filename, CF_TYPE_SINGLE_PLAYERS );
871 #endif
872 }
873
874 void campaign_delete_save( char *cfn, char *pname)
875 {
876         strcpy(Player->callsign, pname);
877         mission_campaign_savefile_delete(cfn);
878 }
879
880 // next function deletes all the save files for this particular pilot.  Just call cfile function
881 // which will delete multiple files
882 // Player_select_mode tells us whether we are deleting single or multiplayer files
883 void mission_campaign_delete_all_savefiles( char *pilot_name, int is_multi )
884 {
885         int dir_type, num_files, i;
886         char *names[MAX_CAMPAIGNS], spec[MAX_FILENAME_LEN + 2], *ext;
887         char filename[1024];
888         int (*filter_save)(char *filename);
889
890         if ( is_multi ) {
891                 return;                         // can't have multiplayer campaign save files
892         }
893
894         ext = NOX(".csg");
895         dir_type = CF_TYPE_SINGLE_PLAYERS;
896
897         sprintf(spec, NOX("%s.*%s"), pilot_name, ext);
898
899         // HACK HACK HACK HACK!!!!  cf_get_file_list is not reentrant.  Pretty dumb because it should
900         // be.  I have to save any file filters
901         filter_save = Get_file_list_filter;
902         Get_file_list_filter = NULL;
903         num_files = cf_get_file_list(MAX_CAMPAIGNS, names, dir_type, spec);
904         Get_file_list_filter = filter_save;
905
906         for (i=0; i<num_files; i++) {
907                 strcpy(filename, names[i]);
908                 strcat(filename, ext);
909                 cf_delete(filename, dir_type);
910                 free(names[i]);
911         }
912 }
913
914 // mission_campaign_savefile_load takes a filename of a campaign file as a parameter and loads all
915 // of the information stored in the campaign file.
916 void mission_campaign_savefile_load( char *cfilename )
917 {
918 #ifdef PLAT_UNIX
919         STUB_FUNCTION;
920 #else
921         char filename[_MAX_FNAME], base[_MAX_FNAME];
922         int id, version, i, num, j, num_stats_blocks;
923         int type_sig;
924         CFILE *fp;
925
926         Assert ( strlen(cfilename) != 0 );
927
928         // probably only called from single player games anymore!!! should be anyway
929         Assert( Game_mode & GM_NORMAL );                // get allender or DaveB.  trying to save campaign in multiplayer
930
931         // build up the filename for the save file.  There could be a problem with filename length,
932         // but this problem can get fixed in several ways -- ignore the problem for now though.
933         _splitpath( cfilename, NULL, NULL, base, NULL );
934         Assert ( (strlen(base) + strlen(Player->callsign) + 1) < _MAX_FNAME );
935
936         if(Game_mode & GM_MULTIPLAYER)
937                 sprintf( filename, NOX("%s.%s.msg"), Player->callsign, base );
938         else
939                 sprintf( filename, NOX("%s.%s.csg"), Player->callsign, base );
940
941         fp = cfopen(filename, "rb", CFILE_NORMAL, CF_TYPE_SINGLE_PLAYERS );
942         if ( !fp )
943                 return;
944
945         id = cfread_int( fp );
946         if ( id != CAMPAIGN_FILE_ID ) {
947                 Warning(LOCATION, "Campaign save file has invalid signature");
948                 cfclose( fp );
949                 return;
950         }
951
952         version = cfread_int( fp );
953         if ( version < CAMPAIGN_FILE_COMPATIBLE_VERSION ) {
954                 Warning(LOCATION, "Campaign save file too old -- not compatible.  Deleting file.\nYou can continue from here without trouble\n\n");
955                 cfclose( fp );
956                 cf_delete( filename, CF_TYPE_SINGLE_PLAYERS );
957                 return;
958         }
959
960         // verify that we are loading the correct type of campaign file for the mode that we are in.
961         if(version >= 3)
962                 type_sig = cfread_int( fp );
963         else
964                 type_sig = CAMPAIGN_SINGLE_PLAYER_SIG;
965         // the actual check
966         Assert( ((Game_mode & GM_MULTIPLAYER) && (type_sig==CAMPAIGN_MULTI_PLAYER_SIG)) || (!(Game_mode & GM_MULTIPLAYER) && (type_sig==CAMPAIGN_SINGLE_PLAYER_SIG)) );
967
968         Campaign.type = type_sig == CAMPAIGN_SINGLE_PLAYER_SIG ? CAMPAIGN_TYPE_SINGLE : CAMPAIGN_TYPE_MULTI_COOP;
969
970         // read in the filename of the campaign and compare the filenames to be sure that
971         // we are reading data that really belongs to this campaign.  I think that this check
972         // is redundant.
973         cfread_string_len( filename, _MAX_FNAME, fp );
974         /*if ( stricmp( filename, cfilename) ) {        //      Used to be !stricmp.  How did this ever work? --MK, 11/9/97
975                 Warning(LOCATION, "Campaign save file appears corrupt because of mismatching filenames.");
976                 cfclose(fp);
977                 return;
978         }*/
979
980         Campaign.prev_mission = cfread_int( fp );
981         Campaign.next_mission = cfread_int( fp );
982         Campaign.loop_reentry = cfread_int( fp );
983         Campaign.loop_enabled = cfread_int( fp );
984
985         //  load information about ships/weapons allowed        
986         int ship_count, weapon_count;
987
988         // if earlier than mission disk version, use old MAX_SHIP_TYPES, otherwise read from the file           
989         if(version <= CAMPAIGN_INITIAL_RELEASE_FILE_VERSION){
990                 ship_count = CAMPAIGN_SAVEFILE_MAX_SHIPS_OLD;
991                 weapon_count = CAMPAIGN_SAVEFILE_MAX_WEAPONS_OLD;
992         } else {
993                 ship_count = cfread_int(fp);
994                 weapon_count = cfread_int(fp);
995         }
996
997         for ( i = 0; i < ship_count; i++ ){
998                 Campaign.ships_allowed[i] = cfread_ubyte( fp );
999         }
1000
1001         for ( i = 0; i < weapon_count; i++ ){
1002                 Campaign.weapons_allowed[i] = cfread_ubyte( fp );
1003         }       
1004
1005         // read in the completed mission matrix.  Used to tell which missions the player
1006         // can replay in the simulator.  Also, each completed mission contains a list of the goals
1007         // that were in the mission along with the goal completion status.
1008         Campaign.num_missions_completed = cfread_int( fp );
1009         for (i = 0; i < Campaign.num_missions_completed; i++ ) {
1010                 num = cfread_int( fp );
1011                 Campaign.missions[num].completed = 1;
1012                 Campaign.missions[num].num_goals = cfread_int( fp );
1013                 
1014                 // be sure to malloc out space for the goals stuff, then zero the memory!!!  Don't do malloc
1015                 // if there are no goals
1016                 Campaign.missions[num].goals = (mgoal *)malloc( Campaign.missions[num].num_goals * sizeof(mgoal) );
1017                 if ( Campaign.missions[num].num_goals > 0 ) {
1018                         memset( Campaign.missions[num].goals, 0, sizeof(mgoal) * Campaign.missions[num].num_goals );
1019                         Assert( Campaign.missions[num].goals != NULL );
1020                 }
1021
1022                 // now read in the goal information for this mission
1023                 for ( j = 0; j < Campaign.missions[num].num_goals; j++ ) {
1024                         cfread_string_len( Campaign.missions[num].goals[j].name, NAME_LENGTH, fp );
1025                         Campaign.missions[num].goals[j].status = cfread_char( fp );
1026                 }
1027
1028                 // get the events from the savefile
1029                 Campaign.missions[num].num_events = cfread_int( fp );
1030                 
1031                 // be sure to malloc out space for the events stuff, then zero the memory!!!  Don't do malloc
1032                 // if there are no events
1033 //              if (Campaign.missions[num].events < 0)
1034 //                      Campaign.missions[num].events = 0;
1035                 Campaign.missions[num].events = (mevent *)malloc( Campaign.missions[num].num_events * sizeof(mevent) );
1036                 if ( Campaign.missions[num].num_events > 0 ) {
1037                         memset( Campaign.missions[num].events, 0, sizeof(mevent) * Campaign.missions[num].num_events );
1038                         Assert( Campaign.missions[num].events != NULL );
1039                 }
1040                 
1041                 // now read in the event information for this mission
1042                 for ( j = 0; j < Campaign.missions[num].num_events; j++ ) {
1043                         cfread_string_len( Campaign.missions[num].events[j].name, NAME_LENGTH, fp );
1044                         Campaign.missions[num].events[j].status = cfread_char( fp );
1045                 }
1046
1047                 // now read flags
1048                 Campaign.missions[num].flags = cfread_int(fp);
1049         }       
1050
1051         cfclose( fp );
1052
1053         // 4/17/98
1054         // now, try and read in the campaign stats saved information.  This code was added for the 1.03 patch
1055         // since the stats data was never written out to disk.  We try and open the file, and if we cannot find
1056         // it, then simply return
1057         sprintf( filename, NOX("%s.%s.css"), Player->callsign, base );
1058
1059         fp = cfopen(filename, "rb", CFILE_NORMAL, CF_TYPE_SINGLE_PLAYERS );
1060         if ( !fp )
1061                 return;
1062
1063         id = cfread_int( fp );
1064         if ( id != CAMPAIGN_STATS_FILE_ID ) {
1065                 Warning(LOCATION, "Campaign stats save file has invalid signature");
1066                 cfclose( fp );
1067                 return;
1068         }
1069
1070         version = cfread_int( fp );
1071         if ( version < CAMPAIGN_STATS_FILE_COMPATIBLE_VERSION ) {
1072                 Warning(LOCATION, "Campaign save file too old -- not compatible.  Deleting file.\nYou can continue from here without trouble\n\n");
1073                 cfclose( fp );
1074                 cf_delete( filename, CF_TYPE_SINGLE_PLAYERS );
1075                 return;
1076         }
1077
1078         num_stats_blocks = cfread_int( fp );
1079         for (i = 0; i < num_stats_blocks; i++ ) {
1080                 num = cfread_int( fp );
1081                 cfread( &Campaign.missions[num].stats, sizeof(scoring_struct), 1, fp );
1082         }
1083
1084         cfclose(fp);
1085
1086 #endif
1087 }
1088
1089 // the following code only ever called by CSFE!!!!
1090 void campaign_savefile_load(char *fname, char *pname)
1091 {
1092         if (Campaign.type==CAMPAIGN_TYPE_SINGLE) {
1093                 Game_mode &= ~GM_MULTIPLAYER;
1094                 Game_mode &= GM_NORMAL;
1095         }
1096         else
1097                 Game_mode |= GM_MULTIPLAYER;
1098         strcpy(Player->callsign, pname);
1099         mission_campaign_savefile_load(fname);
1100 }
1101
1102 // mission_campaign_next_mission sets up the internal veriables of the campaign
1103 // structure so the player can play the next mission.  If there are no more missions
1104 // available in the campaign, this function returns -1, else 0 if the mission was
1105 // set successfully
1106 int mission_campaign_next_mission()
1107 {
1108         if ( (Campaign.next_mission == -1) || (strlen(Campaign.name) == 0) ) // will be set to -1 when there is no next mission
1109                 return -1;
1110
1111         Campaign.current_mission = Campaign.next_mission;       
1112         strncpy( Game_current_mission_filename, Campaign.missions[Campaign.current_mission].name, MAX_FILENAME_LEN );
1113
1114         // check for end of loop.
1115         if (Campaign.current_mission == Campaign.loop_reentry) {
1116                 Campaign.loop_enabled = 0;
1117         }
1118
1119         // reset the number of persistent ships and weapons for the next campaign mission
1120         Num_granted_ships = 0;
1121         Num_granted_weapons = 0;
1122         return 0;
1123 }
1124
1125 // mission_campaign_previous_mission() gets called to go to the previous mission in
1126 // the campaign.  Used only for Red Alert missions
1127 int mission_campaign_previous_mission()
1128 {
1129         if ( !(Game_mode & GM_CAMPAIGN_MODE) )
1130                 return 0;
1131
1132         if ( Campaign.prev_mission == -1 )
1133                 return 0;
1134
1135         Campaign.current_mission = Campaign.prev_mission;
1136         Campaign.next_mission = Campaign.current_mission;
1137         Campaign.num_missions_completed--;
1138         Campaign.missions[Campaign.next_mission].completed = 0;
1139         mission_campaign_savefile_save();
1140
1141         // reset the player stats to be the stats from this level
1142         memcpy( &Player->stats, &Campaign.missions[Campaign.current_mission].stats, sizeof(Player->stats) );
1143
1144         strncpy( Game_current_mission_filename, Campaign.missions[Campaign.current_mission].name, MAX_FILENAME_LEN );
1145         Num_granted_ships = 0;
1146         Num_granted_weapons = 0;
1147
1148         return 1;
1149 }
1150
1151 /*
1152 // determine what the next mission is after the current one.  Because this evaluates an sexp,
1153 // and that could check just about anything, the results are only going to be valid in
1154 // certain places.
1155 // DA 12/09/98 -- To allow for mission loops, need to maintain call with store stats
1156 int mission_campaign_eval_next_mission( int store_stats )
1157 {
1158         char *name;
1159         int cur, i;
1160         cmission *mission;
1161
1162         Campaign.next_mission = -1;
1163         cur = Campaign.current_mission;
1164         name = Campaign.missions[cur].name;
1165
1166         mission = &Campaign.missions[cur];
1167
1168         // first we must save the status of the current missions goals in the campaign mission structure.
1169         // After that, we can determine which mission is tagged as the next mission.  Finally, we
1170         // can save the campaign save file
1171         // we might have goal and event status if the player replayed a mission
1172         if ( mission->num_goals > 0 ) {
1173                 free( mission->goals );
1174         }
1175
1176         mission->num_goals = Num_goals;
1177         if ( mission->num_goals > 0 ) {
1178                 mission->goals = (mgoal *)malloc( sizeof(mgoal) * Num_goals );
1179                 Assert( mission->goals != NULL );
1180         }
1181
1182         // copy the needed info from the Mission_goal struct to our internal structure
1183         for (i = 0; i < Num_goals; i++ ) {
1184                 if ( strlen(Mission_goals[i].name) == 0 ) {
1185                         char name[NAME_LENGTH];
1186
1187                         sprintf(name, NOX("Goal #%d"), i);
1188                         //Warning(LOCATION, "Mission goal in mission %s must have a +Name field! using %s for campaign save file\n", mission->name, name);
1189                         strcpy( mission->goals[i].name, name);
1190                 } else
1191                         strcpy( mission->goals[i].name, Mission_goals[i].name );
1192                 Assert ( Mission_goals[i].satisfied != GOAL_INCOMPLETE );               // should be true or false at this point!!!
1193                 mission->goals[i].status = (char)Mission_goals[i].satisfied;
1194         }
1195
1196         // do the same thing for events as we did for goals
1197         // we might have goal and event status if the player replayed a mission
1198         if ( mission->num_events > 0 ) {
1199                 free( mission->events );
1200         }
1201
1202         mission->num_events = Num_mission_events;
1203         if ( mission->num_events > 0 ) {
1204                 mission->events = (mevent *)malloc( sizeof(mevent) * Num_mission_events );
1205                 Assert( mission->events != NULL );
1206         }
1207
1208         // copy the needed info from the Mission_goal struct to our internal structure
1209         for (i = 0; i < Num_mission_events; i++ ) {
1210                 if ( strlen(Mission_events[i].name) == 0 ) {
1211                         char name[NAME_LENGTH];
1212
1213                         sprintf(name, NOX("Event #%d"), i);
1214                         nprintf(("Warning", "Mission goal in mission %s must have a +Name field! using %s for campaign save file\n", mission->name, name));
1215                         strcpy( mission->events[i].name, name);
1216                 } else
1217                         strcpy( mission->events[i].name, Mission_events[i].name );
1218
1219                 // getting status for the events is a little different.  If the formula value for the event entry
1220                 // is -1, then we know the value of the result field will never change.  If the formula is
1221                 // not -1 (i.e. still being evaluated at mission end time), we will write "incomplete" for the
1222                 // event evaluation
1223                 if ( Mission_events[i].formula == -1 ) {
1224                         if ( Mission_events[i].result )
1225                                 mission->events[i].status = EVENT_SATISFIED;
1226                         else
1227                                 mission->events[i].status = EVENT_FAILED;
1228                 } else
1229                         Int3();
1230         }
1231
1232         // maybe store the alltime stats which would be current at the end of this mission
1233         if ( store_stats ) {
1234                 memcpy( &mission->stats, &Player->stats, sizeof(Player->stats) );
1235                 scoring_backout_accept( &mission->stats );
1236         }
1237
1238         if ( store_stats ) {    // second (last) time through, so use choose loop_mission if chosen
1239                 if ( Campaign.loop_enabled ) {
1240                         Campaign.next_mission = Campaign.loop_mission;
1241                 } else {
1242                         // evaluate next mission (straight path)
1243                         if (Campaign.missions[cur].formula != -1) {
1244                                 flush_sexp_tree(Campaign.missions[cur].formula);  // force formula to be re-evaluated
1245                                 eval_sexp(Campaign.missions[cur].formula);  // this should reset Campaign.next_mission to proper value
1246                         }
1247                 }
1248         } else {
1249
1250                 // evaluate next mission (straight path)
1251                 if (Campaign.missions[cur].formula != -1) {
1252                         flush_sexp_tree(Campaign.missions[cur].formula);  // force formula to be re-evaluated
1253                         eval_sexp(Campaign.missions[cur].formula);  // this should reset Campaign.next_mission to proper value
1254                 }
1255
1256                 // evaluate mission loop mission (if any) so it can be used if chosen
1257                 if ( Campaign.missions[cur].has_mission_loop ) {
1258                         int copy_next_mission = Campaign.next_mission;
1259                         // Set temporarily to -1 so we know if loop formula fails to assign
1260                         Campaign.next_mission = -1;  // Cannot exit campaign from loop
1261                         if (Campaign.missions[cur].mission_loop_formula != -1) {
1262                                 flush_sexp_tree(Campaign.missions[cur].mission_loop_formula);  // force formula to be re-evaluated
1263                                 eval_sexp(Campaign.missions[cur].mission_loop_formula);  // this should reset Campaign.next_mission to proper value
1264                         }
1265
1266                         Campaign.loop_mission = Campaign.next_mission;
1267                         Campaign.next_mission = copy_next_mission;
1268                 }
1269         }
1270
1271         if (Campaign.next_mission == -1)
1272                 nprintf(("allender", "No next mission to proceed to.\n"));
1273         else
1274                 nprintf(("allender", "Next mission is number %d [%s]\n", Campaign.next_mission, Campaign.missions[Campaign.next_mission].name));
1275
1276         return Campaign.next_mission;
1277 } */
1278
1279 // Evaluate next campaign mission - set as Campaign.next_mission.  Also set Campaign.loop_mission
1280 void mission_campaign_eval_next_mission()
1281 {
1282         Campaign.next_mission = -1;
1283         int cur = Campaign.current_mission;
1284
1285         // evaluate next mission (straight path)
1286         if (Campaign.missions[cur].formula != -1) {
1287                 flush_sexp_tree(Campaign.missions[cur].formula);  // force formula to be re-evaluated
1288                 eval_sexp(Campaign.missions[cur].formula);  // this should reset Campaign.next_mission to proper value
1289         }
1290
1291         // evaluate mission loop mission (if any) so it can be used if chosen
1292         if ( Campaign.missions[cur].has_mission_loop ) {
1293                 int copy_next_mission = Campaign.next_mission;
1294                 // Set temporarily to -1 so we know if loop formula fails to assign
1295                 Campaign.next_mission = -1;
1296                 if (Campaign.missions[cur].mission_loop_formula != -1) {
1297                         flush_sexp_tree(Campaign.missions[cur].mission_loop_formula);  // force formula to be re-evaluated
1298                         eval_sexp(Campaign.missions[cur].mission_loop_formula);  // this should reset Campaign.next_mission to proper value
1299                 }
1300
1301                 Campaign.loop_mission = Campaign.next_mission;
1302                 Campaign.next_mission = copy_next_mission;
1303         }
1304
1305         if (Campaign.next_mission == -1) {
1306                 nprintf(("allender", "No next mission to proceed to.\n"));
1307         } else {
1308                 nprintf(("allender", "Next mission is number %d [%s]\n", Campaign.next_mission, Campaign.missions[Campaign.next_mission].name));
1309         }
1310
1311 }
1312
1313 // Store mission's goals and events in Campaign struct
1314 void mission_campaign_store_goals_and_events()
1315 {
1316         char *name;
1317         int cur, i;
1318         cmission *mission;
1319
1320         cur = Campaign.current_mission;
1321         name = Campaign.missions[cur].name;
1322
1323         mission = &Campaign.missions[cur];
1324
1325         // first we must save the status of the current missions goals in the campaign mission structure.
1326         // After that, we can determine which mission is tagged as the next mission.  Finally, we
1327         // can save the campaign save file
1328         // we might have goal and event status if the player replayed a mission
1329         if ( mission->num_goals > 0 ) {
1330                 free( mission->goals );
1331         }
1332
1333         mission->num_goals = Num_goals;
1334         if ( mission->num_goals > 0 ) {
1335                 mission->goals = (mgoal *)malloc( sizeof(mgoal) * Num_goals );
1336                 Assert( mission->goals != NULL );
1337         }
1338
1339         // copy the needed info from the Mission_goal struct to our internal structure
1340         for (i = 0; i < Num_goals; i++ ) {
1341                 if ( strlen(Mission_goals[i].name) == 0 ) {
1342                         char name[NAME_LENGTH];
1343
1344                         sprintf(name, NOX("Goal #%d"), i);
1345                         //Warning(LOCATION, "Mission goal in mission %s must have a +Name field! using %s for campaign save file\n", mission->name, name);
1346                         strcpy( mission->goals[i].name, name);
1347                 } else
1348                         strcpy( mission->goals[i].name, Mission_goals[i].name );
1349                 Assert ( Mission_goals[i].satisfied != GOAL_INCOMPLETE );               // should be true or false at this point!!!
1350                 mission->goals[i].status = (char)Mission_goals[i].satisfied;
1351         }
1352
1353         // do the same thing for events as we did for goals
1354         // we might have goal and event status if the player replayed a mission
1355         if ( mission->num_events > 0 ) {
1356                 free( mission->events );
1357         }
1358
1359         mission->num_events = Num_mission_events;
1360         if ( mission->num_events > 0 ) {
1361                 mission->events = (mevent *)malloc( sizeof(mevent) * Num_mission_events );
1362                 Assert( mission->events != NULL );
1363         }
1364
1365         // copy the needed info from the Mission_goal struct to our internal structure
1366         for (i = 0; i < Num_mission_events; i++ ) {
1367                 if ( strlen(Mission_events[i].name) == 0 ) {
1368                         char name[NAME_LENGTH];
1369
1370                         sprintf(name, NOX("Event #%d"), i);
1371                         nprintf(("Warning", "Mission goal in mission %s must have a +Name field! using %s for campaign save file\n", mission->name, name));
1372                         strcpy( mission->events[i].name, name);
1373                 } else
1374                         strcpy( mission->events[i].name, Mission_events[i].name );
1375
1376                 // getting status for the events is a little different.  If the formula value for the event entry
1377                 // is -1, then we know the value of the result field will never change.  If the formula is
1378                 // not -1 (i.e. still being evaluated at mission end time), we will write "incomplete" for the
1379                 // event evaluation
1380                 if ( Mission_events[i].formula == -1 ) {
1381                         if ( Mission_events[i].result )
1382                                 mission->events[i].status = EVENT_SATISFIED;
1383                         else
1384                                 mission->events[i].status = EVENT_FAILED;
1385                 } else
1386                         Int3();
1387         }
1388 }
1389
1390 // this function is called when the player's mission is over.  It updates the internal store of goals
1391 // and their status then saves the state of the campaign in the campaign file.  This gets called
1392 // after player accepts mission results in debriefing.
1393 void mission_campaign_mission_over()
1394 {
1395         int mission_num, i;
1396         cmission *mission;
1397
1398         // I don't think that we should have a record for these -- maybe we might??????  If we do,
1399         // then we should free them
1400         if ( !(Game_mode & GM_CAMPAIGN_MODE) ){
1401                 return;
1402         }
1403
1404         mission_num = Campaign.current_mission;
1405         Assert( mission_num != -1 );
1406         mission = &Campaign.missions[mission_num];
1407
1408         // determine if any ships/weapons were granted this mission
1409         for ( i=0; i<Num_granted_ships; i++ ){
1410                 Campaign.ships_allowed[Granted_ships[i]] = 1;
1411         }
1412
1413         for ( i=0; i<Num_granted_weapons; i++ ){
1414                 Campaign.weapons_allowed[Granted_weapons[i]] = 1;       
1415         }
1416
1417         // DKA 12/11/98 - Unneeded already evaluated and stored
1418         // determine what new mission we are moving to.
1419         //      mission_campaign_eval_next_mission(1);
1420
1421         // update campaign.mission stats (used to allow backout inRedAlert)
1422         memcpy( &mission->stats, &Player->stats, sizeof(Player->stats) );
1423         if(!(Game_mode & GM_MULTIPLAYER)){
1424                 scoring_backout_accept( &mission->stats );
1425         }
1426
1427         // if we are moving to a new mission, then change our data.  If we are staying on the same mission,
1428         // then we don't want to do anything.  Remove information about goals/events
1429         if ( Campaign.next_mission != mission_num ) {
1430                 Campaign.prev_mission = mission_num;
1431                 Campaign.current_mission = -1;
1432                 Campaign.num_missions_completed++;
1433                 Campaign.missions[mission_num].completed = 1;
1434
1435                 // save the scoring values from the previous mission at the start of this mission -- for red alert
1436
1437                 // save the state of the campaign in the campaign save file and move to the end_game state
1438                 if (Campaign.type == CAMPAIGN_TYPE_SINGLE) {
1439                         mission_campaign_savefile_save();
1440                 }
1441
1442         } else {
1443                 // free up the goals and events which were just malloced.  It's kind of like erasing any fact
1444                 // that the player played this mission in the campaign.
1445                 free( mission->goals );
1446                 mission->num_goals = 0;
1447
1448                 free( mission->events );
1449                 mission->num_events = 0;
1450
1451                 Sexp_nodes[mission->formula].value = SEXP_UNKNOWN;
1452         }
1453
1454         Assert(Player);
1455         if (Campaign.missions[Campaign.next_mission].flags & CMISSION_FLAG_BASTION){
1456                 Player->on_bastion = 1;
1457         } else {
1458                 Player->on_bastion = 0;
1459         }
1460
1461         mission_campaign_next_mission();                        // sets up whatever needs to be set to actually play next mission
1462 }
1463
1464 // called when the game closes -- to get rid of memory errors for Bounds checker
1465 void mission_campaign_close()
1466 {
1467         int i;
1468
1469         if (Campaign.desc)
1470                 free(Campaign.desc);
1471
1472         // be sure to remove all old malloced strings of Mission_names
1473         // we must also free any goal stuff that was from a previous campaign
1474         for ( i=0; i<Campaign.num_missions; i++ ) {
1475                 if ( Campaign.missions[i].name ){
1476                         free(Campaign.missions[i].name);
1477                 }
1478
1479                 if (Campaign.missions[i].notes){
1480                         free(Campaign.missions[i].notes);
1481                 }
1482
1483                 if ( Campaign.missions[i].num_goals > 0 ){
1484                         free ( Campaign.missions[i].goals );
1485                 }
1486
1487                 if ( Campaign.missions[i].num_events > 0 ){
1488                         free ( Campaign.missions[i].events );
1489                 }
1490
1491                 if ( !Fred_running ){
1492                         sexp_unmark_persistent(Campaign.missions[i].formula);           // free any sexpression nodes used by campaign.
1493                 }
1494
1495                 Campaign.missions[i].num_goals = 0;
1496                 Campaign.missions[i].num_events = 0;
1497         }
1498 }
1499
1500 // extract the mission filenames for a campaign.  
1501 //
1502 // filename     =>      name of campaign file
1503 //      dest            => storage for the mission filename, must be already allocated
1504 // num          => output parameter for the number of mission filenames in the campaign
1505 //
1506 // note that dest should allocate at least dest[MAX_CAMPAIGN_MISSIONS][NAME_LENGTH]
1507 int mission_campaign_get_filenames(char *filename, char dest[][NAME_LENGTH], int *num)
1508 {
1509         int     rval;
1510
1511         // read the mission file and get the list of mission filenames
1512         if ((rval = setjmp(parse_abort)) != 0) {
1513                 return rval;
1514
1515         } else {
1516                 read_file_text(filename);
1517                 Assert(strlen(filename) < MAX_FILENAME_LEN - 1);  // make sure no overflow
1518
1519                 reset_parse();
1520                 required_string("$Name:");
1521                 advance_to_eoln(NULL);
1522
1523                 required_string( "$Type:" );
1524                 advance_to_eoln(NULL);
1525
1526                 // parse the mission file and actually read in the mission stuff
1527                 *num = 0;
1528                 while ( skip_to_string("$Mission:") == 1 ) {
1529                         stuff_string(dest[*num], F_NAME, NULL);
1530                         (*num)++;
1531                 }
1532         }
1533
1534         return 0;
1535 }
1536
1537 // function to read the goals and events from a mission in a campaign file and store that information
1538 // in the campaign structure for use in the campaign editor, error checking, etc
1539 void read_mission_goal_list(int num)
1540 {
1541         char *filename, notes[NOTES_LENGTH], goals[MAX_GOALS][NAME_LENGTH];
1542         char events[MAX_MISSION_EVENTS][NAME_LENGTH];
1543         int i, z, r, event_count, count = 0;
1544
1545         filename = Campaign.missions[num].name;
1546         if ((r = setjmp(parse_abort))>0) {
1547                 Warning(LOCATION, "Error reading \"%s\" (code = %d)", filename, r);
1548                 return;
1549         }
1550
1551         // open localization
1552         lcl_ext_open(); 
1553         
1554         read_file_text(filename);
1555         init_parse();
1556
1557         // first, read the mission notes for this mission.  Used in campaign editor
1558         if (skip_to_string("#Mission Info")) {
1559                 if (skip_to_string("$Notes:")) {
1560                         stuff_string(notes, F_NOTES, NULL);
1561                         if (Campaign.missions[num].notes){
1562                                 free(Campaign.missions[num].notes);
1563                         }
1564
1565                         Campaign.missions[num].notes = (char *) malloc(strlen(notes) + 1);
1566                         strcpy(Campaign.missions[num].notes, notes);
1567                 }
1568         }
1569
1570         event_count = 0;
1571         // skip to events section in the mission file.  Events come before goals, so we process them first
1572         if ( skip_to_string("#Events") ) {
1573                 while (1) {
1574                         if (skip_to_string("$Formula:", "#Goals") != 1){
1575                                 break;
1576                         }
1577
1578                         z = skip_to_string("+Name:", "$Formula:");
1579                         if (!z){
1580                                 break;
1581                         }
1582
1583                         if (z == 1){
1584                                 stuff_string(events[event_count], F_NAME, NULL);
1585                         } else {
1586                                 sprintf(events[event_count], NOX("Event #%d"), event_count + 1);
1587                         }
1588
1589                         event_count++;
1590                         Assert(event_count < MAX_MISSION_EVENTS);
1591                 }
1592         }
1593
1594         count = 0;
1595         if (skip_to_string("#Goals")) {
1596                 while (1) {
1597                         if (skip_to_string("$Type:", "#End") != 1){
1598                                 break;
1599                         }
1600
1601                         z = skip_to_string("+Name:", "$Type:");
1602                         if (!z){
1603                                 break;
1604                         }
1605
1606                         if (z == 1){
1607                                 stuff_string(goals[count], F_NAME, NULL);
1608                         } else {
1609                                 sprintf(goals[count], NOX("Goal #%d"), count + 1);
1610                         }
1611
1612                         count++;
1613                         Assert(count < MAX_GOALS);
1614                 }
1615         }
1616
1617         Campaign.missions[num].num_goals = count;
1618         if (count) {
1619                 Campaign.missions[num].goals = (mgoal *) malloc(count * sizeof(mgoal));
1620                 Assert(Campaign.missions[num].goals);  // make sure we got the memory
1621                 memset(Campaign.missions[num].goals, 0, count * sizeof(mgoal));
1622
1623                 for (i=0; i<count; i++){
1624                         strcpy(Campaign.missions[num].goals[i].name, goals[i]);
1625                 }
1626         }
1627                 // copy the events
1628         Campaign.missions[num].num_events = event_count;
1629         if (event_count) {
1630                 Campaign.missions[num].events = (mevent *)malloc(event_count * sizeof(mevent));
1631                 Assert ( Campaign.missions[num].events );
1632                 memset(Campaign.missions[num].events, 0, event_count * sizeof(mevent));
1633
1634                 for (i = 0; i < event_count; i++ ){
1635                         strcpy(Campaign.missions[num].events[i].name, events[i]);
1636                 }
1637         }
1638
1639         // close localization
1640         lcl_ext_close();
1641 }
1642
1643 // function to return index into Campaign's list of missions of the mission with the given
1644 // filename.  This function tried to be a little smart about filename looking for the .fsm
1645 // extension since filenames are stored with the extension in the campaign file.  Returns
1646 // index of mission in campaign structure.  -1 if mission name not found.
1647 int mission_campaign_find_mission( char *name )
1648 {
1649         int i;
1650         char realname[_MAX_PATH];
1651
1652         // look for an extension on the file.  If no extension, add default ".fsm" onto the
1653         // end of the filename
1654         strcpy(realname, name );
1655         if ( strchr(name, '.') == NULL ){
1656                 sprintf(realname, NOX("%s%s"), name, FS_MISSION_FILE_EXT );
1657         }
1658
1659         for (i = 0; i < Campaign.num_missions; i++ ) {
1660                 if ( !stricmp(realname, Campaign.missions[i].name) ){
1661                         return i;
1662                 }
1663         }
1664
1665         return -1;
1666 }
1667
1668 void mission_campaign_maybe_play_movie(int type)
1669 {
1670         int mission;
1671         char *filename;
1672
1673         // only support pre mission movies for now.
1674         Assert ( type == CAMPAIGN_MOVIE_PRE_MISSION );
1675
1676         if ( !(Game_mode & GM_CAMPAIGN_MODE) )
1677                 return;
1678
1679         mission = Campaign.current_mission;
1680         Assert( mission != -1 );
1681
1682         // get a possible filename for a movie to play.
1683         filename = NULL;
1684         switch( type ) {
1685         case CAMPAIGN_MOVIE_PRE_MISSION:
1686                 if ( strlen(Campaign.missions[mission].briefing_cutscene) )
1687                         filename = Campaign.missions[mission].briefing_cutscene;
1688                 break;
1689
1690         default:
1691                 Int3();
1692                 break;
1693         }
1694
1695         // no filename, no movie!
1696         if ( !filename )
1697                 return;
1698
1699         // no soup for you!
1700         // movie_play( filename );
1701 }
1702
1703 // return nonzero if the passed filename is a multiplayer campaign, 0 otherwise
1704 int mission_campaign_parse_is_multi(char *filename, char *name)
1705 {       
1706         int i;
1707         char temp[50];
1708         
1709         read_file_text( filename );
1710         reset_parse();
1711         
1712         required_string("$Name:");
1713         stuff_string( temp, F_NAME, NULL );     
1714         if ( name )
1715                 strcpy( name, temp );
1716
1717         required_string( "$Type:" );
1718         stuff_string( temp, F_NAME, NULL );
1719
1720         for (i = 0; i < MAX_CAMPAIGN_TYPES; i++ ) {
1721                 if ( !stricmp(temp, campaign_types[i]) ) {
1722                         return i;
1723                 }
1724         }
1725
1726         Error(LOCATION, "Unknown campaign type %s", temp );
1727         return -1;
1728 }
1729
1730 // functions to save persistent information during a mission -- which then might or might not get
1731 // saved out when the mission is over depending on whether player replays mission or commits.
1732 void mission_campaign_save_persistent( int type, int sindex )
1733 {
1734         // based on the type of information, save it off for possible saving into the campsign
1735         // savefile when the mission is over
1736         if ( type == CAMPAIGN_PERSISTENT_SHIP ) {
1737                 Assert( Num_granted_ships < MAX_SHIP_TYPES );
1738                 Granted_ships[Num_granted_ships] = sindex;
1739                 Num_granted_ships++;
1740         } else if ( type == CAMPAIGN_PERSISTENT_WEAPON ) {
1741                 Assert( Num_granted_weapons < MAX_WEAPON_TYPES );
1742                 Granted_weapons[Num_granted_weapons] = sindex;
1743                 Num_granted_weapons++;
1744         } else
1745                 Int3();
1746 }
1747
1748 // returns 0: loaded, !0: error
1749 int mission_load_up_campaign()
1750 {
1751         if (strlen(Player->current_campaign))
1752                 return mission_campaign_load(Player->current_campaign);
1753         else
1754                 return mission_campaign_load(BUILTIN_CAMPAIGN);
1755 }
1756
1757 // for end of campaign in the single player game.  Called when the end of campaign state is
1758 // entered, which is triggered when the end-campaign sexpression is hit
1759
1760 void mission_campaign_end_init()
1761 {
1762         // no need to do any initialization.
1763 }
1764
1765 void mission_campaign_end_do()
1766 {
1767         // play the movies
1768         event_music_level_close();
1769         mission_goal_fail_incomplete();
1770         scoring_level_close();
1771         mission_campaign_mission_over();
1772
1773         // eventually we'll want to play one of two options (good ending or bad ending)
1774         // did the supernova blow?
1775         if(Supernova_status == SUPERNOVA_HIT){
1776                 // no soup for you!
1777                 // movie_play_two("endpart1.mve", "endprt2b.mve");                      // good ending
1778         } else {
1779                 // no soup for you!
1780                 // movie_play_two("endpart1.mve", "endprt2a.mve");                      // good ending
1781         }       
1782
1783 #ifdef FS2_DEMO
1784         gameseq_post_event( GS_EVENT_END_DEMO );
1785 #else   
1786         gameseq_post_event( GS_EVENT_MAIN_MENU );
1787 #endif
1788 }
1789
1790 void mission_campaign_end_close()
1791 {
1792         // nothing to do here.
1793 }
1794
1795
1796 // skip to the next mission in the campaign
1797 // this also posts the state change by default.  pass 0 to override that
1798 void mission_campaign_skip_to_next(int start_game)
1799 {
1800         // mark all goals/events complete
1801         // these do not really matter, since is-previous-event-* and is-previous-goal-* sexps check
1802         // to see if the mission was skipped, and use defaults accordingly.
1803         mission_goal_mark_objectives_complete();
1804         mission_goal_mark_events_complete();
1805
1806         // mark mission as skipped
1807         Campaign.missions[Campaign.current_mission].flags |= CMISSION_FLAG_SKIPPED;
1808
1809         // store
1810         mission_campaign_store_goals_and_events();
1811
1812         // now set the next mission
1813         mission_campaign_eval_next_mission();
1814
1815         // clear out relevant player vars
1816         Player->failures_this_session = 0;
1817         Player->show_skip_popup = 1;
1818
1819         if (start_game) {
1820                 // proceed to next mission or main hall
1821                 if ((Campaign.missions[Campaign.current_mission].has_mission_loop) && (Campaign.loop_mission != -1)) {
1822                         // go to loop solicitation
1823                         gameseq_post_event(GS_EVENT_LOOP_BRIEF);
1824                 } else {
1825                         // closes out mission stuff, sets up next one
1826                         mission_campaign_mission_over();
1827
1828                         if ( Campaign.next_mission == -1 ) {
1829                                 // go to main hall, tha campaign is over!
1830                                 gameseq_post_event(GS_EVENT_MAIN_MENU);
1831                         } else {
1832                                 // go to next mission
1833                                 gameseq_post_event(GS_EVENT_START_GAME);
1834                         }
1835                 }
1836         }
1837 }
1838
1839
1840 // breaks your ass out of the loop
1841 // this also posts the state change
1842 void mission_campaign_exit_loop()
1843 {
1844         // set campaign to loop reentry point
1845         Campaign.next_mission = Campaign.loop_reentry;
1846         Campaign.current_mission = -1;
1847         Campaign.loop_enabled = 0;
1848
1849         // set things up for next mission
1850         mission_campaign_next_mission();
1851         gameseq_post_event(GS_EVENT_START_GAME);
1852 }
1853
1854
1855 // used for jumping to a particular campaign mission
1856 // all pvs missions marked skipped
1857 // this relies on correct mission ordering in the campaign file
1858 void mission_campaign_jump_to_mission(char *name)
1859 {
1860         int i = 0;
1861         char dest_name[64];
1862
1863         // load in the campaign junk
1864         mission_load_up_campaign();
1865
1866         // tack the .fs2 onto the input name
1867         strcpy(dest_name, name);
1868         strcat(name, ".fs2");
1869
1870         // search for our mission
1871         for (i=0; i<Campaign.num_missions; i++) {
1872                 if ((Campaign.missions[i].name != NULL) && !stricmp(Campaign.missions[i].name, name) ) {
1873                         Campaign.next_mission = i;
1874                         Campaign.prev_mission = i-1;
1875                         mission_campaign_next_mission();
1876                         Game_mode |= GM_CAMPAIGN_MODE;
1877                         gameseq_post_event(GS_EVENT_START_GAME);
1878                         return;
1879                 } else {
1880                         Campaign.missions[i].flags |= CMISSION_FLAG_SKIPPED;
1881                         Campaign.num_missions_completed = i;
1882                 }
1883         }
1884
1885         // if we got here, no match was found
1886         // restart the campaign
1887         mission_campaign_savefile_delete(Campaign.filename);
1888         mission_campaign_load(Campaign.filename);
1889 }
1890