]> icculus.org git repositories - btb/d2x.git/blob - main/gamesave.c
determine properly if 800x600 resolution is available
[btb/d2x.git] / main / gamesave.c
1 /* $Id: gamesave.c,v 1.30 2005-01-25 21:58:23 schaffner Exp $ */
2 /*
3 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
4 SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
5 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
6 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
7 IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
8 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
9 FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
10 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
11 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
12 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
13 */
14
15 /*
16  *
17  * Save game information
18  *
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include <conf.h>
23 #endif
24
25 #ifdef RCS
26 char gamesave_rcsid[] = "$Id: gamesave.c,v 1.30 2005-01-25 21:58:23 schaffner Exp $";
27 #endif
28
29 #include <stdio.h>
30 #include <string.h>
31
32 #include "pstypes.h"
33 #include "strutil.h"
34 #include "mono.h"
35 #include "key.h"
36 #include "gr.h"
37 #include "palette.h"
38 #include "newmenu.h"
39
40 #include "inferno.h"
41 #ifdef EDITOR
42 #include "editor/editor.h"
43 #endif
44 #include "error.h"
45 #include "object.h"
46 #include "game.h"
47 #include "screens.h"
48 #include "wall.h"
49 #include "gamemine.h"
50 #include "robot.h"
51
52
53 #include "cfile.h"
54 #include "bm.h"
55 #include "menu.h"
56 #include "switch.h"
57 #include "fuelcen.h"
58 #include "cntrlcen.h"
59 #include "powerup.h"
60 #include "weapon.h"
61 #include "newdemo.h"
62 #include "gameseq.h"
63 #include "automap.h"
64 #include "polyobj.h"
65 #include "text.h"
66 #include "gamefont.h"
67 #include "gamesave.h"
68 #include "gamepal.h"
69 #include "laser.h"
70 #include "byteswap.h"
71 #include "multi.h"
72 #include "makesig.h"
73
74 char Gamesave_current_filename[PATH_MAX];
75
76 int Gamesave_current_version;
77
78 #define GAME_VERSION            32
79 #define GAME_COMPATIBLE_VERSION 22
80
81 //version 28->29  add delta light support
82 //version 27->28  controlcen id now is reactor number, not model number
83 //version 28->29  ??
84 //version 29->30  changed trigger structure
85 //version 30->31  changed trigger structure some more
86 //version 31->32  change segment structure, make it 512 bytes w/o editor, add Segment2s array.
87
88 #define MENU_CURSOR_X_MIN       MENU_X
89 #define MENU_CURSOR_X_MAX       MENU_X+6
90
91 #ifdef EDITOR
92 struct {
93         ushort  fileinfo_signature;
94         ushort  fileinfo_version;
95         int     fileinfo_sizeof;
96 } game_top_fileinfo;    // Should be same as first two fields below...
97
98 struct {
99         ushort  fileinfo_signature;
100         ushort  fileinfo_version;
101         int     fileinfo_sizeof;
102         char    mine_filename[15];
103         int     level;
104         int     player_offset;              // Player info
105         int     player_sizeof;
106         int     object_offset;              // Object info
107         int     object_howmany;
108         int     object_sizeof;
109         int     walls_offset;
110         int     walls_howmany;
111         int     walls_sizeof;
112         int     doors_offset;
113         int     doors_howmany;
114         int     doors_sizeof;
115         int     triggers_offset;
116         int     triggers_howmany;
117         int     triggers_sizeof;
118         int     links_offset;
119         int     links_howmany;
120         int     links_sizeof;
121         int     control_offset;
122         int     control_howmany;
123         int     control_sizeof;
124         int     matcen_offset;
125         int     matcen_howmany;
126         int     matcen_sizeof;
127         int     dl_indices_offset;
128         int     dl_indices_howmany;
129         int     dl_indices_sizeof;
130         int     delta_light_offset;
131         int     delta_light_howmany;
132         int     delta_light_sizeof;
133 } game_fileinfo;
134 #endif // EDITOR
135
136 //  LINT: adding function prototypes
137 void read_object(object *obj, CFILE *f, int version);
138 #ifdef EDITOR
139 void write_object(object *obj, FILE *f);
140 void do_load_save_levels(int save);
141 #endif
142 #ifndef NDEBUG
143 void dump_mine_info(void);
144 #endif
145
146 extern char MaxPowerupsAllowed[MAX_POWERUP_TYPES];
147 extern char PowerupsInMine[MAX_POWERUP_TYPES];
148
149 #ifdef EDITOR
150 extern char mine_filename[];
151 extern int save_mine_data_compiled(FILE * SaveFile);
152 //--unused-- #else
153 //--unused-- char mine_filename[128];
154 #endif
155
156 int Gamesave_num_org_robots = 0;
157 //--unused-- grs_bitmap * Gamesave_saved_bitmap = NULL;
158
159 #ifdef EDITOR
160 // Return true if this level has a name of the form "level??"
161 // Note that a pathspec can appear at the beginning of the filename.
162 int is_real_level(char *filename)
163 {
164         int len = strlen(filename);
165
166         if (len < 6)
167                 return 0;
168
169         //mprintf((0, "String = [%s]\n", &filename[len-11]));
170         return !strnicmp(&filename[len-11], "level", 5);
171
172 }
173 #endif
174
175 void change_filename_extension( char *dest, char *src, char *new_ext )
176 {
177         int i;
178
179         strcpy (dest, src);
180
181         if (new_ext[0]=='.')
182                 new_ext++;
183
184         for (i=1; i<strlen(dest); i++ )
185                 if (dest[i]=='.'||dest[i]==' '||dest[i]==0)
186                         break;
187
188         if (i < 123) {
189                 dest[i]='.';
190                 dest[i+1]=new_ext[0];
191                 dest[i+2]=new_ext[1];
192                 dest[i+3]=new_ext[2];
193                 dest[i+4]=0;
194                 return;
195         }
196 }
197
198 //--unused-- vms_angvec zero_angles={0,0,0};
199
200 #define vm_angvec_zero(v) do {(v)->p=(v)->b=(v)->h=0;} while (0)
201
202 int Gamesave_num_players=0;
203
204 int N_save_pof_names;
205 char Save_pof_names[MAX_POLYGON_MODELS][FILENAME_LEN];
206
207 void check_and_fix_matrix(vms_matrix *m);
208
209 void verify_object( object * obj )      {
210
211         obj->lifeleft = IMMORTAL_TIME;          //all loaded object are immortal, for now
212
213         if ( obj->type == OBJ_ROBOT )   {
214                 Gamesave_num_org_robots++;
215
216                 // Make sure valid id...
217                 if ( obj->id >= N_robot_types )
218                         obj->id = obj->id % N_robot_types;
219
220                 // Make sure model number & size are correct...
221                 if ( obj->render_type == RT_POLYOBJ ) {
222                         Assert(Robot_info[obj->id].model_num != -1);
223                                 //if you fail this assert, it means that a robot in this level
224                                 //hasn't been loaded, possibly because he's marked as
225                                 //non-shareware.  To see what robot number, print obj->id.
226
227                         Assert(Robot_info[obj->id].always_0xabcd == 0xabcd);
228                                 //if you fail this assert, it means that the robot_ai for
229                                 //a robot in this level hasn't been loaded, possibly because
230                                 //it's marked as non-shareware.  To see what robot number,
231                                 //print obj->id.
232
233                         obj->rtype.pobj_info.model_num = Robot_info[obj->id].model_num;
234                         obj->size = Polygon_models[obj->rtype.pobj_info.model_num].rad;
235
236                         //@@Took out this ugly hack 1/12/96, because Mike has added code
237                         //@@that should fix it in a better way.
238                         //@@//this is a super-ugly hack.  Since the baby stripe robots have
239                         //@@//their firing point on their bounding sphere, the firing points
240                         //@@//can poke through a wall if the robots are very close to it. So
241                         //@@//we make their radii bigger so the guns can't get too close to 
242                         //@@//the walls
243                         //@@if (Robot_info[obj->id].flags & RIF_BIG_RADIUS)
244                         //@@    obj->size = (obj->size*3)/2;
245
246                         //@@if (obj->control_type==CT_AI && Robot_info[obj->id].attack_type)
247                         //@@    obj->size = obj->size*3/4;
248                 }
249
250                 if (obj->id == 65)                                              //special "reactor" robots
251                         obj->movement_type = MT_NONE;
252
253                 if (obj->movement_type == MT_PHYSICS) {
254                         obj->mtype.phys_info.mass = Robot_info[obj->id].mass;
255                         obj->mtype.phys_info.drag = Robot_info[obj->id].drag;
256                 }
257         }
258         else {          //Robots taken care of above
259
260                 if ( obj->render_type == RT_POLYOBJ ) {
261                         int i;
262                         char *name = Save_pof_names[obj->rtype.pobj_info.model_num];
263
264                         for (i=0;i<N_polygon_models;i++)
265                                 if (!stricmp(Pof_names[i],name)) {              //found it!     
266                                         // mprintf((0,"Mapping <%s> to %d (was %d)\n",name,i,obj->rtype.pobj_info.model_num));
267                                         obj->rtype.pobj_info.model_num = i;
268                                         break;
269                                 }
270                 }
271         }
272
273         if ( obj->type == OBJ_POWERUP ) {
274                 if ( obj->id >= N_powerup_types )       {
275                         obj->id = 0;
276                         Assert( obj->render_type != RT_POLYOBJ );
277                 }
278                 obj->control_type = CT_POWERUP;
279                 obj->size = Powerup_info[obj->id].size;
280                 obj->ctype.powerup_info.creation_time = 0;
281
282 #ifdef NETWORK
283                 if (Game_mode & GM_NETWORK)
284                         {
285                           if (multi_powerup_is_4pack(obj->id))
286                                 {
287                                  PowerupsInMine[obj->id-1]+=4;
288                                  MaxPowerupsAllowed[obj->id-1]+=4;
289                                 }
290                           PowerupsInMine[obj->id]++;
291                      MaxPowerupsAllowed[obj->id]++;
292                           mprintf ((0,"PowerupLimiter: ID=%d\n",obj->id));
293                           if (obj->id>MAX_POWERUP_TYPES)
294                                 mprintf ((1,"POWERUP: Overwriting array bounds!! Get JL!\n"));
295                         }
296 #endif
297
298         }
299
300         if ( obj->type == OBJ_WEAPON )  {
301                 if ( obj->id >= N_weapon_types )        {
302                         obj->id = 0;
303                         Assert( obj->render_type != RT_POLYOBJ );
304                 }
305
306                 if (obj->id == PMINE_ID) {              //make sure pmines have correct values
307
308                         obj->mtype.phys_info.mass = Weapon_info[obj->id].mass;
309                         obj->mtype.phys_info.drag = Weapon_info[obj->id].drag;
310                         obj->mtype.phys_info.flags |= PF_FREE_SPINNING;
311
312                         // Make sure model number & size are correct...         
313                         Assert( obj->render_type == RT_POLYOBJ );
314
315                         obj->rtype.pobj_info.model_num = Weapon_info[obj->id].model_num;
316                         obj->size = Polygon_models[obj->rtype.pobj_info.model_num].rad;
317                 }
318         }
319
320         if ( obj->type == OBJ_CNTRLCEN ) {
321
322                 obj->render_type = RT_POLYOBJ;
323                 obj->control_type = CT_CNTRLCEN;
324
325                 if (Gamesave_current_version <= 1) { // descent 1 reactor
326                         obj->id = 0;                         // used to be only one kind of reactor
327                         obj->rtype.pobj_info.model_num = Reactors[0].model_num;// descent 1 reactor
328                 }
329                 //@@// Make model number is correct...
330                 //@@for (i=0; i<Num_total_object_types; i++ )
331                 //@@    if ( ObjType[i] == OL_CONTROL_CENTER ) {
332                 //@@            obj->rtype.pobj_info.model_num = ObjId[i];
333                 //@@            obj->shields = ObjStrength[i];
334                 //@@            break;
335                 //@@    }
336
337                 #ifdef EDITOR
338                 {
339                 int i;
340                 // Check, and set, strength of reactor
341                 for (i=0; i<Num_total_object_types; i++ )       
342                         if ( ObjType[i]==OL_CONTROL_CENTER && ObjId[i] == obj->id ) {
343                                 obj->shields = ObjStrength[i];
344                                 break;          
345                         }
346                 Assert(i < Num_total_object_types);             //make sure we found it
347                 }
348                 #endif
349         }
350
351         if ( obj->type == OBJ_PLAYER )  {
352                 //int i;
353
354                 //Assert(obj == Player);
355
356                 if ( obj == ConsoleObject )             
357                         init_player_object();
358                 else
359                         if (obj->render_type == RT_POLYOBJ)     //recover from Matt's pof file matchup bug
360                                 obj->rtype.pobj_info.model_num = Player_ship->model_num;
361
362                 //Make sure orient matrix is orthogonal
363                 check_and_fix_matrix(&obj->orient);
364
365                 obj->id = Gamesave_num_players++;
366         }
367
368         if (obj->type == OBJ_HOSTAGE) {
369
370                 //@@if (obj->id > N_hostage_types)
371                 //@@    obj->id = 0;
372
373                 obj->render_type = RT_HOSTAGE;
374                 obj->control_type = CT_POWERUP;
375         }
376
377 }
378
379 //static gs_skip(int len,CFILE *file)
380 //{
381 //
382 //      cfseek(file,len,SEEK_CUR);
383 //}
384
385 #ifdef EDITOR
386 static void gs_write_int(int i,FILE *file)
387 {
388         if (fwrite( &i, sizeof(i), 1, file) != 1)
389                 Error( "Error reading int in gamesave.c" );
390
391 }
392
393 static void gs_write_fix(fix f,FILE *file)
394 {
395         if (fwrite( &f, sizeof(f), 1, file) != 1)
396                 Error( "Error reading fix in gamesave.c" );
397
398 }
399
400 static void gs_write_short(short s,FILE *file)
401 {
402         if (fwrite( &s, sizeof(s), 1, file) != 1)
403                 Error( "Error reading short in gamesave.c" );
404
405 }
406
407 static void gs_write_fixang(fixang f,FILE *file)
408 {
409         if (fwrite( &f, sizeof(f), 1, file) != 1)
410                 Error( "Error reading fixang in gamesave.c" );
411
412 }
413
414 static void gs_write_byte(sbyte b,FILE *file)
415 {
416         if (fwrite( &b, sizeof(b), 1, file) != 1)
417                 Error( "Error reading byte in gamesave.c" );
418
419 }
420
421 static void gr_write_vector(vms_vector *v,FILE *file)
422 {
423         gs_write_fix(v->x,file);
424         gs_write_fix(v->y,file);
425         gs_write_fix(v->z,file);
426 }
427
428 static void gs_write_matrix(vms_matrix *m,FILE *file)
429 {
430         gr_write_vector(&m->rvec,file);
431         gr_write_vector(&m->uvec,file);
432         gr_write_vector(&m->fvec,file);
433 }
434
435 static void gs_write_angvec(vms_angvec *v,FILE *file)
436 {
437         gs_write_fixang(v->p,file);
438         gs_write_fixang(v->b,file);
439         gs_write_fixang(v->h,file);
440 }
441
442 #endif
443
444
445 extern int multi_powerup_is_4pack(int);
446 //reads one object of the given version from the given file
447 void read_object(object *obj,CFILE *f,int version)
448 {
449
450         obj->type           = cfile_read_byte(f);
451         obj->id             = cfile_read_byte(f);
452
453         obj->control_type   = cfile_read_byte(f);
454         obj->movement_type  = cfile_read_byte(f);
455         obj->render_type    = cfile_read_byte(f);
456         obj->flags          = cfile_read_byte(f);
457
458         obj->segnum         = cfile_read_short(f);
459         obj->attached_obj   = -1;
460
461         cfile_read_vector(&obj->pos,f);
462         cfile_read_matrix(&obj->orient,f);
463
464         obj->size           = cfile_read_fix(f);
465         obj->shields        = cfile_read_fix(f);
466
467         cfile_read_vector(&obj->last_pos,f);
468
469         obj->contains_type  = cfile_read_byte(f);
470         obj->contains_id    = cfile_read_byte(f);
471         obj->contains_count = cfile_read_byte(f);
472
473         switch (obj->movement_type) {
474
475                 case MT_PHYSICS:
476
477                         cfile_read_vector(&obj->mtype.phys_info.velocity,f);
478                         cfile_read_vector(&obj->mtype.phys_info.thrust,f);
479
480                         obj->mtype.phys_info.mass               = cfile_read_fix(f);
481                         obj->mtype.phys_info.drag               = cfile_read_fix(f);
482                         obj->mtype.phys_info.brakes     = cfile_read_fix(f);
483
484                         cfile_read_vector(&obj->mtype.phys_info.rotvel,f);
485                         cfile_read_vector(&obj->mtype.phys_info.rotthrust,f);
486
487                         obj->mtype.phys_info.turnroll   = cfile_read_fixang(f);
488                         obj->mtype.phys_info.flags              = cfile_read_short(f);
489
490                         break;
491
492                 case MT_SPINNING:
493
494                         cfile_read_vector(&obj->mtype.spin_rate,f);
495                         break;
496
497                 case MT_NONE:
498                         break;
499
500                 default:
501                         Int3();
502         }
503
504         switch (obj->control_type) {
505
506                 case CT_AI: {
507                         int i;
508
509                         obj->ctype.ai_info.behavior                             = cfile_read_byte(f);
510
511                         for (i=0;i<MAX_AI_FLAGS;i++)
512                                 obj->ctype.ai_info.flags[i]                     = cfile_read_byte(f);
513
514                         obj->ctype.ai_info.hide_segment                 = cfile_read_short(f);
515                         obj->ctype.ai_info.hide_index                   = cfile_read_short(f);
516                         obj->ctype.ai_info.path_length                  = cfile_read_short(f);
517                         obj->ctype.ai_info.cur_path_index               = cfile_read_short(f);
518
519                         if (version <= 25) {
520                                 cfile_read_short(f);    //                              obj->ctype.ai_info.follow_path_start_seg        = 
521                                 cfile_read_short(f);    //                              obj->ctype.ai_info.follow_path_end_seg          = 
522                         }
523
524                         break;
525                 }
526
527                 case CT_EXPLOSION:
528
529                         obj->ctype.expl_info.spawn_time         = cfile_read_fix(f);
530                         obj->ctype.expl_info.delete_time                = cfile_read_fix(f);
531                         obj->ctype.expl_info.delete_objnum      = cfile_read_short(f);
532                         obj->ctype.expl_info.next_attach = obj->ctype.expl_info.prev_attach = obj->ctype.expl_info.attach_parent = -1;
533
534                         break;
535
536                 case CT_WEAPON:
537
538                         //do I really need to read these?  Are they even saved to disk?
539
540                         obj->ctype.laser_info.parent_type               = cfile_read_short(f);
541                         obj->ctype.laser_info.parent_num                = cfile_read_short(f);
542                         obj->ctype.laser_info.parent_signature  = cfile_read_int(f);
543
544                         break;
545
546                 case CT_LIGHT:
547
548                         obj->ctype.light_info.intensity = cfile_read_fix(f);
549                         break;
550
551                 case CT_POWERUP:
552
553                         if (version >= 25)
554                                 obj->ctype.powerup_info.count = cfile_read_int(f);
555                         else
556                                 obj->ctype.powerup_info.count = 1;
557
558                         if (obj->id == POW_VULCAN_WEAPON)
559                                         obj->ctype.powerup_info.count = VULCAN_WEAPON_AMMO_AMOUNT;
560
561                         if (obj->id == POW_GAUSS_WEAPON)
562                                         obj->ctype.powerup_info.count = VULCAN_WEAPON_AMMO_AMOUNT;
563
564                         if (obj->id == POW_OMEGA_WEAPON)
565                                         obj->ctype.powerup_info.count = MAX_OMEGA_CHARGE;
566
567                         break;
568
569
570                 case CT_NONE:
571                 case CT_FLYING:
572                 case CT_DEBRIS:
573                         break;
574
575                 case CT_SLEW:           //the player is generally saved as slew
576                         break;
577
578                 case CT_CNTRLCEN:
579                         break;
580
581                 case CT_MORPH:
582                 case CT_FLYTHROUGH:
583                 case CT_REPAIRCEN:
584                 default:
585                         Int3();
586         
587         }
588
589         switch (obj->render_type) {
590
591                 case RT_NONE:
592                         break;
593
594                 case RT_MORPH:
595                 case RT_POLYOBJ: {
596                         int i,tmo;
597
598                         obj->rtype.pobj_info.model_num          = cfile_read_int(f);
599
600                         for (i=0;i<MAX_SUBMODELS;i++)
601                                 cfile_read_angvec(&obj->rtype.pobj_info.anim_angles[i],f);
602
603                         obj->rtype.pobj_info.subobj_flags       = cfile_read_int(f);
604
605                         tmo = cfile_read_int(f);
606
607                         #ifndef EDITOR
608                         obj->rtype.pobj_info.tmap_override      = tmo;
609                         #else
610                         if (tmo==-1)
611                                 obj->rtype.pobj_info.tmap_override      = -1;
612                         else {
613                                 int xlated_tmo = tmap_xlate_table[tmo];
614                                 if (xlated_tmo < 0)     {
615                                         mprintf( (0, "Couldn't find texture for demo object, model_num = %d\n", obj->rtype.pobj_info.model_num));
616                                         Int3();
617                                         xlated_tmo = 0;
618                                 }
619                                 obj->rtype.pobj_info.tmap_override      = xlated_tmo;
620                         }
621                         #endif
622
623                         obj->rtype.pobj_info.alt_textures       = 0;
624
625                         break;
626                 }
627
628                 case RT_WEAPON_VCLIP:
629                 case RT_HOSTAGE:
630                 case RT_POWERUP:
631                 case RT_FIREBALL:
632
633                         obj->rtype.vclip_info.vclip_num = cfile_read_int(f);
634                         obj->rtype.vclip_info.frametime = cfile_read_fix(f);
635                         obj->rtype.vclip_info.framenum  = cfile_read_byte(f);
636
637                         break;
638
639                 case RT_LASER:
640                         break;
641
642                 default:
643                         Int3();
644
645         }
646
647 }
648
649 #ifdef EDITOR
650
651 //writes one object to the given file
652 void write_object(object *obj,FILE *f)
653 {
654         gs_write_byte(obj->type,f);
655         gs_write_byte(obj->id,f);
656
657         gs_write_byte(obj->control_type,f);
658         gs_write_byte(obj->movement_type,f);
659         gs_write_byte(obj->render_type,f);
660         gs_write_byte(obj->flags,f);
661
662         gs_write_short(obj->segnum,f);
663
664         gr_write_vector(&obj->pos,f);
665         gs_write_matrix(&obj->orient,f);
666
667         gs_write_fix(obj->size,f);
668         gs_write_fix(obj->shields,f);
669
670         gr_write_vector(&obj->last_pos,f);
671
672         gs_write_byte(obj->contains_type,f);
673         gs_write_byte(obj->contains_id,f);
674         gs_write_byte(obj->contains_count,f);
675
676         switch (obj->movement_type) {
677
678                 case MT_PHYSICS:
679
680                         gr_write_vector(&obj->mtype.phys_info.velocity,f);
681                         gr_write_vector(&obj->mtype.phys_info.thrust,f);
682
683                         gs_write_fix(obj->mtype.phys_info.mass,f);
684                         gs_write_fix(obj->mtype.phys_info.drag,f);
685                         gs_write_fix(obj->mtype.phys_info.brakes,f);
686
687                         gr_write_vector(&obj->mtype.phys_info.rotvel,f);
688                         gr_write_vector(&obj->mtype.phys_info.rotthrust,f);
689
690                         gs_write_fixang(obj->mtype.phys_info.turnroll,f);
691                         gs_write_short(obj->mtype.phys_info.flags,f);
692
693                         break;
694
695                 case MT_SPINNING:
696
697                         gr_write_vector(&obj->mtype.spin_rate,f);
698                         break;
699
700                 case MT_NONE:
701                         break;
702
703                 default:
704                         Int3();
705         }
706
707         switch (obj->control_type) {
708
709                 case CT_AI: {
710                         int i;
711
712                         gs_write_byte(obj->ctype.ai_info.behavior,f);
713
714                         for (i=0;i<MAX_AI_FLAGS;i++)
715                                 gs_write_byte(obj->ctype.ai_info.flags[i],f);
716
717                         gs_write_short(obj->ctype.ai_info.hide_segment,f);
718                         gs_write_short(obj->ctype.ai_info.hide_index,f);
719                         gs_write_short(obj->ctype.ai_info.path_length,f);
720                         gs_write_short(obj->ctype.ai_info.cur_path_index,f);
721
722                         // -- unused! mk, 08/13/95 -- gs_write_short(obj->ctype.ai_info.follow_path_start_seg,f);
723                         // -- unused! mk, 08/13/95 -- gs_write_short(obj->ctype.ai_info.follow_path_end_seg,f);
724
725                         break;
726                 }
727
728                 case CT_EXPLOSION:
729
730                         gs_write_fix(obj->ctype.expl_info.spawn_time,f);
731                         gs_write_fix(obj->ctype.expl_info.delete_time,f);
732                         gs_write_short(obj->ctype.expl_info.delete_objnum,f);
733
734                         break;
735
736                 case CT_WEAPON:
737
738                         //do I really need to write these objects?
739
740                         gs_write_short(obj->ctype.laser_info.parent_type,f);
741                         gs_write_short(obj->ctype.laser_info.parent_num,f);
742                         gs_write_int(obj->ctype.laser_info.parent_signature,f);
743
744                         break;
745
746                 case CT_LIGHT:
747
748                         gs_write_fix(obj->ctype.light_info.intensity,f);
749                         break;
750
751                 case CT_POWERUP:
752
753                         gs_write_int(obj->ctype.powerup_info.count,f);
754                         break;
755
756                 case CT_NONE:
757                 case CT_FLYING:
758                 case CT_DEBRIS:
759                         break;
760
761                 case CT_SLEW:           //the player is generally saved as slew
762                         break;
763
764                 case CT_CNTRLCEN:
765                         break;                  //control center object.
766
767                 case CT_MORPH:
768                 case CT_REPAIRCEN:
769                 case CT_FLYTHROUGH:
770                 default:
771                         Int3();
772         
773         }
774
775         switch (obj->render_type) {
776
777                 case RT_NONE:
778                         break;
779
780                 case RT_MORPH:
781                 case RT_POLYOBJ: {
782                         int i;
783
784                         gs_write_int(obj->rtype.pobj_info.model_num,f);
785
786                         for (i=0;i<MAX_SUBMODELS;i++)
787                                 gs_write_angvec(&obj->rtype.pobj_info.anim_angles[i],f);
788
789                         gs_write_int(obj->rtype.pobj_info.subobj_flags,f);
790
791                         gs_write_int(obj->rtype.pobj_info.tmap_override,f);
792
793                         break;
794                 }
795
796                 case RT_WEAPON_VCLIP:
797                 case RT_HOSTAGE:
798                 case RT_POWERUP:
799                 case RT_FIREBALL:
800
801                         gs_write_int(obj->rtype.vclip_info.vclip_num,f);
802                         gs_write_fix(obj->rtype.vclip_info.frametime,f);
803                         gs_write_byte(obj->rtype.vclip_info.framenum,f);
804
805                         break;
806
807                 case RT_LASER:
808                         break;
809
810                 default:
811                         Int3();
812
813         }
814
815 }
816 #endif
817
818 extern int remove_trigger_num(int trigger_num);
819
820 // --------------------------------------------------------------------
821 // Load game 
822 // Loads all the relevant data for a level.
823 // If level != -1, it loads the filename with extension changed to .min
824 // Otherwise it loads the appropriate level mine.
825 // returns 0=everything ok, 1=old version, -1=error
826 int load_game_data(CFILE *LoadFile)
827 {
828         int i,j;
829
830         short game_top_fileinfo_version;
831         int object_offset;
832         int gs_num_objects;
833         int num_delta_lights;
834         int trig_size;
835
836         //===================== READ FILE INFO ========================
837
838 #if 0
839         cfread(&game_top_fileinfo, sizeof(game_top_fileinfo), 1, LoadFile);
840 #endif
841
842         // Check signature
843         if (cfile_read_short(LoadFile) != 0x6705)
844                 return -1;
845
846         // Read and check version number
847         game_top_fileinfo_version = cfile_read_short(LoadFile);
848         if (game_top_fileinfo_version < GAME_COMPATIBLE_VERSION )
849                 return -1;
850
851         // We skip some parts of the former game_top_fileinfo
852         cfseek(LoadFile, 31, SEEK_CUR);
853
854         object_offset = cfile_read_int(LoadFile);
855         gs_num_objects = cfile_read_int(LoadFile);
856         cfseek(LoadFile, 8, SEEK_CUR);
857
858         Num_walls = cfile_read_int(LoadFile);
859         cfseek(LoadFile, 20, SEEK_CUR);
860
861         Num_triggers = cfile_read_int(LoadFile);
862         cfseek(LoadFile, 24, SEEK_CUR);
863
864         trig_size = cfile_read_int(LoadFile);
865         Assert(trig_size == sizeof(ControlCenterTriggers));
866         cfseek(LoadFile, 4, SEEK_CUR);
867
868         Num_robot_centers = cfile_read_int(LoadFile);
869         cfseek(LoadFile, 4, SEEK_CUR);
870
871         if (game_top_fileinfo_version >= 29) {
872                 cfseek(LoadFile, 4, SEEK_CUR);
873                 Num_static_lights = cfile_read_int(LoadFile);
874                 cfseek(LoadFile, 8, SEEK_CUR);
875                 num_delta_lights = cfile_read_int(LoadFile);
876                 cfseek(LoadFile, 4, SEEK_CUR);
877         } else {
878                 Num_static_lights = 0;
879                 num_delta_lights = 0;
880         }
881
882         if (game_top_fileinfo_version >= 31) //load mine filename
883                 // read newline-terminated string, not sure what version this changed.
884                 cfgets(Current_level_name,sizeof(Current_level_name),LoadFile);
885         else if (game_top_fileinfo_version >= 14) { //load mine filename
886                 // read null-terminated string
887                 char *p=Current_level_name;
888                 //must do read one char at a time, since no cfgets()
889                 do *p = cfgetc(LoadFile); while (*p++!=0);
890         }
891         else
892                 Current_level_name[0]=0;
893
894         if (game_top_fileinfo_version >= 19) {  //load pof names
895                 N_save_pof_names = cfile_read_short(LoadFile);
896                 if (N_save_pof_names != 0x614d && N_save_pof_names != 0x5547) { // "Ma"de w/DMB beta/"GU"ILE
897                         Assert(N_save_pof_names < MAX_POLYGON_MODELS);
898                         cfread(Save_pof_names,N_save_pof_names,FILENAME_LEN,LoadFile);
899                 }
900         }
901
902         //===================== READ PLAYER INFO ==========================
903         Object_next_signature = 0;
904
905         //===================== READ OBJECT INFO ==========================
906
907         Gamesave_num_org_robots = 0;
908         Gamesave_num_players = 0;
909
910         if (object_offset > -1) {
911                 if (cfseek( LoadFile, object_offset, SEEK_SET ))
912                         Error( "Error seeking to object_offset in gamesave.c" );
913
914                 for (i = 0; i < gs_num_objects; i++) {
915
916                         read_object(&Objects[i], LoadFile, game_top_fileinfo_version);
917
918                         Objects[i].signature = Object_next_signature++;
919                         verify_object( &Objects[i] );
920                 }
921
922         }
923
924         //===================== READ WALL INFO ============================
925
926         for (i = 0; i < Num_walls; i++) {
927                 if (game_top_fileinfo_version >= 20)
928                         wall_read(&Walls[i], LoadFile); // v20 walls and up.
929                 else if (game_top_fileinfo_version >= 17) {
930                         v19_wall w;
931                         v19_wall_read(&w, LoadFile);
932                         Walls[i].segnum         = w.segnum;
933                         Walls[i].sidenum        = w.sidenum;
934                         Walls[i].linked_wall    = w.linked_wall;
935                         Walls[i].type           = w.type;
936                         Walls[i].flags          = w.flags;
937                         Walls[i].hps            = w.hps;
938                         Walls[i].trigger        = w.trigger;
939                         Walls[i].clip_num       = w.clip_num;
940                         Walls[i].keys           = w.keys;
941                         Walls[i].state          = WALL_DOOR_CLOSED;
942                 } else {
943                         v16_wall w;
944                         v16_wall_read(&w, LoadFile);
945                         Walls[i].segnum = Walls[i].sidenum = Walls[i].linked_wall = -1;
946                         Walls[i].type           = w.type;
947                         Walls[i].flags          = w.flags;
948                         Walls[i].hps            = w.hps;
949                         Walls[i].trigger        = w.trigger;
950                         Walls[i].clip_num       = w.clip_num;
951                         Walls[i].keys           = w.keys;
952                 }
953         }
954
955 #if 0
956         //===================== READ DOOR INFO ============================
957
958         if (game_fileinfo.doors_offset > -1)
959         {
960                 if (!cfseek( LoadFile, game_fileinfo.doors_offset,SEEK_SET ))   {
961
962                         for (i=0;i<game_fileinfo.doors_howmany;i++) {
963
964                                 if (game_top_fileinfo_version >= 20)
965                                         active_door_read(&ActiveDoors[i], LoadFile); // version 20 and up
966                                 else {
967                                         v19_door d;
968                                         int p;
969
970                                         v19_door_read(&d, LoadFile);
971
972                                         ActiveDoors[i].n_parts = d.n_parts;
973
974                                         for (p=0;p<d.n_parts;p++) {
975                                                 int cseg,cside;
976
977                                                 cseg = Segments[d.seg[p]].children[d.side[p]];
978                                                 cside = find_connect_side(&Segments[d.seg[p]],&Segments[cseg]);
979
980                                                 ActiveDoors[i].front_wallnum[p] = Segments[d.seg[p]].sides[d.side[p]].wall_num;
981                                                 ActiveDoors[i].back_wallnum[p] = Segments[cseg].sides[cside].wall_num;
982                                         }
983                                 }
984
985                         }
986                 }
987         }
988 #endif // 0
989
990         //==================== READ TRIGGER INFO ==========================
991
992
993 // for MACINTOSH -- assume all triggers >= verion 31 triggers.
994
995         for (i = 0; i < Num_triggers; i++)
996         {
997                 if (game_top_fileinfo_version < 31)
998                 {
999                         v30_trigger trig;
1000                         int t,type;
1001                         type=0;
1002
1003                         if (game_top_fileinfo_version < 30) {
1004                                 v29_trigger trig29;
1005                                 int t;
1006                                 v29_trigger_read(&trig29, LoadFile);
1007                                 trig.flags      = trig29.flags;
1008                                 trig.num_links  = trig29.num_links;
1009                                 trig.num_links  = trig29.num_links;
1010                                 trig.value      = trig29.value;
1011                                 trig.time       = trig29.time;
1012
1013                                 for (t=0;t<trig.num_links;t++) {
1014                                         trig.seg[t]  = trig29.seg[t];
1015                                         trig.side[t] = trig29.side[t];
1016                                 }
1017                         }
1018                         else
1019                                 v30_trigger_read(&trig, LoadFile);
1020
1021                         //Assert(trig.flags & TRIGGER_ON);
1022                         trig.flags &= ~TRIGGER_ON;
1023
1024                         if (trig.flags & TRIGGER_CONTROL_DOORS)
1025                                 type = TT_OPEN_DOOR;
1026                         else if (trig.flags & TRIGGER_SHIELD_DAMAGE)
1027                                 Int3();
1028                         else if (trig.flags & TRIGGER_ENERGY_DRAIN)
1029                                 Int3();
1030                         else if (trig.flags & TRIGGER_EXIT)
1031                                 type = TT_EXIT;
1032                         else if (trig.flags & TRIGGER_ONE_SHOT)
1033                                 Int3();
1034                         else if (trig.flags & TRIGGER_MATCEN)
1035                                 type = TT_MATCEN;
1036                         else if (trig.flags & TRIGGER_ILLUSION_OFF)
1037                                 type = TT_ILLUSION_OFF;
1038                         else if (trig.flags & TRIGGER_SECRET_EXIT)
1039                                 type = TT_SECRET_EXIT;
1040                         else if (trig.flags & TRIGGER_ILLUSION_ON)
1041                                 type = TT_ILLUSION_ON;
1042                         else if (trig.flags & TRIGGER_UNLOCK_DOORS)
1043                                 type = TT_UNLOCK_DOOR;
1044                         else if (trig.flags & TRIGGER_OPEN_WALL)
1045                                 type = TT_OPEN_WALL;
1046                         else if (trig.flags & TRIGGER_CLOSE_WALL)
1047                                 type = TT_CLOSE_WALL;
1048                         else if (trig.flags & TRIGGER_ILLUSORY_WALL)
1049                                 type = TT_ILLUSORY_WALL;
1050                         else
1051                                 Int3();
1052                         Triggers[i].type        = type;
1053                         Triggers[i].flags       = 0;
1054                         Triggers[i].num_links   = trig.num_links;
1055                         Triggers[i].num_links   = trig.num_links;
1056                         Triggers[i].value       = trig.value;
1057                         Triggers[i].time        = trig.time;
1058                         for (t=0;t<trig.num_links;t++) {
1059                                 Triggers[i].seg[t] = trig.seg[t];
1060                                 Triggers[i].side[t] = trig.side[t];
1061                         }
1062                 }
1063                 else
1064                         trigger_read(&Triggers[i], LoadFile);
1065         }
1066
1067         //================ READ CONTROL CENTER TRIGGER INFO ===============
1068
1069 #if 0
1070         if (game_fileinfo.control_offset > -1)
1071                 if (!cfseek(LoadFile, game_fileinfo.control_offset, SEEK_SET))
1072                 {
1073                         Assert(game_fileinfo.control_sizeof == sizeof(control_center_triggers));
1074 #endif // 0
1075         control_center_triggers_read_n(&ControlCenterTriggers, 1, LoadFile);
1076
1077         //================ READ MATERIALOGRIFIZATIONATORS INFO ===============
1078
1079         // mprintf((0, "Reading %i materialization centers.\n", game_fileinfo.matcen_howmany));
1080         for (i = 0; i < Num_robot_centers; i++) {
1081                 if (game_top_fileinfo_version < 27) {
1082                         old_matcen_info m;
1083                         old_matcen_info_read(&m, LoadFile);
1084                         RobotCenters[i].robot_flags[0] = m.robot_flags;
1085                         RobotCenters[i].robot_flags[1] = 0;
1086                         RobotCenters[i].hit_points = m.hit_points;
1087                         RobotCenters[i].interval = m.interval;
1088                         RobotCenters[i].segnum = m.segnum;
1089                         RobotCenters[i].fuelcen_num = m.fuelcen_num;
1090                 }
1091                 else
1092                         matcen_info_read(&RobotCenters[i], LoadFile);
1093                         //      Set links in RobotCenters to Station array
1094                         for (j = 0; j <= Highest_segment_index; j++)
1095                         if (Segment2s[j].special == SEGMENT_IS_ROBOTMAKER)
1096                                 if (Segment2s[j].matcen_num == i)
1097                                         RobotCenters[i].fuelcen_num = Segment2s[j].value;
1098                         // mprintf((0, "   %i: flags = %08x\n", i, RobotCenters[i].robot_flags));
1099         }
1100
1101         //================ READ DL_INDICES INFO ===============
1102
1103         for (i = 0; i < Num_static_lights; i++) {
1104                 if (game_top_fileinfo_version < 29) {
1105                         mprintf((0, "Warning: Old mine version.  Not reading Dl_indices info.\n"));
1106                         Int3(); //shouldn't be here!!!
1107                 } else
1108                         dl_index_read(&Dl_indices[i], LoadFile);
1109         }
1110
1111         //      Indicate that no light has been subtracted from any vertices.
1112         clear_light_subtracted();
1113
1114         //================ READ DELTA LIGHT INFO ===============
1115
1116         for (i = 0; i < num_delta_lights; i++) {
1117                 if (game_top_fileinfo_version < 29) {
1118                         mprintf((0, "Warning: Old mine version.  Not reading delta light info.\n"));
1119                 } else
1120                         delta_light_read(&Delta_lights[i], LoadFile);
1121         }
1122
1123         //========================= UPDATE VARIABLES ======================
1124
1125         reset_objects(gs_num_objects);
1126
1127         for (i=0; i<MAX_OBJECTS; i++) {
1128                 Objects[i].next = Objects[i].prev = -1;
1129                 if (Objects[i].type != OBJ_NONE) {
1130                         int objsegnum = Objects[i].segnum;
1131
1132                         if (objsegnum > Highest_segment_index)          //bogus object
1133                                 Objects[i].type = OBJ_NONE;
1134                         else {
1135                                 Objects[i].segnum = -1;                 //avoid Assert()
1136                                 obj_link(i,objsegnum);
1137                         }
1138                 }
1139         }
1140
1141         clear_transient_objects(1);             //1 means clear proximity bombs
1142
1143         // Make sure non-transparent doors are set correctly.
1144         for (i=0; i< Num_segments; i++)
1145                 for (j=0;j<MAX_SIDES_PER_SEGMENT;j++) {
1146                         side    *sidep = &Segments[i].sides[j];
1147                         if ((sidep->wall_num != -1) && (Walls[sidep->wall_num].clip_num != -1)) {
1148                                 //mprintf((0, "Checking Wall %d\n", Segments[i].sides[j].wall_num));
1149                                 if (WallAnims[Walls[sidep->wall_num].clip_num].flags & WCF_TMAP1) {
1150                                         //mprintf((0, "Fixing non-transparent door.\n"));
1151                                         sidep->tmap_num = WallAnims[Walls[sidep->wall_num].clip_num].frames[0];
1152                                         sidep->tmap_num2 = 0;
1153                                 }
1154                         }
1155                 }
1156
1157
1158         reset_walls();
1159
1160 #if 0
1161         Num_open_doors = game_fileinfo.doors_howmany;
1162 #endif // 0
1163         Num_open_doors = 0;
1164
1165         //go through all walls, killing references to invalid triggers
1166         for (i=0;i<Num_walls;i++)
1167                 if (Walls[i].trigger >= Num_triggers) {
1168                         mprintf((0,"Removing reference to invalid trigger %d from wall %d\n",Walls[i].trigger,i));
1169                         Walls[i].trigger = -1;  //kill trigger
1170                 }
1171
1172         //go through all triggers, killing unused ones
1173         for (i=0;i<Num_triggers;) {
1174                 int w;
1175
1176                 //      Find which wall this trigger is connected to.
1177                 for (w=0; w<Num_walls; w++)
1178                         if (Walls[w].trigger == i)
1179                                 break;
1180
1181         #ifdef EDITOR
1182                 if (w == Num_walls) {
1183                         mprintf((0,"Removing unreferenced trigger %d\n",i));
1184                         remove_trigger_num(i);
1185                 }
1186                 else
1187         #endif
1188                         i++;
1189         }
1190
1191         //      MK, 10/17/95: Make walls point back at the triggers that control them.
1192         //      Go through all triggers, stuffing controlling_trigger field in Walls.
1193         {       int t;
1194
1195         for (i=0; i<Num_walls; i++)
1196                 Walls[i].controlling_trigger = -1;
1197
1198         for (t=0; t<Num_triggers; t++) {
1199                 int     l;
1200                 for (l=0; l<Triggers[t].num_links; l++) {
1201                         int     seg_num, side_num, wall_num;
1202
1203                         seg_num = Triggers[t].seg[l];
1204                         side_num = Triggers[t].side[l];
1205                         wall_num = Segments[seg_num].sides[side_num].wall_num;
1206
1207                         // -- if (Walls[wall_num].controlling_trigger != -1)
1208                         // --   Int3();
1209
1210                         //check to see that if a trigger requires a wall that it has one,
1211                         //and if it requires a matcen that it has one
1212
1213                         if (Triggers[t].type == TT_MATCEN) {
1214                                 if (Segment2s[seg_num].special != SEGMENT_IS_ROBOTMAKER)
1215                                         Int3();         //matcen trigger doesn't point to matcen
1216                         }
1217                         else if (Triggers[t].type != TT_LIGHT_OFF && Triggers[t].type != TT_LIGHT_ON) { //light triggers don't require walls
1218                                 if (wall_num == -1)
1219                                         Int3(); //      This is illegal.  This trigger requires a wall
1220                                 else
1221                                         Walls[wall_num].controlling_trigger = t;
1222                         }
1223                 }
1224         }
1225         }
1226
1227         //fix old wall structs
1228         if (game_top_fileinfo_version < 17) {
1229                 int segnum,sidenum,wallnum;
1230
1231                 for (segnum=0; segnum<=Highest_segment_index; segnum++)
1232                         for (sidenum=0;sidenum<6;sidenum++)
1233                                 if ((wallnum=Segments[segnum].sides[sidenum].wall_num) != -1) {
1234                                         Walls[wallnum].segnum = segnum;
1235                                         Walls[wallnum].sidenum = sidenum;
1236                                 }
1237         }
1238
1239         #ifndef NDEBUG
1240         {
1241                 int     sidenum;
1242                 for (sidenum=0; sidenum<6; sidenum++) {
1243                         int     wallnum = Segments[Highest_segment_index].sides[sidenum].wall_num;
1244                         if (wallnum != -1)
1245                                 if ((Walls[wallnum].segnum != Highest_segment_index) || (Walls[wallnum].sidenum != sidenum))
1246                                         Int3(); //      Error.  Bogus walls in this segment.
1247                                                                 // Consult Yuan or Mike.
1248                 }
1249         }
1250         #endif
1251
1252         //create_local_segment_data();
1253
1254         fix_object_segs();
1255
1256         #ifndef NDEBUG
1257         dump_mine_info();
1258         #endif
1259
1260         if (game_top_fileinfo_version < GAME_VERSION
1261             && !(game_top_fileinfo_version == 25 && GAME_VERSION == 26))
1262                 return 1;               //means old version
1263         else
1264                 return 0;
1265 }
1266
1267
1268 int check_segment_connections(void);
1269
1270 extern void     set_ambient_sound_flags(void);
1271
1272 // ----------------------------------------------------------------------------
1273
1274 #define LEVEL_FILE_VERSION      8
1275 //1 -> 2  add palette name
1276 //2 -> 3  add control center explosion time
1277 //3 -> 4  add reactor strength
1278 //4 -> 5  killed hostage text stuff
1279 //5 -> 6  added Secret_return_segment and Secret_return_orient
1280 //6 -> 7  added flickering lights
1281 //7 -> 8  made version 8 to be not compatible with D2 1.0 & 1.1
1282
1283 #ifndef RELEASE
1284 char *Level_being_loaded=NULL;
1285 #endif
1286
1287 #ifdef COMPACT_SEGS
1288 extern void ncache_flush();
1289 #endif
1290
1291 extern int Slide_segs_computed;
1292
1293 int no_old_level_file_error=0;
1294
1295 //loads a level (.LVL) file from disk
1296 //returns 0 if success, else error code
1297 int load_level(char * filename_passed)
1298 {
1299 #ifdef EDITOR
1300         int use_compiled_level=1;
1301 #endif
1302         CFILE * LoadFile;
1303         char filename[PATH_MAX];
1304         int sig, minedata_offset, gamedata_offset;
1305         int mine_err, game_err;
1306 #ifdef NETWORK
1307         int i;
1308 #endif
1309
1310         Slide_segs_computed = 0;
1311
1312 #ifdef NETWORK
1313    if (Game_mode & GM_NETWORK)
1314          {
1315           for (i=0;i<MAX_POWERUP_TYPES;i++)
1316                 {
1317                         MaxPowerupsAllowed[i]=0;
1318                         PowerupsInMine[i]=0;
1319                 }
1320          }
1321 #endif
1322
1323         #ifdef COMPACT_SEGS
1324         ncache_flush();
1325         #endif
1326
1327         #ifndef RELEASE
1328         Level_being_loaded = filename_passed;
1329         #endif
1330
1331         strcpy(filename,filename_passed);
1332
1333         #ifdef EDITOR
1334                 //if we have the editor, try the LVL first, no matter what was passed.
1335                 //if we don't have an LVL, try what was passed or RL2  
1336                 //if we don't have the editor, we just use what was passed
1337         
1338                 change_filename_extension(filename,filename_passed,".lvl");
1339                 use_compiled_level = 0;
1340         
1341                 if (!cfexist(filename))
1342                 {
1343                         char *p = strrchr(filename_passed, '.');
1344
1345                         if (stricmp(p, ".lvl"))
1346                                 strcpy(filename, filename_passed);      // set to what was passed
1347                         else
1348                                 change_filename_extension(filename, filename, ".rl2");
1349                         use_compiled_level = 1;
1350                 }               
1351         #endif
1352
1353         LoadFile = cfopen( filename, "rb" );
1354
1355         if (!LoadFile)  {
1356                 #ifdef EDITOR
1357                         mprintf((0,"Can't open level file <%s>\n", filename));
1358                         return 1;
1359                 #else
1360                         Error("Can't open file <%s>\n",filename);
1361                 #endif
1362         }
1363
1364         strcpy( Gamesave_current_filename, filename );
1365
1366 //      #ifdef NEWDEMO
1367 //      if ( Newdemo_state == ND_STATE_RECORDING )
1368 //              newdemo_record_start_demo();
1369 //      #endif
1370
1371         sig                      = cfile_read_int(LoadFile);
1372         Gamesave_current_version = cfile_read_int(LoadFile);
1373         mprintf((0, "Gamesave_current_version = %d\n", Gamesave_current_version));
1374         minedata_offset          = cfile_read_int(LoadFile);
1375         gamedata_offset          = cfile_read_int(LoadFile);
1376
1377         Assert(sig == MAKE_SIG('P','L','V','L'));
1378
1379         if (Gamesave_current_version >= 8) {    //read dummy data
1380                 cfile_read_int(LoadFile);
1381                 cfile_read_short(LoadFile);
1382                 cfile_read_byte(LoadFile);
1383         }
1384
1385         if (Gamesave_current_version < 5)
1386                 cfile_read_int(LoadFile);       //was hostagetext_offset
1387
1388         if (Gamesave_current_version > 1)
1389                 cfgets(Current_level_palette,sizeof(Current_level_palette),LoadFile);
1390         if (Gamesave_current_version <= 1 || Current_level_palette[0]==0) // descent 1 level
1391                 strcpy(Current_level_palette, DEFAULT_LEVEL_PALETTE);
1392
1393         if (Gamesave_current_version >= 3)
1394                 Base_control_center_explosion_time = cfile_read_int(LoadFile);
1395         else
1396                 Base_control_center_explosion_time = DEFAULT_CONTROL_CENTER_EXPLOSION_TIME;
1397
1398         if (Gamesave_current_version >= 4)
1399                 Reactor_strength = cfile_read_int(LoadFile);
1400         else
1401                 Reactor_strength = -1;  //use old defaults
1402
1403         if (Gamesave_current_version >= 7) {
1404                 int i;
1405
1406                 Num_flickering_lights = cfile_read_int(LoadFile);
1407                 Assert((Num_flickering_lights >= 0) && (Num_flickering_lights < MAX_FLICKERING_LIGHTS));
1408                 for (i = 0; i < Num_flickering_lights; i++)
1409                         flickering_light_read(&Flickering_lights[i], LoadFile);
1410         }
1411         else
1412                 Num_flickering_lights = 0;
1413
1414         if (Gamesave_current_version < 6) {
1415                 Secret_return_segment = 0;
1416                 Secret_return_orient.rvec.x = F1_0;
1417                 Secret_return_orient.rvec.y = 0;
1418                 Secret_return_orient.rvec.z = 0;
1419                 Secret_return_orient.fvec.x = 0;
1420                 Secret_return_orient.fvec.y = F1_0;
1421                 Secret_return_orient.fvec.z = 0;
1422                 Secret_return_orient.uvec.x = 0;
1423                 Secret_return_orient.uvec.y = 0;
1424                 Secret_return_orient.uvec.z = F1_0;
1425         } else {
1426                 Secret_return_segment = cfile_read_int(LoadFile);
1427                 Secret_return_orient.rvec.x = cfile_read_int(LoadFile);
1428                 Secret_return_orient.rvec.y = cfile_read_int(LoadFile);
1429                 Secret_return_orient.rvec.z = cfile_read_int(LoadFile);
1430                 Secret_return_orient.fvec.x = cfile_read_int(LoadFile);
1431                 Secret_return_orient.fvec.y = cfile_read_int(LoadFile);
1432                 Secret_return_orient.fvec.z = cfile_read_int(LoadFile);
1433                 Secret_return_orient.uvec.x = cfile_read_int(LoadFile);
1434                 Secret_return_orient.uvec.y = cfile_read_int(LoadFile);
1435                 Secret_return_orient.uvec.z = cfile_read_int(LoadFile);
1436         }
1437
1438         cfseek(LoadFile,minedata_offset,SEEK_SET);
1439         #ifdef EDITOR
1440         if (!use_compiled_level) {
1441                 mine_err = load_mine_data(LoadFile);
1442 #if 0 // get from d1src if needed
1443                 // Compress all uv coordinates in mine, improves texmap precision. --MK, 02/19/96
1444                 compress_uv_coordinates_all();
1445 #endif
1446         } else
1447         #endif
1448                 //NOTE LINK TO ABOVE!!
1449                 mine_err = load_mine_data_compiled(LoadFile);
1450
1451         if (mine_err == -1) {   //error!!
1452                 cfclose(LoadFile);
1453                 return 2;
1454         }
1455
1456         cfseek(LoadFile,gamedata_offset,SEEK_SET);
1457         game_err = load_game_data(LoadFile);
1458
1459         if (game_err == -1) {   //error!!
1460                 cfclose(LoadFile);
1461                 return 3;
1462         }
1463
1464         //======================== CLOSE FILE =============================
1465
1466         cfclose( LoadFile );
1467
1468         set_ambient_sound_flags();
1469
1470         #ifdef EDITOR
1471         write_game_text_file(filename);
1472         if (Errors_in_mine) {
1473                 if (is_real_level(filename)) {
1474                         char  ErrorMessage[200];
1475
1476                         sprintf( ErrorMessage, "Warning: %i errors in %s!\n", Errors_in_mine, Level_being_loaded );
1477                         stop_time();
1478                         gr_palette_load(gr_palette);
1479                         nm_messagebox( NULL, 1, "Continue", ErrorMessage );
1480                         start_time();
1481                 } else
1482                         mprintf((1, "Error: %i errors in %s.\n", Errors_in_mine, Level_being_loaded));
1483         }
1484         #endif
1485
1486         #ifdef EDITOR
1487         //If an old version, ask the use if he wants to save as new version
1488         if (!no_old_level_file_error && (Function_mode == FMODE_EDITOR) && (((LEVEL_FILE_VERSION > 3) && Gamesave_current_version < LEVEL_FILE_VERSION) || mine_err == 1 || game_err == 1)) {
1489                 char  ErrorMessage[200];
1490
1491                 sprintf( ErrorMessage,
1492                                         "You just loaded a old version\n"
1493                                         "level.  Would you like to save\n"
1494                                         "it as a current version level?");
1495
1496                 stop_time();
1497                 gr_palette_load(gr_palette);
1498                 if (nm_messagebox( NULL, 2, "Don't Save", "Save", ErrorMessage )==1)
1499                         save_level(filename);
1500                 start_time();
1501         }
1502         #endif
1503
1504         #ifdef EDITOR
1505         if (Function_mode == FMODE_EDITOR)
1506                 editor_status("Loaded NEW mine %s, \"%s\"",filename,Current_level_name);
1507         #endif
1508
1509         #ifdef EDITOR
1510         if (check_segment_connections())
1511                 nm_messagebox( "ERROR", 1, "Ok", 
1512                                 "Connectivity errors detected in\n"
1513                                 "mine.  See monochrome screen for\n"
1514                                 "details, and contact Matt or Mike." );
1515         #endif
1516
1517         return 0;
1518 }
1519
1520 #ifdef EDITOR
1521 int get_level_name()
1522 {
1523 //NO_UI!!!      UI_WINDOW                               *NameWindow = NULL;
1524 //NO_UI!!!      UI_GADGET_INPUTBOX      *NameText;
1525 //NO_UI!!!      UI_GADGET_BUTTON                *QuitButton;
1526 //NO_UI!!!
1527 //NO_UI!!!      // Open a window with a quit button
1528 //NO_UI!!!      NameWindow = ui_open_window( 20, 20, 300, 110, WIN_DIALOG );
1529 //NO_UI!!!      QuitButton = ui_add_gadget_button( NameWindow, 150-24, 60, 48, 40, "Done", NULL );
1530 //NO_UI!!!
1531 //NO_UI!!!      ui_wprintf_at( NameWindow, 10, 12,"Please enter a name for this mine:" );
1532 //NO_UI!!!      NameText = ui_add_gadget_inputbox( NameWindow, 10, 30, LEVEL_NAME_LEN, LEVEL_NAME_LEN, Current_level_name );
1533 //NO_UI!!!
1534 //NO_UI!!!      NameWindow->keyboard_focus_gadget = (UI_GADGET *)NameText;
1535 //NO_UI!!!      QuitButton->hotkey = KEY_ENTER;
1536 //NO_UI!!!
1537 //NO_UI!!!      ui_gadget_calc_keys(NameWindow);
1538 //NO_UI!!!
1539 //NO_UI!!!      while (!QuitButton->pressed && last_keypress!=KEY_ENTER) {
1540 //NO_UI!!!              ui_mega_process();
1541 //NO_UI!!!              ui_window_do_gadgets(NameWindow);
1542 //NO_UI!!!      }
1543 //NO_UI!!!
1544 //NO_UI!!!      strcpy( Current_level_name, NameText->text );
1545 //NO_UI!!!
1546 //NO_UI!!!      if ( NameWindow!=NULL ) {
1547 //NO_UI!!!              ui_close_window( NameWindow );
1548 //NO_UI!!!              NameWindow = NULL;
1549 //NO_UI!!!      }
1550 //NO_UI!!!
1551
1552         newmenu_item m[2];
1553
1554         m[0].type = NM_TYPE_TEXT; m[0].text = "Please enter a name for this mine:";
1555         m[1].type = NM_TYPE_INPUT; m[1].text = Current_level_name; m[1].text_len = LEVEL_NAME_LEN;
1556
1557         return newmenu_do( NULL, "Enter mine name", 2, m, NULL ) >= 0;
1558
1559 }
1560 #endif
1561
1562
1563 #ifdef EDITOR
1564
1565 int     Errors_in_mine;
1566
1567 // -----------------------------------------------------------------------------
1568 int compute_num_delta_light_records(void)
1569 {
1570         int     i;
1571         int     total = 0;
1572
1573         for (i=0; i<Num_static_lights; i++) {
1574                 total += Dl_indices[i].count;
1575         }
1576
1577         return total;
1578
1579 }
1580
1581 // -----------------------------------------------------------------------------
1582 // Save game
1583 int save_game_data(FILE * SaveFile)
1584 {
1585         int  player_offset, object_offset, walls_offset, doors_offset, triggers_offset, control_offset, matcen_offset; //, links_offset;
1586         int     dl_indices_offset, delta_light_offset;
1587         int start_offset,end_offset;
1588
1589         start_offset = ftell(SaveFile);
1590
1591         //===================== SAVE FILE INFO ========================
1592
1593         game_fileinfo.fileinfo_signature =      0x6705;
1594         game_fileinfo.fileinfo_version  =       GAME_VERSION;
1595         game_fileinfo.level                                     =  Current_level_num;
1596         game_fileinfo.fileinfo_sizeof           =       sizeof(game_fileinfo);
1597         game_fileinfo.player_offset             =       -1;
1598         game_fileinfo.player_sizeof             =       sizeof(player);
1599         game_fileinfo.object_offset             =       -1;
1600         game_fileinfo.object_howmany            =       Highest_object_index+1;
1601         game_fileinfo.object_sizeof             =       sizeof(object);
1602         game_fileinfo.walls_offset                      =       -1;
1603         game_fileinfo.walls_howmany             =       Num_walls;
1604         game_fileinfo.walls_sizeof                      =       sizeof(wall);
1605         game_fileinfo.doors_offset                      =       -1;
1606         game_fileinfo.doors_howmany             =       Num_open_doors;
1607         game_fileinfo.doors_sizeof                      =       sizeof(active_door);
1608         game_fileinfo.triggers_offset           =       -1;
1609         game_fileinfo.triggers_howmany  =       Num_triggers;
1610         game_fileinfo.triggers_sizeof           =       sizeof(trigger);
1611         game_fileinfo.control_offset            =       -1;
1612         game_fileinfo.control_howmany           =  1;
1613         game_fileinfo.control_sizeof            =  sizeof(control_center_triggers);
1614         game_fileinfo.matcen_offset             =       -1;
1615         game_fileinfo.matcen_howmany            =       Num_robot_centers;
1616         game_fileinfo.matcen_sizeof             =       sizeof(matcen_info);
1617
1618         game_fileinfo.dl_indices_offset         =       -1;
1619         game_fileinfo.dl_indices_howmany                =       Num_static_lights;
1620         game_fileinfo.dl_indices_sizeof         =       sizeof(dl_index);
1621
1622         game_fileinfo.delta_light_offset                =       -1;
1623         game_fileinfo.delta_light_howmany       =       compute_num_delta_light_records();
1624         game_fileinfo.delta_light_sizeof                =       sizeof(delta_light);
1625
1626         // Write the fileinfo
1627         fwrite( &game_fileinfo, sizeof(game_fileinfo), 1, SaveFile );
1628
1629         // Write the mine name
1630         fprintf(SaveFile,"%s\n",Current_level_name);
1631
1632         fwrite(&N_polygon_models,2,1,SaveFile);
1633         fwrite(Pof_names,N_polygon_models,sizeof(*Pof_names),SaveFile);
1634
1635         //==================== SAVE PLAYER INFO ===========================
1636
1637         player_offset = ftell(SaveFile);
1638         fwrite( &Players[Player_num], sizeof(player), 1, SaveFile );
1639
1640         //==================== SAVE OBJECT INFO ===========================
1641
1642         object_offset = ftell(SaveFile);
1643         //fwrite( &Objects, sizeof(object), game_fileinfo.object_howmany, SaveFile );
1644         {
1645                 int i;
1646                 for (i=0;i<game_fileinfo.object_howmany;i++)
1647                         write_object(&Objects[i],SaveFile);
1648         }
1649
1650         //==================== SAVE WALL INFO =============================
1651
1652         walls_offset = ftell(SaveFile);
1653         fwrite( Walls, sizeof(wall), game_fileinfo.walls_howmany, SaveFile );
1654
1655         //==================== SAVE DOOR INFO =============================
1656
1657         doors_offset = ftell(SaveFile);
1658         fwrite( ActiveDoors, sizeof(active_door), game_fileinfo.doors_howmany, SaveFile );
1659
1660         //==================== SAVE TRIGGER INFO =============================
1661
1662         triggers_offset = ftell(SaveFile);
1663         fwrite( Triggers, sizeof(trigger), game_fileinfo.triggers_howmany, SaveFile );
1664
1665         //================ SAVE CONTROL CENTER TRIGGER INFO ===============
1666
1667         control_offset = ftell(SaveFile);
1668         fwrite( &ControlCenterTriggers, sizeof(control_center_triggers), 1, SaveFile );
1669
1670
1671         //================ SAVE MATERIALIZATION CENTER TRIGGER INFO ===============
1672
1673         matcen_offset = ftell(SaveFile);
1674         // mprintf((0, "Writing %i materialization centers\n", game_fileinfo.matcen_howmany));
1675         // { int i;
1676         // for (i=0; i<game_fileinfo.matcen_howmany; i++)
1677         //      mprintf((0, "   %i: robot_flags = %08x\n", i, RobotCenters[i].robot_flags));
1678         // }
1679         fwrite( RobotCenters, sizeof(matcen_info), game_fileinfo.matcen_howmany, SaveFile );
1680
1681         //================ SAVE DELTA LIGHT INFO ===============
1682         dl_indices_offset = ftell(SaveFile);
1683         fwrite( Dl_indices, sizeof(dl_index), game_fileinfo.dl_indices_howmany, SaveFile );
1684
1685         delta_light_offset = ftell(SaveFile);
1686         fwrite( Delta_lights, sizeof(delta_light), game_fileinfo.delta_light_howmany, SaveFile );
1687
1688         //============= REWRITE FILE INFO, TO SAVE OFFSETS ===============
1689
1690         // Update the offset fields
1691         game_fileinfo.player_offset             =       player_offset;
1692         game_fileinfo.object_offset             =       object_offset;
1693         game_fileinfo.walls_offset                      =       walls_offset;
1694         game_fileinfo.doors_offset                      =       doors_offset;
1695         game_fileinfo.triggers_offset           =       triggers_offset;
1696         game_fileinfo.control_offset            =       control_offset;
1697         game_fileinfo.matcen_offset             =       matcen_offset;
1698         game_fileinfo.dl_indices_offset =       dl_indices_offset;
1699         game_fileinfo.delta_light_offset        =       delta_light_offset;
1700
1701
1702         end_offset = ftell(SaveFile);
1703
1704         // Write the fileinfo
1705         fseek(  SaveFile, start_offset, SEEK_SET );  // Move to TOF
1706         fwrite( &game_fileinfo, sizeof(game_fileinfo), 1, SaveFile );
1707
1708         // Go back to end of data
1709         fseek(SaveFile,end_offset,SEEK_SET);
1710
1711         return 0;
1712 }
1713
1714 int save_mine_data(FILE * SaveFile);
1715
1716 // -----------------------------------------------------------------------------
1717 // Save game
1718 int save_level_sub(char * filename, int compiled_version)
1719 {
1720         FILE * SaveFile;
1721         char temp_filename[PATH_MAX];
1722         int sig = MAKE_SIG('P','L','V','L'),version=LEVEL_FILE_VERSION;
1723         int minedata_offset=0,gamedata_offset=0;
1724
1725         if ( !compiled_version )        {
1726                 write_game_text_file(filename);
1727
1728                 if (Errors_in_mine) {
1729                         if (is_real_level(filename)) {
1730                                 char  ErrorMessage[200];
1731         
1732                                 sprintf( ErrorMessage, "Warning: %i errors in this mine!\n", Errors_in_mine );
1733                                 stop_time();
1734                                 gr_palette_load(gr_palette);
1735          
1736                                 if (nm_messagebox( NULL, 2, "Cancel Save", "Save", ErrorMessage )!=1)   {
1737                                         start_time();
1738                                         return 1;
1739                                 }
1740                                 start_time();
1741                         } else
1742                                 mprintf((1, "Error: %i errors in this mine.  See the 'txm' file.\n", Errors_in_mine));
1743                 }
1744                 change_filename_extension(temp_filename,filename,".LVL");
1745         }
1746         else
1747         {
1748                 // macs are using the regular hog/rl2 files for shareware
1749                 #if defined(SHAREWARE) && !defined(MACINTOSH)
1750                         change_filename_extension(temp_filename,filename,".SL2");
1751                 #else
1752                         change_filename_extension(temp_filename,filename,".RL2");
1753                 #endif
1754         }
1755
1756         SaveFile = fopen( temp_filename, "wb" );
1757         if (!SaveFile)
1758         {
1759                 char ErrorMessage[256];
1760
1761                 char fname[20];
1762                 _splitpath( temp_filename, NULL, NULL, fname, NULL );
1763
1764                 sprintf( ErrorMessage, \
1765                         "ERROR: Cannot write to '%s'.\nYou probably need to check out a locked\nversion of the file. You should save\nthis under a different filename, and then\ncheck out a locked copy by typing\n\'co -l %s.lvl'\nat the DOS prompt.\n" 
1766                         , temp_filename, fname );
1767                 stop_time();
1768                 gr_palette_load(gr_palette);
1769                 nm_messagebox( NULL, 1, "Ok", ErrorMessage );
1770                 start_time();
1771                 return 1;
1772         }
1773
1774         if (Current_level_name[0] == 0)
1775                 strcpy(Current_level_name,"Untitled");
1776
1777         clear_transient_objects(1);             //1 means clear proximity bombs
1778
1779         compress_objects();             //after this, Highest_object_index == num objects
1780
1781         //make sure player is in a segment
1782         if (update_object_seg(&Objects[Players[0].objnum]) == 0) {
1783                 if (ConsoleObject->segnum > Highest_segment_index)
1784                         ConsoleObject->segnum = 0;
1785                 compute_segment_center(&ConsoleObject->pos,&(Segments[ConsoleObject->segnum]));
1786         }
1787  
1788         fix_object_segs();
1789
1790         //Write the header
1791
1792         gs_write_int(sig,SaveFile);
1793         gs_write_int(version,SaveFile);
1794
1795         //save placeholders
1796         gs_write_int(minedata_offset,SaveFile);
1797         gs_write_int(gamedata_offset,SaveFile);
1798
1799         //Now write the damn data
1800
1801         //write the version 8 data (to make file unreadable by 1.0 & 1.1)
1802         gs_write_int(GameTime,SaveFile);
1803         gs_write_short(FrameCount,SaveFile);
1804         gs_write_byte(FrameTime,SaveFile);
1805
1806         // Write the palette file name
1807         fprintf(SaveFile,"%s\n",Current_level_palette);
1808
1809         gs_write_int(Base_control_center_explosion_time,SaveFile);
1810         gs_write_int(Reactor_strength,SaveFile);
1811
1812         gs_write_int(Num_flickering_lights,SaveFile);
1813         fwrite(Flickering_lights,sizeof(*Flickering_lights),Num_flickering_lights,SaveFile);
1814         
1815         gs_write_int(Secret_return_segment, SaveFile);
1816         gs_write_int(Secret_return_orient.rvec.x, SaveFile);
1817         gs_write_int(Secret_return_orient.rvec.y, SaveFile);
1818         gs_write_int(Secret_return_orient.rvec.z, SaveFile);
1819         gs_write_int(Secret_return_orient.fvec.x, SaveFile);
1820         gs_write_int(Secret_return_orient.fvec.y, SaveFile);
1821         gs_write_int(Secret_return_orient.fvec.z, SaveFile);
1822         gs_write_int(Secret_return_orient.uvec.x, SaveFile);
1823         gs_write_int(Secret_return_orient.uvec.y, SaveFile);
1824         gs_write_int(Secret_return_orient.uvec.z, SaveFile);
1825
1826         minedata_offset = ftell(SaveFile);
1827         if ( !compiled_version )        
1828                 save_mine_data(SaveFile);
1829         else
1830                 save_mine_data_compiled(SaveFile);
1831         gamedata_offset = ftell(SaveFile);
1832         save_game_data(SaveFile);
1833
1834         fseek(SaveFile,sizeof(sig)+sizeof(version),SEEK_SET);
1835         gs_write_int(minedata_offset,SaveFile);
1836         gs_write_int(gamedata_offset,SaveFile);
1837
1838         //==================== CLOSE THE FILE =============================
1839         fclose(SaveFile);
1840
1841         if ( !compiled_version )        {
1842                 if (Function_mode == FMODE_EDITOR)
1843                         editor_status("Saved mine %s, \"%s\"",filename,Current_level_name);
1844         }
1845
1846         return 0;
1847
1848 }
1849
1850 #if 0 //dunno - 3rd party stuff?
1851 extern void compress_uv_coordinates_all(void);
1852 #endif
1853
1854 int save_level(char * filename)
1855 {
1856         int r1;
1857
1858         // Save normal version...
1859         r1 = save_level_sub(filename, 0);
1860
1861         // Save compiled version...
1862         save_level_sub(filename, 1);
1863
1864         return r1;
1865 }
1866
1867 #endif  //EDITOR
1868
1869 #ifndef NDEBUG
1870 void dump_mine_info(void)
1871 {
1872         int     segnum, sidenum;
1873         fix     min_u, max_u, min_v, max_v, min_l, max_l, max_sl;
1874
1875         min_u = F1_0*1000;
1876         min_v = min_u;
1877         min_l = min_u;
1878
1879         max_u = -min_u;
1880         max_v = max_u;
1881         max_l = max_u;
1882
1883         max_sl = 0;
1884
1885         for (segnum=0; segnum<=Highest_segment_index; segnum++) {
1886                 for (sidenum=0; sidenum<MAX_SIDES_PER_SEGMENT; sidenum++) {
1887                         int     vertnum;
1888                         side    *sidep = &Segments[segnum].sides[sidenum];
1889
1890                         if (Segment2s[segnum].static_light > max_sl)
1891                                 max_sl = Segment2s[segnum].static_light;
1892
1893                         for (vertnum=0; vertnum<4; vertnum++) {
1894                                 if (sidep->uvls[vertnum].u < min_u)
1895                                         min_u = sidep->uvls[vertnum].u;
1896                                 else if (sidep->uvls[vertnum].u > max_u)
1897                                         max_u = sidep->uvls[vertnum].u;
1898
1899                                 if (sidep->uvls[vertnum].v < min_v)
1900                                         min_v = sidep->uvls[vertnum].v;
1901                                 else if (sidep->uvls[vertnum].v > max_v)
1902                                         max_v = sidep->uvls[vertnum].v;
1903
1904                                 if (sidep->uvls[vertnum].l < min_l)
1905                                         min_l = sidep->uvls[vertnum].l;
1906                                 else if (sidep->uvls[vertnum].l > max_l)
1907                                         max_l = sidep->uvls[vertnum].l;
1908                         }
1909
1910                 }
1911         }
1912
1913 //      mprintf((0, "Smallest uvl = %7.3f %7.3f %7.3f.  Largest uvl = %7.3f %7.3f %7.3f\n", f2fl(min_u), f2fl(min_v), f2fl(min_l), f2fl(max_u), f2fl(max_v), f2fl(max_l)));
1914 //      mprintf((0, "Static light maximum = %7.3f\n", f2fl(max_sl)));
1915 //      mprintf((0, "Number of walls: %i\n", Num_walls));
1916
1917 }
1918
1919 #endif
1920
1921 #ifdef EDITOR
1922
1923 //read in every level in mission and save out compiled version 
1924 void save_all_compiled_levels(void)
1925 {
1926         do_load_save_levels(1);
1927 }
1928
1929 //read in every level in mission
1930 void load_all_levels(void)
1931 {
1932         do_load_save_levels(0);
1933 }
1934
1935
1936 void do_load_save_levels(int save)
1937 {
1938         int level_num;
1939
1940         if (! SafetyCheck())
1941                 return;
1942
1943         no_old_level_file_error=1;
1944
1945         for (level_num=1;level_num<=Last_level;level_num++) {
1946                 load_level(Level_names[level_num-1]);
1947                 load_palette(Current_level_palette,1,1);                //don't change screen
1948                 if (save)
1949                         save_level_sub(Level_names[level_num-1],1);
1950         }
1951
1952         for (level_num=-1;level_num>=Last_secret_level;level_num--) {
1953                 load_level(Secret_level_names[-level_num-1]);
1954                 load_palette(Current_level_palette,1,1);                //don't change screen
1955                 if (save)
1956                         save_level_sub(Secret_level_names[-level_num-1],1);
1957         }
1958
1959         no_old_level_file_error=0;
1960
1961 }
1962
1963 #endif