FS2_Open
Open source remastering of the Freespace 2 engine
missioncampaign.cpp
Go to the documentation of this file.
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 the
6  * source.
7  *
8 */
9 
10 
11 
12 #include <stdio.h>
13 #include <string.h>
14 #include <setjmp.h>
15 #include <errno.h>
16 
17 #ifdef _WIN32
18 #include <direct.h>
19 #include <io.h>
20 #endif
21 
22 #ifdef SCP_UNIX
23 #include <sys/stat.h>
24 #include <glob.h>
25 #endif
26 
27 #include "cfile/cfile.h"
28 #include "cutscene/cutscenes.h"
29 #include "cutscene/movie.h"
30 #include "freespace2/freespace.h"
32 #include "gamesnd/eventmusic.h"
33 #include "localization/localize.h"
34 #include "menuui/mainhallmenu.h"
35 #include "menuui/techmenu.h"
37 #include "mission/missiongoals.h"
39 #include "missionui/redalert.h"
40 #include "parse/parselo.h"
41 #include "parse/sexp.h"
42 #include "pilotfile/pilotfile.h"
43 #include "playerman/player.h"
44 #include "popup/popup.h"
45 #include "ship/ship.h"
46 #include "starfield/supernova.h"
47 #include "ui/ui.h"
48 #include "weapon/weapon.h"
49 
50 
51 // campaign wasn't ended
53 
54 // stuff for selecting campaigns. We need to keep both arrays around since we display the
55 // list of campaigns by name, but must load campaigns by filename
56 char *Campaign_names[MAX_CAMPAIGNS] = { NULL };
58 char *Campaign_descs[MAX_CAMPAIGNS] = { NULL };
64 
66 
67 // stuff used for campaign list building
68 static bool MC_desc = false;
69 static bool MC_multiplayer = false;
70 
72 {
73 //XSTR:OFF
74  "single",
75  "multi coop",
76  "multi teams"
77 //XSTR:ON
78 };
79 
80 // modules local variables to deal with getting new ships/weapons available to the player
81 int Num_granted_ships, Num_granted_weapons; // per mission counts of new ships and weapons
84 
85 // variables to control the UI stuff for loading campaigns
89 
90 // the campaign!!!!!
92 
93 
94 bool campaign_is_ignored(const char *filename);
95 
101 int mission_campaign_get_info(const char *filename, char *name, int *type, int *max_players, char **desc)
102 {
103  int i, success = 0;
104  char campaign_type[NAME_LENGTH], fname[MAX_FILENAME_LEN];
105 
106  Assert( name != NULL );
107  Assert( type != NULL );
108 
109  strncpy(fname, filename, MAX_FILENAME_LEN - 1);
110  int fname_len = strlen(fname);
111  if ((fname_len < 4) || stricmp(fname + fname_len - 4, FS_CAMPAIGN_FILE_EXT)){
113  fname_len += 4;
114  }
115  Assert(fname_len < MAX_FILENAME_LEN);
116 
117  *type = -1;
118  do {
119  try
120  {
121  read_file_text(fname);
122  reset_parse();
123 
124  required_string("$Name:");
126  if (name == NULL) {
127  nprintf(("Warning", "No name found for campaign file %s\n", filename));
128  break;
129  }
130 
131  required_string("$Type:");
132  stuff_string(campaign_type, F_NAME, NAME_LENGTH);
133 
134  for (i = 0; i < MAX_CAMPAIGN_TYPES; i++) {
135  if (!stricmp(campaign_type, campaign_types[i])) {
136  *type = i;
137  }
138  }
139 
140  if (name == NULL) {
141  Warning(LOCATION, "Invalid campaign type \"%s\"\n", campaign_type);
142  break;
143  }
144 
145  if (desc) {
146  if (optional_string("+Description:")) {
147  *desc = stuff_and_malloc_string(F_MULTITEXT, NULL);
148  }
149  else {
150  *desc = NULL;
151  }
152  }
153 
154  // if this is a multiplayer campaign, get the max players
155  if ((*type) != CAMPAIGN_TYPE_SINGLE) {
156  skip_to_string("+Num Players:");
157  stuff_int(max_players);
158  }
159 
160  // if we found a valid campaign type
161  if ((*type) >= 0) {
162  success = 1;
163  }
164  }
165  catch (const parse::ParseException& e)
166  {
167  mprintf(("MISSIONCAMPAIGN: Unable to parse '%s'! Error message = %s.\n", fname, e.what()));
168  break;
169  }
170  } while (0);
171 
172  Assert(success);
173  return success;
174 }
175 
181 int mission_campaign_get_mission_list(const char *filename, char **list, int max)
182 {
183  int i, num = 0;
184  char name[MAX_FILENAME_LEN];
185 
186  filename = cf_add_ext(filename, FS_CAMPAIGN_FILE_EXT);
187 
188  // read the campaign file and get the list of mission filenames
189  try
190  {
191  read_file_text(filename);
192  reset_parse();
193 
194  while (skip_to_string("$Mission:") > 0) {
196  if (num < max)
197  list[num++] = vm_strdup(name);
198  else
199  Warning(LOCATION, "Maximum number of missions exceeded (%d)!", max);
200  }
201  }
202  catch (const parse::ParseException& e)
203  {
204  mprintf(("MISSIONCAMPAIGN: Unable to parse '%s'! Error message = %s.\n", filename, e.what()));
205 
206  // since we can't return count of allocated elements, free them instead
207  for (i = 0; i<num; i++)
208  vm_free(list[i]);
209 
210  num = -1;
211  }
212 
213  return num;
214 }
215 
217 {
218  int i;
219 
220  if ( !Campaign_names_inited )
221  return;
222 
223  for (i = 0; i < Num_campaigns; i++) {
224  if (Campaign_names[i] != NULL) {
226  Campaign_names[i] = NULL;
227  }
228 
229  if (Campaign_file_names[i] != NULL) {
231  Campaign_file_names[i] = NULL;
232  }
233 
234  if (Campaign_descs[i] != NULL) {
236  Campaign_descs[i] = NULL;
237  }
238  }
239 
240  Num_campaigns = 0;
242 }
243 
245 {
246  char name[NAME_LENGTH];
247  char *desc = NULL;
248  int type, max_players;
249 
250  // don't add ignored campaigns
251  if (campaign_is_ignored(filename)) {
252  return 0;
253  }
254 
255  if ( mission_campaign_get_info( filename, name, &type, &max_players, &desc) ) {
256  if ( !MC_multiplayer && (type == CAMPAIGN_TYPE_SINGLE) ) {
258 
259  if (MC_desc)
261 
262  Num_campaigns++;
263 
264  return 1;
265  }
266  }
267 
268  if (desc != NULL)
269  vm_free(desc);
270 
271  return 0;
272 }
273 
279 void mission_campaign_build_list(bool desc, bool sort, bool multiplayer)
280 {
281  char wild_card[10];
282  int i, j, incr = 0;
283  char *t = NULL;
284  int rc = 0;
285 
287  return;
288 
289  MC_desc = desc;
290  MC_multiplayer = multiplayer;
291 
292  memset(wild_card, 0, sizeof(wild_card));
293  strcpy_s(wild_card, NOX("*"));
294  strcat_s(wild_card, FS_CAMPAIGN_FILE_EXT);
295 
296  // if we have already been loaded then free everything and reload
297  if (Num_campaigns != 0)
299 
300  // set filter for cf_get_file_list() if there isn't one set already (the simroom has a special one)
301  if (Get_file_list_filter == NULL)
303 
304  // now get the list of all mission names
305  // NOTE: we don't do sorting here, but we assume CF_SORT_NAME, and do it manually below
307  Assert( rc == Num_campaigns );
308 
309  // now sort everything, if we are supposed to
310  if (sort) {
311  incr = Num_campaigns / 2;
312 
313  while (incr > 0) {
314  for (i = incr; i < Num_campaigns; i++) {
315  j = i - incr;
316 
317  while (j >= 0) {
318  char *name1 = Campaign_names[j];
319  char *name2 = Campaign_names[j + incr];
320 
321  // if we hit this then a coder probably did something dumb (like not needing to sort)
322  if ( (name1 == NULL) || (name2 == NULL) ) {
323  Int3();
324  break;
325  }
326 
327  if ( !strnicmp(name1, "the ", 4) )
328  name1 += 4;
329 
330  if ( !strnicmp(name2, "the ", 4) )
331  name2 += 4;
332 
333  if (stricmp(name1, name2) > 0) {
334  // first, do filenames
335  t = Campaign_file_names[j];
337  Campaign_file_names[j + incr] = t;
338 
339  // next, actual names
340  t = Campaign_names[j];
341  Campaign_names[j] = Campaign_names[j + incr];
342  Campaign_names[j + incr] = t;
343 
344  // finally, do descriptions
345  if (desc) {
346  t = Campaign_descs[j];
347  Campaign_descs[j] = Campaign_descs[j + incr];
348  Campaign_descs[j + incr] = t;
349  }
350 
351  j -= incr;
352  } else {
353  break;
354  }
355  }
356  }
357 
358  incr /= 2;
359  }
360  }
361 
362  // Done!
364 }
365 
366 
371 {
372  int i, count, ship_list[MAX_SHIP_CLASSES], weapon_list[MAX_WEAPON_TYPES];
373 
374  // set allowable ships to the SIF_PLAYER_SHIPs
375  memset( Campaign.ships_allowed, 0, sizeof(Campaign.ships_allowed) );
376  for (auto it = Ship_info.cbegin(); it != Ship_info.cend(); ++it ) {
377  if ( it->flags & SIF_PLAYER_SHIP )
378  Campaign.ships_allowed[std::distance(Ship_info.cbegin(), it)] = 1;
379  }
380 
381  for (i = 0; i < MAX_WEAPON_TYPES; i++ )
382  Campaign.weapons_allowed[i] = 1;
383 
384  if ( optional_string("+Starting Ships:") ) {
385  for (i = 0; i < static_cast<int>(Ship_info.size()); i++ )
386  Campaign.ships_allowed[i] = 0;
387 
388  count = stuff_int_list(ship_list, MAX_SHIP_CLASSES, SHIP_INFO_TYPE);
389 
390  // now set the array elements stating which ships we are allowed
391  for (i = 0; i < count; i++ ) {
392  if ( Ship_info[ship_list[i]].flags & SIF_PLAYER_SHIP )
393  Campaign.ships_allowed[ship_list[i]] = 1;
394  }
395  }
396 
397  if ( optional_string("+Starting Weapons:") ) {
398  for (i = 0; i < MAX_WEAPON_TYPES; i++ )
399  Campaign.weapons_allowed[i] = 0;
400 
401  count = stuff_int_list(weapon_list, MAX_WEAPON_TYPES, WEAPON_POOL_TYPE);
402 
403  // now set the array elements stating which ships we are allowed
404  for (i = 0; i < count; i++ )
405  Campaign.weapons_allowed[weapon_list[i]] = 1;
406  }
407 }
408 
417 int mission_campaign_load( char *filename, player *pl, int load_savefile, bool reset_stats )
418 {
419  int len, i;
421 
422  if (campaign_is_ignored(filename)) {
424  return CAMPAIGN_ERROR_IGNORED;
425  }
426 
427  filename = cf_add_ext(filename, FS_CAMPAIGN_FILE_EXT);
428 
429  if ( pl == NULL )
430  pl = Player;
431 
432  if ( !Fred_running && load_savefile && (pl == NULL) ) {
433  Int3();
434  load_savefile = 0;
435  }
436 
437  // read the mission file and get the list of mission filenames
438  try
439  {
440  // be sure to remove all old malloced strings of Mission_names
441  // we must also free any goal stuff that was from a previous campaign
442  // this also frees sexpressions so the next call to init_sexp will be able to reclaim
443  // nodes previously used by another campaign.
445 
446  strcpy_s( Campaign.filename, filename );
447 
448  // only initialize the sexpression stuff when Fred isn't running. It'll screw things up major
449  // if it does
450  if ( !Fred_running ){
451  init_sexp(); // must initialize the sexpression stuff
452  }
453 
454  read_file_text( filename );
455  reset_parse();
456 
457  // copy filename to campaign structure minus the extension
458  len = strlen(filename) - 4;
459  Assert(len < MAX_FILENAME_LEN);
460  strncpy(Campaign.filename, filename, len);
461  Campaign.filename[len] = 0;
462 
463  required_string("$Name:");
464  stuff_string( name, F_NAME, NAME_LENGTH );
465 
466  //Store campaign name in the global struct
467  strcpy_s( Campaign.name, name );
468 
469  required_string( "$Type:" );
470  stuff_string( type, F_NAME, NAME_LENGTH );
471 
472  for (i = 0; i < MAX_CAMPAIGN_TYPES; i++ ) {
473  if ( !stricmp(type, campaign_types[i]) ) {
474  Campaign.type = i;
475  break;
476  }
477  }
478 
479  if ( i == MAX_CAMPAIGN_TYPES )
480  Error(LOCATION, "Unknown campaign type %s!", type);
481 
482  Campaign.desc = NULL;
483  if (optional_string("+Description:"))
484  Campaign.desc = stuff_and_malloc_string(F_MULTITEXT, NULL);
485 
486  // if the type is multiplayer -- get the number of players
487  Campaign.num_players = 0;
488  if ( Campaign.type != CAMPAIGN_TYPE_SINGLE) {
489  required_string("+Num players:");
490  stuff_int( &(Campaign.num_players) );
491  }
492 
493  // parse flags - Goober5000
494  Campaign.flags = CF_DEFAULT_VALUE;
495  if (optional_string("$Flags:"))
496  {
497  stuff_int( &(Campaign.flags) );
498  }
499 
500  // parse the optional ship/weapon information
502 
503  // parse the mission file and actually read in the mission stuff
504  Campaign.num_missions = 0;
505  while ( required_string_either("#End", "$Mission:") ) {
506  cmission *cm;
507 
508  required_string("$Mission:");
510  cm = &Campaign.missions[Campaign.num_missions];
511  cm->name = vm_strdup(name);
512 
513  cm->notes = NULL;
514 
515  cm->briefing_cutscene[0] = 0;
516  if ( optional_string("+Briefing Cutscene:") )
518 
519  cm->flags = 0;
520  if (optional_string("+Flags:"))
521  stuff_int(&cm->flags);
522 
523  cm->main_hall = "0";
524  // deal with previous campaign versions
525  if (cm->flags & CMISSION_FLAG_BASTION) {
526  cm->main_hall = "1";
527  }
528 
529  // Goober5000 - new main hall stuff!
530  // Updated by CommanderDJ
531  if (optional_string("+Main Hall:")) {
532  stuff_string(temp, F_RAW, 32);
533  cm->main_hall = temp;
534  }
535 
536  // Goober5000 - substitute main hall (like substitute music)
537  if (optional_string("+Substitute Main Hall:")) {
538  stuff_string(temp, F_RAW, 32);
539 
540  // see if this main hall exists
542  if (mhd != NULL) {
543  cm->main_hall = temp;
544  } else {
545  mprintf(("Substitute main hall '%s' not found\n", temp));
546  }
547  }
548 
549  // Goober5000 - new debriefing persona stuff!
550  cm->debrief_persona_index = 0;
551  if (optional_string("+Debriefing Persona Index:"))
553 
554  cm->formula = -1;
555  if ( optional_string("+Formula:") ) {
556  cm->formula = get_sexp_main();
557  if ( !Fred_running ) {
558  Assert ( cm->formula != -1 );
560 
561  } else {
562  if ( cm->formula == -1 ){
565  }
566  }
567  }
568 
569  // Do mission branching stuff
570  if ( optional_string("+Mission Loop:") ) {
572  } else if ( optional_string("+Mission Fork:") ) {
574  }
575 
576  cm->mission_branch_desc = NULL;
577  if ( optional_string("+Mission Loop Text:") || optional_string("+Mission Fork Text:") ) {
579  }
580 
581  cm->mission_branch_brief_anim = NULL;
582  if ( optional_string("+Mission Loop Brief Anim:") || optional_string("+Mission Fork Brief Anim:") ) {
584  }
585 
586  cm->mission_branch_brief_sound = NULL;
587  if ( optional_string("+Mission Loop Brief Sound:") || optional_string("+Mission Fork Brief Sound:") ) {
589  }
590 
591  cm->mission_loop_formula = -1;
592  if ( optional_string("+Formula:") ) {
594  if ( !Fred_running ) {
595  Assert ( cm->mission_loop_formula != -1 );
597 
598  } else {
599  if ( cm->mission_loop_formula == -1 ){
602  }
603  }
604  }
605 
606  cm->level = 0;
607  if (optional_string("+Level:")) {
608  stuff_int( &cm->level );
609  if ( cm->level == 0 ) // check if the top (root) of the whole tree
610  Campaign.next_mission = Campaign.num_missions;
611 
612  } else
613  Campaign.realign_required = 1;
614 
615  cm->pos = 0;
616  if (optional_string("+Position:"))
617  stuff_int( &cm->pos );
618  else
619  Campaign.realign_required = 1;
620 
621  if (Fred_running) {
622  cm->num_goals = -1;
623  cm->num_events = -1;
624  cm->num_variables = -1;
625 
626  } else {
627  cm->num_goals = 0;
628  cm->num_events = 0;
629  cm->num_variables = 0;
630  }
631 
632  // it's possible to have data already loaded from the pilotfile
633  // if so free it to avoid memory leaks
634  if (cm->goals != nullptr) {
635  vm_free(cm->goals);
636  cm->goals = nullptr;
637  }
638  if (cm->events != nullptr) {
639  vm_free(cm->events);
640  cm->events = nullptr;
641  }
642  if (cm->variables != nullptr) {
643  vm_free(cm->variables);
644  cm->variables = nullptr;
645  }
646 
647  Campaign.num_missions++;
648  }
649  }
650  catch (const parse::ParseException& e)
651  {
652  mprintf(("Error parsing '%s'\r\nError message = %s.\r\n", filename, e.what()));
653 
654  Campaign.filename[0] = 0;
655  Campaign.num_missions = 0;
656 
657  if (!Fred_running && !(Game_mode & GM_MULTIPLAYER)) {
660  return CAMPAIGN_ERROR_MISSING;
661  }
662 
663  return CAMPAIGN_ERROR_CORRUPT;
664  }
665 
666  // set up the other variables for the campaign stuff. After initializing, we must try and load
667  // the campaign save file for this player. Since all campaign loads go through this routine, I
668  // think this place should be the only necessary place to load the campaign save stuff. The campaign
669  // save file will get written when a mission has ended by player choice.
670  Campaign.next_mission = 0;
671  Campaign.prev_mission = -1;
672  Campaign.current_mission = -1;
674  Campaign.num_missions_completed = 0;
675 
676  // loading the campaign will get us to the current and next mission that the player must fly
677  // plus load all of the old goals that future missions might rely on.
678  if (!Fred_running && load_savefile && (Campaign.type == CAMPAIGN_TYPE_SINGLE)) {
679  // savefile can fail to load for numerous otherwise non-fatal reasons
680  // if it doesn't load in that case then it will be (re)created at save
681  if ( !Pilot.load_savefile(Campaign.filename) ) {
682  // but if the data is invalid for the savefile then it is fatal
683  if ( Pilot.is_invalid() ) {
684  Campaign.filename[0] = 0;
685  Campaign.num_missions = 0;
688  } else {
689  // make sure we initialize red alert data for the new CSG
690  red_alert_clear();
691  // and reset stats when requested
692  if (reset_stats) {
693  pl->stats.init();
694  }
696  }
697  }
698  }
699 
700  // all is good here, move along
702 
703  return 0;
704 }
705 
712 {
713  char name[NAME_LENGTH],test[5];
714  int type,max_players;
715 
716  // make sure to tack on .fsc on the end if its not there already
717  if(filename[0] != '\0'){
718  if(strlen(filename) > 4){
719  strcpy_s(test,filename+(strlen(filename)-4));
720  if(strcmp(test, FS_CAMPAIGN_FILE_EXT)!=0){
721  strcat(filename, FS_CAMPAIGN_FILE_EXT);
722  }
723  } else {
724  strcat(filename, FS_CAMPAIGN_FILE_EXT);
725  }
726  } else {
727  Error(LOCATION,"Tried to load campaign file with illegal length/extension!");
728  }
729 
730  if (!mission_campaign_get_info(filename, name, &type, &max_players)){
731  return -1;
732  }
733 
734  Num_campaigns = 0;
737  Num_campaigns++;
738  mission_campaign_load(filename);
739  return 0;
740 }
741 
742 int mission_campaign_load_by_name_csfe( char *filename, char *callsign )
743 {
744  Game_mode |= GM_NORMAL;
745  strcpy_s(Player->callsign, callsign);
746  return mission_campaign_load_by_name( filename);
747 }
748 
749 /*
750  * initialise Player_loadout with default values
751  */
753 {
754  int i = 0, j = 0;
755 
756  memset(Player_loadout.filename, 0, sizeof(Player_loadout.filename));
758 
759  for ( i = 0; i < MAX_SHIP_CLASSES; i++ ) {
761  }
762 
763  for ( i = 0; i < MAX_WEAPON_TYPES; i++ ) {
765  }
766 
767  for ( i = 0; i < MAX_WSS_SLOTS; i++ ) {
769 
770  for ( j = 0; j < MAX_SHIP_WEAPONS; j++ ) {
771  Player_loadout.unit_data[i].wep[j] = 0;
773  }
774  }
775 
776 }
777 
782 {
784 
786 
788 }
789 
794 {
795  char base[_MAX_FNAME];
796 
797  Assert ( strlen(Campaign.filename) != 0 ); //-V805
798 
799  if (pl == NULL) {
800  Assert((Player_num >= 0) && (Player_num < MAX_PLAYERS));
801  pl = &Players[Player_num];
802  }
803 
804  Assert( pl != NULL );
805 
806  // build up the filename for the save file. There could be a problem with filename length,
807  // but this problem can get fixed in several ways -- ignore the problem for now though.
808  _splitpath( Campaign.filename, NULL, NULL, base, NULL );
809 
810  Assert ( (strlen(base) + strlen(pl->callsign) + 1) < _MAX_FNAME );
811 
812  sprintf( filename, NOX("%s.%s."), pl->callsign, base );
813 }
814 
819 {
820  if (Campaign.type == CAMPAIGN_TYPE_SINGLE)
822  else
824 
825  strcpy_s(Player->callsign, pname);
826 
827  return (int)Pilot.save_savefile();
828 }
829 
830 
831 // the below two functions is internal to this module. It is here so that I can group the save/load
832 // functions together.
833 //
834 
839 void mission_campaign_savefile_delete( char *cfilename )
840 {
841  char filename[_MAX_FNAME], base[_MAX_FNAME];
842 
843  _splitpath( cfilename, NULL, NULL, base, NULL );
844 
846  return; // no such thing as a multiplayer campaign savefile
847  }
848 
849  sprintf( filename, NOX("%s.%s.csg"), Player->callsign, base ); // only support the new filename here - taylor
850 
851  cf_delete( filename, CF_TYPE_PLAYERS );
852 }
853 
854 void campaign_delete_save( char *cfn, char *pname)
855 {
856  strcpy_s(Player->callsign, pname);
858 }
859 
867 {
868  int dir_type, num_files, i;
869  char file_spec[MAX_FILENAME_LEN + 2], *ext;
870  char filename[1024];
871  int (*filter_save)(const char *filename);
873 
874  ext = NOX(".csg");
875  dir_type = CF_TYPE_PLAYERS;
876 
877  sprintf(file_spec, NOX("%s.*%s"), pilot_name, ext);
878 
879  // HACK HACK HACK HACK!!!! cf_get_file_list is not reentrant. Pretty dumb because it should
880  // be. I have to save any file filters
881  filter_save = Get_file_list_filter;
882  Get_file_list_filter = NULL;
883  num_files = cf_get_file_list(names, dir_type, file_spec);
884  Get_file_list_filter = filter_save;
885 
886  for (i=0; i<num_files; i++) {
887  strcpy_s(filename, names[i].c_str());
888  strcat_s(filename, ext);
889  cf_delete(filename, dir_type);
890  }
891 }
892 
896 void campaign_savefile_load(char *fname, char *pname)
897 {
898  if (Campaign.type==CAMPAIGN_TYPE_SINGLE) {
900  Game_mode &= GM_NORMAL;
901  }
902  else
904 
905  strcpy_s(Player->callsign, pname);
906 
907  Pilot.load_savefile(fname);
908 }
909 
916 {
917  if ( (Campaign.next_mission == -1) || (Campaign.name[0] == '\0')) // will be set to -1 when there is no next mission
918  return -1;
919 
920  if(Campaign.num_missions < 1)
921  return -2;
922 
923  Campaign.current_mission = Campaign.next_mission;
925 
926  // check for end of loop.
927  if (Campaign.current_mission == Campaign.loop_reentry) {
928  Campaign.loop_enabled = 0;
929  }
930 
931  // reset the number of persistent ships and weapons for the next campaign mission
932  Num_granted_ships = 0;
934  return 0;
935 }
936 
941 {
942  if ( !(Game_mode & GM_CAMPAIGN_MODE) )
943  return 0;
944 
945  if ( Campaign.prev_mission == -1 )
946  return 0;
947 
948  Campaign.current_mission = Campaign.prev_mission;
949  Campaign.prev_mission = -1;
950  Campaign.next_mission = Campaign.current_mission;
951  Campaign.num_missions_completed--;
952  Campaign.missions[Campaign.next_mission].completed = 0;
953 
954  if (Campaign.num_variables > 0) {
955  vm_free( Campaign.variables );
956  }
957 
958  Campaign.num_variables = Campaign.redalert_num_variables;
959 
960  // copy backed up variables over
961  if (Campaign.redalert_num_variables > 0) {
962  Assert( Campaign.redalert_variables );
963 
964  Campaign.variables = (sexp_variable *) vm_malloc(Campaign.num_variables * sizeof(sexp_variable));
965  memcpy(Campaign.variables, Campaign.redalert_variables, Campaign.redalert_num_variables * sizeof(sexp_variable));
966  }
967 
969 
970  // reset the player stats to be the stats from this level
971  Player->stats.assign( Campaign.missions[Campaign.current_mission].stats );
972 
974  Num_granted_ships = 0;
976 
977  return 1;
978 }
979 
984 {
985  Campaign.next_mission = -1;
986  int cur = Campaign.current_mission;
987 
988  // evaluate next mission (straight path)
989  if (Campaign.missions[cur].formula != -1) {
990  flush_sexp_tree(Campaign.missions[cur].formula); // force formula to be re-evaluated
991  eval_sexp(Campaign.missions[cur].formula); // this should reset Campaign.next_mission to proper value
992  }
993 
994  // evaluate mission loop mission (if any) so it can be used if chosen
995  if ( Campaign.missions[cur].flags & CMISSION_FLAG_HAS_LOOP ) {
996  int copy_next_mission = Campaign.next_mission;
997  // Set temporarily to -1 so we know if loop formula fails to assign
998  Campaign.next_mission = -1;
999  if (Campaign.missions[cur].mission_loop_formula != -1) {
1000  flush_sexp_tree(Campaign.missions[cur].mission_loop_formula); // force formula to be re-evaluated
1001  eval_sexp(Campaign.missions[cur].mission_loop_formula); // this should reset Campaign.next_mission to proper value
1002  }
1003 
1004  Campaign.loop_mission = Campaign.next_mission;
1005  Campaign.next_mission = copy_next_mission;
1006  }
1007 
1008  if (Campaign.next_mission == -1) {
1009  nprintf(("allender", "No next mission to proceed to.\n"));
1010  } else {
1011  nprintf(("allender", "Next mission is number %d [%s]\n", Campaign.next_mission, Campaign.missions[Campaign.next_mission].name));
1012  }
1013 
1014 }
1015 
1020 {
1021  char *name;
1022  int cur, i;
1023  cmission *mission_obj;
1024 
1025  cur = Campaign.current_mission;
1026  name = Campaign.missions[cur].name;
1027 
1028  mission_obj = &Campaign.missions[cur];
1029 
1030  // first we must save the status of the current missions goals in the campaign mission structure.
1031  // After that, we can determine which mission is tagged as the next mission. Finally, we
1032  // can save the campaign save file
1033  // we might have goal and event status if the player replayed a mission
1034  if ( mission_obj->goals != NULL ) {
1035  vm_free( mission_obj->goals );
1036  mission_obj->goals = NULL;
1037  }
1038 
1039  mission_obj->num_goals = Num_goals;
1040  if ( mission_obj->num_goals > 0 ) {
1041  mission_obj->goals = (mgoal *)vm_malloc( sizeof(mgoal) * Num_goals );
1042  Assert( mission_obj->goals != NULL );
1043  }
1044 
1045  // copy the needed info from the Mission_goal struct to our internal structure
1046  for (i = 0; i < Num_goals; i++ ) {
1047  if (Mission_goals[i].name[0] == '\0') {
1048  char goal_name[NAME_LENGTH];
1049 
1050  sprintf(goal_name, NOX("Goal #%d"), i);
1051  strcpy_s( mission_obj->goals[i].name, goal_name);
1052  } else
1053  strcpy_s( mission_obj->goals[i].name, Mission_goals[i].name );
1054  Assert ( Mission_goals[i].satisfied != GOAL_INCOMPLETE ); // should be true or false at this point!!!
1055  mission_obj->goals[i].status = (char)Mission_goals[i].satisfied;
1056  }
1057 
1058  // do the same thing for events as we did for goals
1059  // we might have goal and event status if the player replayed a mission
1060  if ( mission_obj->events != NULL ) {
1061  vm_free( mission_obj->events );
1062  mission_obj->events = NULL;
1063  }
1064 
1065  mission_obj->num_events = Num_mission_events;
1066  if ( mission_obj->num_events > 0 ) {
1067  mission_obj->events = (mevent *)vm_malloc( sizeof(mevent) * Num_mission_events );
1068  Assert( mission_obj->events != NULL );
1069  }
1070 
1071  // copy the needed info from the Mission_goal struct to our internal structure
1072  for (i = 0; i < Num_mission_events; i++ ) {
1073  if (Mission_events[i].name[0] == '\0') {
1074  char event_name[NAME_LENGTH];
1075 
1076  sprintf(event_name, NOX("Event #%d"), i);
1077  nprintf(("Warning", "Mission goal in mission %s must have a +Name field! using %s for campaign save file\n", mission_obj->name, name));
1078  strcpy_s( mission_obj->events[i].name, event_name);
1079  } else
1080  strcpy_s( mission_obj->events[i].name, Mission_events[i].name );
1081 
1082  // getting status for the events is a little different. If the formula value for the event entry
1083  // is -1, then we know the value of the result field will never change. If the formula is
1084  // not -1 (i.e. still being evaluated at mission end time), we will write "incomplete" for the
1085  // event evaluation
1086  if ( Mission_events[i].formula == -1 ) {
1087  if ( Mission_events[i].result )
1088  mission_obj->events[i].status = EVENT_SATISFIED;
1089  else
1090  mission_obj->events[i].status = EVENT_FAILED;
1091  } else
1092  Int3();
1093  }
1094 }
1095 
1097 {
1098  int cur, i, j;
1099  cmission *mission_obj;
1100 
1101  cur = Campaign.current_mission;
1102  mission_obj = &Campaign.missions[cur];
1103 
1104  // Goober5000 - handle campaign-persistent variables -------------------------------------
1105  if (mission_obj->variables != NULL) {
1106  vm_free( mission_obj->variables );
1107  mission_obj->variables = NULL;
1108  }
1109 
1110  int num_mission_variables = sexp_campaign_persistent_variable_count();
1111 
1112  if (num_mission_variables > 0) {
1113  int variable_count = 0;
1114  int total_variables = Campaign.num_variables;
1115  int matching_variables = 0;
1116  int persistent_variables_in_mission = 0;
1117 
1118  // get count of new variables
1119  for (i = 0; i < sexp_variable_count(); i++) {
1121  persistent_variables_in_mission++;
1122 
1123  // see if we already have a variable with this name
1124  for (j = 0; j < Campaign.num_variables; j++) {
1125  if (!(stricmp(Sexp_variables[i].variable_name, Campaign.variables[j].variable_name) )) {
1126  matching_variables++;
1127  break;
1128  }
1129  }
1130  }
1131  }
1132 
1133  Assert(persistent_variables_in_mission >= matching_variables);
1134  total_variables += (persistent_variables_in_mission - matching_variables);
1135 
1136  // allocate new storage
1137  sexp_variable *n_variables = (sexp_variable *) vm_malloc(total_variables * sizeof(sexp_variable));
1138  Assert( n_variables );
1139 
1140  if (Campaign.redalert_num_variables > 0) {
1141  vm_free( Campaign.redalert_variables );
1142  }
1143 
1144  Campaign.redalert_num_variables = Campaign.num_variables;
1145 
1146  // copy existing variables over
1147  if (Campaign.num_variables > 0) {
1148  Assert( Campaign.variables );
1149 
1150  Campaign.redalert_variables = (sexp_variable *) vm_malloc(Campaign.num_variables * sizeof(sexp_variable));
1151  memcpy(Campaign.redalert_variables, Campaign.variables, Campaign.num_variables * sizeof(sexp_variable));
1152  memcpy(n_variables, Campaign.variables, Campaign.num_variables * sizeof(sexp_variable));
1153 
1154  variable_count = Campaign.num_variables;
1155 
1156  vm_free(Campaign.variables);
1157  Campaign.variables = NULL;
1158  }
1159 
1160  // update/add variables
1161  for (i = 0; i < sexp_variable_count(); i++) {
1163  continue;
1164  }
1165 
1166  bool add_it = true;
1167 
1168  // maybe update...
1169  for (j = 0; j < Campaign.num_variables; j++) {
1170  if ( !stricmp(Sexp_variables[i].variable_name, n_variables[j].variable_name) ) {
1171  n_variables[j].type = Sexp_variables[i].type;
1172  strcpy_s(n_variables[j].text, Sexp_variables[i].text);
1173  add_it = false;
1174  break;
1175  }
1176  }
1177 
1178  // otherwise add...
1179  if (add_it) {
1180  n_variables[variable_count].type = Sexp_variables[i].type;
1181  strcpy_s(n_variables[variable_count].text, Sexp_variables[i].text);
1182  strcpy_s(n_variables[variable_count].variable_name, Sexp_variables[i].variable_name);
1183  variable_count++;
1184  }
1185  }
1186 
1187  Assert( variable_count == total_variables );
1188  Assert( Campaign.variables == NULL );
1189 
1190  // update with new data/count
1191  Campaign.variables = n_variables;
1192  Campaign.num_variables = total_variables;
1193  }
1194  // --------------------------------------------------------------------------
1195 }
1196 
1198 {
1201 }
1202 
1208 void mission_campaign_mission_over(bool do_next_mission)
1209 {
1210  int mission_num, i;
1211  cmission *mission_obj;
1212 
1213  // I don't think that we should have a record for these -- maybe we might?????? If we do,
1214  // then we should free them
1215  if ( !(Game_mode & GM_CAMPAIGN_MODE) ){
1216  return;
1217  }
1218 
1219  mission_num = Campaign.current_mission;
1220  Assert( mission_num != -1 );
1221  mission_obj = &Campaign.missions[mission_num];
1222 
1223  // determine if any ships/weapons were granted this mission
1224  for ( i=0; i<Num_granted_ships; i++ ){
1225  Campaign.ships_allowed[Granted_ships[i]] = 1;
1226  }
1227 
1228  for ( i=0; i<Num_granted_weapons; i++ ){
1229  Campaign.weapons_allowed[Granted_weapons[i]] = 1;
1230  }
1231 
1232  // Goober5000 - player-persistent variables are handled when the mission is
1233  // over, not necessarily when the mission is accepted
1235 
1236  // update campaign.mission stats (used to allow backout inRedAlert)
1237  // .. but we don't do this if we are inside of the prev/current loop hack
1238  if ( Campaign.prev_mission != Campaign.current_mission ) {
1239  mission_obj->stats.assign( Player->stats );
1240  if(!(Game_mode & GM_MULTIPLAYER)){
1241  scoring_backout_accept( &mission_obj->stats );
1242  }
1243  }
1244 
1245  // if we are moving to a new mission, then change our data. If we are staying on the same mission,
1246  // then we don't want to do anything. Remove information about goals/events
1247  if ( Campaign.next_mission != mission_num ) {
1248  Campaign.prev_mission = mission_num;
1249  Campaign.current_mission = -1;
1250 
1251  // very minor support for non-linear campaigns - taylor
1252  if (Campaign.missions[mission_num].completed != 1) {
1253  Campaign.num_missions_completed++;
1254  Campaign.missions[mission_num].completed = 1;
1255  }
1256 
1257  // save the scoring values from the previous mission at the start of this mission -- for red alert
1258 
1259  // save the state of the campaign in the campaign save file and move to the end_game state
1260  if (Campaign.type == CAMPAIGN_TYPE_SINGLE) {
1261  Pilot.save_savefile();
1262  }
1263 
1264  } else {
1265  // free up the goals and events which were just malloced. It's kind of like erasing any fact
1266  // that the player played this mission in the campaign.
1267  if (mission_obj->goals != NULL) {
1268  vm_free( mission_obj->goals );
1269  mission_obj->goals = NULL;
1270  }
1271  mission_obj->num_goals = 0;
1272 
1273  if (mission_obj->events != NULL) {
1274  vm_free( mission_obj->events );
1275  mission_obj->events = NULL;
1276  }
1277  mission_obj->num_events = 0;
1278 
1279  // Goober5000
1280  if (mission_obj->variables != NULL) {
1281  vm_free( mission_obj->variables );
1282  mission_obj->variables = NULL;
1283  }
1284  mission_obj->num_variables = 0;
1285 
1286  Sexp_nodes[mission_obj->formula].value = SEXP_UNKNOWN;
1287  }
1288 
1289  if (do_next_mission)
1290  mission_campaign_next_mission(); // sets up whatever needs to be set to actually play next mission
1291 }
1292 
1298 {
1299  int i;
1300 
1301  if (Campaign.desc != NULL) {
1302  vm_free(Campaign.desc);
1303  Campaign.desc = NULL;
1304  }
1305 
1306  // be sure to remove all old malloced strings of Mission_names
1307  // we must also free any goal stuff that was from a previous campaign
1308  for ( i=0; i<Campaign.num_missions; i++ ) {
1309  if ( Campaign.missions[i].name != NULL ) {
1310  vm_free(Campaign.missions[i].name);
1311  Campaign.missions[i].name = NULL;
1312  }
1313 
1314  if (Campaign.missions[i].notes != NULL) {
1315  vm_free(Campaign.missions[i].notes);
1316  Campaign.missions[i].notes = NULL;
1317  }
1318 
1319  if ( Campaign.missions[i].goals != NULL ) {
1320  vm_free ( Campaign.missions[i].goals );
1321  Campaign.missions[i].goals = NULL;
1322  }
1323 
1324  if ( Campaign.missions[i].events != NULL ) {
1325  vm_free ( Campaign.missions[i].events );
1326  Campaign.missions[i].events = NULL;
1327  }
1328 
1329  // Goober5000
1330  if ( Campaign.missions[i].variables != NULL ) {
1331  vm_free ( Campaign.missions[i].variables );
1332  Campaign.missions[i].variables = NULL;
1333  }
1334 
1335  // the next three are strdup'd return values from parselo.cpp - taylor
1336  if (Campaign.missions[i].mission_branch_desc != NULL) {
1337  vm_free(Campaign.missions[i].mission_branch_desc);
1338  Campaign.missions[i].mission_branch_desc = NULL;
1339  }
1340 
1341  if (Campaign.missions[i].mission_branch_brief_anim != NULL) {
1343  Campaign.missions[i].mission_branch_brief_anim = NULL;
1344  }
1345 
1346  if (Campaign.missions[i].mission_branch_brief_sound != NULL) {
1348  Campaign.missions[i].mission_branch_brief_sound = NULL;
1349  }
1350 
1351  if ( !Fred_running ){
1352  sexp_unmark_persistent(Campaign.missions[i].formula); // free any sexpression nodes used by campaign.
1353  }
1354 
1355  memset(Campaign.missions[i].briefing_cutscene, 0, NAME_LENGTH);
1356  Campaign.missions[i].formula = 0;
1357  Campaign.missions[i].completed = 0;
1358  Campaign.missions[i].num_goals = 0;
1359  Campaign.missions[i].num_events = 0;
1360  Campaign.missions[i].num_variables = 0; // Goober5000
1361  Campaign.missions[i].mission_loop_formula = 0;
1362  Campaign.missions[i].level = 0;
1363  Campaign.missions[i].pos = 0;
1364  Campaign.missions[i].flags = 0;
1365  Campaign.missions[i].main_hall = "";
1366  Campaign.missions[i].debrief_persona_index = 0;
1367 
1368  Campaign.missions[i].stats.init();
1369  }
1370 
1371  memset(Campaign.name, 0, NAME_LENGTH);
1372  memset(Campaign.filename, 0, MAX_FILENAME_LEN);
1373  Campaign.type = 0;
1374  Campaign.flags = 0;
1375  Campaign.num_missions = 0;
1376  Campaign.num_missions_completed = 0;
1377  Campaign.current_mission = -1;
1378  Campaign.next_mission = -1;
1379  Campaign.prev_mission = -1;
1380  Campaign.loop_enabled = 0;
1382  Campaign.loop_reentry = 0;
1383  Campaign.realign_required = 0;
1384  Campaign.num_players = 0;
1385  memset( Campaign.ships_allowed, 0, sizeof(Campaign.ships_allowed) );
1386  memset( Campaign.weapons_allowed, 0, sizeof(Campaign.weapons_allowed) );
1387  Campaign.num_variables = 0;
1388  if (Campaign.variables != NULL) {
1389  vm_free(Campaign.variables);
1390  Campaign.variables = NULL;
1391  }
1392  Campaign.redalert_num_variables = 0;
1393  if (Campaign.redalert_variables != NULL) {
1394  vm_free(Campaign.redalert_variables);
1395  Campaign.redalert_variables = NULL;
1396  }
1397 }
1398 
1409 {
1410  // read the mission file and get the list of mission filenames
1411  try
1412  {
1413  Assert( strlen(filename) < MAX_FILENAME_LEN ); // make sure no overflow
1414  read_file_text(filename);
1415  reset_parse();
1416 
1417  required_string("$Name:");
1418  advance_to_eoln(NULL);
1419 
1420  required_string( "$Type:" );
1421  advance_to_eoln(NULL);
1422 
1423  // parse the mission file and actually read in the mission stuff
1424  *num = 0;
1425  while ( skip_to_string("$Mission:") == 1 ) {
1426  stuff_string(dest[*num], F_NAME, NAME_LENGTH);
1427  (*num)++;
1428  }
1429  }
1430  catch (const parse::ParseException& e)
1431  {
1432  mprintf(("MISSIONCAMPAIGN: Unable to parse '%s'! Error message = %s.\n", filename, e.what()));
1433  return 1;
1434  }
1435 
1436  return 0;
1437 }
1438 
1444 {
1445  char *filename, notes[NOTES_LENGTH], goals[MAX_GOALS][NAME_LENGTH];
1446  char events[MAX_MISSION_EVENTS][NAME_LENGTH];
1447  int i, z, event_count, count = 0;
1448 
1449  filename = Campaign.missions[num].name;
1450 
1451  try
1452  {
1453  read_file_text(filename);
1454  reset_parse();
1455  init_sexp();
1456 
1457  // first, read the mission notes for this mission. Used in campaign editor
1458  if (skip_to_string("#Mission Info")) {
1459  if (skip_to_string("$Notes:")) {
1461  if (Campaign.missions[num].notes){
1462  vm_free(Campaign.missions[num].notes);
1463  }
1464 
1465  Campaign.missions[num].notes = (char *)vm_malloc(strlen(notes) + 1);
1466  strcpy(Campaign.missions[num].notes, notes);
1467  }
1468  }
1469 
1470  event_count = 0;
1471  // skip to events section in the mission file. Events come before goals, so we process them first
1472  if (skip_to_string("#Events")) {
1473  while (1) {
1474  if (skip_to_string("$Formula:", "#Goals") != 1){
1475  break;
1476  }
1477 
1478  z = skip_to_string("+Name:", "$Formula:");
1479  if (!z){
1480  break;
1481  }
1482 
1483  if (z == 1){
1484  stuff_string(events[event_count], F_NAME, NAME_LENGTH);
1485  }
1486  else {
1487  sprintf(events[event_count], NOX("Event #%d"), event_count + 1);
1488  }
1489 
1490  event_count++;
1491  if (event_count > MAX_MISSION_EVENTS) {
1492  Warning(LOCATION, "Maximum number of events exceeded (%d)!", MAX_MISSION_EVENTS);
1493  event_count = MAX_MISSION_EVENTS;
1494  break;
1495  }
1496  }
1497  }
1498 
1499  count = 0;
1500  if (skip_to_string("#Goals")) {
1501  while (1) {
1502  if (skip_to_string("$Type:", "#End") != 1){
1503  break;
1504  }
1505 
1506  z = skip_to_string("+Name:", "$Type:");
1507  if (!z){
1508  break;
1509  }
1510 
1511  if (z == 1){
1512  stuff_string(goals[count], F_NAME, NAME_LENGTH);
1513  }
1514  else {
1515  sprintf(goals[count], NOX("Goal #%d"), count + 1);
1516  }
1517 
1518  count++;
1519  if (count > MAX_GOALS) {
1520  Warning(LOCATION, "Maximum number of goals exceeded (%d)!", MAX_GOALS);
1521  count = MAX_GOALS;
1522  break;
1523  }
1524  }
1525  }
1526 
1527  Campaign.missions[num].num_goals = count;
1528  if (count) {
1529  Campaign.missions[num].goals = (mgoal *)vm_malloc(count * sizeof(mgoal));
1530  Assert(Campaign.missions[num].goals); // make sure we got the memory
1531  memset(Campaign.missions[num].goals, 0, count * sizeof(mgoal));
1532 
1533  for (i = 0; i < count; i++){
1534  strcpy_s(Campaign.missions[num].goals[i].name, goals[i]);
1535  }
1536  }
1537  // copy the events
1538  Campaign.missions[num].num_events = event_count;
1539  if (event_count) {
1540  Campaign.missions[num].events = (mevent *)vm_malloc(event_count * sizeof(mevent));
1541  Assert(Campaign.missions[num].events);
1542  memset(Campaign.missions[num].events, 0, event_count * sizeof(mevent));
1543 
1544  for (i = 0; i < event_count; i++){
1545  strcpy_s(Campaign.missions[num].events[i].name, events[i]);
1546  }
1547  }
1548 
1549  // Goober5000 - variables do not need to be read here
1550  }
1551  catch (const parse::ParseException& e)
1552  {
1553  mprintf(("MISSIONCAMPAIGN: Unable to parse '%s'! Error message = %s.\n", filename, e.what()));
1554  return;
1555  }
1556 }
1557 
1568 {
1569  int i;
1570  char realname[_MAX_PATH];
1571 
1572  if (name == NULL)
1573  return -1;
1574 
1575  // look for an extension on the file. If no extension, add default ".fsm" onto the
1576  // end of the filename
1577  strcpy_s(realname, name );
1578  if ( strchr(name, '.') == NULL ){
1579  sprintf(realname, NOX("%s%s"), name, FS_MISSION_FILE_EXT );
1580  }
1581 
1582  for (i = 0; i < Campaign.num_missions; i++ ) {
1583  if ( !stricmp(realname, Campaign.missions[i].name) ){
1584  return i;
1585  }
1586  }
1587 
1588  return -1;
1589 }
1590 
1592 {
1593  int mission_idx;
1594  char *filename;
1595 
1596  // only support pre mission movies for now.
1597  Assert ( type == CAMPAIGN_MOVIE_PRE_MISSION );
1598 
1599  if ( !(Game_mode & GM_CAMPAIGN_MODE) )
1600  return;
1601 
1602  mission_idx = Campaign.current_mission;
1603  Assert( mission_idx != -1 );
1604 
1605  // get a possible filename for a movie to play.
1606  filename = NULL;
1607  switch( type ) {
1609  if ( strlen(Campaign.missions[mission_idx].briefing_cutscene) )
1610  filename = Campaign.missions[mission_idx].briefing_cutscene;
1611  break;
1612 
1613  default:
1614  Int3();
1615  break;
1616  }
1617 
1618  // no filename, no movie!
1619  if ( !filename )
1620  return;
1621 
1622  movie_play( filename ); //Play the movie!
1623  cutscene_mark_viewable( filename );
1624 }
1625 
1630 {
1631  int i;
1632  char temp[NAME_LENGTH];
1633 
1634  try
1635  {
1636  read_file_text(filename);
1637  reset_parse();
1638 
1639  required_string("$Name:");
1641  if (name)
1642  strcpy(name, temp);
1643 
1644  required_string("$Type:");
1646 
1647  for (i = 0; i < MAX_CAMPAIGN_TYPES; i++) {
1648  if (!stricmp(temp, campaign_types[i])) {
1649  return i;
1650  }
1651  }
1652 
1653  Error(LOCATION, "Unknown campaign type %s", temp);
1654  return -1;
1655  }
1656  catch (const parse::ParseException& e)
1657  {
1658  mprintf(("MISSIONCAMPAIGN: Unable to parse '%s'! Error message = %s.\n", filename, e.what()));
1659  return -1;
1660  }
1661 }
1662 
1668 {
1669  // based on the type of information, save it off for possible saving into the campsign
1670  // savefile when the mission is over
1671  if ( type == CAMPAIGN_PERSISTENT_SHIP ) {
1673  Granted_ships[Num_granted_ships] = sindex;
1675  } else if ( type == CAMPAIGN_PERSISTENT_WEAPON ) {
1679  } else
1680  Int3();
1681 }
1682 
1684 {
1685  SCP_string filename_no_ext = filename;
1686  drop_extension(filename_no_ext);
1687 
1688  for (SCP_vector<SCP_string>::iterator ii = Ignored_campaigns.begin(); ii != Ignored_campaigns.end(); ++ii) {
1689  if (ii->compare(filename_no_ext) == 0) {
1690  return true;
1691  }
1692  }
1693 
1694  return false;
1695 }
1696 
1697 // returns 0: loaded, !0: error
1699 {
1700  int rc = -1, idx;
1701 
1702  if ( pl == NULL )
1703  pl = Player;
1704 
1705  // find best campaign to use:
1706  // 1) last used
1707  // 2) builtin
1708  // 3) anything else
1709 
1710  // last used...
1711  if ( strlen(pl->current_campaign) ) {
1713  return mission_campaign_load(pl->current_campaign, pl);
1714  }
1715  else {
1717  }
1718  }
1719 
1721 
1722  // builtin...
1723  if (rc < 0) {
1725  }
1726 
1727  // everything else...
1728  if (rc < 0) {
1729  // no descriptions, no sorting
1730  mission_campaign_build_list(false, false);
1731 
1732  for (idx = 0; (idx < Num_campaigns) && (rc < 0); idx++) {
1733  if ( (Campaign_file_names[idx] == NULL) || !strlen(Campaign_file_names[idx]) ) {
1734  continue;
1735  }
1736 
1737  // skip current and builtin since they already didn't work
1738  if ( !stricmp(Campaign_file_names[idx], pl->current_campaign) ) {
1739  continue;
1740  }
1741 
1743  continue;
1744  }
1745 
1746  // try to load it, whatever "it" is
1748  }
1749 
1751  }
1752 
1753  // update pilot with the new current campaign
1754  if (rc == 0) {
1755  strcpy_s(pl->current_campaign, Campaign.filename);
1756  }
1757 
1758  return rc;
1759 }
1760 
1766 {
1767  // no need to do any initialization.
1768 }
1769 
1771 {
1772  // close out the mission
1777 
1778  // play the movies
1779  // eventually we'll want to play one of two options (good ending or bad ending)
1780 
1781  // this is specific to the FreeSpace 2 single-player campaign
1782  if (!stricmp(Campaign.filename, "freespace2")) {
1783  // did the supernova blow?
1785  movie_play_two("endpart1.mve", "endprt2b.mve"); // bad ending
1786  } else {
1787  movie_play_two("endpart1.mve", "endprt2a.mve"); // good ending
1788  }
1789  } else {
1791  }
1792 
1794 }
1795 
1797 {
1798  // no need to do any initialization.
1799 }
1800 
1801 
1806 void mission_campaign_skip_to_next(int start_game)
1807 {
1808  // mark all goals/events complete
1809  // these do not really matter, since is-previous-event-* and is-previous-goal-* sexps check
1810  // to see if the mission was skipped, and use defaults accordingly.
1813 
1814  // mark mission as skipped
1815  Campaign.missions[Campaign.current_mission].flags |= CMISSION_FLAG_SKIPPED;
1816 
1817  // store
1819 
1820  // now set the next mission
1822 
1823  // clear out relevant player vars
1825  Player->show_skip_popup = 1;
1826 
1827  if (start_game) {
1828  // proceed to next mission or main hall
1829  if ((Campaign.missions[Campaign.current_mission].flags & CMISSION_FLAG_HAS_LOOP) && (Campaign.loop_mission != -1)) {
1830  // go to loop solicitation
1832  } else {
1833  // closes out mission stuff, sets up next one
1835 
1836  if ( Campaign.next_mission == -1 || (The_mission.flags & MISSION_FLAG_END_TO_MAINHALL) ) {
1837  // go to main hall, either the campaign is over or the FREDer requested it.
1839  } else {
1840  // go to next mission
1842  }
1843  }
1844  }
1845 }
1846 
1852 {
1853  // set campaign to loop reentry point
1854  Campaign.next_mission = Campaign.loop_reentry;
1855  Campaign.current_mission = -1;
1856  Campaign.loop_enabled = 0;
1857 
1858  // set things up for next mission
1861 }
1862 
1863 
1869 void mission_campaign_jump_to_mission(char *name, bool no_skip)
1870 {
1871  int i = 0, mission_num = -1;
1872  char dest_name[64], *p;
1873 
1874  // load in the campaign junk
1876 
1877  // tack the .fs2 onto the input name
1878  strcpy_s(dest_name, name);
1879  p = strchr(dest_name, '.');
1880  if (p != NULL)
1881  *p = '\0';
1882  strcat_s(dest_name, ".fs2");
1883 
1884  // search for our mission
1885  for (i = 0; i < Campaign.num_missions; i++) {
1886  if ((Campaign.missions[i].name != NULL) && !stricmp(Campaign.missions[i].name, dest_name)) {
1887  mission_num = i;
1888  break;
1889  } else if (!no_skip) {
1890  Campaign.missions[i].flags |= CMISSION_FLAG_SKIPPED;
1891  Campaign.num_missions_completed = i;
1892  }
1893  }
1894 
1895  if (mission_num < 0) {
1896  // if we got here, no match was found
1897  // restart the campaign
1899  mission_campaign_load(Campaign.filename);
1900  } else {
1901  for (SCP_vector<ship_info>::iterator it = Ship_info.begin(); it != Ship_info.end(); it++) {
1902  i = static_cast<int>(std::distance(Ship_info.begin(), it));
1903  Campaign.ships_allowed[i] = 1;
1904  }
1905  for (i = 0; i < Num_weapon_types; i++) {
1906  Campaign.weapons_allowed[i] = 1;
1907  }
1908 
1909  Campaign.next_mission = mission_num;
1910  Campaign.prev_mission = mission_num - 1;
1913 
1915  }
1916 }
1917 
1918 // Goober5000
1920 {
1921  int i;
1922 
1923  // make sure we are actually playing a campaign
1924  if (!(Game_mode & GM_CAMPAIGN_MODE))
1925  return;
1926 
1927  // make sure this is a single-player campaign
1928  if (!(Campaign.type == CAMPAIGN_TYPE_SINGLE))
1929  return;
1930 
1931  // now save variables
1932  for (i = 0; i < sexp_variable_count(); i++) {
1933  // we only want the player persistent ones
1935  continue;
1936  }
1937 
1938  bool found = false;
1939 
1940  // check if variable already exists and updated it
1941  for (size_t j = 0; j < Player->variables.size(); j++) {
1942  if ( !(stricmp(Sexp_variables[i].variable_name, Player->variables[j].variable_name)) ) {
1944 
1945  found = true;
1946  break;
1947  }
1948  }
1949 
1950  // if not found then add new entry
1951  if ( !found ) {
1952  Player->variables.push_back( Sexp_variables[i] );
1953  }
1954  }
1955 }
1956 
1958 {
1959  if (Campaign_load_failure == 0) {
1960  return;
1961  }
1962 
1964  popup(PF_TITLE_BIG | PF_TITLE_RED | PF_USE_AFFIRMATIVE_ICON | PF_NO_NETWORKING, 1, POPUP_OK, XSTR("Error!\n\nRequested campaign is corrupt and cannot be loaded.\n\n"
1965  "Please select a different campaign in the Campaign Room.", 1614));
1967  popup(PF_TITLE_BIG | PF_TITLE_RED | PF_USE_AFFIRMATIVE_ICON | PF_NO_NETWORKING, 1, POPUP_OK, XSTR("Error!\n\nRequested campaign requires too many SEXPs and cannot be loaded.\n\n"
1968  "Please select a different campaign in the Campaign Room.", 1615));
1970  // if it's just the campaign missing, there's another popup to deal with that
1971  ;
1973  popup(PF_TITLE_BIG | PF_TITLE_RED | PF_USE_AFFIRMATIVE_ICON | PF_NO_NETWORKING, 1, POPUP_OK, XSTR("Error!\n\nThe pilot savefile "
1974  "for this campaign is invalid for the current mod.\n\nPlease select another campaign or switch to the correct "
1975  "mod in order to use this campaign.", 1617));
1976  }
1977 
1979 }
1980 
GLuint64EXT * result
Definition: Glext.h:10775
cfbp dir_type
Definition: cfile.cpp:1069
#define CAMPAIGN_ERROR_IGNORED
void advance_to_eoln(char *more_terminators)
Definition: parselo.cpp:335
#define MAX_FILENAME_LEN
Definition: pstypes.h:324
pilotfile Pilot
Definition: pilotfile.cpp:7
#define FS_MISSION_FILE_EXT
Definition: missionparse.h:25
int i
Definition: multi_pxo.cpp:466
#define vm_free(ptr)
Definition: pstypes.h:548
#define CAMPAIGN_TYPE_SINGLE
char * Campaign_names[MAX_CAMPAIGNS]
void mission_campaign_exit_loop()
int cf_delete(const char *filename, int path_type)
Delete the specified file.
Definition: cfile.cpp:483
int Num_mission_events
#define CAMPAIGN_MOVIE_PRE_MISSION
int flags
Definition: player.h:104
int Game_mode
Definition: systemvars.cpp:24
#define SIF_PLAYER_SHIP
Definition: ship.h:875
char Game_current_mission_filename[MAX_FILENAME_LEN]
Definition: fredstubs.cpp:26
#define EVENT_SATISFIED
Definition: missiongoals.h:78
#define CF_TYPE_PLAYERS
Definition: cfile.h:67
#define MAX_CAMPAIGN_TYPES
int realign_required
void mission_campaign_clear()
char name[NAME_LENGTH]
bool save_savefile()
Definition: csg.cpp:1524
int Fred_running
Definition: fred.cpp:44
void campaign_delete_save(char *cfn, char *pname)
char * stuff_and_malloc_string(int type, char *terminators)
Definition: parselo.cpp:1406
char * mission_branch_desc
bool is_invalid()
Definition: pilotfile.h:53
LOCAL UI_BUTTON Campaign_cancelb
int mission_campaign_parse_is_multi(char *filename, char *name)
LOCAL UI_BUTTON Campaign_okb
int mission_loop_formula
void _cdecl void void _cdecl void _cdecl Warning(char *filename, int line, SCP_FORMAT_STRING const char *format,...) SCP_FORMAT_STRING_ARGS(3
Assert(pm!=NULL)
#define CAMPAIGN_ERROR_CORRUPT
char filename[MAX_FILENAME_LEN]
void mission_campaign_delete_all_savefiles(char *pilot_name)
#define mprintf(args)
Definition: pstypes.h:238
GLuint GLuint * names
Definition: Glext.h:11016
char * Campaign_descs[MAX_CAMPAIGNS]
ubyte debrief_persona_index
int weapon_pool[MAX_WEAPON_TYPES]
#define _MAX_PATH
Definition: config.h:221
void flush_sexp_tree(int node)
Definition: sexp.cpp:1342
int Num_granted_weapons
LOCAL UI_WINDOW Campaign_window
char briefing_cutscene[NAME_LENGTH]
int sexp_campaign_persistent_variable_count()
Definition: sexp.cpp:29506
#define MAX_WSS_SLOTS
int Campaign_load_failure
#define CF_DEFAULT_VALUE
sexp_variable Sexp_variables[MAX_SEXP_VARIABLES]
Definition: sexp.cpp:846
void mission_campaign_end_do()
void mission_campaign_mission_over(bool do_next_mission)
char name[NAME_LENGTH]
Definition: ui.h:195
std::basic_string< char, std::char_traits< char >, std::allocator< char > > SCP_string
Definition: vmallocator.h:21
#define CMISSION_FLAG_BASTION
int mission_campaign_get_info(const char *filename, char *name, int *type, int *max_players, char **desc)
ubyte ships_allowed[MAX_SHIP_CLASSES]
#define Int3()
Definition: pstypes.h:292
int required_string_either(char *str1, char *str2)
Checks for one of two required strings.
Definition: parselo.cpp:673
int get_sexp_main()
Definition: sexp.cpp:25494
mission_goal Mission_goals[MAX_GOALS]
char callsign[CALLSIGN_LEN+1]
Definition: player.h:91
int Campaign_names_inited
char * name
GLenum type
Definition: Gl.h:1492
void campaign_savefile_load(char *fname, char *pname)
int mission_campaign_get_filenames(char *filename, char dest[][NAME_LENGTH], int *num)
int wep_count[MAX_SHIP_WEAPONS]
sexp_variable * redalert_variables
void mission_campaign_skip_to_next(int start_game)
int Campaign_file_missing
player Players[MAX_PLAYERS]
#define F_NOTES
Definition: parselo.h:36
#define FS_CAMPAIGN_FILE_EXT
Definition: freespace.h:26
int mission_campaign_maybe_add(const char *filename)
int mission_campaign_previous_mission()
void sexp_mark_persistent(int n)
Definition: sexp.cpp:1220
typedef int(SCP_EXT_CALLCONV *SCPDLL_PFVERSION)(SCPDLL_Version *)
Definition: player.h:85
void mission_campaign_maybe_play_movie(int type)
void mission_goal_mark_objectives_complete()
#define MAX_SHIP_WEAPONS
Definition: globals.h:64
void scoring_backout_accept(scoring_struct *score)
Definition: scoring.cpp:378
void common_maybe_play_cutscene(int movie_type, bool restart_music, int music)
GLenum pname
Definition: Gl.h:1517
#define nprintf(args)
Definition: pstypes.h:239
#define GM_MULTIPLAYER
Definition: systemvars.h:18
char current_campaign[MAX_FILENAME_LEN+1]
Definition: player.h:101
void mission_campaign_store_goals_and_events()
char * notes
void scoring_level_close(int accepted)
Definition: scoring.cpp:432
#define CAMPAIGN_ERROR_SAVEFILE
ubyte weapons_allowed[MAX_WEAPON_TYPES]
#define MAX_GOALS
Definition: missiongoals.h:23
main_hall_defines * main_hall_get_pointer(const SCP_string &name_to_find)
void mission_campaign_end_close()
#define MAX_SHIP_CLASSES
Definition: globals.h:48
char * filename
void mission_campaign_end_init()
#define strnicmp(s1, s2, n)
Definition: config.h:272
mission_event Mission_events[MAX_MISSION_EVENTS]
#define MAX_CAMPAIGNS
void mission_campaign_get_sw_info()
void stuff_string(char *outstr, int type, int len, char *terminators)
Definition: parselo.cpp:1189
#define MAX_WEAPON_TYPES
Definition: globals.h:73
void movie_play_two(char *name1, char *name2)
Definition: movie.cpp:159
#define MISSION_FLAG_END_TO_MAINHALL
Definition: missionparse.h:92
sprintf(buf,"(%f,%f,%f)", v3->xyz.x, v3->xyz.y, v3->xyz.z)
char Default_campaign_file_name[MAX_FILENAME_LEN-4]
#define CAMPAIGN_PERSISTENT_SHIP
wss_unit unit_data[MAX_WSS_SLOTS]
GLdouble GLdouble z
Definition: Glext.h:5451
void sexp_unmark_persistent(int n)
Definition: sexp.cpp:1242
int num_variables
#define vm_strdup(ptr)
Definition: pstypes.h:549
int required_string(const char *pstr)
Definition: parselo.cpp:468
#define WEAPON_POOL_TYPE
Definition: parselo.h:53
#define MAX_PLAYERS
Definition: pstypes.h:32
int optional_string(const char *pstr)
Definition: parselo.cpp:539
int failures_this_session
Definition: player.h:202
#define BUILTIN_CAMPAIGN
ubyte show_skip_popup
Definition: player.h:203
void read_file_text(const char *filename, int mode, char *processed_text, char *raw_text)
Definition: parselo.cpp:1995
int Num_granted_ships
char * mission_branch_brief_anim
int idx
Definition: multiui.cpp:761
sexp_node * Sexp_nodes
Definition: sexp.cpp:844
GLdouble GLdouble t
Definition: Glext.h:5329
char name[NAME_LENGTH]
int type
Definition: sexp.h:1043
void mission_campaign_savefile_delete(char *cfilename)
void player_loadout_init()
#define CF_SORT_NONE
Definition: cfile.h:92
int sexp_variable_count()
Definition: sexp.cpp:29490
const char * XSTR(const char *str, int index)
Definition: localize.cpp:851
int mission_campaign_find_mission(char *name)
int mission_load_up_campaign(player *pl)
sexp_variable * variables
void _splitpath(char *path, char *drive, char *dir, char *fname, char *ext)
#define NOX(s)
Definition: pstypes.h:473
void _cdecl void void _cdecl Error(const char *filename, int line, SCP_FORMAT_STRING const char *format,...) SCP_FORMAT_STRING_ARGS(3
#define CMISSION_FLAG_HAS_FORK
void red_alert_clear()
Definition: redalert.cpp:1103
void mission_campaign_savefile_generate_root(char *filename, player *pl)
GLbitfield flags
Definition: Glext.h:6722
int Num_weapon_types
Definition: weapons.cpp:105
#define vm_malloc(size)
Definition: pstypes.h:547
#define GOAL_INCOMPLETE
Definition: missiongoals.h:40
#define SHIP_INFO_TYPE
Definition: parselo.h:50
void reset_parse(char *text)
Definition: parselo.cpp:3305
void assign(const scoring_struct &s)
Definition: scoring.cpp:173
GLuint const GLchar * name
Definition: Glext.h:5608
void mission_goal_fail_incomplete()
char * mission_branch_brief_sound
char * cf_add_ext(const char *filename, const char *ext)
Definition: cfile.cpp:458
int current_mission
void mission_campaign_store_variables()
void stuff_int(int *i)
Definition: parselo.cpp:2372
int stuff_int_list(int *ilp, int max_ints, int lookup_type)
Definition: parselo.cpp:2782
char status
#define CMISSION_FLAG_SKIPPED
if(aifft_max_checks<=0)
Definition: aiturret.cpp:1581
GLuint GLuint num
Definition: Glext.h:9089
#define EVENT_FAILED
Definition: missiongoals.h:79
void mission_campaign_eval_next_mission()
SCP_string main_hall
bool load_savefile(const char *campaign)
Definition: csg.cpp:1347
void mission_campaign_store_goals_and_events_and_variables()
campaign Campaign
#define strcat_s(...)
Definition: safe_strings.h:68
int mission_campaign_load(char *filename, player *pl, int load_savefile, bool reset_stats)
#define NAME_LENGTH
Definition: globals.h:15
int wep[MAX_SHIP_WEAPONS]
int mission_campaign_load_by_name_csfe(char *filename, char *callsign)
loadout_data Player_loadout
int eval_sexp(int cur_node, int referenced_node)
Definition: sexp.cpp:22894
player * Player
void read_mission_goal_list(int num)
int Campaign_ending_via_supernova
Definition: ui.h:584
int mission_campaign_load_by_name(char *filename)
#define CAMPAIGN_ERROR_SEXP_EXHAUSTED
#define SEXP_VARIABLE_CAMPAIGN_PERSISTENT
Definition: sexp.h:886
void mission_campaign_jump_to_mission(char *name, bool no_skip)
mgoal * goals
#define CF_TYPE_MISSIONS
Definition: cfile.h:74
GLfloat GLfloat p
Definition: Glext.h:8373
void mission_campaign_init()
int mission_campaign_get_mission_list(const char *filename, char **list, int max)
#define F_NAME
Definition: parselo.h:34
SCP_vector< ship_info > Ship_info
Definition: ship.cpp:164
#define LOCATION
Definition: pstypes.h:245
LOCAL UI_LISTBOX Campaign_listbox
mevent * events
int mission_campaign_next_mission()
SCP_vector< SCP_string > Ignored_campaigns
bool movie_play(char *name)
Definition: movie.cpp:72
#define MAX_MISSION_EVENTS
Definition: missiongoals.h:71
char name[NAME_LENGTH]
Definition: missiongoals.h:55
void mission_campaign_save_player_persistent_variables()
void mission_campaign_save_persistent(int type, int sindex)
SCP_vector< sexp_variable > variables
Definition: player.h:206
int campaign_savefile_save(char *pname)
void event_music_level_close()
Definition: eventmusic.cpp:691
char name[NAME_LENGTH]
Definition: missiongoals.h:102
GLint GLsizei count
Definition: Gl.h:1491
#define CAMPAIGN_LOOP_MISSION_UNINITIALIZED
char status
void cutscene_mark_viewable(char *filename)
Definition: cutscenes.cpp:126
GLenum GLsizei len
Definition: Glext.h:6283
int value
Definition: sexp.h:1033
char variable_name[TOKEN_LENGTH]
Definition: sexp.h:1045
char filename[MAX_FILENAME_LEN]
int temp
Definition: lua.cpp:4996
void stuff_ubyte(ubyte *i)
Definition: parselo.cpp:2646
void mission_campaign_build_list(bool desc, bool sort, bool multiplayer)
int Supernova_status
Definition: supernova.cpp:44
#define PLAYER_FLAGS_IS_MULTI
Definition: player.h:42
void mission_goal_mark_events_complete()
#define GM_NORMAL
Definition: systemvars.h:19
#define NOTES_LENGTH
Definition: globals.h:20
scoring_struct stats
int Granted_weapons[MAX_WEAPON_TYPES]
bool drop_extension(char *str)
Definition: parselo.cpp:4043
mission The_mission
int Num_campaigns
int Num_goals
#define SEXP_UNKNOWN
Definition: sexp.h:923
int num_missions_completed
void init_sexp()
Definition: sexp.cpp:1090
int cf_get_file_list(SCP_vector< SCP_string > &list, int pathtype, const char *filter, int sort=CF_SORT_NONE, SCP_vector< file_list_info > *info=NULL)
#define LOCAL
Definition: pstypes.h:37
#define CAMPAIGN_PERSISTENT_WEAPON
#define CAMPAIGN_ERROR_MISSING
sexp_variable * variables
void gameseq_post_event(int event)
#define CMISSION_FLAG_HAS_LOOP
Definition: ui.h:522
#define F_MULTITEXT
Definition: parselo.h:43
#define GM_CAMPAIGN_MODE
Definition: systemvars.h:29
char * Campaign_file_names[MAX_CAMPAIGNS]
#define stricmp(s1, s2)
Definition: config.h:271
bool campaign_is_ignored(const char *filename)
int ship_pool[MAX_SHIP_CLASSES]
char * campaign_types[MAX_CAMPAIGN_TYPES]
int skip_to_string(char *pstr, char *end)
Definition: parselo.cpp:375
int Granted_ships[MAX_SHIP_CLASSES]
void mission_campaign_free_list()
cmission missions[MAX_CAMPAIGN_MISSIONS]
int redalert_num_variables
int(* Get_file_list_filter)(const char *filename)
#define F_RAW
Definition: parselo.h:44
scoring_struct stats
Definition: player.h:127
char last_modified[DATE_TIME_LENGTH]
#define SUPERNOVA_HIT
Definition: supernova.h:34
int Player_num
#define strcpy_s(...)
Definition: safe_strings.h:67
void mission_campaign_load_failure_popup()
#define SEXP_VARIABLE_PLAYER_PERSISTENT
Definition: sexp.h:883
#define _MAX_FNAME
Definition: config.h:220
#define MOVIE_END_CAMPAIGN
Definition: missionparse.h:121