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