2 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
3 SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO
4 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
5 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
6 IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
7 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
8 FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
9 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS
10 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
11 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED.
16 * Save game information
33 #include "editor/editor.h"
39 #include "ignorecase.h"
42 char Gamesave_current_filename[PATH_MAX];
44 int Gamesave_current_version;
46 #define GAME_VERSION 32
47 #define GAME_COMPATIBLE_VERSION 22
49 //version 28->29 add delta light support
50 //version 27->28 controlcen id now is reactor number, not model number
52 //version 29->30 changed trigger structure
53 //version 30->31 changed trigger structure some more
54 //version 31->32 change segment structure, make it 512 bytes w/o editor, add Segment2s array.
56 #define MENU_CURSOR_X_MIN MENU_X
57 #define MENU_CURSOR_X_MAX MENU_X+6
61 ushort fileinfo_signature;
62 ushort fileinfo_version;
64 } game_top_fileinfo; // Should be same as first two fields below...
67 ushort fileinfo_signature;
68 ushort fileinfo_version;
70 char mine_filename[15];
72 int player_offset; // Player info
74 int object_offset; // Object info
95 int dl_indices_offset;
96 int dl_indices_howmany;
97 int dl_indices_sizeof;
98 int delta_light_offset;
99 int delta_light_howmany;
100 int delta_light_sizeof;
104 // LINT: adding function prototypes
105 void read_object(object *obj, CFILE *f, int version);
107 void write_object(object *obj, short version, PHYSFS_file *f);
108 void do_load_save_levels(int save);
111 void dump_mine_info(void);
114 extern char MaxPowerupsAllowed[MAX_POWERUP_TYPES];
115 extern char PowerupsInMine[MAX_POWERUP_TYPES];
118 extern char mine_filename[];
119 extern int save_mine_data_compiled(PHYSFS_file *SaveFile);
121 //--unused-- char mine_filename[128];
124 int Gamesave_num_org_robots = 0;
125 //--unused-- grs_bitmap * Gamesave_saved_bitmap = NULL;
128 // Return true if this level has a name of the form "level??"
129 // Note that a pathspec can appear at the beginning of the filename.
130 int is_real_level(char *filename)
132 int len = (int)strlen(filename);
137 //mprintf((0, "String = [%s]\n", &filename[len-11]));
138 return !strnicmp(&filename[len-11], "level", 5);
143 //--unused-- vms_angvec zero_angles={0,0,0};
145 #define vm_angvec_zero(v) do {(v)->p=(v)->b=(v)->h=0;} while (0)
147 int Gamesave_num_players=0;
149 int N_save_pof_names;
150 char Save_pof_names[MAX_POLYGON_MODELS][FILENAME_LEN];
152 void check_and_fix_matrix(vms_matrix *m);
154 void verify_object( object * obj ) {
156 obj->lifeleft = IMMORTAL_TIME; //all loaded object are immortal, for now
158 if ( obj->type == OBJ_ROBOT ) {
159 Gamesave_num_org_robots++;
161 // Make sure valid id...
162 if ( obj->id >= N_robot_types )
163 obj->id = obj->id % N_robot_types;
165 // Make sure model number & size are correct...
166 if ( obj->render_type == RT_POLYOBJ ) {
167 Assert(Robot_info[obj->id].model_num != -1);
168 //if you fail this assert, it means that a robot in this level
169 //hasn't been loaded, possibly because he's marked as
170 //non-shareware. To see what robot number, print obj->id.
172 Assert(Robot_info[obj->id].always_0xabcd == 0xabcd);
173 //if you fail this assert, it means that the robot_ai for
174 //a robot in this level hasn't been loaded, possibly because
175 //it's marked as non-shareware. To see what robot number,
178 obj->rtype.pobj_info.model_num = Robot_info[obj->id].model_num;
179 obj->size = Polygon_models[obj->rtype.pobj_info.model_num].rad;
181 //@@Took out this ugly hack 1/12/96, because Mike has added code
182 //@@that should fix it in a better way.
183 //@@//this is a super-ugly hack. Since the baby stripe robots have
184 //@@//their firing point on their bounding sphere, the firing points
185 //@@//can poke through a wall if the robots are very close to it. So
186 //@@//we make their radii bigger so the guns can't get too close to
188 //@@if (Robot_info[obj->id].flags & RIF_BIG_RADIUS)
189 //@@ obj->size = (obj->size*3)/2;
191 //@@if (obj->control_type==CT_AI && Robot_info[obj->id].attack_type)
192 //@@ obj->size = obj->size*3/4;
195 if (obj->id == 65) //special "reactor" robots
196 obj->movement_type = MT_NONE;
198 if (obj->movement_type == MT_PHYSICS) {
199 obj->mtype.phys_info.mass = Robot_info[obj->id].mass;
200 obj->mtype.phys_info.drag = Robot_info[obj->id].drag;
203 else { //Robots taken care of above
205 if ( obj->render_type == RT_POLYOBJ ) {
207 char *name = Save_pof_names[obj->rtype.pobj_info.model_num];
209 for (i=0;i<N_polygon_models;i++)
210 if (!stricmp(Pof_names[i],name)) { //found it!
211 // mprintf((0,"Mapping <%s> to %d (was %d)\n",name,i,obj->rtype.pobj_info.model_num));
212 obj->rtype.pobj_info.model_num = i;
218 if ( obj->type == OBJ_POWERUP ) {
219 if ( obj->id >= N_powerup_types ) {
221 Assert( obj->render_type != RT_POLYOBJ );
223 obj->control_type = CT_POWERUP;
224 obj->size = Powerup_info[obj->id].size;
225 obj->ctype.powerup_info.creation_time = 0;
228 if (Game_mode & GM_NETWORK)
230 if (multi_powerup_is_4pack(obj->id))
232 PowerupsInMine[obj->id-1]+=4;
233 MaxPowerupsAllowed[obj->id-1]+=4;
235 PowerupsInMine[obj->id]++;
236 MaxPowerupsAllowed[obj->id]++;
237 mprintf ((0,"PowerupLimiter: ID=%d\n",obj->id));
238 if (obj->id>MAX_POWERUP_TYPES)
239 mprintf ((1,"POWERUP: Overwriting array bounds!! Get JL!\n"));
245 if ( obj->type == OBJ_WEAPON ) {
246 if ( obj->id >= N_weapon_types ) {
248 Assert( obj->render_type != RT_POLYOBJ );
251 if (obj->id == PMINE_ID) { //make sure pmines have correct values
253 obj->mtype.phys_info.mass = Weapon_info[obj->id].mass;
254 obj->mtype.phys_info.drag = Weapon_info[obj->id].drag;
255 obj->mtype.phys_info.flags |= PF_FREE_SPINNING;
257 // Make sure model number & size are correct...
258 Assert( obj->render_type == RT_POLYOBJ );
260 obj->rtype.pobj_info.model_num = Weapon_info[obj->id].model_num;
261 obj->size = Polygon_models[obj->rtype.pobj_info.model_num].rad;
265 if ( obj->type == OBJ_CNTRLCEN ) {
267 obj->render_type = RT_POLYOBJ;
268 obj->control_type = CT_CNTRLCEN;
270 if (Gamesave_current_version <= 1) { // descent 1 reactor
271 obj->id = 0; // used to be only one kind of reactor
272 obj->rtype.pobj_info.model_num = Reactors[0].model_num;// descent 1 reactor
275 // Make sure model number is correct...
276 //obj->rtype.pobj_info.model_num = Reactors[obj->id].model_num;
279 if ( obj->type == OBJ_PLAYER ) {
282 //Assert(obj == Player);
284 if ( obj == ConsoleObject )
285 init_player_object();
287 if (obj->render_type == RT_POLYOBJ) //recover from Matt's pof file matchup bug
288 obj->rtype.pobj_info.model_num = Player_ship->model_num;
290 //Make sure orient matrix is orthogonal
291 check_and_fix_matrix(&obj->orient);
293 obj->id = Gamesave_num_players++;
296 if (obj->type == OBJ_HOSTAGE) {
298 //@@if (obj->id > N_hostage_types)
301 obj->render_type = RT_HOSTAGE;
302 obj->control_type = CT_POWERUP;
307 //static gs_skip(int len,CFILE *file)
310 // cfseek(file,len,SEEK_CUR);
314 extern int multi_powerup_is_4pack(int);
315 //reads one object of the given version from the given file
316 void read_object(object *obj,CFILE *f,int version)
319 obj->type = cfile_read_byte(f);
320 obj->id = cfile_read_byte(f);
322 obj->control_type = cfile_read_byte(f);
323 obj->movement_type = cfile_read_byte(f);
324 obj->render_type = cfile_read_byte(f);
325 obj->flags = cfile_read_byte(f);
327 obj->segnum = cfile_read_short(f);
328 obj->attached_obj = -1;
330 cfile_read_vector(&obj->pos,f);
331 cfile_read_matrix(&obj->orient,f);
333 obj->size = cfile_read_fix(f);
334 obj->shields = cfile_read_fix(f);
336 cfile_read_vector(&obj->last_pos,f);
338 obj->contains_type = cfile_read_byte(f);
339 obj->contains_id = cfile_read_byte(f);
340 obj->contains_count = cfile_read_byte(f);
342 switch (obj->movement_type) {
346 cfile_read_vector(&obj->mtype.phys_info.velocity,f);
347 cfile_read_vector(&obj->mtype.phys_info.thrust,f);
349 obj->mtype.phys_info.mass = cfile_read_fix(f);
350 obj->mtype.phys_info.drag = cfile_read_fix(f);
351 obj->mtype.phys_info.brakes = cfile_read_fix(f);
353 cfile_read_vector(&obj->mtype.phys_info.rotvel,f);
354 cfile_read_vector(&obj->mtype.phys_info.rotthrust,f);
356 obj->mtype.phys_info.turnroll = cfile_read_fixang(f);
357 obj->mtype.phys_info.flags = cfile_read_short(f);
363 cfile_read_vector(&obj->mtype.spin_rate,f);
373 switch (obj->control_type) {
378 obj->ctype.ai_info.behavior = cfile_read_byte(f);
380 for (i=0;i<MAX_AI_FLAGS;i++)
381 obj->ctype.ai_info.flags[i] = cfile_read_byte(f);
383 obj->ctype.ai_info.hide_segment = cfile_read_short(f);
384 obj->ctype.ai_info.hide_index = cfile_read_short(f);
385 obj->ctype.ai_info.path_length = cfile_read_short(f);
386 obj->ctype.ai_info.cur_path_index = cfile_read_short(f);
389 cfile_read_short(f); // obj->ctype.ai_info.follow_path_start_seg =
390 cfile_read_short(f); // obj->ctype.ai_info.follow_path_end_seg =
398 obj->ctype.expl_info.spawn_time = cfile_read_fix(f);
399 obj->ctype.expl_info.delete_time = cfile_read_fix(f);
400 obj->ctype.expl_info.delete_objnum = cfile_read_short(f);
401 obj->ctype.expl_info.next_attach = obj->ctype.expl_info.prev_attach = obj->ctype.expl_info.attach_parent = -1;
407 //do I really need to read these? Are they even saved to disk?
409 obj->ctype.laser_info.parent_type = cfile_read_short(f);
410 obj->ctype.laser_info.parent_num = cfile_read_short(f);
411 obj->ctype.laser_info.parent_signature = cfile_read_int(f);
417 obj->ctype.light_info.intensity = cfile_read_fix(f);
423 obj->ctype.powerup_info.count = cfile_read_int(f);
425 obj->ctype.powerup_info.count = 1;
427 if (obj->id == POW_VULCAN_WEAPON)
428 obj->ctype.powerup_info.count = VULCAN_WEAPON_AMMO_AMOUNT;
430 if (obj->id == POW_GAUSS_WEAPON)
431 obj->ctype.powerup_info.count = VULCAN_WEAPON_AMMO_AMOUNT;
433 if (obj->id == POW_OMEGA_WEAPON)
434 obj->ctype.powerup_info.count = MAX_OMEGA_CHARGE;
444 case CT_SLEW: //the player is generally saved as slew
458 switch (obj->render_type) {
467 obj->rtype.pobj_info.model_num = cfile_read_int(f);
469 for (i=0;i<MAX_SUBMODELS;i++)
470 cfile_read_angvec(&obj->rtype.pobj_info.anim_angles[i],f);
472 obj->rtype.pobj_info.subobj_flags = cfile_read_int(f);
474 tmo = cfile_read_int(f);
477 obj->rtype.pobj_info.tmap_override = tmo;
480 obj->rtype.pobj_info.tmap_override = -1;
482 int xlated_tmo = tmap_xlate_table[tmo];
483 if (xlated_tmo < 0) {
484 mprintf( (0, "Couldn't find texture for demo object, model_num = %d\n", obj->rtype.pobj_info.model_num));
488 obj->rtype.pobj_info.tmap_override = xlated_tmo;
492 obj->rtype.pobj_info.alt_textures = 0;
497 case RT_WEAPON_VCLIP:
502 obj->rtype.vclip_info.vclip_num = cfile_read_int(f);
503 obj->rtype.vclip_info.frametime = cfile_read_fix(f);
504 obj->rtype.vclip_info.framenum = cfile_read_byte(f);
520 //writes one object to the given file
521 void write_object(object *obj, short version, PHYSFS_file *f)
523 PHYSFSX_writeU8(f, obj->type);
524 PHYSFSX_writeU8(f, obj->id);
526 PHYSFSX_writeU8(f, obj->control_type);
527 PHYSFSX_writeU8(f, obj->movement_type);
528 PHYSFSX_writeU8(f, obj->render_type);
529 PHYSFSX_writeU8(f, obj->flags);
531 PHYSFS_writeSLE16(f, obj->segnum);
533 PHYSFSX_writeVector(f, &obj->pos);
534 PHYSFSX_writeMatrix(f, &obj->orient);
536 PHYSFSX_writeFix(f, obj->size);
537 PHYSFSX_writeFix(f, obj->shields);
539 PHYSFSX_writeVector(f, &obj->last_pos);
541 PHYSFSX_writeU8(f, obj->contains_type);
542 PHYSFSX_writeU8(f, obj->contains_id);
543 PHYSFSX_writeU8(f, obj->contains_count);
545 switch (obj->movement_type) {
549 PHYSFSX_writeVector(f, &obj->mtype.phys_info.velocity);
550 PHYSFSX_writeVector(f, &obj->mtype.phys_info.thrust);
552 PHYSFSX_writeFix(f, obj->mtype.phys_info.mass);
553 PHYSFSX_writeFix(f, obj->mtype.phys_info.drag);
554 PHYSFSX_writeFix(f, obj->mtype.phys_info.brakes);
556 PHYSFSX_writeVector(f, &obj->mtype.phys_info.rotvel);
557 PHYSFSX_writeVector(f, &obj->mtype.phys_info.rotthrust);
559 PHYSFSX_writeFixAng(f, obj->mtype.phys_info.turnroll);
560 PHYSFS_writeSLE16(f, obj->mtype.phys_info.flags);
566 PHYSFSX_writeVector(f, &obj->mtype.spin_rate);
576 switch (obj->control_type) {
581 PHYSFSX_writeU8(f, obj->ctype.ai_info.behavior);
583 for (i = 0; i < MAX_AI_FLAGS; i++)
584 PHYSFSX_writeU8(f, obj->ctype.ai_info.flags[i]);
586 PHYSFS_writeSLE16(f, obj->ctype.ai_info.hide_segment);
587 PHYSFS_writeSLE16(f, obj->ctype.ai_info.hide_index);
588 PHYSFS_writeSLE16(f, obj->ctype.ai_info.path_length);
589 PHYSFS_writeSLE16(f, obj->ctype.ai_info.cur_path_index);
593 PHYSFS_writeSLE16(f, -1); //obj->ctype.ai_info.follow_path_start_seg
594 PHYSFS_writeSLE16(f, -1); //obj->ctype.ai_info.follow_path_end_seg
602 PHYSFSX_writeFix(f, obj->ctype.expl_info.spawn_time);
603 PHYSFSX_writeFix(f, obj->ctype.expl_info.delete_time);
604 PHYSFS_writeSLE16(f, obj->ctype.expl_info.delete_objnum);
610 //do I really need to write these objects?
612 PHYSFS_writeSLE16(f, obj->ctype.laser_info.parent_type);
613 PHYSFS_writeSLE16(f, obj->ctype.laser_info.parent_num);
614 PHYSFS_writeSLE32(f, obj->ctype.laser_info.parent_signature);
620 PHYSFSX_writeFix(f, obj->ctype.light_info.intensity);
626 PHYSFS_writeSLE32(f, obj->ctype.powerup_info.count);
634 case CT_SLEW: //the player is generally saved as slew
638 break; //control center object.
648 switch (obj->render_type) {
657 PHYSFS_writeSLE32(f, obj->rtype.pobj_info.model_num);
659 for (i = 0; i < MAX_SUBMODELS; i++)
660 PHYSFSX_writeAngleVec(f, &obj->rtype.pobj_info.anim_angles[i]);
662 PHYSFS_writeSLE32(f, obj->rtype.pobj_info.subobj_flags);
664 PHYSFS_writeSLE32(f, obj->rtype.pobj_info.tmap_override);
669 case RT_WEAPON_VCLIP:
674 PHYSFS_writeSLE32(f, obj->rtype.vclip_info.vclip_num);
675 PHYSFSX_writeFix(f, obj->rtype.vclip_info.frametime);
676 PHYSFSX_writeU8(f, obj->rtype.vclip_info.framenum);
691 extern int remove_trigger_num(int trigger_num);
693 // --------------------------------------------------------------------
695 // Loads all the relevant data for a level.
696 // If level != -1, it loads the filename with extension changed to .min
697 // Otherwise it loads the appropriate level mine.
698 // returns 0=everything ok, 1=old version, -1=error
699 int load_game_data(CFILE *LoadFile)
703 short game_top_fileinfo_version;
706 int num_delta_lights;
709 //===================== READ FILE INFO ========================
712 cfread(&game_top_fileinfo, sizeof(game_top_fileinfo), 1, LoadFile);
716 if (cfile_read_short(LoadFile) != 0x6705)
719 // Read and check version number
720 game_top_fileinfo_version = cfile_read_short(LoadFile);
721 if (game_top_fileinfo_version < GAME_COMPATIBLE_VERSION )
724 // We skip some parts of the former game_top_fileinfo
725 cfseek(LoadFile, 31, SEEK_CUR);
727 object_offset = cfile_read_int(LoadFile);
728 gs_num_objects = cfile_read_int(LoadFile);
729 cfseek(LoadFile, 8, SEEK_CUR);
731 Num_walls = cfile_read_int(LoadFile);
732 cfseek(LoadFile, 20, SEEK_CUR);
734 Num_triggers = cfile_read_int(LoadFile);
735 cfseek(LoadFile, 24, SEEK_CUR);
737 trig_size = cfile_read_int(LoadFile);
738 Assert(trig_size == sizeof(ControlCenterTriggers));
739 cfseek(LoadFile, 4, SEEK_CUR);
741 Num_robot_centers = cfile_read_int(LoadFile);
742 cfseek(LoadFile, 4, SEEK_CUR);
744 if (game_top_fileinfo_version >= 29) {
745 cfseek(LoadFile, 4, SEEK_CUR);
746 Num_static_lights = cfile_read_int(LoadFile);
747 cfseek(LoadFile, 8, SEEK_CUR);
748 num_delta_lights = cfile_read_int(LoadFile);
749 cfseek(LoadFile, 4, SEEK_CUR);
751 Num_static_lights = 0;
752 num_delta_lights = 0;
755 if (game_top_fileinfo_version >= 31) //load mine filename
756 // read newline-terminated string, not sure what version this changed.
757 cfgets(Current_level_name,sizeof(Current_level_name),LoadFile);
758 else if (game_top_fileinfo_version >= 14) { //load mine filename
759 // read null-terminated string
760 char *p=Current_level_name;
761 //must do read one char at a time, since no cfgets()
762 do *p = cfgetc(LoadFile); while (*p++!=0);
765 Current_level_name[0]=0;
767 if (game_top_fileinfo_version >= 19) { //load pof names
768 N_save_pof_names = cfile_read_short(LoadFile);
769 if (N_save_pof_names != 0x614d && N_save_pof_names != 0x5547) { // "Ma"de w/DMB beta/"GU"ILE
770 Assert(N_save_pof_names < MAX_POLYGON_MODELS);
771 cfread(Save_pof_names,N_save_pof_names,FILENAME_LEN,LoadFile);
775 //===================== READ PLAYER INFO ==========================
776 Object_next_signature = 0;
778 //===================== READ OBJECT INFO ==========================
780 Gamesave_num_org_robots = 0;
781 Gamesave_num_players = 0;
783 if (object_offset > -1) {
784 if (cfseek( LoadFile, object_offset, SEEK_SET ))
785 Error( "Error seeking to object_offset in gamesave.c" );
787 for (i = 0; i < gs_num_objects; i++) {
789 read_object(&Objects[i], LoadFile, game_top_fileinfo_version);
791 Objects[i].signature = Object_next_signature++;
792 verify_object( &Objects[i] );
797 //===================== READ WALL INFO ============================
799 for (i = 0; i < Num_walls; i++) {
800 if (game_top_fileinfo_version >= 20)
801 wall_read(&Walls[i], LoadFile); // v20 walls and up.
802 else if (game_top_fileinfo_version >= 17) {
804 v19_wall_read(&w, LoadFile);
805 Walls[i].segnum = w.segnum;
806 Walls[i].sidenum = w.sidenum;
807 Walls[i].linked_wall = w.linked_wall;
808 Walls[i].type = w.type;
809 Walls[i].flags = w.flags;
810 Walls[i].hps = w.hps;
811 Walls[i].trigger = w.trigger;
812 Walls[i].clip_num = w.clip_num;
813 Walls[i].keys = w.keys;
814 Walls[i].state = WALL_DOOR_CLOSED;
817 v16_wall_read(&w, LoadFile);
818 Walls[i].segnum = Walls[i].sidenum = Walls[i].linked_wall = -1;
819 Walls[i].type = w.type;
820 Walls[i].flags = w.flags;
821 Walls[i].hps = w.hps;
822 Walls[i].trigger = w.trigger;
823 Walls[i].clip_num = w.clip_num;
824 Walls[i].keys = w.keys;
829 //===================== READ DOOR INFO ============================
831 if (game_fileinfo.doors_offset > -1)
833 if (!cfseek( LoadFile, game_fileinfo.doors_offset,SEEK_SET )) {
835 for (i=0;i<game_fileinfo.doors_howmany;i++) {
837 if (game_top_fileinfo_version >= 20)
838 active_door_read(&ActiveDoors[i], LoadFile); // version 20 and up
843 v19_door_read(&d, LoadFile);
845 ActiveDoors[i].n_parts = d.n_parts;
847 for (p=0;p<d.n_parts;p++) {
850 cseg = Segments[d.seg[p]].children[d.side[p]];
851 cside = find_connect_side(&Segments[d.seg[p]],&Segments[cseg]);
853 ActiveDoors[i].front_wallnum[p] = Segments[d.seg[p]].sides[d.side[p]].wall_num;
854 ActiveDoors[i].back_wallnum[p] = Segments[cseg].sides[cside].wall_num;
863 //==================== READ TRIGGER INFO ==========================
866 // for MACINTOSH -- assume all triggers >= verion 31 triggers.
868 for (i = 0; i < Num_triggers; i++)
870 if (game_top_fileinfo_version < 31)
876 if (game_top_fileinfo_version < 30) {
879 v29_trigger_read(&trig29, LoadFile);
880 trig.flags = trig29.flags;
881 trig.num_links = trig29.num_links;
882 trig.num_links = trig29.num_links;
883 trig.value = trig29.value;
884 trig.time = trig29.time;
886 for (t=0;t<trig.num_links;t++) {
887 trig.seg[t] = trig29.seg[t];
888 trig.side[t] = trig29.side[t];
892 v30_trigger_read(&trig, LoadFile);
894 //Assert(trig.flags & TRIGGER_ON);
895 trig.flags &= ~TRIGGER_ON;
897 if (trig.flags & TRIGGER_CONTROL_DOORS)
899 else if (trig.flags & TRIGGER_SHIELD_DAMAGE)
901 else if (trig.flags & TRIGGER_ENERGY_DRAIN)
903 else if (trig.flags & TRIGGER_EXIT)
905 else if (trig.flags & TRIGGER_ONE_SHOT)
907 else if (trig.flags & TRIGGER_MATCEN)
909 else if (trig.flags & TRIGGER_ILLUSION_OFF)
910 type = TT_ILLUSION_OFF;
911 else if (trig.flags & TRIGGER_SECRET_EXIT)
912 type = TT_SECRET_EXIT;
913 else if (trig.flags & TRIGGER_ILLUSION_ON)
914 type = TT_ILLUSION_ON;
915 else if (trig.flags & TRIGGER_UNLOCK_DOORS)
916 type = TT_UNLOCK_DOOR;
917 else if (trig.flags & TRIGGER_OPEN_WALL)
919 else if (trig.flags & TRIGGER_CLOSE_WALL)
920 type = TT_CLOSE_WALL;
921 else if (trig.flags & TRIGGER_ILLUSORY_WALL)
922 type = TT_ILLUSORY_WALL;
925 Triggers[i].type = type;
926 Triggers[i].flags = 0;
927 Triggers[i].num_links = trig.num_links;
928 Triggers[i].num_links = trig.num_links;
929 Triggers[i].value = trig.value;
930 Triggers[i].time = trig.time;
931 for (t=0;t<trig.num_links;t++) {
932 Triggers[i].seg[t] = trig.seg[t];
933 Triggers[i].side[t] = trig.side[t];
937 trigger_read(&Triggers[i], LoadFile);
940 //================ READ CONTROL CENTER TRIGGER INFO ===============
943 if (game_fileinfo.control_offset > -1)
944 if (!cfseek(LoadFile, game_fileinfo.control_offset, SEEK_SET))
946 Assert(game_fileinfo.control_sizeof == sizeof(control_center_triggers));
948 control_center_triggers_read_n(&ControlCenterTriggers, 1, LoadFile);
950 //================ READ MATERIALOGRIFIZATIONATORS INFO ===============
952 // mprintf((0, "Reading %i materialization centers.\n", game_fileinfo.matcen_howmany));
953 for (i = 0; i < Num_robot_centers; i++) {
954 if (game_top_fileinfo_version < 27) {
956 old_matcen_info_read(&m, LoadFile);
957 RobotCenters[i].robot_flags[0] = m.robot_flags;
958 RobotCenters[i].robot_flags[1] = 0;
959 RobotCenters[i].hit_points = m.hit_points;
960 RobotCenters[i].interval = m.interval;
961 RobotCenters[i].segnum = m.segnum;
962 RobotCenters[i].fuelcen_num = m.fuelcen_num;
965 matcen_info_read(&RobotCenters[i], LoadFile);
967 // Set links in RobotCenters to Station array
968 for (j = 0; j <= Highest_segment_index; j++)
969 if (Segment2s[j].special == SEGMENT_IS_ROBOTMAKER)
970 if (Segment2s[j].matcen_num == i)
971 RobotCenters[i].fuelcen_num = Segment2s[j].value;
972 // mprintf((0, " %i: flags = %08x\n", i, RobotCenters[i].robot_flags));
975 //================ READ DL_INDICES INFO ===============
977 for (i = 0; i < Num_static_lights; i++) {
978 if (game_top_fileinfo_version < 29) {
979 mprintf((0, "Warning: Old mine version. Not reading Dl_indices info.\n"));
980 Int3(); //shouldn't be here!!!
982 dl_index_read(&Dl_indices[i], LoadFile);
985 // Indicate that no light has been subtracted from any vertices.
986 clear_light_subtracted();
988 //================ READ DELTA LIGHT INFO ===============
990 for (i = 0; i < num_delta_lights; i++) {
991 if (game_top_fileinfo_version < 29) {
992 mprintf((0, "Warning: Old mine version. Not reading delta light info.\n"));
994 delta_light_read(&Delta_lights[i], LoadFile);
997 //========================= UPDATE VARIABLES ======================
999 reset_objects(gs_num_objects);
1001 for (i=0; i<MAX_OBJECTS; i++) {
1002 Objects[i].next = Objects[i].prev = -1;
1003 if (Objects[i].type != OBJ_NONE) {
1004 int objsegnum = Objects[i].segnum;
1006 if (objsegnum > Highest_segment_index) //bogus object
1007 Objects[i].type = OBJ_NONE;
1009 Objects[i].segnum = -1; //avoid Assert()
1010 obj_link(i,objsegnum);
1015 clear_transient_objects(1); //1 means clear proximity bombs
1017 // Make sure non-transparent doors are set correctly.
1018 for (i=0; i< Num_segments; i++)
1019 for (j=0;j<MAX_SIDES_PER_SEGMENT;j++) {
1020 side *sidep = &Segments[i].sides[j];
1021 if ((sidep->wall_num != -1) && (Walls[sidep->wall_num].clip_num != -1)) {
1022 //mprintf((0, "Checking Wall %d\n", Segments[i].sides[j].wall_num));
1023 if (WallAnims[Walls[sidep->wall_num].clip_num].flags & WCF_TMAP1) {
1024 //mprintf((0, "Fixing non-transparent door.\n"));
1025 sidep->tmap_num = WallAnims[Walls[sidep->wall_num].clip_num].frames[0];
1026 sidep->tmap_num2 = 0;
1035 Num_open_doors = game_fileinfo.doors_howmany;
1039 //go through all walls, killing references to invalid triggers
1040 for (i=0;i<Num_walls;i++)
1041 if (Walls[i].trigger >= Num_triggers) {
1042 mprintf((0,"Removing reference to invalid trigger %d from wall %d\n",Walls[i].trigger,i));
1043 Walls[i].trigger = -1; //kill trigger
1046 //go through all triggers, killing unused ones
1047 for (i=0;i<Num_triggers;) {
1050 // Find which wall this trigger is connected to.
1051 for (w=0; w<Num_walls; w++)
1052 if (Walls[w].trigger == i)
1056 if (w == Num_walls) {
1057 mprintf((0,"Removing unreferenced trigger %d\n",i));
1058 remove_trigger_num(i);
1065 // MK, 10/17/95: Make walls point back at the triggers that control them.
1066 // Go through all triggers, stuffing controlling_trigger field in Walls.
1069 for (i=0; i<Num_walls; i++)
1070 Walls[i].controlling_trigger = -1;
1072 for (t=0; t<Num_triggers; t++) {
1074 for (l=0; l<Triggers[t].num_links; l++) {
1075 int seg_num, side_num, wall_num;
1077 seg_num = Triggers[t].seg[l];
1078 side_num = Triggers[t].side[l];
1079 wall_num = Segments[seg_num].sides[side_num].wall_num;
1081 // -- if (Walls[wall_num].controlling_trigger != -1)
1084 //check to see that if a trigger requires a wall that it has one,
1085 //and if it requires a matcen that it has one
1087 if (Triggers[t].type == TT_MATCEN) {
1088 if (Segment2s[seg_num].special != SEGMENT_IS_ROBOTMAKER)
1089 Int3(); //matcen trigger doesn't point to matcen
1091 else if (Triggers[t].type != TT_LIGHT_OFF && Triggers[t].type != TT_LIGHT_ON) { //light triggers don't require walls
1093 Int3(); // This is illegal. This trigger requires a wall
1095 Walls[wall_num].controlling_trigger = t;
1101 //fix old wall structs
1102 if (game_top_fileinfo_version < 17) {
1103 int segnum,sidenum,wallnum;
1105 for (segnum=0; segnum<=Highest_segment_index; segnum++)
1106 for (sidenum=0;sidenum<6;sidenum++)
1107 if ((wallnum=Segments[segnum].sides[sidenum].wall_num) != -1) {
1108 Walls[wallnum].segnum = segnum;
1109 Walls[wallnum].sidenum = sidenum;
1116 for (sidenum=0; sidenum<6; sidenum++) {
1117 int wallnum = Segments[Highest_segment_index].sides[sidenum].wall_num;
1119 if ((Walls[wallnum].segnum != Highest_segment_index) || (Walls[wallnum].sidenum != sidenum))
1120 Int3(); // Error. Bogus walls in this segment.
1121 // Consult Yuan or Mike.
1126 //create_local_segment_data();
1134 if (game_top_fileinfo_version < GAME_VERSION
1135 && !(game_top_fileinfo_version == 25 && GAME_VERSION == 26))
1136 return 1; //means old version
1142 int check_segment_connections(void);
1144 extern void set_ambient_sound_flags(void);
1146 // ----------------------------------------------------------------------------
1148 #define LEVEL_FILE_VERSION 8
1149 //1 -> 2 add palette name
1150 //2 -> 3 add control center explosion time
1151 //3 -> 4 add reactor strength
1152 //4 -> 5 killed hostage text stuff
1153 //5 -> 6 added Secret_return_segment and Secret_return_orient
1154 //6 -> 7 added flickering lights
1155 //7 -> 8 made version 8 to be not compatible with D2 1.0 & 1.1
1158 const char *Level_being_loaded=NULL;
1162 extern void ncache_flush();
1165 extern int Slide_segs_computed;
1166 extern int d1_pig_present;
1168 int no_old_level_file_error=0;
1170 //loads a level (.LVL) file from disk
1171 //returns 0 if success, else error code
1172 int load_level(const char * filename_passed)
1175 int use_compiled_level=1;
1178 char filename[PATH_MAX];
1179 int sig, minedata_offset, gamedata_offset;
1180 int mine_err, game_err;
1185 Slide_segs_computed = 0;
1188 if (Game_mode & GM_NETWORK)
1190 for (i=0;i<MAX_POWERUP_TYPES;i++)
1192 MaxPowerupsAllowed[i]=0;
1193 PowerupsInMine[i]=0;
1203 Level_being_loaded = filename_passed;
1206 strcpy(filename,filename_passed);
1208 PHYSFSEXT_locateCorrectCase(filename);
1211 //if we have the editor, try the LVL first, no matter what was passed.
1212 //if we don't have an LVL, try what was passed or RL2
1213 //if we don't have the editor, we just use what was passed
1215 change_filename_extension(filename,filename_passed,".lvl");
1216 use_compiled_level = 0;
1218 if (!cfexist(filename))
1220 char *p = strrchr(filename_passed, '.');
1222 if (stricmp(p, ".lvl"))
1223 strcpy(filename, filename_passed); // set to what was passed
1225 change_filename_extension(filename, filename, ".rl2");
1226 use_compiled_level = 1;
1230 LoadFile = cfopen( filename, "rb" );
1234 mprintf((0,"Can't open level file <%s>\n", filename));
1237 Error("Can't open file <%s>\n",filename);
1241 strcpy( Gamesave_current_filename, filename );
1244 // if ( Newdemo_state == ND_STATE_RECORDING )
1245 // newdemo_record_start_demo();
1248 sig = cfile_read_int(LoadFile);
1249 Gamesave_current_version = cfile_read_int(LoadFile);
1250 mprintf((0, "Gamesave_current_version = %d\n", Gamesave_current_version));
1251 minedata_offset = cfile_read_int(LoadFile);
1252 gamedata_offset = cfile_read_int(LoadFile);
1254 Assert(sig == MAKE_SIG('P','L','V','L'));
1256 if (Gamesave_current_version >= 8) { //read dummy data
1257 cfile_read_int(LoadFile);
1258 cfile_read_short(LoadFile);
1259 cfile_read_byte(LoadFile);
1262 if (Gamesave_current_version < 5)
1263 cfile_read_int(LoadFile); //was hostagetext_offset
1265 if (Gamesave_current_version > 1)
1266 cfgets(Current_level_palette,sizeof(Current_level_palette),LoadFile);
1267 if (Gamesave_current_version <= 1 || Current_level_palette[0]==0) // descent 1 level
1268 strcpy(Current_level_palette, DEFAULT_LEVEL_PALETTE);
1270 if (Gamesave_current_version >= 3)
1271 Base_control_center_explosion_time = cfile_read_int(LoadFile);
1273 Base_control_center_explosion_time = DEFAULT_CONTROL_CENTER_EXPLOSION_TIME;
1275 if (Gamesave_current_version >= 4)
1276 Reactor_strength = cfile_read_int(LoadFile);
1278 Reactor_strength = -1; //use old defaults
1280 if (Gamesave_current_version >= 7) {
1283 Num_flickering_lights = cfile_read_int(LoadFile);
1284 Assert((Num_flickering_lights >= 0) && (Num_flickering_lights < MAX_FLICKERING_LIGHTS));
1285 for (i = 0; i < Num_flickering_lights; i++)
1286 flickering_light_read(&Flickering_lights[i], LoadFile);
1289 Num_flickering_lights = 0;
1291 if (Gamesave_current_version < 6) {
1292 Secret_return_segment = 0;
1293 Secret_return_orient.rvec.x = F1_0;
1294 Secret_return_orient.rvec.y = 0;
1295 Secret_return_orient.rvec.z = 0;
1296 Secret_return_orient.fvec.x = 0;
1297 Secret_return_orient.fvec.y = F1_0;
1298 Secret_return_orient.fvec.z = 0;
1299 Secret_return_orient.uvec.x = 0;
1300 Secret_return_orient.uvec.y = 0;
1301 Secret_return_orient.uvec.z = F1_0;
1303 Secret_return_segment = cfile_read_int(LoadFile);
1304 Secret_return_orient.rvec.x = cfile_read_int(LoadFile);
1305 Secret_return_orient.rvec.y = cfile_read_int(LoadFile);
1306 Secret_return_orient.rvec.z = cfile_read_int(LoadFile);
1307 Secret_return_orient.fvec.x = cfile_read_int(LoadFile);
1308 Secret_return_orient.fvec.y = cfile_read_int(LoadFile);
1309 Secret_return_orient.fvec.z = cfile_read_int(LoadFile);
1310 Secret_return_orient.uvec.x = cfile_read_int(LoadFile);
1311 Secret_return_orient.uvec.y = cfile_read_int(LoadFile);
1312 Secret_return_orient.uvec.z = cfile_read_int(LoadFile);
1315 cfseek(LoadFile,minedata_offset,SEEK_SET);
1317 if (!use_compiled_level) {
1318 mine_err = load_mine_data(LoadFile);
1319 #if 0 // get from d1src if needed
1320 // Compress all uv coordinates in mine, improves texmap precision. --MK, 02/19/96
1321 compress_uv_coordinates_all();
1325 //NOTE LINK TO ABOVE!!
1326 mine_err = load_mine_data_compiled(LoadFile);
1328 if (mine_err == -1) { //error!!
1333 cfseek(LoadFile,gamedata_offset,SEEK_SET);
1334 game_err = load_game_data(LoadFile);
1336 if (game_err == -1) { //error!!
1341 //======================== CLOSE FILE =============================
1343 cfclose( LoadFile );
1345 set_ambient_sound_flags();
1348 write_game_text_file(filename);
1349 if (Errors_in_mine) {
1350 if (is_real_level(filename)) {
1351 char ErrorMessage[200];
1353 sprintf( ErrorMessage, "Warning: %i errors in %s!\n", Errors_in_mine, Level_being_loaded );
1355 gr_palette_load(gr_palette);
1356 nm_messagebox( NULL, 1, "Continue", ErrorMessage );
1359 mprintf((1, "Error: %i errors in %s.\n", Errors_in_mine, Level_being_loaded));
1364 //If a Descent 1 level and the Descent 1 pig isn't present, pretend it's a Descent 2 level.
1365 if ((Function_mode == FMODE_EDITOR) && (Gamesave_current_version <= 3) && !d1_pig_present)
1367 if (!no_old_level_file_error)
1368 Warning("A Descent 1 level was loaded,\n"
1369 "and there is no Descent 1 texture\n"
1370 "set available. Saving it will\n"
1371 "convert it to a Descent 2 level.");
1373 Gamesave_current_version = LEVEL_FILE_VERSION;
1378 if (Function_mode == FMODE_EDITOR)
1379 editor_status("Loaded NEW mine %s, \"%s\"",filename,Current_level_name);
1382 #if !defined(NDEBUG) && !defined(COMPACT_SEGS)
1383 if (check_segment_connections())
1384 nm_messagebox( "ERROR", 1, "Ok",
1385 "Connectivity errors detected in\n"
1386 "mine. See monochrome screen for\n"
1387 "details, and contact Matt or Mike." );
1394 int get_level_name()
1396 //NO_UI!!! UI_WINDOW *NameWindow = NULL;
1397 //NO_UI!!! UI_GADGET_INPUTBOX *NameText;
1398 //NO_UI!!! UI_GADGET_BUTTON *QuitButton;
1400 //NO_UI!!! // Open a window with a quit button
1401 //NO_UI!!! NameWindow = ui_open_window( 20, 20, 300, 110, WIN_DIALOG );
1402 //NO_UI!!! QuitButton = ui_add_gadget_button( NameWindow, 150-24, 60, 48, 40, "Done", NULL );
1404 //NO_UI!!! ui_wprintf_at( NameWindow, 10, 12,"Please enter a name for this mine:" );
1405 //NO_UI!!! NameText = ui_add_gadget_inputbox( NameWindow, 10, 30, LEVEL_NAME_LEN, LEVEL_NAME_LEN, Current_level_name );
1407 //NO_UI!!! NameWindow->keyboard_focus_gadget = (UI_GADGET *)NameText;
1408 //NO_UI!!! QuitButton->hotkey = KEY_ENTER;
1410 //NO_UI!!! ui_gadget_calc_keys(NameWindow);
1412 //NO_UI!!! while (!QuitButton->pressed && last_keypress!=KEY_ENTER) {
1413 //NO_UI!!! ui_mega_process();
1414 //NO_UI!!! ui_window_do_gadgets(NameWindow);
1417 //NO_UI!!! strcpy( Current_level_name, NameText->text );
1419 //NO_UI!!! if ( NameWindow!=NULL ) {
1420 //NO_UI!!! ui_close_window( NameWindow );
1421 //NO_UI!!! NameWindow = NULL;
1427 m[0].type = NM_TYPE_TEXT; m[0].text = "Please enter a name for this mine:";
1428 m[1].type = NM_TYPE_INPUT; m[1].text = Current_level_name; m[1].text_len = LEVEL_NAME_LEN;
1430 return newmenu_do( NULL, "Enter mine name", 2, m, NULL ) >= 0;
1440 // -----------------------------------------------------------------------------
1441 int compute_num_delta_light_records(void)
1446 for (i=0; i<Num_static_lights; i++) {
1447 total += Dl_indices[i].count;
1454 // -----------------------------------------------------------------------------
1456 int save_game_data(PHYSFS_file *SaveFile)
1458 short game_top_fileinfo_version = Gamesave_current_version >= 5 ? 31 : 25;
1459 int player_offset = 0, object_offset = 0, walls_offset = 0, doors_offset = 0, triggers_offset = 0, control_offset = 0, matcen_offset = 0; //, links_offset;
1460 int dl_indices_offset = 0, delta_light_offset = 0;
1461 int offset_offset = 0, end_offset = 0;
1462 int num_delta_lights = 0;
1465 //===================== SAVE FILE INFO ========================
1467 PHYSFS_writeSLE16(SaveFile, 0x6705); // signature
1468 PHYSFS_writeSLE16(SaveFile, game_top_fileinfo_version);
1469 PHYSFS_writeSLE32(SaveFile, sizeof(game_fileinfo));
1470 PHYSFS_write(SaveFile, Current_level_name, 15, 1);
1471 PHYSFS_writeSLE32(SaveFile, Current_level_num);
1472 offset_offset = (int)PHYSFS_tell(SaveFile); // write the offsets later
1473 PHYSFS_writeSLE32(SaveFile, -1);
1474 PHYSFS_writeSLE32(SaveFile, sizeof(player));
1476 #define WRITE_HEADER_ENTRY(t, n) do { PHYSFS_writeSLE32(SaveFile, -1); PHYSFS_writeSLE32(SaveFile, n); PHYSFS_writeSLE32(SaveFile, sizeof(t)); } while(0)
1478 WRITE_HEADER_ENTRY(object, Highest_object_index + 1);
1479 WRITE_HEADER_ENTRY(wall, Num_walls);
1480 WRITE_HEADER_ENTRY(active_door, Num_open_doors);
1481 WRITE_HEADER_ENTRY(trigger, Num_triggers);
1482 WRITE_HEADER_ENTRY(0, 0); // links (removed by Parallax)
1483 WRITE_HEADER_ENTRY(control_center_triggers, 1);
1484 WRITE_HEADER_ENTRY(matcen_info, Num_robot_centers);
1486 if (game_top_fileinfo_version >= 29)
1488 WRITE_HEADER_ENTRY(dl_index, Num_static_lights);
1489 WRITE_HEADER_ENTRY(delta_light, num_delta_lights = compute_num_delta_light_records());
1492 // Write the mine name
1493 if (game_top_fileinfo_version >= 31)
1494 PHYSFSX_printf(SaveFile, "%s\n", Current_level_name);
1495 else if (game_top_fileinfo_version >= 14)
1496 PHYSFSX_writeString(SaveFile, Current_level_name);
1498 if (game_top_fileinfo_version >= 19)
1500 PHYSFS_writeSLE16(SaveFile, N_polygon_models);
1501 PHYSFS_write(SaveFile, Pof_names, sizeof(*Pof_names), N_polygon_models);
1504 //==================== SAVE PLAYER INFO ===========================
1506 player_offset = (int)PHYSFS_tell(SaveFile);
1507 PHYSFS_write(SaveFile, &Players[Player_num], sizeof(player), 1); // not endian friendly, but not used either
1509 //==================== SAVE OBJECT INFO ===========================
1511 object_offset = (int)PHYSFS_tell(SaveFile);
1512 //fwrite( &Objects, sizeof(object), game_fileinfo.object_howmany, SaveFile );
1514 for (i = 0; i <= Highest_object_index; i++)
1515 write_object(&Objects[i], game_top_fileinfo_version, SaveFile);
1518 //==================== SAVE WALL INFO =============================
1520 walls_offset = (int)PHYSFS_tell(SaveFile);
1521 for (i = 0; i < Num_walls; i++)
1522 wall_write(&Walls[i], game_top_fileinfo_version, SaveFile);
1524 //==================== SAVE DOOR INFO =============================
1527 doors_offset = PHYSFS_tell(SaveFile);
1528 for (i = 0; i < Num_open_doors; i++)
1529 door_write(&ActiveDoors[i], game_top_fileinfo_version, SaveFile);
1531 doors_offset = 0; // kill warning
1534 //==================== SAVE TRIGGER INFO =============================
1536 triggers_offset = (int)PHYSFS_tell(SaveFile);
1537 for (i = 0; i < Num_triggers; i++)
1538 trigger_write(&Triggers[i], game_top_fileinfo_version, SaveFile);
1540 //================ SAVE CONTROL CENTER TRIGGER INFO ===============
1542 control_offset = (int)PHYSFS_tell(SaveFile);
1543 control_center_triggers_write(&ControlCenterTriggers, SaveFile);
1546 //================ SAVE MATERIALIZATION CENTER TRIGGER INFO ===============
1548 matcen_offset = (int)PHYSFS_tell(SaveFile);
1549 // mprintf((0, "Writing %i materialization centers\n", game_fileinfo.matcen_howmany));
1551 // for (i=0; i<game_fileinfo.matcen_howmany; i++)
1552 // mprintf((0, " %i: robot_flags = %08x\n", i, RobotCenters[i].robot_flags));
1554 for (i = 0; i < Num_robot_centers; i++)
1555 matcen_info_write(&RobotCenters[i], game_top_fileinfo_version, SaveFile);
1557 //================ SAVE DELTA LIGHT INFO ===============
1558 if (game_top_fileinfo_version >= 29)
1560 dl_indices_offset = (int)PHYSFS_tell(SaveFile);
1561 for (i = 0; i < Num_static_lights; i++)
1562 dl_index_write(&Dl_indices[i], SaveFile);
1564 delta_light_offset = (int)PHYSFS_tell(SaveFile);
1565 for (i = 0; i < num_delta_lights; i++)
1566 delta_light_write(&Delta_lights[i], SaveFile);
1569 //============= SAVE OFFSETS ===============
1571 end_offset = (int)PHYSFS_tell(SaveFile);
1573 // Update the offset fields
1575 #define WRITE_OFFSET(o, n) do { PHYSFS_seek(SaveFile, offset_offset); PHYSFS_writeSLE32(SaveFile, o ## _offset); offset_offset += sizeof(int)*n; } while (0)
1577 WRITE_OFFSET(player, 2);
1578 WRITE_OFFSET(object, 3);
1579 WRITE_OFFSET(walls, 3);
1580 WRITE_OFFSET(doors, 3);
1581 WRITE_OFFSET(triggers, 6);
1582 WRITE_OFFSET(control, 3);
1583 WRITE_OFFSET(matcen, 3);
1584 if (game_top_fileinfo_version >= 29)
1586 WRITE_OFFSET(dl_indices, 3);
1587 WRITE_OFFSET(delta_light, 0);
1590 // Go back to end of data
1591 PHYSFS_seek(SaveFile, end_offset);
1596 int save_mine_data(FILE * SaveFile);
1598 // -----------------------------------------------------------------------------
1600 int save_level_sub(char * filename, int compiled_version)
1602 PHYSFS_file * SaveFile;
1603 char temp_filename[PATH_MAX];
1604 int minedata_offset=0,gamedata_offset=0;
1606 // if ( !compiled_version )
1608 write_game_text_file(filename);
1610 if (Errors_in_mine) {
1611 if (is_real_level(filename)) {
1612 char ErrorMessage[200];
1614 sprintf( ErrorMessage, "Warning: %i errors in this mine!\n", Errors_in_mine );
1616 gr_palette_load(gr_palette);
1618 if (nm_messagebox( NULL, 2, "Cancel Save", "Save", ErrorMessage )!=1) {
1624 mprintf((1, "Error: %i errors in this mine. See the 'txm' file.\n", Errors_in_mine));
1626 // change_filename_extension(temp_filename,filename,".LVL");
1630 if (Gamesave_current_version <= 3)
1631 change_filename_extension(temp_filename, filename, ".RDL");
1633 change_filename_extension(temp_filename, filename, ".RL2");
1636 SaveFile = PHYSFSX_openWriteBuffered(temp_filename);
1639 char ErrorMessage[256];
1642 _splitpath( temp_filename, NULL, NULL, fname, NULL );
1644 sprintf( ErrorMessage, \
1645 "ERROR: Cannot write to '%.19s'.\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 %.19s.lvl'\nat the DOS prompt.\n"
1646 , temp_filename, fname );
1648 gr_palette_load(gr_palette);
1649 nm_messagebox( NULL, 1, "Ok", ErrorMessage );
1654 if (Current_level_name[0] == 0)
1655 strcpy(Current_level_name,"Untitled");
1657 clear_transient_objects(1); //1 means clear proximity bombs
1659 compress_objects(); //after this, Highest_object_index == num objects
1661 //make sure player is in a segment
1662 if (update_object_seg(&Objects[Players[0].objnum]) == 0) {
1663 if (ConsoleObject->segnum > Highest_segment_index)
1664 ConsoleObject->segnum = 0;
1665 compute_segment_center(&ConsoleObject->pos,&(Segments[ConsoleObject->segnum]));
1672 PHYSFS_writeSLE32(SaveFile, MAKE_SIG('P','L','V','L'));
1673 PHYSFS_writeSLE32(SaveFile, Gamesave_current_version);
1676 PHYSFS_writeSLE32(SaveFile, minedata_offset);
1677 PHYSFS_writeSLE32(SaveFile, gamedata_offset);
1679 //Now write the damn data
1681 if (Gamesave_current_version >= 8)
1683 //write the version 8 data (to make file unreadable by 1.0 & 1.1)
1684 PHYSFS_writeSLE32(SaveFile, GameTime);
1685 PHYSFS_writeSLE16(SaveFile, FrameCount);
1686 PHYSFSX_writeU8(SaveFile, FrameTime);
1689 if (Gamesave_current_version < 5)
1690 PHYSFS_writeSLE32(SaveFile, -1); //was hostagetext_offset
1692 // Write the palette file name
1693 if (Gamesave_current_version > 1)
1694 PHYSFSX_printf(SaveFile, "%s\n", Current_level_palette);
1696 if (Gamesave_current_version >= 3)
1697 PHYSFS_writeSLE32(SaveFile, Base_control_center_explosion_time);
1698 if (Gamesave_current_version >= 4)
1699 PHYSFS_writeSLE32(SaveFile, Reactor_strength);
1701 if (Gamesave_current_version >= 7)
1705 PHYSFS_writeSLE32(SaveFile, Num_flickering_lights);
1706 for (i = 0; i < Num_flickering_lights; i++)
1707 flickering_light_write(&Flickering_lights[i], SaveFile);
1710 if (Gamesave_current_version >= 6)
1712 PHYSFS_writeSLE32(SaveFile, Secret_return_segment);
1713 PHYSFSX_writeVector(SaveFile, &Secret_return_orient.rvec);
1714 PHYSFSX_writeVector(SaveFile, &Secret_return_orient.fvec);
1715 PHYSFSX_writeVector(SaveFile, &Secret_return_orient.uvec);
1718 minedata_offset = (int)PHYSFS_tell(SaveFile);
1719 #if 0 // only save compiled mine data
1720 if ( !compiled_version )
1721 save_mine_data(SaveFile);
1724 save_mine_data_compiled(SaveFile);
1725 gamedata_offset = (int)PHYSFS_tell(SaveFile);
1726 save_game_data(SaveFile);
1728 PHYSFS_seek(SaveFile, sizeof(int) + sizeof(Gamesave_current_version));
1729 PHYSFS_writeSLE32(SaveFile, minedata_offset);
1730 PHYSFS_writeSLE32(SaveFile, gamedata_offset);
1732 if (Gamesave_current_version < 5)
1733 PHYSFS_writeSLE32(SaveFile, (PHYSFS_sint32)PHYSFS_fileLength(SaveFile));
1735 //==================== CLOSE THE FILE =============================
1736 PHYSFS_close(SaveFile);
1738 // if ( !compiled_version )
1740 if (Function_mode == FMODE_EDITOR)
1741 editor_status("Saved mine %s, \"%s\"",filename,Current_level_name);
1748 #if 0 //dunno - 3rd party stuff?
1749 extern void compress_uv_coordinates_all(void);
1752 int save_level(char * filename)
1756 // Save normal version...
1757 //save_level_sub(filename, 0); // just save compiled one
1759 // Save compiled version...
1760 r1 = save_level_sub(filename, 1);
1768 void dump_mine_info(void)
1770 int segnum, sidenum;
1771 fix min_u, max_u, min_v, max_v, min_l, max_l, max_sl;
1783 for (segnum=0; segnum<=Highest_segment_index; segnum++) {
1784 for (sidenum=0; sidenum<MAX_SIDES_PER_SEGMENT; sidenum++) {
1786 side *sidep = &Segments[segnum].sides[sidenum];
1788 if (Segment2s[segnum].static_light > max_sl)
1789 max_sl = Segment2s[segnum].static_light;
1791 for (vertnum=0; vertnum<4; vertnum++) {
1792 if (sidep->uvls[vertnum].u < min_u)
1793 min_u = sidep->uvls[vertnum].u;
1794 else if (sidep->uvls[vertnum].u > max_u)
1795 max_u = sidep->uvls[vertnum].u;
1797 if (sidep->uvls[vertnum].v < min_v)
1798 min_v = sidep->uvls[vertnum].v;
1799 else if (sidep->uvls[vertnum].v > max_v)
1800 max_v = sidep->uvls[vertnum].v;
1802 if (sidep->uvls[vertnum].l < min_l)
1803 min_l = sidep->uvls[vertnum].l;
1804 else if (sidep->uvls[vertnum].l > max_l)
1805 max_l = sidep->uvls[vertnum].l;
1811 // 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)));
1812 // mprintf((0, "Static light maximum = %7.3f\n", f2fl(max_sl)));
1813 // mprintf((0, "Number of walls: %i\n", Num_walls));
1821 //read in every level in mission and save out compiled version
1822 void save_all_compiled_levels(void)
1824 do_load_save_levels(1);
1827 //read in every level in mission
1828 void load_all_levels(void)
1830 do_load_save_levels(0);
1834 void do_load_save_levels(int save)
1838 if (! SafetyCheck())
1841 no_old_level_file_error=1;
1843 for (level_num=1;level_num<=Last_level;level_num++) {
1844 load_level(Level_names[level_num-1]);
1845 load_palette(Current_level_palette,1,1); //don't change screen
1847 save_level_sub(Level_names[level_num-1],1);
1850 for (level_num=-1;level_num>=Last_secret_level;level_num--) {
1851 load_level(Secret_level_names[-level_num-1]);
1852 load_palette(Current_level_palette,1,1); //don't change screen
1854 save_level_sub(Secret_level_names[-level_num-1],1);
1857 no_old_level_file_error=0;