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