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