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