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