1 /* $Id: gamesave.c,v 1.31 2005-03-12 07:50:53 chris Exp $ */
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.
17 * Save game information
26 char gamesave_rcsid[] = "$Id: gamesave.c,v 1.31 2005-03-12 07:50:53 chris Exp $";
42 #include "editor/editor.h"
74 char Gamesave_current_filename[PATH_MAX];
76 int Gamesave_current_version;
78 #define GAME_VERSION 32
79 #define GAME_COMPATIBLE_VERSION 22
81 //version 28->29 add delta light support
82 //version 27->28 controlcen id now is reactor number, not model number
84 //version 29->30 changed trigger structure
85 //version 30->31 changed trigger structure some more
86 //version 31->32 change segment structure, make it 512 bytes w/o editor, add Segment2s array.
88 #define MENU_CURSOR_X_MIN MENU_X
89 #define MENU_CURSOR_X_MAX MENU_X+6
93 ushort fileinfo_signature;
94 ushort fileinfo_version;
96 } game_top_fileinfo; // Should be same as first two fields below...
99 ushort fileinfo_signature;
100 ushort fileinfo_version;
102 char mine_filename[15];
104 int player_offset; // Player info
106 int object_offset; // Object info
116 int triggers_howmany;
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;
136 // LINT: adding function prototypes
137 void read_object(object *obj, CFILE *f, int version);
139 void write_object(object *obj, FILE *f);
140 void do_load_save_levels(int save);
143 void dump_mine_info(void);
146 extern char MaxPowerupsAllowed[MAX_POWERUP_TYPES];
147 extern char PowerupsInMine[MAX_POWERUP_TYPES];
150 extern char mine_filename[];
151 extern int save_mine_data_compiled(FILE * SaveFile);
153 //--unused-- char mine_filename[128];
156 int Gamesave_num_org_robots = 0;
157 //--unused-- grs_bitmap * Gamesave_saved_bitmap = NULL;
160 // Return true if this level has a name of the form "level??"
161 // Note that a pathspec can appear at the beginning of the filename.
162 int is_real_level(char *filename)
164 int len = strlen(filename);
169 //mprintf((0, "String = [%s]\n", &filename[len-11]));
170 return !strnicmp(&filename[len-11], "level", 5);
175 void change_filename_extension( char *dest, char *src, char *new_ext )
184 for (i=1; i<strlen(dest); i++ )
185 if (dest[i]=='.'||dest[i]==' '||dest[i]==0)
190 dest[i+1]=new_ext[0];
191 dest[i+2]=new_ext[1];
192 dest[i+3]=new_ext[2];
198 //--unused-- vms_angvec zero_angles={0,0,0};
200 #define vm_angvec_zero(v) do {(v)->p=(v)->b=(v)->h=0;} while (0)
202 int Gamesave_num_players=0;
204 int N_save_pof_names;
205 char Save_pof_names[MAX_POLYGON_MODELS][FILENAME_LEN];
207 void check_and_fix_matrix(vms_matrix *m);
209 void verify_object( object * obj ) {
211 obj->lifeleft = IMMORTAL_TIME; //all loaded object are immortal, for now
213 if ( obj->type == OBJ_ROBOT ) {
214 Gamesave_num_org_robots++;
216 // Make sure valid id...
217 if ( obj->id >= N_robot_types )
218 obj->id = obj->id % N_robot_types;
220 // Make sure model number & size are correct...
221 if ( obj->render_type == RT_POLYOBJ ) {
222 Assert(Robot_info[obj->id].model_num != -1);
223 //if you fail this assert, it means that a robot in this level
224 //hasn't been loaded, possibly because he's marked as
225 //non-shareware. To see what robot number, print obj->id.
227 Assert(Robot_info[obj->id].always_0xabcd == 0xabcd);
228 //if you fail this assert, it means that the robot_ai for
229 //a robot in this level hasn't been loaded, possibly because
230 //it's marked as non-shareware. To see what robot number,
233 obj->rtype.pobj_info.model_num = Robot_info[obj->id].model_num;
234 obj->size = Polygon_models[obj->rtype.pobj_info.model_num].rad;
236 //@@Took out this ugly hack 1/12/96, because Mike has added code
237 //@@that should fix it in a better way.
238 //@@//this is a super-ugly hack. Since the baby stripe robots have
239 //@@//their firing point on their bounding sphere, the firing points
240 //@@//can poke through a wall if the robots are very close to it. So
241 //@@//we make their radii bigger so the guns can't get too close to
243 //@@if (Robot_info[obj->id].flags & RIF_BIG_RADIUS)
244 //@@ obj->size = (obj->size*3)/2;
246 //@@if (obj->control_type==CT_AI && Robot_info[obj->id].attack_type)
247 //@@ obj->size = obj->size*3/4;
250 if (obj->id == 65) //special "reactor" robots
251 obj->movement_type = MT_NONE;
253 if (obj->movement_type == MT_PHYSICS) {
254 obj->mtype.phys_info.mass = Robot_info[obj->id].mass;
255 obj->mtype.phys_info.drag = Robot_info[obj->id].drag;
258 else { //Robots taken care of above
260 if ( obj->render_type == RT_POLYOBJ ) {
262 char *name = Save_pof_names[obj->rtype.pobj_info.model_num];
264 for (i=0;i<N_polygon_models;i++)
265 if (!stricmp(Pof_names[i],name)) { //found it!
266 // mprintf((0,"Mapping <%s> to %d (was %d)\n",name,i,obj->rtype.pobj_info.model_num));
267 obj->rtype.pobj_info.model_num = i;
273 if ( obj->type == OBJ_POWERUP ) {
274 if ( obj->id >= N_powerup_types ) {
276 Assert( obj->render_type != RT_POLYOBJ );
278 obj->control_type = CT_POWERUP;
279 obj->size = Powerup_info[obj->id].size;
280 obj->ctype.powerup_info.creation_time = 0;
283 if (Game_mode & GM_NETWORK)
285 if (multi_powerup_is_4pack(obj->id))
287 PowerupsInMine[obj->id-1]+=4;
288 MaxPowerupsAllowed[obj->id-1]+=4;
290 PowerupsInMine[obj->id]++;
291 MaxPowerupsAllowed[obj->id]++;
292 mprintf ((0,"PowerupLimiter: ID=%d\n",obj->id));
293 if (obj->id>MAX_POWERUP_TYPES)
294 mprintf ((1,"POWERUP: Overwriting array bounds!! Get JL!\n"));
300 if ( obj->type == OBJ_WEAPON ) {
301 if ( obj->id >= N_weapon_types ) {
303 Assert( obj->render_type != RT_POLYOBJ );
306 if (obj->id == PMINE_ID) { //make sure pmines have correct values
308 obj->mtype.phys_info.mass = Weapon_info[obj->id].mass;
309 obj->mtype.phys_info.drag = Weapon_info[obj->id].drag;
310 obj->mtype.phys_info.flags |= PF_FREE_SPINNING;
312 // Make sure model number & size are correct...
313 Assert( obj->render_type == RT_POLYOBJ );
315 obj->rtype.pobj_info.model_num = Weapon_info[obj->id].model_num;
316 obj->size = Polygon_models[obj->rtype.pobj_info.model_num].rad;
320 if ( obj->type == OBJ_CNTRLCEN ) {
322 obj->render_type = RT_POLYOBJ;
323 obj->control_type = CT_CNTRLCEN;
325 if (Gamesave_current_version <= 1) { // descent 1 reactor
326 obj->id = 0; // used to be only one kind of reactor
327 obj->rtype.pobj_info.model_num = Reactors[0].model_num;// descent 1 reactor
330 // Make sure model number is correct...
331 //obj->rtype.pobj_info.model_num = Reactors[obj->id].model_num;
334 if ( obj->type == OBJ_PLAYER ) {
337 //Assert(obj == Player);
339 if ( obj == ConsoleObject )
340 init_player_object();
342 if (obj->render_type == RT_POLYOBJ) //recover from Matt's pof file matchup bug
343 obj->rtype.pobj_info.model_num = Player_ship->model_num;
345 //Make sure orient matrix is orthogonal
346 check_and_fix_matrix(&obj->orient);
348 obj->id = Gamesave_num_players++;
351 if (obj->type == OBJ_HOSTAGE) {
353 //@@if (obj->id > N_hostage_types)
356 obj->render_type = RT_HOSTAGE;
357 obj->control_type = CT_POWERUP;
362 //static gs_skip(int len,CFILE *file)
365 // cfseek(file,len,SEEK_CUR);
369 static void gs_write_int(int i,FILE *file)
371 if (fwrite( &i, sizeof(i), 1, file) != 1)
372 Error( "Error reading int in gamesave.c" );
376 static void gs_write_fix(fix f,FILE *file)
378 if (fwrite( &f, sizeof(f), 1, file) != 1)
379 Error( "Error reading fix in gamesave.c" );
383 static void gs_write_short(short s,FILE *file)
385 if (fwrite( &s, sizeof(s), 1, file) != 1)
386 Error( "Error reading short in gamesave.c" );
390 static void gs_write_fixang(fixang f,FILE *file)
392 if (fwrite( &f, sizeof(f), 1, file) != 1)
393 Error( "Error reading fixang in gamesave.c" );
397 static void gs_write_byte(sbyte b,FILE *file)
399 if (fwrite( &b, sizeof(b), 1, file) != 1)
400 Error( "Error reading byte in gamesave.c" );
404 static void gr_write_vector(vms_vector *v,FILE *file)
406 gs_write_fix(v->x,file);
407 gs_write_fix(v->y,file);
408 gs_write_fix(v->z,file);
411 static void gs_write_matrix(vms_matrix *m,FILE *file)
413 gr_write_vector(&m->rvec,file);
414 gr_write_vector(&m->uvec,file);
415 gr_write_vector(&m->fvec,file);
418 static void gs_write_angvec(vms_angvec *v,FILE *file)
420 gs_write_fixang(v->p,file);
421 gs_write_fixang(v->b,file);
422 gs_write_fixang(v->h,file);
428 extern int multi_powerup_is_4pack(int);
429 //reads one object of the given version from the given file
430 void read_object(object *obj,CFILE *f,int version)
433 obj->type = cfile_read_byte(f);
434 obj->id = cfile_read_byte(f);
436 obj->control_type = cfile_read_byte(f);
437 obj->movement_type = cfile_read_byte(f);
438 obj->render_type = cfile_read_byte(f);
439 obj->flags = cfile_read_byte(f);
441 obj->segnum = cfile_read_short(f);
442 obj->attached_obj = -1;
444 cfile_read_vector(&obj->pos,f);
445 cfile_read_matrix(&obj->orient,f);
447 obj->size = cfile_read_fix(f);
448 obj->shields = cfile_read_fix(f);
450 cfile_read_vector(&obj->last_pos,f);
452 obj->contains_type = cfile_read_byte(f);
453 obj->contains_id = cfile_read_byte(f);
454 obj->contains_count = cfile_read_byte(f);
456 switch (obj->movement_type) {
460 cfile_read_vector(&obj->mtype.phys_info.velocity,f);
461 cfile_read_vector(&obj->mtype.phys_info.thrust,f);
463 obj->mtype.phys_info.mass = cfile_read_fix(f);
464 obj->mtype.phys_info.drag = cfile_read_fix(f);
465 obj->mtype.phys_info.brakes = cfile_read_fix(f);
467 cfile_read_vector(&obj->mtype.phys_info.rotvel,f);
468 cfile_read_vector(&obj->mtype.phys_info.rotthrust,f);
470 obj->mtype.phys_info.turnroll = cfile_read_fixang(f);
471 obj->mtype.phys_info.flags = cfile_read_short(f);
477 cfile_read_vector(&obj->mtype.spin_rate,f);
487 switch (obj->control_type) {
492 obj->ctype.ai_info.behavior = cfile_read_byte(f);
494 for (i=0;i<MAX_AI_FLAGS;i++)
495 obj->ctype.ai_info.flags[i] = cfile_read_byte(f);
497 obj->ctype.ai_info.hide_segment = cfile_read_short(f);
498 obj->ctype.ai_info.hide_index = cfile_read_short(f);
499 obj->ctype.ai_info.path_length = cfile_read_short(f);
500 obj->ctype.ai_info.cur_path_index = cfile_read_short(f);
503 cfile_read_short(f); // obj->ctype.ai_info.follow_path_start_seg =
504 cfile_read_short(f); // obj->ctype.ai_info.follow_path_end_seg =
512 obj->ctype.expl_info.spawn_time = cfile_read_fix(f);
513 obj->ctype.expl_info.delete_time = cfile_read_fix(f);
514 obj->ctype.expl_info.delete_objnum = cfile_read_short(f);
515 obj->ctype.expl_info.next_attach = obj->ctype.expl_info.prev_attach = obj->ctype.expl_info.attach_parent = -1;
521 //do I really need to read these? Are they even saved to disk?
523 obj->ctype.laser_info.parent_type = cfile_read_short(f);
524 obj->ctype.laser_info.parent_num = cfile_read_short(f);
525 obj->ctype.laser_info.parent_signature = cfile_read_int(f);
531 obj->ctype.light_info.intensity = cfile_read_fix(f);
537 obj->ctype.powerup_info.count = cfile_read_int(f);
539 obj->ctype.powerup_info.count = 1;
541 if (obj->id == POW_VULCAN_WEAPON)
542 obj->ctype.powerup_info.count = VULCAN_WEAPON_AMMO_AMOUNT;
544 if (obj->id == POW_GAUSS_WEAPON)
545 obj->ctype.powerup_info.count = VULCAN_WEAPON_AMMO_AMOUNT;
547 if (obj->id == POW_OMEGA_WEAPON)
548 obj->ctype.powerup_info.count = MAX_OMEGA_CHARGE;
558 case CT_SLEW: //the player is generally saved as slew
572 switch (obj->render_type) {
581 obj->rtype.pobj_info.model_num = cfile_read_int(f);
583 for (i=0;i<MAX_SUBMODELS;i++)
584 cfile_read_angvec(&obj->rtype.pobj_info.anim_angles[i],f);
586 obj->rtype.pobj_info.subobj_flags = cfile_read_int(f);
588 tmo = cfile_read_int(f);
591 obj->rtype.pobj_info.tmap_override = tmo;
594 obj->rtype.pobj_info.tmap_override = -1;
596 int xlated_tmo = tmap_xlate_table[tmo];
597 if (xlated_tmo < 0) {
598 mprintf( (0, "Couldn't find texture for demo object, model_num = %d\n", obj->rtype.pobj_info.model_num));
602 obj->rtype.pobj_info.tmap_override = xlated_tmo;
606 obj->rtype.pobj_info.alt_textures = 0;
611 case RT_WEAPON_VCLIP:
616 obj->rtype.vclip_info.vclip_num = cfile_read_int(f);
617 obj->rtype.vclip_info.frametime = cfile_read_fix(f);
618 obj->rtype.vclip_info.framenum = cfile_read_byte(f);
634 //writes one object to the given file
635 void write_object(object *obj,FILE *f)
637 gs_write_byte(obj->type,f);
638 gs_write_byte(obj->id,f);
640 gs_write_byte(obj->control_type,f);
641 gs_write_byte(obj->movement_type,f);
642 gs_write_byte(obj->render_type,f);
643 gs_write_byte(obj->flags,f);
645 gs_write_short(obj->segnum,f);
647 gr_write_vector(&obj->pos,f);
648 gs_write_matrix(&obj->orient,f);
650 gs_write_fix(obj->size,f);
651 gs_write_fix(obj->shields,f);
653 gr_write_vector(&obj->last_pos,f);
655 gs_write_byte(obj->contains_type,f);
656 gs_write_byte(obj->contains_id,f);
657 gs_write_byte(obj->contains_count,f);
659 switch (obj->movement_type) {
663 gr_write_vector(&obj->mtype.phys_info.velocity,f);
664 gr_write_vector(&obj->mtype.phys_info.thrust,f);
666 gs_write_fix(obj->mtype.phys_info.mass,f);
667 gs_write_fix(obj->mtype.phys_info.drag,f);
668 gs_write_fix(obj->mtype.phys_info.brakes,f);
670 gr_write_vector(&obj->mtype.phys_info.rotvel,f);
671 gr_write_vector(&obj->mtype.phys_info.rotthrust,f);
673 gs_write_fixang(obj->mtype.phys_info.turnroll,f);
674 gs_write_short(obj->mtype.phys_info.flags,f);
680 gr_write_vector(&obj->mtype.spin_rate,f);
690 switch (obj->control_type) {
695 gs_write_byte(obj->ctype.ai_info.behavior,f);
697 for (i=0;i<MAX_AI_FLAGS;i++)
698 gs_write_byte(obj->ctype.ai_info.flags[i],f);
700 gs_write_short(obj->ctype.ai_info.hide_segment,f);
701 gs_write_short(obj->ctype.ai_info.hide_index,f);
702 gs_write_short(obj->ctype.ai_info.path_length,f);
703 gs_write_short(obj->ctype.ai_info.cur_path_index,f);
705 // -- unused! mk, 08/13/95 -- gs_write_short(obj->ctype.ai_info.follow_path_start_seg,f);
706 // -- unused! mk, 08/13/95 -- gs_write_short(obj->ctype.ai_info.follow_path_end_seg,f);
713 gs_write_fix(obj->ctype.expl_info.spawn_time,f);
714 gs_write_fix(obj->ctype.expl_info.delete_time,f);
715 gs_write_short(obj->ctype.expl_info.delete_objnum,f);
721 //do I really need to write these objects?
723 gs_write_short(obj->ctype.laser_info.parent_type,f);
724 gs_write_short(obj->ctype.laser_info.parent_num,f);
725 gs_write_int(obj->ctype.laser_info.parent_signature,f);
731 gs_write_fix(obj->ctype.light_info.intensity,f);
736 gs_write_int(obj->ctype.powerup_info.count,f);
744 case CT_SLEW: //the player is generally saved as slew
748 break; //control center object.
758 switch (obj->render_type) {
767 gs_write_int(obj->rtype.pobj_info.model_num,f);
769 for (i=0;i<MAX_SUBMODELS;i++)
770 gs_write_angvec(&obj->rtype.pobj_info.anim_angles[i],f);
772 gs_write_int(obj->rtype.pobj_info.subobj_flags,f);
774 gs_write_int(obj->rtype.pobj_info.tmap_override,f);
779 case RT_WEAPON_VCLIP:
784 gs_write_int(obj->rtype.vclip_info.vclip_num,f);
785 gs_write_fix(obj->rtype.vclip_info.frametime,f);
786 gs_write_byte(obj->rtype.vclip_info.framenum,f);
801 extern int remove_trigger_num(int trigger_num);
803 // --------------------------------------------------------------------
805 // Loads all the relevant data for a level.
806 // If level != -1, it loads the filename with extension changed to .min
807 // Otherwise it loads the appropriate level mine.
808 // returns 0=everything ok, 1=old version, -1=error
809 int load_game_data(CFILE *LoadFile)
813 short game_top_fileinfo_version;
816 int num_delta_lights;
819 //===================== READ FILE INFO ========================
822 cfread(&game_top_fileinfo, sizeof(game_top_fileinfo), 1, LoadFile);
826 if (cfile_read_short(LoadFile) != 0x6705)
829 // Read and check version number
830 game_top_fileinfo_version = cfile_read_short(LoadFile);
831 if (game_top_fileinfo_version < GAME_COMPATIBLE_VERSION )
834 // We skip some parts of the former game_top_fileinfo
835 cfseek(LoadFile, 31, SEEK_CUR);
837 object_offset = cfile_read_int(LoadFile);
838 gs_num_objects = cfile_read_int(LoadFile);
839 cfseek(LoadFile, 8, SEEK_CUR);
841 Num_walls = cfile_read_int(LoadFile);
842 cfseek(LoadFile, 20, SEEK_CUR);
844 Num_triggers = cfile_read_int(LoadFile);
845 cfseek(LoadFile, 24, SEEK_CUR);
847 trig_size = cfile_read_int(LoadFile);
848 Assert(trig_size == sizeof(ControlCenterTriggers));
849 cfseek(LoadFile, 4, SEEK_CUR);
851 Num_robot_centers = cfile_read_int(LoadFile);
852 cfseek(LoadFile, 4, SEEK_CUR);
854 if (game_top_fileinfo_version >= 29) {
855 cfseek(LoadFile, 4, SEEK_CUR);
856 Num_static_lights = cfile_read_int(LoadFile);
857 cfseek(LoadFile, 8, SEEK_CUR);
858 num_delta_lights = cfile_read_int(LoadFile);
859 cfseek(LoadFile, 4, SEEK_CUR);
861 Num_static_lights = 0;
862 num_delta_lights = 0;
865 if (game_top_fileinfo_version >= 31) //load mine filename
866 // read newline-terminated string, not sure what version this changed.
867 cfgets(Current_level_name,sizeof(Current_level_name),LoadFile);
868 else if (game_top_fileinfo_version >= 14) { //load mine filename
869 // read null-terminated string
870 char *p=Current_level_name;
871 //must do read one char at a time, since no cfgets()
872 do *p = cfgetc(LoadFile); while (*p++!=0);
875 Current_level_name[0]=0;
877 if (game_top_fileinfo_version >= 19) { //load pof names
878 N_save_pof_names = cfile_read_short(LoadFile);
879 if (N_save_pof_names != 0x614d && N_save_pof_names != 0x5547) { // "Ma"de w/DMB beta/"GU"ILE
880 Assert(N_save_pof_names < MAX_POLYGON_MODELS);
881 cfread(Save_pof_names,N_save_pof_names,FILENAME_LEN,LoadFile);
885 //===================== READ PLAYER INFO ==========================
886 Object_next_signature = 0;
888 //===================== READ OBJECT INFO ==========================
890 Gamesave_num_org_robots = 0;
891 Gamesave_num_players = 0;
893 if (object_offset > -1) {
894 if (cfseek( LoadFile, object_offset, SEEK_SET ))
895 Error( "Error seeking to object_offset in gamesave.c" );
897 for (i = 0; i < gs_num_objects; i++) {
899 read_object(&Objects[i], LoadFile, game_top_fileinfo_version);
901 Objects[i].signature = Object_next_signature++;
902 verify_object( &Objects[i] );
907 //===================== READ WALL INFO ============================
909 for (i = 0; i < Num_walls; i++) {
910 if (game_top_fileinfo_version >= 20)
911 wall_read(&Walls[i], LoadFile); // v20 walls and up.
912 else if (game_top_fileinfo_version >= 17) {
914 v19_wall_read(&w, LoadFile);
915 Walls[i].segnum = w.segnum;
916 Walls[i].sidenum = w.sidenum;
917 Walls[i].linked_wall = w.linked_wall;
918 Walls[i].type = w.type;
919 Walls[i].flags = w.flags;
920 Walls[i].hps = w.hps;
921 Walls[i].trigger = w.trigger;
922 Walls[i].clip_num = w.clip_num;
923 Walls[i].keys = w.keys;
924 Walls[i].state = WALL_DOOR_CLOSED;
927 v16_wall_read(&w, LoadFile);
928 Walls[i].segnum = Walls[i].sidenum = Walls[i].linked_wall = -1;
929 Walls[i].type = w.type;
930 Walls[i].flags = w.flags;
931 Walls[i].hps = w.hps;
932 Walls[i].trigger = w.trigger;
933 Walls[i].clip_num = w.clip_num;
934 Walls[i].keys = w.keys;
939 //===================== READ DOOR INFO ============================
941 if (game_fileinfo.doors_offset > -1)
943 if (!cfseek( LoadFile, game_fileinfo.doors_offset,SEEK_SET )) {
945 for (i=0;i<game_fileinfo.doors_howmany;i++) {
947 if (game_top_fileinfo_version >= 20)
948 active_door_read(&ActiveDoors[i], LoadFile); // version 20 and up
953 v19_door_read(&d, LoadFile);
955 ActiveDoors[i].n_parts = d.n_parts;
957 for (p=0;p<d.n_parts;p++) {
960 cseg = Segments[d.seg[p]].children[d.side[p]];
961 cside = find_connect_side(&Segments[d.seg[p]],&Segments[cseg]);
963 ActiveDoors[i].front_wallnum[p] = Segments[d.seg[p]].sides[d.side[p]].wall_num;
964 ActiveDoors[i].back_wallnum[p] = Segments[cseg].sides[cside].wall_num;
973 //==================== READ TRIGGER INFO ==========================
976 // for MACINTOSH -- assume all triggers >= verion 31 triggers.
978 for (i = 0; i < Num_triggers; i++)
980 if (game_top_fileinfo_version < 31)
986 if (game_top_fileinfo_version < 30) {
989 v29_trigger_read(&trig29, LoadFile);
990 trig.flags = trig29.flags;
991 trig.num_links = trig29.num_links;
992 trig.num_links = trig29.num_links;
993 trig.value = trig29.value;
994 trig.time = trig29.time;
996 for (t=0;t<trig.num_links;t++) {
997 trig.seg[t] = trig29.seg[t];
998 trig.side[t] = trig29.side[t];
1002 v30_trigger_read(&trig, LoadFile);
1004 //Assert(trig.flags & TRIGGER_ON);
1005 trig.flags &= ~TRIGGER_ON;
1007 if (trig.flags & TRIGGER_CONTROL_DOORS)
1008 type = TT_OPEN_DOOR;
1009 else if (trig.flags & TRIGGER_SHIELD_DAMAGE)
1011 else if (trig.flags & TRIGGER_ENERGY_DRAIN)
1013 else if (trig.flags & TRIGGER_EXIT)
1015 else if (trig.flags & TRIGGER_ONE_SHOT)
1017 else if (trig.flags & TRIGGER_MATCEN)
1019 else if (trig.flags & TRIGGER_ILLUSION_OFF)
1020 type = TT_ILLUSION_OFF;
1021 else if (trig.flags & TRIGGER_SECRET_EXIT)
1022 type = TT_SECRET_EXIT;
1023 else if (trig.flags & TRIGGER_ILLUSION_ON)
1024 type = TT_ILLUSION_ON;
1025 else if (trig.flags & TRIGGER_UNLOCK_DOORS)
1026 type = TT_UNLOCK_DOOR;
1027 else if (trig.flags & TRIGGER_OPEN_WALL)
1028 type = TT_OPEN_WALL;
1029 else if (trig.flags & TRIGGER_CLOSE_WALL)
1030 type = TT_CLOSE_WALL;
1031 else if (trig.flags & TRIGGER_ILLUSORY_WALL)
1032 type = TT_ILLUSORY_WALL;
1035 Triggers[i].type = type;
1036 Triggers[i].flags = 0;
1037 Triggers[i].num_links = trig.num_links;
1038 Triggers[i].num_links = trig.num_links;
1039 Triggers[i].value = trig.value;
1040 Triggers[i].time = trig.time;
1041 for (t=0;t<trig.num_links;t++) {
1042 Triggers[i].seg[t] = trig.seg[t];
1043 Triggers[i].side[t] = trig.side[t];
1047 trigger_read(&Triggers[i], LoadFile);
1050 //================ READ CONTROL CENTER TRIGGER INFO ===============
1053 if (game_fileinfo.control_offset > -1)
1054 if (!cfseek(LoadFile, game_fileinfo.control_offset, SEEK_SET))
1056 Assert(game_fileinfo.control_sizeof == sizeof(control_center_triggers));
1058 control_center_triggers_read_n(&ControlCenterTriggers, 1, LoadFile);
1060 //================ READ MATERIALOGRIFIZATIONATORS INFO ===============
1062 // mprintf((0, "Reading %i materialization centers.\n", game_fileinfo.matcen_howmany));
1063 for (i = 0; i < Num_robot_centers; i++) {
1064 if (game_top_fileinfo_version < 27) {
1066 old_matcen_info_read(&m, LoadFile);
1067 RobotCenters[i].robot_flags[0] = m.robot_flags;
1068 RobotCenters[i].robot_flags[1] = 0;
1069 RobotCenters[i].hit_points = m.hit_points;
1070 RobotCenters[i].interval = m.interval;
1071 RobotCenters[i].segnum = m.segnum;
1072 RobotCenters[i].fuelcen_num = m.fuelcen_num;
1075 matcen_info_read(&RobotCenters[i], LoadFile);
1076 // Set links in RobotCenters to Station array
1077 for (j = 0; j <= Highest_segment_index; j++)
1078 if (Segment2s[j].special == SEGMENT_IS_ROBOTMAKER)
1079 if (Segment2s[j].matcen_num == i)
1080 RobotCenters[i].fuelcen_num = Segment2s[j].value;
1081 // mprintf((0, " %i: flags = %08x\n", i, RobotCenters[i].robot_flags));
1084 //================ READ DL_INDICES INFO ===============
1086 for (i = 0; i < Num_static_lights; i++) {
1087 if (game_top_fileinfo_version < 29) {
1088 mprintf((0, "Warning: Old mine version. Not reading Dl_indices info.\n"));
1089 Int3(); //shouldn't be here!!!
1091 dl_index_read(&Dl_indices[i], LoadFile);
1094 // Indicate that no light has been subtracted from any vertices.
1095 clear_light_subtracted();
1097 //================ READ DELTA LIGHT INFO ===============
1099 for (i = 0; i < num_delta_lights; i++) {
1100 if (game_top_fileinfo_version < 29) {
1101 mprintf((0, "Warning: Old mine version. Not reading delta light info.\n"));
1103 delta_light_read(&Delta_lights[i], LoadFile);
1106 //========================= UPDATE VARIABLES ======================
1108 reset_objects(gs_num_objects);
1110 for (i=0; i<MAX_OBJECTS; i++) {
1111 Objects[i].next = Objects[i].prev = -1;
1112 if (Objects[i].type != OBJ_NONE) {
1113 int objsegnum = Objects[i].segnum;
1115 if (objsegnum > Highest_segment_index) //bogus object
1116 Objects[i].type = OBJ_NONE;
1118 Objects[i].segnum = -1; //avoid Assert()
1119 obj_link(i,objsegnum);
1124 clear_transient_objects(1); //1 means clear proximity bombs
1126 // Make sure non-transparent doors are set correctly.
1127 for (i=0; i< Num_segments; i++)
1128 for (j=0;j<MAX_SIDES_PER_SEGMENT;j++) {
1129 side *sidep = &Segments[i].sides[j];
1130 if ((sidep->wall_num != -1) && (Walls[sidep->wall_num].clip_num != -1)) {
1131 //mprintf((0, "Checking Wall %d\n", Segments[i].sides[j].wall_num));
1132 if (WallAnims[Walls[sidep->wall_num].clip_num].flags & WCF_TMAP1) {
1133 //mprintf((0, "Fixing non-transparent door.\n"));
1134 sidep->tmap_num = WallAnims[Walls[sidep->wall_num].clip_num].frames[0];
1135 sidep->tmap_num2 = 0;
1144 Num_open_doors = game_fileinfo.doors_howmany;
1148 //go through all walls, killing references to invalid triggers
1149 for (i=0;i<Num_walls;i++)
1150 if (Walls[i].trigger >= Num_triggers) {
1151 mprintf((0,"Removing reference to invalid trigger %d from wall %d\n",Walls[i].trigger,i));
1152 Walls[i].trigger = -1; //kill trigger
1155 //go through all triggers, killing unused ones
1156 for (i=0;i<Num_triggers;) {
1159 // Find which wall this trigger is connected to.
1160 for (w=0; w<Num_walls; w++)
1161 if (Walls[w].trigger == i)
1165 if (w == Num_walls) {
1166 mprintf((0,"Removing unreferenced trigger %d\n",i));
1167 remove_trigger_num(i);
1174 // MK, 10/17/95: Make walls point back at the triggers that control them.
1175 // Go through all triggers, stuffing controlling_trigger field in Walls.
1178 for (i=0; i<Num_walls; i++)
1179 Walls[i].controlling_trigger = -1;
1181 for (t=0; t<Num_triggers; t++) {
1183 for (l=0; l<Triggers[t].num_links; l++) {
1184 int seg_num, side_num, wall_num;
1186 seg_num = Triggers[t].seg[l];
1187 side_num = Triggers[t].side[l];
1188 wall_num = Segments[seg_num].sides[side_num].wall_num;
1190 // -- if (Walls[wall_num].controlling_trigger != -1)
1193 //check to see that if a trigger requires a wall that it has one,
1194 //and if it requires a matcen that it has one
1196 if (Triggers[t].type == TT_MATCEN) {
1197 if (Segment2s[seg_num].special != SEGMENT_IS_ROBOTMAKER)
1198 Int3(); //matcen trigger doesn't point to matcen
1200 else if (Triggers[t].type != TT_LIGHT_OFF && Triggers[t].type != TT_LIGHT_ON) { //light triggers don't require walls
1202 Int3(); // This is illegal. This trigger requires a wall
1204 Walls[wall_num].controlling_trigger = t;
1210 //fix old wall structs
1211 if (game_top_fileinfo_version < 17) {
1212 int segnum,sidenum,wallnum;
1214 for (segnum=0; segnum<=Highest_segment_index; segnum++)
1215 for (sidenum=0;sidenum<6;sidenum++)
1216 if ((wallnum=Segments[segnum].sides[sidenum].wall_num) != -1) {
1217 Walls[wallnum].segnum = segnum;
1218 Walls[wallnum].sidenum = sidenum;
1225 for (sidenum=0; sidenum<6; sidenum++) {
1226 int wallnum = Segments[Highest_segment_index].sides[sidenum].wall_num;
1228 if ((Walls[wallnum].segnum != Highest_segment_index) || (Walls[wallnum].sidenum != sidenum))
1229 Int3(); // Error. Bogus walls in this segment.
1230 // Consult Yuan or Mike.
1235 //create_local_segment_data();
1243 if (game_top_fileinfo_version < GAME_VERSION
1244 && !(game_top_fileinfo_version == 25 && GAME_VERSION == 26))
1245 return 1; //means old version
1251 int check_segment_connections(void);
1253 extern void set_ambient_sound_flags(void);
1255 // ----------------------------------------------------------------------------
1257 #define LEVEL_FILE_VERSION 8
1258 //1 -> 2 add palette name
1259 //2 -> 3 add control center explosion time
1260 //3 -> 4 add reactor strength
1261 //4 -> 5 killed hostage text stuff
1262 //5 -> 6 added Secret_return_segment and Secret_return_orient
1263 //6 -> 7 added flickering lights
1264 //7 -> 8 made version 8 to be not compatible with D2 1.0 & 1.1
1267 char *Level_being_loaded=NULL;
1271 extern void ncache_flush();
1274 extern int Slide_segs_computed;
1276 int no_old_level_file_error=0;
1278 //loads a level (.LVL) file from disk
1279 //returns 0 if success, else error code
1280 int load_level(char * filename_passed)
1283 int use_compiled_level=1;
1286 char filename[PATH_MAX];
1287 int sig, minedata_offset, gamedata_offset;
1288 int mine_err, game_err;
1293 Slide_segs_computed = 0;
1296 if (Game_mode & GM_NETWORK)
1298 for (i=0;i<MAX_POWERUP_TYPES;i++)
1300 MaxPowerupsAllowed[i]=0;
1301 PowerupsInMine[i]=0;
1311 Level_being_loaded = filename_passed;
1314 strcpy(filename,filename_passed);
1317 //if we have the editor, try the LVL first, no matter what was passed.
1318 //if we don't have an LVL, try what was passed or RL2
1319 //if we don't have the editor, we just use what was passed
1321 change_filename_extension(filename,filename_passed,".lvl");
1322 use_compiled_level = 0;
1324 if (!cfexist(filename))
1326 char *p = strrchr(filename_passed, '.');
1328 if (stricmp(p, ".lvl"))
1329 strcpy(filename, filename_passed); // set to what was passed
1331 change_filename_extension(filename, filename, ".rl2");
1332 use_compiled_level = 1;
1336 LoadFile = cfopen( filename, "rb" );
1340 mprintf((0,"Can't open level file <%s>\n", filename));
1343 Error("Can't open file <%s>\n",filename);
1347 strcpy( Gamesave_current_filename, filename );
1350 // if ( Newdemo_state == ND_STATE_RECORDING )
1351 // newdemo_record_start_demo();
1354 sig = cfile_read_int(LoadFile);
1355 Gamesave_current_version = cfile_read_int(LoadFile);
1356 mprintf((0, "Gamesave_current_version = %d\n", Gamesave_current_version));
1357 minedata_offset = cfile_read_int(LoadFile);
1358 gamedata_offset = cfile_read_int(LoadFile);
1360 Assert(sig == MAKE_SIG('P','L','V','L'));
1362 if (Gamesave_current_version >= 8) { //read dummy data
1363 cfile_read_int(LoadFile);
1364 cfile_read_short(LoadFile);
1365 cfile_read_byte(LoadFile);
1368 if (Gamesave_current_version < 5)
1369 cfile_read_int(LoadFile); //was hostagetext_offset
1371 if (Gamesave_current_version > 1)
1372 cfgets(Current_level_palette,sizeof(Current_level_palette),LoadFile);
1373 if (Gamesave_current_version <= 1 || Current_level_palette[0]==0) // descent 1 level
1374 strcpy(Current_level_palette, DEFAULT_LEVEL_PALETTE);
1376 if (Gamesave_current_version >= 3)
1377 Base_control_center_explosion_time = cfile_read_int(LoadFile);
1379 Base_control_center_explosion_time = DEFAULT_CONTROL_CENTER_EXPLOSION_TIME;
1381 if (Gamesave_current_version >= 4)
1382 Reactor_strength = cfile_read_int(LoadFile);
1384 Reactor_strength = -1; //use old defaults
1386 if (Gamesave_current_version >= 7) {
1389 Num_flickering_lights = cfile_read_int(LoadFile);
1390 Assert((Num_flickering_lights >= 0) && (Num_flickering_lights < MAX_FLICKERING_LIGHTS));
1391 for (i = 0; i < Num_flickering_lights; i++)
1392 flickering_light_read(&Flickering_lights[i], LoadFile);
1395 Num_flickering_lights = 0;
1397 if (Gamesave_current_version < 6) {
1398 Secret_return_segment = 0;
1399 Secret_return_orient.rvec.x = F1_0;
1400 Secret_return_orient.rvec.y = 0;
1401 Secret_return_orient.rvec.z = 0;
1402 Secret_return_orient.fvec.x = 0;
1403 Secret_return_orient.fvec.y = F1_0;
1404 Secret_return_orient.fvec.z = 0;
1405 Secret_return_orient.uvec.x = 0;
1406 Secret_return_orient.uvec.y = 0;
1407 Secret_return_orient.uvec.z = F1_0;
1409 Secret_return_segment = cfile_read_int(LoadFile);
1410 Secret_return_orient.rvec.x = cfile_read_int(LoadFile);
1411 Secret_return_orient.rvec.y = cfile_read_int(LoadFile);
1412 Secret_return_orient.rvec.z = cfile_read_int(LoadFile);
1413 Secret_return_orient.fvec.x = cfile_read_int(LoadFile);
1414 Secret_return_orient.fvec.y = cfile_read_int(LoadFile);
1415 Secret_return_orient.fvec.z = cfile_read_int(LoadFile);
1416 Secret_return_orient.uvec.x = cfile_read_int(LoadFile);
1417 Secret_return_orient.uvec.y = cfile_read_int(LoadFile);
1418 Secret_return_orient.uvec.z = cfile_read_int(LoadFile);
1421 cfseek(LoadFile,minedata_offset,SEEK_SET);
1423 if (!use_compiled_level) {
1424 mine_err = load_mine_data(LoadFile);
1425 #if 0 // get from d1src if needed
1426 // Compress all uv coordinates in mine, improves texmap precision. --MK, 02/19/96
1427 compress_uv_coordinates_all();
1431 //NOTE LINK TO ABOVE!!
1432 mine_err = load_mine_data_compiled(LoadFile);
1434 if (mine_err == -1) { //error!!
1439 cfseek(LoadFile,gamedata_offset,SEEK_SET);
1440 game_err = load_game_data(LoadFile);
1442 if (game_err == -1) { //error!!
1447 //======================== CLOSE FILE =============================
1449 cfclose( LoadFile );
1451 set_ambient_sound_flags();
1454 write_game_text_file(filename);
1455 if (Errors_in_mine) {
1456 if (is_real_level(filename)) {
1457 char ErrorMessage[200];
1459 sprintf( ErrorMessage, "Warning: %i errors in %s!\n", Errors_in_mine, Level_being_loaded );
1461 gr_palette_load(gr_palette);
1462 nm_messagebox( NULL, 1, "Continue", ErrorMessage );
1465 mprintf((1, "Error: %i errors in %s.\n", Errors_in_mine, Level_being_loaded));
1470 //If an old version, ask the use if he wants to save as new version
1471 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)) {
1472 char ErrorMessage[200];
1474 sprintf( ErrorMessage,
1475 "You just loaded a old version\n"
1476 "level. Would you like to save\n"
1477 "it as a current version level?");
1480 gr_palette_load(gr_palette);
1481 if (nm_messagebox( NULL, 2, "Don't Save", "Save", ErrorMessage )==1)
1482 save_level(filename);
1488 if (Function_mode == FMODE_EDITOR)
1489 editor_status("Loaded NEW mine %s, \"%s\"",filename,Current_level_name);
1493 if (check_segment_connections())
1494 nm_messagebox( "ERROR", 1, "Ok",
1495 "Connectivity errors detected in\n"
1496 "mine. See monochrome screen for\n"
1497 "details, and contact Matt or Mike." );
1504 int get_level_name()
1506 //NO_UI!!! UI_WINDOW *NameWindow = NULL;
1507 //NO_UI!!! UI_GADGET_INPUTBOX *NameText;
1508 //NO_UI!!! UI_GADGET_BUTTON *QuitButton;
1510 //NO_UI!!! // Open a window with a quit button
1511 //NO_UI!!! NameWindow = ui_open_window( 20, 20, 300, 110, WIN_DIALOG );
1512 //NO_UI!!! QuitButton = ui_add_gadget_button( NameWindow, 150-24, 60, 48, 40, "Done", NULL );
1514 //NO_UI!!! ui_wprintf_at( NameWindow, 10, 12,"Please enter a name for this mine:" );
1515 //NO_UI!!! NameText = ui_add_gadget_inputbox( NameWindow, 10, 30, LEVEL_NAME_LEN, LEVEL_NAME_LEN, Current_level_name );
1517 //NO_UI!!! NameWindow->keyboard_focus_gadget = (UI_GADGET *)NameText;
1518 //NO_UI!!! QuitButton->hotkey = KEY_ENTER;
1520 //NO_UI!!! ui_gadget_calc_keys(NameWindow);
1522 //NO_UI!!! while (!QuitButton->pressed && last_keypress!=KEY_ENTER) {
1523 //NO_UI!!! ui_mega_process();
1524 //NO_UI!!! ui_window_do_gadgets(NameWindow);
1527 //NO_UI!!! strcpy( Current_level_name, NameText->text );
1529 //NO_UI!!! if ( NameWindow!=NULL ) {
1530 //NO_UI!!! ui_close_window( NameWindow );
1531 //NO_UI!!! NameWindow = NULL;
1537 m[0].type = NM_TYPE_TEXT; m[0].text = "Please enter a name for this mine:";
1538 m[1].type = NM_TYPE_INPUT; m[1].text = Current_level_name; m[1].text_len = LEVEL_NAME_LEN;
1540 return newmenu_do( NULL, "Enter mine name", 2, m, NULL ) >= 0;
1550 // -----------------------------------------------------------------------------
1551 int compute_num_delta_light_records(void)
1556 for (i=0; i<Num_static_lights; i++) {
1557 total += Dl_indices[i].count;
1564 // -----------------------------------------------------------------------------
1566 int save_game_data(FILE * SaveFile)
1568 int player_offset, object_offset, walls_offset, doors_offset, triggers_offset, control_offset, matcen_offset; //, links_offset;
1569 int dl_indices_offset, delta_light_offset;
1570 int start_offset,end_offset;
1572 start_offset = ftell(SaveFile);
1574 //===================== SAVE FILE INFO ========================
1576 game_fileinfo.fileinfo_signature = 0x6705;
1577 game_fileinfo.fileinfo_version = GAME_VERSION;
1578 game_fileinfo.level = Current_level_num;
1579 game_fileinfo.fileinfo_sizeof = sizeof(game_fileinfo);
1580 game_fileinfo.player_offset = -1;
1581 game_fileinfo.player_sizeof = sizeof(player);
1582 game_fileinfo.object_offset = -1;
1583 game_fileinfo.object_howmany = Highest_object_index+1;
1584 game_fileinfo.object_sizeof = sizeof(object);
1585 game_fileinfo.walls_offset = -1;
1586 game_fileinfo.walls_howmany = Num_walls;
1587 game_fileinfo.walls_sizeof = sizeof(wall);
1588 game_fileinfo.doors_offset = -1;
1589 game_fileinfo.doors_howmany = Num_open_doors;
1590 game_fileinfo.doors_sizeof = sizeof(active_door);
1591 game_fileinfo.triggers_offset = -1;
1592 game_fileinfo.triggers_howmany = Num_triggers;
1593 game_fileinfo.triggers_sizeof = sizeof(trigger);
1594 game_fileinfo.control_offset = -1;
1595 game_fileinfo.control_howmany = 1;
1596 game_fileinfo.control_sizeof = sizeof(control_center_triggers);
1597 game_fileinfo.matcen_offset = -1;
1598 game_fileinfo.matcen_howmany = Num_robot_centers;
1599 game_fileinfo.matcen_sizeof = sizeof(matcen_info);
1601 game_fileinfo.dl_indices_offset = -1;
1602 game_fileinfo.dl_indices_howmany = Num_static_lights;
1603 game_fileinfo.dl_indices_sizeof = sizeof(dl_index);
1605 game_fileinfo.delta_light_offset = -1;
1606 game_fileinfo.delta_light_howmany = compute_num_delta_light_records();
1607 game_fileinfo.delta_light_sizeof = sizeof(delta_light);
1609 // Write the fileinfo
1610 fwrite( &game_fileinfo, sizeof(game_fileinfo), 1, SaveFile );
1612 // Write the mine name
1613 fprintf(SaveFile,"%s\n",Current_level_name);
1615 fwrite(&N_polygon_models,2,1,SaveFile);
1616 fwrite(Pof_names,N_polygon_models,sizeof(*Pof_names),SaveFile);
1618 //==================== SAVE PLAYER INFO ===========================
1620 player_offset = ftell(SaveFile);
1621 fwrite( &Players[Player_num], sizeof(player), 1, SaveFile );
1623 //==================== SAVE OBJECT INFO ===========================
1625 object_offset = ftell(SaveFile);
1626 //fwrite( &Objects, sizeof(object), game_fileinfo.object_howmany, SaveFile );
1629 for (i=0;i<game_fileinfo.object_howmany;i++)
1630 write_object(&Objects[i],SaveFile);
1633 //==================== SAVE WALL INFO =============================
1635 walls_offset = ftell(SaveFile);
1636 fwrite( Walls, sizeof(wall), game_fileinfo.walls_howmany, SaveFile );
1638 //==================== SAVE DOOR INFO =============================
1640 doors_offset = ftell(SaveFile);
1641 fwrite( ActiveDoors, sizeof(active_door), game_fileinfo.doors_howmany, SaveFile );
1643 //==================== SAVE TRIGGER INFO =============================
1645 triggers_offset = ftell(SaveFile);
1646 fwrite( Triggers, sizeof(trigger), game_fileinfo.triggers_howmany, SaveFile );
1648 //================ SAVE CONTROL CENTER TRIGGER INFO ===============
1650 control_offset = ftell(SaveFile);
1651 fwrite( &ControlCenterTriggers, sizeof(control_center_triggers), 1, SaveFile );
1654 //================ SAVE MATERIALIZATION CENTER TRIGGER INFO ===============
1656 matcen_offset = ftell(SaveFile);
1657 // mprintf((0, "Writing %i materialization centers\n", game_fileinfo.matcen_howmany));
1659 // for (i=0; i<game_fileinfo.matcen_howmany; i++)
1660 // mprintf((0, " %i: robot_flags = %08x\n", i, RobotCenters[i].robot_flags));
1662 fwrite( RobotCenters, sizeof(matcen_info), game_fileinfo.matcen_howmany, SaveFile );
1664 //================ SAVE DELTA LIGHT INFO ===============
1665 dl_indices_offset = ftell(SaveFile);
1666 fwrite( Dl_indices, sizeof(dl_index), game_fileinfo.dl_indices_howmany, SaveFile );
1668 delta_light_offset = ftell(SaveFile);
1669 fwrite( Delta_lights, sizeof(delta_light), game_fileinfo.delta_light_howmany, SaveFile );
1671 //============= REWRITE FILE INFO, TO SAVE OFFSETS ===============
1673 // Update the offset fields
1674 game_fileinfo.player_offset = player_offset;
1675 game_fileinfo.object_offset = object_offset;
1676 game_fileinfo.walls_offset = walls_offset;
1677 game_fileinfo.doors_offset = doors_offset;
1678 game_fileinfo.triggers_offset = triggers_offset;
1679 game_fileinfo.control_offset = control_offset;
1680 game_fileinfo.matcen_offset = matcen_offset;
1681 game_fileinfo.dl_indices_offset = dl_indices_offset;
1682 game_fileinfo.delta_light_offset = delta_light_offset;
1685 end_offset = ftell(SaveFile);
1687 // Write the fileinfo
1688 fseek( SaveFile, start_offset, SEEK_SET ); // Move to TOF
1689 fwrite( &game_fileinfo, sizeof(game_fileinfo), 1, SaveFile );
1691 // Go back to end of data
1692 fseek(SaveFile,end_offset,SEEK_SET);
1697 int save_mine_data(FILE * SaveFile);
1699 // -----------------------------------------------------------------------------
1701 int save_level_sub(char * filename, int compiled_version)
1704 char temp_filename[PATH_MAX];
1705 int sig = MAKE_SIG('P','L','V','L'),version=LEVEL_FILE_VERSION;
1706 int minedata_offset=0,gamedata_offset=0;
1708 if ( !compiled_version ) {
1709 write_game_text_file(filename);
1711 if (Errors_in_mine) {
1712 if (is_real_level(filename)) {
1713 char ErrorMessage[200];
1715 sprintf( ErrorMessage, "Warning: %i errors in this mine!\n", Errors_in_mine );
1717 gr_palette_load(gr_palette);
1719 if (nm_messagebox( NULL, 2, "Cancel Save", "Save", ErrorMessage )!=1) {
1725 mprintf((1, "Error: %i errors in this mine. See the 'txm' file.\n", Errors_in_mine));
1727 change_filename_extension(temp_filename,filename,".LVL");
1731 // macs are using the regular hog/rl2 files for shareware
1732 #if defined(SHAREWARE) && !defined(MACINTOSH)
1733 change_filename_extension(temp_filename,filename,".SL2");
1735 change_filename_extension(temp_filename,filename,".RL2");
1739 SaveFile = fopen( temp_filename, "wb" );
1742 char ErrorMessage[256];
1745 _splitpath( temp_filename, NULL, NULL, fname, NULL );
1747 sprintf( ErrorMessage, \
1748 "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"
1749 , temp_filename, fname );
1751 gr_palette_load(gr_palette);
1752 nm_messagebox( NULL, 1, "Ok", ErrorMessage );
1757 if (Current_level_name[0] == 0)
1758 strcpy(Current_level_name,"Untitled");
1760 clear_transient_objects(1); //1 means clear proximity bombs
1762 compress_objects(); //after this, Highest_object_index == num objects
1764 //make sure player is in a segment
1765 if (update_object_seg(&Objects[Players[0].objnum]) == 0) {
1766 if (ConsoleObject->segnum > Highest_segment_index)
1767 ConsoleObject->segnum = 0;
1768 compute_segment_center(&ConsoleObject->pos,&(Segments[ConsoleObject->segnum]));
1775 gs_write_int(sig,SaveFile);
1776 gs_write_int(version,SaveFile);
1779 gs_write_int(minedata_offset,SaveFile);
1780 gs_write_int(gamedata_offset,SaveFile);
1782 //Now write the damn data
1784 //write the version 8 data (to make file unreadable by 1.0 & 1.1)
1785 gs_write_int(GameTime,SaveFile);
1786 gs_write_short(FrameCount,SaveFile);
1787 gs_write_byte(FrameTime,SaveFile);
1789 // Write the palette file name
1790 fprintf(SaveFile,"%s\n",Current_level_palette);
1792 gs_write_int(Base_control_center_explosion_time,SaveFile);
1793 gs_write_int(Reactor_strength,SaveFile);
1795 gs_write_int(Num_flickering_lights,SaveFile);
1796 fwrite(Flickering_lights,sizeof(*Flickering_lights),Num_flickering_lights,SaveFile);
1798 gs_write_int(Secret_return_segment, SaveFile);
1799 gs_write_int(Secret_return_orient.rvec.x, SaveFile);
1800 gs_write_int(Secret_return_orient.rvec.y, SaveFile);
1801 gs_write_int(Secret_return_orient.rvec.z, SaveFile);
1802 gs_write_int(Secret_return_orient.fvec.x, SaveFile);
1803 gs_write_int(Secret_return_orient.fvec.y, SaveFile);
1804 gs_write_int(Secret_return_orient.fvec.z, SaveFile);
1805 gs_write_int(Secret_return_orient.uvec.x, SaveFile);
1806 gs_write_int(Secret_return_orient.uvec.y, SaveFile);
1807 gs_write_int(Secret_return_orient.uvec.z, SaveFile);
1809 minedata_offset = ftell(SaveFile);
1810 if ( !compiled_version )
1811 save_mine_data(SaveFile);
1813 save_mine_data_compiled(SaveFile);
1814 gamedata_offset = ftell(SaveFile);
1815 save_game_data(SaveFile);
1817 fseek(SaveFile,sizeof(sig)+sizeof(version),SEEK_SET);
1818 gs_write_int(minedata_offset,SaveFile);
1819 gs_write_int(gamedata_offset,SaveFile);
1821 //==================== CLOSE THE FILE =============================
1824 if ( !compiled_version ) {
1825 if (Function_mode == FMODE_EDITOR)
1826 editor_status("Saved mine %s, \"%s\"",filename,Current_level_name);
1833 #if 0 //dunno - 3rd party stuff?
1834 extern void compress_uv_coordinates_all(void);
1837 int save_level(char * filename)
1841 // Save normal version...
1842 r1 = save_level_sub(filename, 0);
1844 // Save compiled version...
1845 save_level_sub(filename, 1);
1853 void dump_mine_info(void)
1855 int segnum, sidenum;
1856 fix min_u, max_u, min_v, max_v, min_l, max_l, max_sl;
1868 for (segnum=0; segnum<=Highest_segment_index; segnum++) {
1869 for (sidenum=0; sidenum<MAX_SIDES_PER_SEGMENT; sidenum++) {
1871 side *sidep = &Segments[segnum].sides[sidenum];
1873 if (Segment2s[segnum].static_light > max_sl)
1874 max_sl = Segment2s[segnum].static_light;
1876 for (vertnum=0; vertnum<4; vertnum++) {
1877 if (sidep->uvls[vertnum].u < min_u)
1878 min_u = sidep->uvls[vertnum].u;
1879 else if (sidep->uvls[vertnum].u > max_u)
1880 max_u = sidep->uvls[vertnum].u;
1882 if (sidep->uvls[vertnum].v < min_v)
1883 min_v = sidep->uvls[vertnum].v;
1884 else if (sidep->uvls[vertnum].v > max_v)
1885 max_v = sidep->uvls[vertnum].v;
1887 if (sidep->uvls[vertnum].l < min_l)
1888 min_l = sidep->uvls[vertnum].l;
1889 else if (sidep->uvls[vertnum].l > max_l)
1890 max_l = sidep->uvls[vertnum].l;
1896 // 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)));
1897 // mprintf((0, "Static light maximum = %7.3f\n", f2fl(max_sl)));
1898 // mprintf((0, "Number of walls: %i\n", Num_walls));
1906 //read in every level in mission and save out compiled version
1907 void save_all_compiled_levels(void)
1909 do_load_save_levels(1);
1912 //read in every level in mission
1913 void load_all_levels(void)
1915 do_load_save_levels(0);
1919 void do_load_save_levels(int save)
1923 if (! SafetyCheck())
1926 no_old_level_file_error=1;
1928 for (level_num=1;level_num<=Last_level;level_num++) {
1929 load_level(Level_names[level_num-1]);
1930 load_palette(Current_level_palette,1,1); //don't change screen
1932 save_level_sub(Level_names[level_num-1],1);
1935 for (level_num=-1;level_num>=Last_secret_level;level_num--) {
1936 load_level(Secret_level_names[-level_num-1]);
1937 load_palette(Current_level_palette,1,1); //don't change screen
1939 save_level_sub(Secret_level_names[-level_num-1],1);
1942 no_old_level_file_error=0;