]> icculus.org git repositories - taylor/freespace2.git/blob - src/mission/missionload.cpp
myrand() fixes
[taylor/freespace2.git] / src / mission / missionload.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/MissionLoad.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  *
15  * C source module for mission loading
16  *
17  * $Log$
18  * Revision 1.3  2005/03/31 00:04:25  taylor
19  * fix directory separator (thanks Pierre\!)
20  *
21  * Revision 1.2  2002/06/09 04:41:22  relnev
22  * added copyright header
23  *
24  * Revision 1.1.1.1  2002/05/03 03:28:09  root
25  * Initial import.
26  *
27  * 
28  * 5     7/20/99 1:49p Dave
29  * Peter Drake build. Fixed some release build warnings.
30  * 
31  * 4     10/13/98 9:28a Dave
32  * Started neatening up freespace.h. Many variables renamed and
33  * reorganized. Added AlphaColors.[h,cpp]
34  * 
35  * 3     10/07/98 6:27p Dave
36  * Globalized mission and campaign file extensions. Removed Silent Threat
37  * special code. Moved \cache \players and \multidata into the \data
38  * directory.
39  * 
40  * 2     10/07/98 10:53a Dave
41  * Initial checkin.
42  * 
43  * 1     10/07/98 10:49a Dave
44  * 
45  * 102   5/19/98 1:19p Allender
46  * new low level reliable socket reading code.  Make all missions/campaign
47  * load/save to data missions folder (i.e. we are rid of the player
48  * missions folder)
49  * 
50  * 101   5/10/98 10:05p Allender
51  * only show cutscenes which have been seen before.  Made Fred able to
52  * write missions anywhere, defaulting to player misison folder, not data
53  * mission folder.  Fix FreeSpace code to properly read missions from
54  * correct locations
55  * 
56  * 100   4/30/98 4:53p John
57  * Restructured and cleaned up cfile code.  Added capability to read off
58  * of CD-ROM drive and out of multiple pack files.
59  * 
60  * 99    2/23/98 6:55p Lawrance
61  * Rip out obsolete code.
62  * 
63  * 98    2/23/98 8:53a John
64  * String externalization
65  * 
66  * 97    1/19/98 9:37p Allender
67  * Great Compiler Warning Purge of Jan, 1998.  Used pragma's in a couple
68  * of places since I was unsure of what to do with code.
69  * 
70  * 96    1/17/98 8:49p Hoffoss
71  * Fixed mission_load() calls to handle failure correctly.
72  * 
73  * 95    12/28/97 1:34p John
74  * Fixed yet another mission filename bug
75  * 
76  * 94    12/28/97 12:42p John
77  * Put in support for reading archive files; Made missionload use the
78  * cf_get_file_list function.   Moved demos directory out of data tree.
79  * 
80  * 93    12/27/97 2:39p John
81  * Took out the outdated ui_getfilelist functions.  Made the mission load
82  * screen use cf_get_filelist instead.  Fixed a bug in mission load that
83  * crashed the program if there are no missions available.
84  * 
85  * 92    12/23/97 12:00p Allender
86  * change write_pilot_file to *not* take is_single as a default parameter.
87  * causing multiplayer pilots to get written to the single player folder
88  * 
89  * 91    11/11/97 4:57p Dave
90  * Put in support for single vs. multiplayer pilots. Began work on
91  * multiplayer campaign saving. Put in initial player select screen
92  * 
93  * 90    10/31/97 11:27a John
94  * appended path for j:\tmp missions
95  * 
96  * 89    10/31/97 11:19a John
97  * added filter catagory for j:\tmp\*.fsm
98  * 
99  * 88    10/12/97 5:22p Lawrance
100  * have ESC back out of mission load screen
101  * 
102  * 87    9/18/97 10:19p Lawrance
103  * Add a mission campaign filter to the load screen
104  * 
105  * 86    9/16/97 2:41p Allender
106  * beginning of code to change way player starts are handled.  Reordered
107  * some code when missions loading since player ship is now created at
108  * mission load time instead of before misison load
109  * 
110  * 85    8/25/97 5:47p Mike
111  * Increase number of missions supported in mission load list (outside
112  * campaign) to 256 and Assert() if there are more than 256.
113  * 
114  * 84    8/20/97 5:19p Hoffoss
115  * Fixed bug where creating a new pilot causes the mission load mission
116  * list box to be empty.
117  * 
118  * 83    7/28/97 10:53a Lawrance
119  * initialize a timestamp
120  * 
121  * 82    7/17/97 4:25p John
122  * First, broken, stage of changing config stuff
123  * 
124  * 81    7/05/97 1:47p Lawrance
125  * write pilot file when a mission is loaded
126  * 
127  * 80    6/26/97 5:53p Lawrance
128  * save recently played missions, allow player to choose from list
129  * 
130  * 79    6/12/97 12:39p John
131  * made ui use freespace colors
132  * 
133  * 78    6/12/97 11:35a John
134  * more menu backgrounds
135  * 
136  * 77    5/14/97 1:33p Allender
137  * remmoved extern declaration
138  * 
139  * 76    5/12/97 4:59p Allender
140  * move the rest of the mission initialization functions into
141  * game_level_init().  All mission loading now going through this
142  * fucntion.
143  * 
144  * 75    5/12/97 3:21p Allender
145  * re-ordered mission load code into single function in Freespace.
146  * Simulation part now runs as seperate thread
147  * 
148  * 74    5/12/97 12:27p John
149  * Restructured Graphics Library to add support for multiple renderers.
150  * 
151  * 73    4/28/97 5:43p Lawrance
152  * allow hotkey assignment screen to work from ship selection
153  * 
154  * 72    4/25/97 11:31a Allender
155  * Campaign state now saved in campaign save file in player directory.
156  * Made some global variables follow naming convention.  Solidified
157  * continuing campaigns based on new structure
158  * 
159  * 71    4/23/97 4:46p Allender
160  * remove unused code
161  * 
162  * 70    4/23/97 3:21p Allender
163  * more campaign stuff -- mission branching through campaign file now
164  * works!!!!
165  * 
166  * 69    4/22/97 10:44a Allender
167  * more campaign stuff.  Info about multiple campaigns now stored in
168  * player file -- not saving some player information in save games.
169  * 
170  * 68    4/18/97 9:59a Allender
171  * more campaign stuff.  All campaign related varaibles now stored in
172  * campaign structure
173  * 
174  * 67    4/17/97 9:02p Allender
175  * new campaign stuff.  all campaign related material stored in external
176  * file.  Continuing campaign won't work at this time
177  * 
178  * 66    4/15/97 4:37p Lawrance
179  * removed unused variables
180  *
181 */
182
183 #include "missionload.h"
184 #include "missiongoals.h"
185 #include "missionparse.h"    
186 #include "missionshipchoice.h"
187 #include "missionlog.h"
188 #include "missionmessage.h"
189 #include "cfile.h"
190 #include "osapi.h"
191 #include "vecmat.h"
192 #include "player.h"
193 #include "object.h"
194 #include "ship.h"
195 #include "ailocal.h"
196 #include "managepilot.h"
197 #include "hud.h"
198 #include "freespace.h"
199 #include "key.h"
200 #include "2d.h"
201 #include "line.h"
202 #include "timer.h"
203 #include "math.h"
204 #include "linklist.h"
205 #include "mouse.h"
206 #include "weapon.h"
207 #include "gamesequence.h"
208 #include "ui.h"
209 #include "sexp.h"
210 #include "missionhotkey.h"
211 #include "missioncampaign.h"
212 #include "cfilesystem.h"
213 #include "alphacolors.h"
214
215
216 extern mission The_mission;  // need to send this info to the briefing
217 extern int shifted_ascii_table[];
218 extern int ascii_table[];
219
220 // -----------------------------------------------
221 // For recording most recent missions played
222 // -----------------------------------------------
223 char    Recent_missions[MAX_RECENT_MISSIONS][MAX_FILENAME_LEN];
224 int     Num_recent_missions;
225
226
227 // -----------------------------------------------------
228 // ml_update_recent_missions()
229 //
230 //      Update the Recent_missions[][] array
231 //
232 void ml_update_recent_missions(char *filename)
233 {
234         char    tmp[MAX_RECENT_MISSIONS][MAX_FILENAME_LEN], *p;
235         int     i,j;
236         
237
238         for ( i = 0; i < Num_recent_missions; i++ ) {
239                 strcpy( tmp[i], Recent_missions[i] );
240         }
241
242         // get a pointer to just the basename of the filename (including extension)
243         p = strrchr(filename, DIR_SEPARATOR_CHAR);
244         if ( p == NULL ) {
245                 p = filename;
246         } else {
247                 p++;
248         }
249
250         Assert(strlen(p) < MAX_FILENAME_LEN);
251         strcpy( Recent_missions[0], p );
252
253         j = 1;
254         for ( i = 0; i < Num_recent_missions; i++ ) {
255                 if ( stricmp(Recent_missions[0], tmp[i]) ) {
256                         strcpy(Recent_missions[j++], tmp[i]);
257                         if ( j >= MAX_RECENT_MISSIONS ) {
258                                 break;
259                         }
260                 }
261         }
262
263         Num_recent_missions = j;
264         Assert(Num_recent_missions <= MAX_RECENT_MISSIONS);
265 }
266
267 // Mission_load takes no parameters.
268 // It expects the following global variables to be set correctly:
269 //   Game_current_mission_filename
270
271 // returns -1 if failed, 0 if successful
272 int mission_load()
273 {
274         char filename[128], *ext;       
275
276         mprintf(("MISSION LOAD: '%s'\n", Game_current_mission_filename));
277
278         strncpy(filename, Game_current_mission_filename, 127);
279         ext = strchr(filename, '.');
280         if (ext) {
281                 mprintf(( "Hmmm... Extension passed to mission_load...\n" ));
282                 *ext = 0;                               // remove any extension!
283         }
284
285         strcat(filename, FS_MISSION_FILE_EXT);
286
287         // does the magical mission parsing
288         // creates all objects, except for the player object
289         // save the player object later since the player may get
290         // to choose the type of ship that he is to fly
291         // return value of 0 indicates success, other is failure.
292
293         if ( parse_main(filename) )
294                 return -1;
295
296         if (Select_default_ship) {
297                 int ret;
298                 ret = create_default_player_ship();
299                 Assert(!ret);
300         }
301
302         ml_update_recent_missions(Game_current_mission_filename);  // update recently played missions list
303         write_pilot_file();
304         return 0;
305 }
306
307 //====================================
308 // Mission Load Menu stuff
309 #define MLM_MAX_MISSIONS 256
310 int mlm_active=0;
311 UI_WINDOW mlm_window;
312 UI_LISTBOX mlm_mission_list;
313 UI_LISTBOX recent_mission_list;
314 UI_LISTBOX campaign_filter;
315
316 UI_BUTTON mlm_ok, mlm_cancel;
317 char * mlm_missions[MLM_MAX_MISSIONS];
318 char * recent_missions[MAX_RECENT_MISSIONS];
319 char * campaign_names[MAX_CAMPAIGNS+2];
320 char * campaign_missions[MAX_CAMPAIGN_MISSIONS];
321 int mlm_nfiles = 0;
322 static int      last_recent_current = -1;
323 static int      last_mlm_current = -1;
324 static int      Campaign_filter_index;
325
326 char * jtmp_missions[MLM_MAX_MISSIONS];
327 int jtmp_nfiles = 0;
328
329
330 void ml_change_listbox()
331 {
332         if ( !Num_recent_missions       || !mlm_nfiles )
333                 return;
334
335         if ( mlm_mission_list.current() != -1 ) {
336                 mlm_mission_list.set_current(-1);
337                 last_mlm_current = -1;
338                 recent_mission_list.set_focus();
339                 recent_mission_list.set_current(0);
340                 return;
341         }
342
343         if ( recent_mission_list.current() != -1 ) {
344                 recent_mission_list.set_current(-1);
345                 last_recent_current = -1;
346                 mlm_mission_list.set_focus();
347                 mlm_mission_list.set_current(0);
348                 return;
349         }
350 }
351
352 static char Campaign_missions[MAX_CAMPAIGN_MISSIONS][NAME_LENGTH];
353 static char Campaign_name_list[MAX_CAMPAIGNS+2][NAME_LENGTH];
354 static int      Num_campaign_missions;
355
356 // get the mission filenames that make up a campaign
357 extern int mission_campaign_get_filenames(const char *filename, char dest[][NAME_LENGTH], int *num);
358
359 void mission_load_menu_init()
360 {
361         int i;
362         char wild_card[256];
363         Assert( mlm_active == 0 );
364         mlm_active = 1;
365
366         memset(wild_card, 0, 256);
367         strcpy(wild_card, NOX("*"));
368         strcat(wild_card, FS_MISSION_FILE_EXT);
369         mlm_nfiles = cf_get_file_list( MLM_MAX_MISSIONS, mlm_missions, CF_TYPE_MISSIONS, wild_card, CF_SORT_NAME );
370         jtmp_nfiles = 0;        
371                 
372         Assert(mlm_nfiles <= MLM_MAX_MISSIONS);
373
374         mlm_window.create( 100,100,500,300, 0 );        //WIN_DIALOG
375
376         mlm_ok.create( &mlm_window, NOX("Ok"), 125, 420, 80, 40 );
377         mlm_cancel.create( &mlm_window, NOX("Cancel"), 250, 420, 80, 40 );
378         mlm_cancel.set_hotkey( KEY_ESC );
379
380         mlm_mission_list.create( &mlm_window, 450, 150, 150, 200, mlm_nfiles, mlm_missions );
381
382         for ( i = 0; i < Num_recent_missions; i++ ) {
383                 recent_missions[i] = Recent_missions[i];
384         }
385         recent_mission_list.create( &mlm_window, 250, 150, 150, 200, Num_recent_missions, recent_missions );
386
387         mlm_mission_list.set_focus();   
388         mlm_mission_list.set_current(0);
389
390
391         mission_campaign_build_list(0);
392         for ( i = 0; i < Num_campaigns; i++ ) {
393                 strcpy(Campaign_name_list[i+1], Campaign_names[i]);
394         }
395         strcpy(Campaign_name_list[0], NOX("All campaigns"));
396         strcpy(Campaign_name_list[1], NOX("Player Missions"));
397
398         for ( i = 0; i < Num_campaigns+2; i++ ) {
399                 campaign_names[i] = Campaign_name_list[i];
400         }
401
402         campaign_filter.create( &mlm_window, 50, 150, 150, 200, Num_campaigns+2, campaign_names );
403         Campaign_filter_index = 0;
404         campaign_filter.set_current(Campaign_filter_index);
405 }
406
407 void mission_load_menu_do()
408 {
409         int     selected, key_in, recent_current, mlm_current, use_recent_flag, i;
410         
411
412         Assert( mlm_active == 1 );
413
414         key_in = mlm_window.process();
415
416         if ( key_in ) {
417
418                 switch ( key_in & KEY_MASK ) {
419
420                 case KEY_UP:
421                 case KEY_DOWN:
422                 case KEY_HOME:
423                 case KEY_END:
424                 case KEY_PAGEUP:
425                 case KEY_PAGEDOWN:
426                 case KEY_ENTER:
427                         break;
428
429                 case KEY_RIGHT:
430                 case KEY_LEFT:
431                         ml_change_listbox();
432                         break;
433
434                 case KEY_ESC:
435                         gameseq_post_event(GS_EVENT_MAIN_MENU);
436                         break;
437
438                 default:
439                         break;
440
441                 } // end switch
442
443         }
444
445         if ( campaign_filter.current() != Campaign_filter_index ) {
446                 Campaign_filter_index = campaign_filter.current();
447
448                 if ( Campaign_filter_index > 1 ) {
449                         mission_campaign_get_filenames(Campaign_file_names[Campaign_filter_index-2], Campaign_missions, &Num_campaign_missions);
450
451                         for ( i = 0; i < Num_campaign_missions; i++ ) {
452                                 campaign_missions[i] = Campaign_missions[i];
453                         }
454                         mlm_mission_list.set_new_list(Num_campaign_missions, campaign_missions);
455                 } else if ( Campaign_filter_index == 0 ) {
456                         mlm_mission_list.set_new_list(mlm_nfiles, mlm_missions);
457                 } else if ( Campaign_filter_index == 1 ) {
458                         mlm_mission_list.set_new_list(jtmp_nfiles, jtmp_missions); 
459                 }
460                 mlm_current = 0;
461         }
462
463         mlm_current = mlm_mission_list.current();
464         recent_current = recent_mission_list.current();
465
466         if ( mlm_current != last_mlm_current ) {
467                 recent_mission_list.set_current(-1);
468                 last_recent_current = -1;
469         }
470         last_mlm_current = mlm_current;
471
472         if ( recent_current != last_recent_current ) {
473                 mlm_mission_list.set_current(-1);
474                 last_mlm_current = -1;
475         }
476         last_recent_current = recent_current;
477
478         if (mlm_cancel.pressed())
479                 gameseq_post_event(GS_EVENT_MAIN_MENU);
480
481         // Check if they hit OK, if so, use the current listbox
482         // selection.
483         selected = -1;
484         use_recent_flag = 0;
485         if (mlm_ok.pressed())   {
486                 selected = mlm_mission_list.current();
487                 if ( selected == -1 ) {
488                         selected = recent_mission_list.current();
489                         use_recent_flag = 1;
490                 }
491         } else  {
492                 // If they didn't hit OK, then check for a double-click on
493                 // a list box item.
494                 selected = mlm_mission_list.selected();
495                 if ( selected == -1 ) {
496                         selected = recent_mission_list.selected();
497                         use_recent_flag = 1;
498                 }
499         }
500
501         char mission_name_final[512] = "";
502
503         if ( selected > -1  )   {
504                 Campaign.current_mission = -1;
505                 if ( use_recent_flag ) {
506                         strncpy( mission_name_final, recent_missions[selected], MAX_FILENAME_LEN );
507                 } else {
508                         char mission_name[NAME_LENGTH];
509                         if ( Campaign_filter_index == 0 )       {
510                                 strcpy(mission_name, mlm_missions[selected]);
511                         } else if (Campaign_filter_index == 1 ) {
512                                 strcpy( mission_name, jtmp_missions[selected]);
513                         } else {
514                                 strcpy(mission_name, Campaign_missions[selected]);
515                         }
516                         strncpy( mission_name_final, mission_name, MAX_FILENAME_LEN );
517                 }
518
519                 // go
520 #ifdef PD_BUILD
521                 // if this valid
522                 if((game_find_builtin_mission(mission_name_final) != NULL) || strstr(mission_name_final, "peterdrake")){
523                         strcpy(Game_current_mission_filename, mission_name_final);
524                         mprintf(( "Selected '%s'\n", Game_current_mission_filename ));
525                         gameseq_post_event(GS_EVENT_START_GAME);                        
526                 }
527 #else
528                 strcpy(Game_current_mission_filename, mission_name_final);
529                 mprintf(( "Selected '%s'\n", Game_current_mission_filename ));
530                 gameseq_post_event(GS_EVENT_START_GAME);                        
531 #endif
532         }
533
534         gr_clear();
535         gr_set_color_fast( &Color_bright );
536         gr_printf( 0x8000, 10, NOX("Select Mission") );
537
538         gr_printf( 50, 135, NOX("Campaign Filter"));
539         gr_printf( 250, 135, NOX("Recently Played"));
540         gr_printf( 450, 135, NOX("Mission List"));
541         mlm_window.draw();
542
543         gr_flip();
544 }
545
546 void mission_load_menu_close()
547 {
548         int i;
549
550         Assert( mlm_active == 1 );
551         mlm_active = 0;
552
553         for (i=0; i<mlm_nfiles; i++ )   {
554                 if (mlm_missions[i] )   {
555                         free(mlm_missions[i]);
556                         mlm_missions[i] = NULL;
557                 }
558         }
559
560         for (i=0; i<jtmp_nfiles; i++ )  {
561                 if (jtmp_missions[i] )  {
562                         free(jtmp_missions[i]);
563                         jtmp_missions[i] = NULL;
564                 }
565         }
566
567
568         mlm_window.destroy();
569
570 }
571