RCS header added
[btb/d2x.git] / main / mission.c
1 /*
2 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
3 SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
4 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
5 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
6 IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
7 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
8 FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
9 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
10 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.  
11 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
12 */
13
14 /*
15  * $Source: /cvs/cvsroot/d2x/main/mission.c,v $
16  * $Revision: 1.4 $
17  * $Author: bradleyb $
18  * $Date: 2002-01-29 10:14:25 $
19  *
20  * Stuff for loading missions
21  *
22  * $Log: not supported by cvs2svn $
23  *
24  */
25
26 #ifdef HAVE_CONFIG_H
27 #include <conf.h>
28 #endif
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <ctype.h>
34 #include <limits.h>
35
36 #include "pstypes.h"
37 #include "cfile.h"
38
39 #include "strutil.h"
40 #include "inferno.h"
41 #include "mission.h"
42 #include "gameseq.h"
43 #include "titles.h"
44 #include "songs.h"
45 #include "mono.h"
46 #include "error.h"
47 #include "findfile.h"
48
49 mle Mission_list[MAX_MISSIONS];
50
51 int Current_mission_num;
52 int     N_secret_levels;                //      Made a global by MK for scoring purposes.  August 1, 1995.
53 char *Current_mission_filename,*Current_mission_longname;
54
55 //this stuff should get defined elsewhere
56
57 char Level_names[MAX_LEVELS_PER_MISSION][FILENAME_LEN];
58 char Secret_level_names[MAX_SECRET_LEVELS_PER_MISSION][FILENAME_LEN];
59
60 //where the missions go
61 #ifndef EDITOR
62 #define MISSION_DIR "missions/"
63 #else
64 #define MISSION_DIR "./"
65 #endif
66
67 #ifdef SHAREWARE
68
69 //
70 //  Special versions of mission routines for shareware
71 //
72
73 #define SHAREWARE_MISSION_FILENAME      "d2demo"
74 #define SHAREWARE_MISSION_NAME          "Descent 2 Demo"
75
76 int build_mission_list(int anarchy_mode)
77 {
78         anarchy_mode++;         //kill warning
79
80         strcpy(Mission_list[0].filename,SHAREWARE_MISSION_FILENAME);
81         strcpy(Mission_list[0].mission_name,SHAREWARE_MISSION_NAME);
82         Mission_list[0].anarchy_only_flag = 0;
83
84         return load_mission(0);
85 }
86
87 int load_mission(int mission_num)
88 {
89         Assert(mission_num == 0);
90
91         Current_mission_num = 0;
92         Current_mission_filename = Mission_list[0].filename;
93         Current_mission_longname = Mission_list[0].mission_name;
94
95         N_secret_levels = 0;
96
97         Assert(Last_level == 3);
98
99 #ifdef MACINTOSH        // mac demo is using the regular hog and rl2 files
100         strcpy(Level_names[0],"d2leva-1.rl2");
101         strcpy(Level_names[1],"d2leva-2.rl2");
102         strcpy(Level_names[2],"d2leva-3.rl2");
103 #else
104         strcpy(Level_names[0],"d2leva-1.sl2");
105         strcpy(Level_names[1],"d2leva-2.sl2");
106         strcpy(Level_names[2],"d2leva-3.sl2");
107 #endif  // end of ifdef macintosh
108
109         return 1;
110 }
111
112 int load_mission_by_name(char *mission_name)
113 {
114         if (strcmp(mission_name,SHAREWARE_MISSION_FILENAME))
115                 return 0;               //cannot load requested mission
116         else
117                 return load_mission(0);
118
119 }
120
121
122
123 #else
124
125 #if defined(D2_OEM)
126
127 //
128 //  Special versions of mission routines for Diamond/S3 version
129 //
130
131 #define OEM_MISSION_FILENAME    "d2"
132 #define OEM_MISSION_NAME                "D2 Destination:Quartzon"
133
134 int build_mission_list(int anarchy_mode)
135 {
136         anarchy_mode++;         //kill warning
137
138         strcpy(Mission_list[0].filename,OEM_MISSION_FILENAME);
139         strcpy(Mission_list[0].mission_name,OEM_MISSION_NAME);
140         Mission_list[0].anarchy_only_flag = 0;
141
142         return load_mission(0);
143 }
144
145 int load_mission(int mission_num)
146 {
147         Assert(mission_num == 0);
148
149         Current_mission_num = 0;
150         Current_mission_filename = Mission_list[0].filename;
151         Current_mission_longname = Mission_list[0].mission_name;
152
153         N_secret_levels = 2;
154
155         Assert(Last_level == 8);
156
157         strcpy(Level_names[0],"d2leva-1.rl2");
158         strcpy(Level_names[1],"d2leva-2.rl2");
159         strcpy(Level_names[2],"d2leva-3.rl2");
160         strcpy(Level_names[3],"d2leva-4.rl2");
161
162         strcpy(Secret_level_names[0],"d2leva-s.rl2");
163
164         strcpy(Level_names[4],"d2levb-1.rl2");
165         strcpy(Level_names[5],"d2levb-2.rl2");
166         strcpy(Level_names[6],"d2levb-3.rl2");
167         strcpy(Level_names[7],"d2levb-4.rl2");
168
169         strcpy(Secret_level_names[1],"d2levb-s.rl2");
170
171         Secret_level_table[0] = 1;
172         Secret_level_table[1] = 5;
173
174         return 1;
175 }
176
177 int load_mission_by_name(char *mission_name)
178 {
179         if (strcmp(mission_name,OEM_MISSION_FILENAME))
180                 return 0;               //cannot load requested mission
181         else
182                 return load_mission(0);
183
184 }
185
186 #else
187
188 //strips damn newline from end of line
189 char *mfgets(char *s,int n,CFILE *f)
190 {
191         char *r;
192
193         r = cfgets(s,n,f);
194         if (r && s[strlen(s)-1] == '\n')
195                 s[strlen(s)-1] = 0;
196
197         return r;
198 }
199
200 //compare a string for a token. returns true if match
201 int istok(char *buf,char *tok)
202 {
203         return strnicmp(buf,tok,strlen(tok)) == 0;
204
205 }
206
207 //adds a terminating 0 after a string at the first white space
208 void add_term(char *s)
209 {
210         while (*s && !isspace(*s)) s++;
211
212         *s = 0;         //terminate!
213 }
214
215 //returns ptr to string after '=' & white space, or NULL if no '='
216 //adds 0 after parm at first white space
217 char *get_value(char *buf)
218 {
219         char *t;
220
221         t = strchr(buf,'=')+1;
222
223         if (t) {
224                 while (*t && isspace(*t)) t++;
225
226                 if (*t)
227                         return t;
228         }
229
230         return NULL;            //error!
231 }
232
233 //reads a line, returns ptr to value of passed parm.  returns NULL if none
234 char *get_parm_value(char *parm,CFILE *f)
235 {
236         static char buf[80];
237         
238         if (!mfgets(buf,80,f))
239                 return NULL;
240
241         if (istok(buf,parm))
242                 return get_value(buf);
243         else
244                 return NULL;
245 }
246
247 int ml_sort_func(mle *e0,mle *e1)
248 {
249         return stricmp(e0->mission_name,e1->mission_name);
250
251 }
252
253 extern char CDROM_dir[];
254 extern int HoardEquipped();
255
256 #define BUILTIN_MISSION "d2.mn2"
257
258 //returns 1 if file read ok, else 0
259 int read_mission_file(char *filename,int count,int location)
260 {
261         char filename2[100];
262         CFILE *mfile;
263
264         switch (location) {
265                 case ML_MISSIONDIR:
266                         strcpy(filename2,MISSION_DIR);  
267                         break;
268
269                 case ML_CDROM:                  
270                         songs_stop_redbook();           //so we can read from the CD
271                         strcpy(filename2,CDROM_dir);            
272                         break;
273
274                 default:                                        
275                         Int3();         //fall through
276
277                 case ML_CURDIR:         
278                         strcpy(filename2,"");
279                         break;
280         }
281         strcat(filename2,filename);
282
283         mfile = cfopen(filename2,"rb");
284
285         if (mfile) {
286                 char *p;
287                 char temp[FILENAME_LEN],*t;
288
289                 strcpy(temp,filename);
290                 if ((t = strchr(temp,'.')) == NULL)
291                         return 0;       //missing extension
292                 *t = 0;                 //kill extension
293
294                 strncpy( Mission_list[count].filename, temp, 9 );
295                 Mission_list[count].anarchy_only_flag = 0;
296                 Mission_list[count].location = location;
297
298                 p = get_parm_value("name",mfile);
299
300                 if (!p) {               //try enhanced mission
301                         cfseek(mfile,0,SEEK_SET);
302                         p = get_parm_value("xname",mfile);
303                 }
304
305 #ifdef NETWORK
306                 if (HoardEquipped())
307                 {
308                         if (!p) {               //try super-enhanced mission!
309                                 cfseek(mfile,0,SEEK_SET);
310                                 p = get_parm_value("zname",mfile);
311                         }
312                 }
313 #endif
314
315                 if (p) {
316                         char *t;
317                         if ((t=strchr(p,';'))!=NULL)
318                                 *t=0;
319                         t = p + strlen(p)-1;
320                         while (isspace(*t)) t--;
321                         strncpy(Mission_list[count].mission_name,p,MISSION_NAME_LEN);
322                 }
323                 else {
324                         cfclose(mfile);
325                         return 0;
326                 }
327
328                 p = get_parm_value("type",mfile);
329
330                 //get mission type 
331                 if (p)
332                         Mission_list[count].anarchy_only_flag = istok(p,"anarchy");
333
334                 cfclose(mfile);
335
336                 return 1;
337         }
338
339         return 0;
340 }
341
342
343 //fills in the global list of missions.  Returns the number of missions
344 //in the list.  If anarchy_mode set, don't include non-anarchy levels.
345 //if there is only one mission, this function will call load_mission on it.
346
347 extern char CDROM_dir[];
348 extern char AltHogDir[];
349 extern char AltHogdir_initialized;
350
351 int build_mission_list(int anarchy_mode)
352 {
353         static int num_missions=-1;
354         int count=0,special_count=0;
355         FILEFINDSTRUCT find;
356         char search_name[PATH_MAX + 5] = MISSION_DIR "*.mn2";
357
358         //now search for levels on disk
359
360 //@@Took out this code because after this routine was called once for
361 //@@a list of single-player missions, a subsequent call for a list of
362 //@@anarchy missions would not scan again, and thus would not find the
363 //@@anarchy-only missions.  If we retain the minimum level of install,
364 //@@we may want to put the code back in, having it always scan for all
365 //@@missions, and have the code that uses it sort out the ones it wants.
366 //@@    if (num_missions != -1) {
367 //@@            if (Current_mission_num != 0)
368 //@@                    load_mission(0);                                //set built-in mission as default
369 //@@            return num_missions;
370 //@@    }
371
372         if (!read_mission_file(BUILTIN_MISSION,0,ML_CURDIR))            //read built-in first
373                 Error("Could not find required mission file <%s>",BUILTIN_MISSION);
374
375         special_count = count=1; 
376
377         if( !FileFindFirst( search_name, &find ) ) {
378                 do      {
379                         if (stricmp(find.name,BUILTIN_MISSION)==0)
380                                 continue;               //skip the built-in
381
382                         if (read_mission_file(find.name,count,ML_MISSIONDIR)) {
383
384                                 if (anarchy_mode || !Mission_list[count].anarchy_only_flag)
385                                         count++;
386                         }
387
388                 } while( !FileFindNext( &find ) && count<MAX_MISSIONS);
389                 FileFindClose();
390         }
391
392         if (AltHogdir_initialized) {
393                 strcpy(search_name, AltHogDir);
394                 strcat(search_name, "/" MISSION_DIR "*.mn2");
395                 if( !FileFindFirst( search_name, &find ) ) {
396                         do      {
397                                 if (stricmp(find.name,BUILTIN_MISSION)==0)
398                                         continue;               //skip the built-in
399
400                                 if (read_mission_file(find.name,count,ML_MISSIONDIR)) {
401
402                                         if (anarchy_mode || !Mission_list[count].anarchy_only_flag)
403                                                 count++;
404                                 }
405
406                         } while( !FileFindNext( &find ) && count<MAX_MISSIONS);
407                         FileFindClose();
408                 }
409         }
410
411         //move vertigo to top of mission list
412         {
413                 int i;
414
415                 for (i=special_count;i<count;i++)
416                         if (!stricmp(Mission_list[i].filename,"D2X")) {         //swap!
417                                 mle temp;
418
419                                 temp = Mission_list[special_count];
420                                 Mission_list[special_count] = Mission_list[i];
421                                 Mission_list[i] = temp;
422
423                                 special_count++;
424
425                                 break;
426                         }
427         }
428
429
430         if (count>special_count)
431                 qsort(&Mission_list[special_count],count-special_count,sizeof(*Mission_list),
432                                 (int (*)( const void *, const void * ))ml_sort_func);
433
434         load_mission(0);                        //set built-in mission as default
435
436         num_missions = count;
437
438         return count;
439 }
440
441 void init_extra_robot_movie(char *filename);
442
443 //values for built-in mission
444
445 //loads the specfied mission from the mission list.  build_mission_list()
446 //must have been called.  If build_mission_list() returns 0, this function
447 //does not need to be called.  Returns true if mission loaded ok, else false.
448 int load_mission(int mission_num)
449 {
450         CFILE *mfile;
451         char buf[80], *v;
452         int found_hogfile;
453         int enhanced_mission = 0;
454
455         Current_mission_num = mission_num;
456
457         mprintf(( 0, "Loading mission %d\n", mission_num ));
458
459         //read mission from file 
460
461         switch (Mission_list[mission_num].location) {
462                 case ML_MISSIONDIR:     strcpy(buf,MISSION_DIR);        break;
463                 case ML_CDROM:                  strcpy(buf,CDROM_dir);          break;
464                 default:                                        Int3();                                                 //fall through
465                 case ML_CURDIR:         strcpy(buf,"");                         break;
466         }
467         strcat(buf,Mission_list[mission_num].filename);
468         strcat(buf,".mn2");
469
470         mfile = cfopen(buf,"rb");
471         if (mfile == NULL) {
472                 Current_mission_num = -1;
473                 return 0;               //error!
474         }
475
476         if (mission_num != 0) {         //for non-builtin missions, load HOG
477
478                 strcpy(buf+strlen(buf)-4,".hog");               //change extension
479
480                 found_hogfile = cfile_use_alternate_hogfile(buf);
481
482                 #ifdef RELEASE                          //for release, require mission to be in hogfile
483                 if (! found_hogfile) {
484                         cfclose(mfile);
485                         Current_mission_num = -1;
486                         return 0;
487                 }
488                 #endif
489         }
490
491         //init vars
492         Last_level =            0;
493         Last_secret_level = 0;
494
495         while (mfgets(buf,80,mfile)) {
496
497                 if (istok(buf,"name"))
498                         continue;                                               //already have name, go to next line
499                 if (istok(buf,"xname")) {
500                         enhanced_mission = 1;
501                         continue;                                               //already have name, go to next line
502                 }
503                 if (istok(buf,"zname")) {
504                         enhanced_mission = 2;
505                         continue;                                               //already have name, go to next line
506                 }
507                 else if (istok(buf,"type"))
508                         continue;                                               //already have name, go to next line                            
509                 else if (istok(buf,"hog")) {
510                         char    *bufp = buf;
511
512                         while (*(bufp++) != '=')
513                                 ;
514
515                         if (*bufp == ' ')
516                                 while (*(++bufp) == ' ')
517                                         ;
518
519                         cfile_use_alternate_hogfile(bufp);
520                         mprintf((0, "Hog file override = [%s]\n", bufp));
521                 }
522                 else if (istok(buf,"num_levels")) {
523
524                         if ((v=get_value(buf))!=NULL) {
525                                 int n_levels,i;
526
527                                 n_levels = atoi(v);
528
529                                 for (i=0;i<n_levels && mfgets(buf,80,mfile);i++) {
530
531                                         add_term(buf);
532                                         if (strlen(buf) <= 12) {
533                                                 strcpy(Level_names[i],buf);
534                                                 Last_level++;
535                                         }
536                                         else
537                                                 break;
538                                 }
539
540                         }
541                 }
542                 else if (istok(buf,"num_secrets")) {
543                         if ((v=get_value(buf))!=NULL) {
544                                 int i;
545
546                                 N_secret_levels = atoi(v);
547
548                                 Assert(N_secret_levels <= MAX_SECRET_LEVELS_PER_MISSION);
549
550                                 for (i=0;i<N_secret_levels && mfgets(buf,80,mfile);i++) {
551                                         char *t;
552
553                                         
554                                         if ((t=strchr(buf,','))!=NULL) *t++=0;
555                                         else
556                                                 break;
557
558                                         add_term(buf);
559                                         if (strlen(buf) <= 12) {
560                                                 strcpy(Secret_level_names[i],buf);
561                                                 Secret_level_table[i] = atoi(t);
562                                                 if (Secret_level_table[i]<1 || Secret_level_table[i]>Last_level)
563                                                         break;
564                                                 Last_secret_level--;
565                                         }
566                                         else
567                                                 break;
568                                 }
569
570                         }
571                 }
572
573         }
574
575         cfclose(mfile);
576
577         if (Last_level <= 0) {
578                 Current_mission_num = -1;               //no valid mission loaded
579                 return 0;
580         }
581
582         Current_mission_filename = Mission_list[Current_mission_num].filename;
583         Current_mission_longname = Mission_list[Current_mission_num].mission_name;
584
585         if (enhanced_mission) {
586                 char t[50];
587                 extern void bm_read_extra_robots();
588                 sprintf(t,"%s.ham",Current_mission_filename);
589                 bm_read_extra_robots(t,enhanced_mission);
590                 strncpy(t,Current_mission_filename,6);
591                 strcat(t,"-l.mvl");
592                 init_extra_robot_movie(t);
593         }
594
595         return 1;
596 }
597
598 //loads the named mission if exists.
599 //Returns true if mission loaded ok, else false.
600 int load_mission_by_name(char *mission_name)
601 {
602         int n,i;
603
604         n = build_mission_list(1);
605
606         for (i=0;i<n;i++)
607                 if (!stricmp(mission_name,Mission_list[i].filename))
608                         return load_mission(i);
609
610         return 0;               //couldn't find mission
611 }
612
613 #endif
614 #endif
615