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
27 char gamesave_rcsid[] = "$Id: gamesave.c,v 1.7 2002-07-27 04:39:23 btb Exp $";
43 #include "editor/editor.h"
75 char Gamesave_current_filename[128];
77 #define GAME_VERSION 32
78 #define GAME_COMPATIBLE_VERSION 22
80 //version 28->29 add delta light support
81 //version 27->28 controlcen id now is reactor number, not model number
83 //version 29->30 changed trigger structure
84 //version 30->31 changed trigger structure some more
85 //version 31->32 change segment structure, make it 512 bytes w/o editor, add Segment2s array.
87 #define MENU_CURSOR_X_MIN MENU_X
88 #define MENU_CURSOR_X_MAX MENU_X+6
91 ushort fileinfo_signature;
92 ushort fileinfo_version;
94 } game_top_fileinfo; // Should be same as first two fields below...
97 ushort fileinfo_signature;
98 ushort fileinfo_version;
100 char mine_filename[15];
102 int player_offset; // Player info
104 int object_offset; // Object info
114 int triggers_howmany;
125 int dl_indices_offset;
126 int dl_indices_howmany;
127 int dl_indices_sizeof;
128 int delta_light_offset;
129 int delta_light_howmany;
130 int delta_light_sizeof;
133 // LINT: adding function prototypes
134 void read_object(object *obj, CFILE *f, int version);
135 void write_object(object *obj, FILE *f);
136 void do_load_save_levels(int save);
137 void dump_mine_info(void);
139 extern char MaxPowerupsAllowed[MAX_POWERUP_TYPES];
140 extern char PowerupsInMine[MAX_POWERUP_TYPES];
143 extern char mine_filename[];
144 extern int save_mine_data_compiled(FILE * SaveFile);
146 //--unused-- char mine_filename[128];
149 int Gamesave_num_org_robots = 0;
150 //--unused-- grs_bitmap * Gamesave_saved_bitmap = NULL;
153 // Return true if this level has a name of the form "level??"
154 // Note that a pathspec can appear at the beginning of the filename.
155 int is_real_level(char *filename)
157 int len = strlen(filename);
162 //mprintf((0, "String = [%s]\n", &filename[len-11]));
163 return !strnicmp(&filename[len-11], "level", 5);
168 void change_filename_extension( char *dest, char *src, char *new_ext )
177 for (i=1; i<strlen(dest); i++ )
178 if (dest[i]=='.'||dest[i]==' '||dest[i]==0)
183 dest[i+1]=new_ext[0];
184 dest[i+2]=new_ext[1];
185 dest[i+3]=new_ext[2];
191 //--unused-- vms_angvec zero_angles={0,0,0};
193 #define vm_angvec_zero(v) do {(v)->p=(v)->b=(v)->h=0;} while (0)
195 int Gamesave_num_players=0;
197 int N_save_pof_names;
198 char Save_pof_names[MAX_POLYGON_MODELS][FILENAME_LEN];
200 void check_and_fix_matrix(vms_matrix *m);
202 void verify_object( object * obj ) {
204 obj->lifeleft = IMMORTAL_TIME; //all loaded object are immortal, for now
206 if ( obj->type == OBJ_ROBOT ) {
207 Gamesave_num_org_robots++;
209 // Make sure valid id...
210 if ( obj->id >= N_robot_types )
211 obj->id = obj->id % N_robot_types;
213 // Make sure model number & size are correct...
214 if ( obj->render_type == RT_POLYOBJ ) {
215 Assert(Robot_info[obj->id].model_num != -1);
216 //if you fail this assert, it means that a robot in this level
217 //hasn't been loaded, possibly because he's marked as
218 //non-shareware. To see what robot number, print obj->id.
220 Assert(Robot_info[obj->id].always_0xabcd == 0xabcd);
221 //if you fail this assert, it means that the robot_ai for
222 //a robot in this level hasn't been loaded, possibly because
223 //it's marked as non-shareware. To see what robot number,
226 obj->rtype.pobj_info.model_num = Robot_info[obj->id].model_num;
227 obj->size = Polygon_models[obj->rtype.pobj_info.model_num].rad;
229 //@@Took out this ugly hack 1/12/96, because Mike has added code
230 //@@that should fix it in a better way.
231 //@@//this is a super-ugly hack. Since the baby stripe robots have
232 //@@//their firing point on their bounding sphere, the firing points
233 //@@//can poke through a wall if the robots are very close to it. So
234 //@@//we make their radii bigger so the guns can't get too close to
236 //@@if (Robot_info[obj->id].flags & RIF_BIG_RADIUS)
237 //@@ obj->size = (obj->size*3)/2;
239 //@@if (obj->control_type==CT_AI && Robot_info[obj->id].attack_type)
240 //@@ obj->size = obj->size*3/4;
243 if (obj->id == 65) //special "reactor" robots
244 obj->movement_type = MT_NONE;
246 if (obj->movement_type == MT_PHYSICS) {
247 obj->mtype.phys_info.mass = Robot_info[obj->id].mass;
248 obj->mtype.phys_info.drag = Robot_info[obj->id].drag;
251 else { //Robots taken care of above
253 if ( obj->render_type == RT_POLYOBJ ) {
255 char *name = Save_pof_names[obj->rtype.pobj_info.model_num];
257 for (i=0;i<N_polygon_models;i++)
258 if (!stricmp(Pof_names[i],name)) { //found it!
259 // mprintf((0,"Mapping <%s> to %d (was %d)\n",name,i,obj->rtype.pobj_info.model_num));
260 obj->rtype.pobj_info.model_num = i;
266 if ( obj->type == OBJ_POWERUP ) {
267 if ( obj->id >= N_powerup_types ) {
269 Assert( obj->render_type != RT_POLYOBJ );
271 obj->control_type = CT_POWERUP;
272 obj->size = Powerup_info[obj->id].size;
273 obj->ctype.powerup_info.creation_time = 0;
276 if (Game_mode & GM_NETWORK)
278 if (multi_powerup_is_4pack(obj->id))
280 PowerupsInMine[obj->id-1]+=4;
281 MaxPowerupsAllowed[obj->id-1]+=4;
283 PowerupsInMine[obj->id]++;
284 MaxPowerupsAllowed[obj->id]++;
285 mprintf ((0,"PowerupLimiter: ID=%d\n",obj->id));
286 if (obj->id>MAX_POWERUP_TYPES)
287 mprintf ((1,"POWERUP: Overwriting array bounds!! Get JL!\n"));
293 if ( obj->type == OBJ_WEAPON ) {
294 if ( obj->id >= N_weapon_types ) {
296 Assert( obj->render_type != RT_POLYOBJ );
299 if (obj->id == PMINE_ID) { //make sure pmines have correct values
301 obj->mtype.phys_info.mass = Weapon_info[obj->id].mass;
302 obj->mtype.phys_info.drag = Weapon_info[obj->id].drag;
303 obj->mtype.phys_info.flags |= PF_FREE_SPINNING;
305 // Make sure model number & size are correct...
306 Assert( obj->render_type == RT_POLYOBJ );
308 obj->rtype.pobj_info.model_num = Weapon_info[obj->id].model_num;
309 obj->size = Polygon_models[obj->rtype.pobj_info.model_num].rad;
313 if ( obj->type == OBJ_CNTRLCEN ) {
315 obj->render_type = RT_POLYOBJ;
316 obj->control_type = CT_CNTRLCEN;
318 //@@// Make model number is correct...
319 //@@for (i=0; i<Num_total_object_types; i++ )
320 //@@ if ( ObjType[i] == OL_CONTROL_CENTER ) {
321 //@@ obj->rtype.pobj_info.model_num = ObjId[i];
322 //@@ obj->shields = ObjStrength[i];
329 // Check, and set, strength of reactor
330 for (i=0; i<Num_total_object_types; i++ )
331 if ( ObjType[i]==OL_CONTROL_CENTER && ObjId[i] == obj->id ) {
332 obj->shields = ObjStrength[i];
335 Assert(i < Num_total_object_types); //make sure we found it
340 if ( obj->type == OBJ_PLAYER ) {
343 //Assert(obj == Player);
345 if ( obj == ConsoleObject )
346 init_player_object();
348 if (obj->render_type == RT_POLYOBJ) //recover from Matt's pof file matchup bug
349 obj->rtype.pobj_info.model_num = Player_ship->model_num;
351 //Make sure orient matrix is orthogonal
352 check_and_fix_matrix(&obj->orient);
354 obj->id = Gamesave_num_players++;
357 if (obj->type == OBJ_HOSTAGE) {
359 //@@if (obj->id > N_hostage_types)
362 obj->render_type = RT_HOSTAGE;
363 obj->control_type = CT_POWERUP;
368 //static gs_skip(int len,CFILE *file)
371 // cfseek(file,len,SEEK_CUR);
375 static void gs_write_int(int i,FILE *file)
377 if (fwrite( &i, sizeof(i), 1, file) != 1)
378 Error( "Error reading int in gamesave.c" );
382 static void gs_write_fix(fix f,FILE *file)
384 if (fwrite( &f, sizeof(f), 1, file) != 1)
385 Error( "Error reading fix in gamesave.c" );
389 static void gs_write_short(short s,FILE *file)
391 if (fwrite( &s, sizeof(s), 1, file) != 1)
392 Error( "Error reading short in gamesave.c" );
396 static void gs_write_fixang(fixang f,FILE *file)
398 if (fwrite( &f, sizeof(f), 1, file) != 1)
399 Error( "Error reading fixang in gamesave.c" );
403 static void gs_write_byte(byte b,FILE *file)
405 if (fwrite( &b, sizeof(b), 1, file) != 1)
406 Error( "Error reading byte in gamesave.c" );
410 static void gr_write_vector(vms_vector *v,FILE *file)
412 gs_write_fix(v->x,file);
413 gs_write_fix(v->y,file);
414 gs_write_fix(v->z,file);
417 static void gs_write_matrix(vms_matrix *m,FILE *file)
419 gr_write_vector(&m->rvec,file);
420 gr_write_vector(&m->uvec,file);
421 gr_write_vector(&m->fvec,file);
424 static void gs_write_angvec(vms_angvec *v,FILE *file)
426 gs_write_fixang(v->p,file);
427 gs_write_fixang(v->b,file);
428 gs_write_fixang(v->h,file);
434 extern int multi_powerup_is_4pack(int);
435 //reads one object of the given version from the given file
436 void read_object(object *obj,CFILE *f,int version)
439 obj->type = cfile_read_byte(f);
440 obj->id = cfile_read_byte(f);
442 if (obj->type == OBJ_CNTRLCEN && version<28)
443 obj->id = 0; //used to be only one kind of reactor
445 obj->control_type = cfile_read_byte(f);
446 obj->movement_type = cfile_read_byte(f);
447 obj->render_type = cfile_read_byte(f);
448 obj->flags = cfile_read_byte(f);
450 obj->segnum = cfile_read_short(f);
451 obj->attached_obj = -1;
453 cfile_read_vector(&obj->pos,f);
454 cfile_read_matrix(&obj->orient,f);
456 obj->size = cfile_read_fix(f);
457 obj->shields = cfile_read_fix(f);
459 cfile_read_vector(&obj->last_pos,f);
461 obj->contains_type = cfile_read_byte(f);
462 obj->contains_id = cfile_read_byte(f);
463 obj->contains_count = cfile_read_byte(f);
465 switch (obj->movement_type) {
469 cfile_read_vector(&obj->mtype.phys_info.velocity,f);
470 cfile_read_vector(&obj->mtype.phys_info.thrust,f);
472 obj->mtype.phys_info.mass = cfile_read_fix(f);
473 obj->mtype.phys_info.drag = cfile_read_fix(f);
474 obj->mtype.phys_info.brakes = cfile_read_fix(f);
476 cfile_read_vector(&obj->mtype.phys_info.rotvel,f);
477 cfile_read_vector(&obj->mtype.phys_info.rotthrust,f);
479 obj->mtype.phys_info.turnroll = cfile_read_fixang(f);
480 obj->mtype.phys_info.flags = cfile_read_short(f);
486 cfile_read_vector(&obj->mtype.spin_rate,f);
496 switch (obj->control_type) {
501 obj->ctype.ai_info.behavior = cfile_read_byte(f);
503 for (i=0;i<MAX_AI_FLAGS;i++)
504 obj->ctype.ai_info.flags[i] = cfile_read_byte(f);
506 obj->ctype.ai_info.hide_segment = cfile_read_short(f);
507 obj->ctype.ai_info.hide_index = cfile_read_short(f);
508 obj->ctype.ai_info.path_length = cfile_read_short(f);
509 obj->ctype.ai_info.cur_path_index = cfile_read_short(f);
512 cfile_read_short(f); // obj->ctype.ai_info.follow_path_start_seg =
513 cfile_read_short(f); // obj->ctype.ai_info.follow_path_end_seg =
521 obj->ctype.expl_info.spawn_time = cfile_read_fix(f);
522 obj->ctype.expl_info.delete_time = cfile_read_fix(f);
523 obj->ctype.expl_info.delete_objnum = cfile_read_short(f);
524 obj->ctype.expl_info.next_attach = obj->ctype.expl_info.prev_attach = obj->ctype.expl_info.attach_parent = -1;
530 //do I really need to read these? Are they even saved to disk?
532 obj->ctype.laser_info.parent_type = cfile_read_short(f);
533 obj->ctype.laser_info.parent_num = cfile_read_short(f);
534 obj->ctype.laser_info.parent_signature = cfile_read_int(f);
540 obj->ctype.light_info.intensity = cfile_read_fix(f);
546 obj->ctype.powerup_info.count = cfile_read_int(f);
548 obj->ctype.powerup_info.count = 1;
550 if (obj->id == POW_VULCAN_WEAPON)
551 obj->ctype.powerup_info.count = VULCAN_WEAPON_AMMO_AMOUNT;
553 if (obj->id == POW_GAUSS_WEAPON)
554 obj->ctype.powerup_info.count = VULCAN_WEAPON_AMMO_AMOUNT;
556 if (obj->id == POW_OMEGA_WEAPON)
557 obj->ctype.powerup_info.count = MAX_OMEGA_CHARGE;
567 case CT_SLEW: //the player is generally saved as slew
581 switch (obj->render_type) {
590 obj->rtype.pobj_info.model_num = cfile_read_int(f);
592 for (i=0;i<MAX_SUBMODELS;i++)
593 cfile_read_angvec(&obj->rtype.pobj_info.anim_angles[i],f);
595 obj->rtype.pobj_info.subobj_flags = cfile_read_int(f);
597 tmo = cfile_read_int(f);
600 obj->rtype.pobj_info.tmap_override = tmo;
603 obj->rtype.pobj_info.tmap_override = -1;
605 int xlated_tmo = tmap_xlate_table[tmo];
606 if (xlated_tmo < 0) {
607 mprintf( (0, "Couldn't find texture for demo object, model_num = %d\n", obj->rtype.pobj_info.model_num));
611 obj->rtype.pobj_info.tmap_override = xlated_tmo;
615 obj->rtype.pobj_info.alt_textures = 0;
620 case RT_WEAPON_VCLIP:
625 obj->rtype.vclip_info.vclip_num = cfile_read_int(f);
626 obj->rtype.vclip_info.frametime = cfile_read_fix(f);
627 obj->rtype.vclip_info.framenum = cfile_read_byte(f);
643 //writes one object to the given file
644 void write_object(object *obj,FILE *f)
646 gs_write_byte(obj->type,f);
647 gs_write_byte(obj->id,f);
649 gs_write_byte(obj->control_type,f);
650 gs_write_byte(obj->movement_type,f);
651 gs_write_byte(obj->render_type,f);
652 gs_write_byte(obj->flags,f);
654 gs_write_short(obj->segnum,f);
656 gr_write_vector(&obj->pos,f);
657 gs_write_matrix(&obj->orient,f);
659 gs_write_fix(obj->size,f);
660 gs_write_fix(obj->shields,f);
662 gr_write_vector(&obj->last_pos,f);
664 gs_write_byte(obj->contains_type,f);
665 gs_write_byte(obj->contains_id,f);
666 gs_write_byte(obj->contains_count,f);
668 switch (obj->movement_type) {
672 gr_write_vector(&obj->mtype.phys_info.velocity,f);
673 gr_write_vector(&obj->mtype.phys_info.thrust,f);
675 gs_write_fix(obj->mtype.phys_info.mass,f);
676 gs_write_fix(obj->mtype.phys_info.drag,f);
677 gs_write_fix(obj->mtype.phys_info.brakes,f);
679 gr_write_vector(&obj->mtype.phys_info.rotvel,f);
680 gr_write_vector(&obj->mtype.phys_info.rotthrust,f);
682 gs_write_fixang(obj->mtype.phys_info.turnroll,f);
683 gs_write_short(obj->mtype.phys_info.flags,f);
689 gr_write_vector(&obj->mtype.spin_rate,f);
699 switch (obj->control_type) {
704 gs_write_byte(obj->ctype.ai_info.behavior,f);
706 for (i=0;i<MAX_AI_FLAGS;i++)
707 gs_write_byte(obj->ctype.ai_info.flags[i],f);
709 gs_write_short(obj->ctype.ai_info.hide_segment,f);
710 gs_write_short(obj->ctype.ai_info.hide_index,f);
711 gs_write_short(obj->ctype.ai_info.path_length,f);
712 gs_write_short(obj->ctype.ai_info.cur_path_index,f);
714 // -- unused! mk, 08/13/95 -- gs_write_short(obj->ctype.ai_info.follow_path_start_seg,f);
715 // -- unused! mk, 08/13/95 -- gs_write_short(obj->ctype.ai_info.follow_path_end_seg,f);
722 gs_write_fix(obj->ctype.expl_info.spawn_time,f);
723 gs_write_fix(obj->ctype.expl_info.delete_time,f);
724 gs_write_short(obj->ctype.expl_info.delete_objnum,f);
730 //do I really need to write these objects?
732 gs_write_short(obj->ctype.laser_info.parent_type,f);
733 gs_write_short(obj->ctype.laser_info.parent_num,f);
734 gs_write_int(obj->ctype.laser_info.parent_signature,f);
740 gs_write_fix(obj->ctype.light_info.intensity,f);
745 gs_write_int(obj->ctype.powerup_info.count,f);
753 case CT_SLEW: //the player is generally saved as slew
757 break; //control center object.
767 switch (obj->render_type) {
776 gs_write_int(obj->rtype.pobj_info.model_num,f);
778 for (i=0;i<MAX_SUBMODELS;i++)
779 gs_write_angvec(&obj->rtype.pobj_info.anim_angles[i],f);
781 gs_write_int(obj->rtype.pobj_info.subobj_flags,f);
783 gs_write_int(obj->rtype.pobj_info.tmap_override,f);
788 case RT_WEAPON_VCLIP:
793 gs_write_int(obj->rtype.vclip_info.vclip_num,f);
794 gs_write_fix(obj->rtype.vclip_info.frametime,f);
795 gs_write_byte(obj->rtype.vclip_info.framenum,f);
811 int robot_flags; // Up to 32 different robots
812 fix hit_points; // How hard it is to destroy this particular matcen
813 fix interval; // Interval between materialogrifizations
814 short segnum; // Segment this is attached to.
815 short fuelcen_num; // Index in fuelcen array.
818 extern int remove_trigger_num(int trigger_num);
820 // -----------------------------------------------------------------------------
822 // Loads all the relevant data for a level.
823 // If level != -1, it loads the filename with extension changed to .min
824 // Otherwise it loads the appropriate level mine.
825 // returns 0=everything ok, 1=old version, -1=error
826 int load_game_data(CFILE *LoadFile)
831 start_offset = cftell(LoadFile);
833 //===================== READ FILE INFO ========================
835 // Set default values
836 game_fileinfo.level = -1;
837 game_fileinfo.player_offset = -1;
838 game_fileinfo.player_sizeof = sizeof(player);
839 game_fileinfo.object_offset = -1;
840 game_fileinfo.object_howmany = 0;
841 game_fileinfo.object_sizeof = sizeof(object);
842 game_fileinfo.walls_offset = -1;
843 game_fileinfo.walls_howmany = 0;
844 game_fileinfo.walls_sizeof = sizeof(wall);
845 game_fileinfo.doors_offset = -1;
846 game_fileinfo.doors_howmany = 0;
847 game_fileinfo.doors_sizeof = sizeof(active_door);
848 game_fileinfo.triggers_offset = -1;
849 game_fileinfo.triggers_howmany = 0;
850 game_fileinfo.triggers_sizeof = sizeof(trigger);
851 game_fileinfo.control_offset = -1;
852 game_fileinfo.control_howmany = 0;
853 game_fileinfo.control_sizeof = sizeof(control_center_triggers);
854 game_fileinfo.matcen_offset = -1;
855 game_fileinfo.matcen_howmany = 0;
856 game_fileinfo.matcen_sizeof = sizeof(matcen_info);
858 game_fileinfo.dl_indices_offset = -1;
859 game_fileinfo.dl_indices_howmany = 0;
860 game_fileinfo.dl_indices_sizeof = sizeof(dl_index);
862 game_fileinfo.delta_light_offset = -1;
863 game_fileinfo.delta_light_howmany = 0;
864 game_fileinfo.delta_light_sizeof = sizeof(delta_light);
866 // Read in game_top_fileinfo to get size of saved fileinfo.
868 if (cfseek( LoadFile, start_offset, SEEK_SET ))
869 Error( "Error seeking in gamesave.c" );
871 // if (cfread( &game_top_fileinfo, sizeof(game_top_fileinfo), 1, LoadFile) != 1)
872 // Error( "Error reading game_top_fileinfo in gamesave.c" );
874 game_top_fileinfo.fileinfo_signature = cfile_read_short(LoadFile);
875 game_top_fileinfo.fileinfo_version = cfile_read_short(LoadFile);
876 game_top_fileinfo.fileinfo_sizeof = cfile_read_int(LoadFile);
879 if (game_top_fileinfo.fileinfo_signature != 0x6705)
882 // Check version number
883 if (game_top_fileinfo.fileinfo_version < GAME_COMPATIBLE_VERSION )
886 // Now, Read in the fileinfo
887 if (cfseek( LoadFile, start_offset, SEEK_SET ))
888 Error( "Error seeking to game_fileinfo in gamesave.c" );
890 // if (cfread( &game_fileinfo, game_top_fileinfo.fileinfo_sizeof, 1, LoadFile )!=1)
891 // Error( "Error reading game_fileinfo in gamesave.c" );
893 game_fileinfo.fileinfo_signature = cfile_read_short(LoadFile);
894 game_fileinfo.fileinfo_version = cfile_read_short(LoadFile);
895 game_fileinfo.fileinfo_sizeof = cfile_read_int(LoadFile);
897 game_fileinfo.mine_filename[i] = cfile_read_byte(LoadFile);
898 game_fileinfo.level = cfile_read_int(LoadFile);
899 game_fileinfo.player_offset = cfile_read_int(LoadFile); // Player info
900 game_fileinfo.player_sizeof = cfile_read_int(LoadFile);
901 game_fileinfo.object_offset = cfile_read_int(LoadFile); // Object info
902 game_fileinfo.object_howmany = cfile_read_int(LoadFile);
903 game_fileinfo.object_sizeof = cfile_read_int(LoadFile);
904 game_fileinfo.walls_offset = cfile_read_int(LoadFile);
905 game_fileinfo.walls_howmany = cfile_read_int(LoadFile);
906 game_fileinfo.walls_sizeof = cfile_read_int(LoadFile);
907 game_fileinfo.doors_offset = cfile_read_int(LoadFile);
908 game_fileinfo.doors_howmany = cfile_read_int(LoadFile);
909 game_fileinfo.doors_sizeof = cfile_read_int(LoadFile);
910 game_fileinfo.triggers_offset = cfile_read_int(LoadFile);
911 game_fileinfo.triggers_howmany = cfile_read_int(LoadFile);
912 game_fileinfo.triggers_sizeof = cfile_read_int(LoadFile);
913 game_fileinfo.links_offset = cfile_read_int(LoadFile);
914 game_fileinfo.links_howmany = cfile_read_int(LoadFile);
915 game_fileinfo.links_sizeof = cfile_read_int(LoadFile);
916 game_fileinfo.control_offset = cfile_read_int(LoadFile);
917 game_fileinfo.control_howmany = cfile_read_int(LoadFile);
918 game_fileinfo.control_sizeof = cfile_read_int(LoadFile);
919 game_fileinfo.matcen_offset = cfile_read_int(LoadFile);
920 game_fileinfo.matcen_howmany = cfile_read_int(LoadFile);
921 game_fileinfo.matcen_sizeof = cfile_read_int(LoadFile);
923 if (game_top_fileinfo.fileinfo_version >= 29) {
924 game_fileinfo.dl_indices_offset = cfile_read_int(LoadFile);
925 game_fileinfo.dl_indices_howmany = cfile_read_int(LoadFile);
926 game_fileinfo.dl_indices_sizeof = cfile_read_int(LoadFile);
928 game_fileinfo.delta_light_offset = cfile_read_int(LoadFile);
929 game_fileinfo.delta_light_howmany = cfile_read_int(LoadFile);
930 game_fileinfo.delta_light_sizeof = cfile_read_int(LoadFile);
933 if (game_top_fileinfo.fileinfo_version >= 14) { //load mine filename
934 //@@char *p=Current_level_name;
935 //@@//must do read one char at a time, since no cfgets()
936 //@@do *p = cfgetc(LoadFile); while (*p++!=0);
938 cfgets(Current_level_name,sizeof(Current_level_name),LoadFile);
940 if (Current_level_name[strlen(Current_level_name)-1] == '\n')
941 Current_level_name[strlen(Current_level_name)-1] = 0;
944 Current_level_name[0]=0;
946 if (game_top_fileinfo.fileinfo_version >= 19) { //load pof names
947 // cfread(&N_save_pof_names,2,1,LoadFile);
948 N_save_pof_names = cfile_read_short(LoadFile);
949 cfread(Save_pof_names,N_save_pof_names,FILENAME_LEN,LoadFile);
952 //===================== READ PLAYER INFO ==========================
953 Object_next_signature = 0;
955 //===================== READ OBJECT INFO ==========================
957 Gamesave_num_org_robots = 0;
958 Gamesave_num_players = 0;
960 if (game_fileinfo.object_offset > -1) {
961 if (cfseek( LoadFile, game_fileinfo.object_offset, SEEK_SET ))
962 Error( "Error seeking to object_offset in gamesave.c" );
964 for (i=0;i<game_fileinfo.object_howmany;i++) {
966 read_object(&Objects[i],LoadFile,game_top_fileinfo.fileinfo_version);
968 Objects[i].signature = Object_next_signature++;
969 verify_object( &Objects[i] );
974 //===================== READ WALL INFO ============================
976 if (game_fileinfo.walls_offset > -1)
979 if (!cfseek( LoadFile, game_fileinfo.walls_offset,SEEK_SET )) {
980 for (i=0;i<game_fileinfo.walls_howmany;i++) {
982 if (game_top_fileinfo.fileinfo_version >= 20)
983 wall_read(&Walls[i], LoadFile); // v20 walls and up.
984 else if (game_top_fileinfo.fileinfo_version >= 17) {
987 v19_wall_read(&w, LoadFile);
989 Walls[i].segnum = w.segnum;
990 Walls[i].sidenum = w.sidenum;
991 Walls[i].linked_wall = w.linked_wall;
993 Walls[i].type = w.type;
994 Walls[i].flags = w.flags;
995 Walls[i].hps = w.hps;
996 Walls[i].trigger = w.trigger;
997 Walls[i].clip_num = w.clip_num;
998 Walls[i].keys = w.keys;
1000 Walls[i].state = WALL_DOOR_CLOSED;
1004 v16_wall_read(&w, LoadFile);
1006 Walls[i].segnum = Walls[i].sidenum = Walls[i].linked_wall = -1;
1008 Walls[i].type = w.type;
1009 Walls[i].flags = w.flags;
1010 Walls[i].hps = w.hps;
1011 Walls[i].trigger = w.trigger;
1012 Walls[i].clip_num = w.clip_num;
1013 Walls[i].keys = w.keys;
1020 //===================== READ DOOR INFO ============================
1022 if (game_fileinfo.doors_offset > -1)
1024 if (!cfseek( LoadFile, game_fileinfo.doors_offset,SEEK_SET )) {
1026 for (i=0;i<game_fileinfo.doors_howmany;i++) {
1028 if (game_top_fileinfo.fileinfo_version >= 20)
1029 active_door_read(&ActiveDoors[i], LoadFile); // version 20 and up
1034 v19_door_read(&d, LoadFile);
1036 ActiveDoors[i].n_parts = d.n_parts;
1038 for (p=0;p<d.n_parts;p++) {
1041 cseg = Segments[d.seg[p]].children[d.side[p]];
1042 cside = find_connect_side(&Segments[d.seg[p]],&Segments[cseg]);
1044 ActiveDoors[i].front_wallnum[p] = Segments[d.seg[p]].sides[d.side[p]].wall_num;
1045 ActiveDoors[i].back_wallnum[p] = Segments[cseg].sides[cside].wall_num;
1053 //==================== READ TRIGGER INFO ==========================
1056 // for MACINTOSH -- assume all triggers >= verion 31 triggers.
1058 if (game_fileinfo.triggers_offset > -1)
1060 if (!cfseek( LoadFile, game_fileinfo.triggers_offset,SEEK_SET )) {
1061 for (i=0;i<game_fileinfo.triggers_howmany;i++)
1062 if (game_top_fileinfo.fileinfo_version < 31) {
1068 if (game_top_fileinfo.fileinfo_version < 30) {
1072 v29_trigger_read(&trig29, LoadFile);
1074 trig.flags = trig29.flags;
1075 trig.num_links = trig29.num_links;
1076 trig.num_links = trig29.num_links;
1077 trig.value = trig29.value;
1078 trig.time = trig29.time;
1080 for (t=0;t<trig.num_links;t++) {
1081 trig.seg[t] = trig29.seg[t];
1082 trig.side[t] = trig29.side[t];
1086 v30_trigger_read(&trig, LoadFile);
1088 //Assert(trig.flags & TRIGGER_ON);
1089 trig.flags &= ~TRIGGER_ON;
1091 if (trig.flags & TRIGGER_CONTROL_DOORS)
1092 type = TT_OPEN_DOOR;
1093 else if (trig.flags & TRIGGER_SHIELD_DAMAGE)
1095 else if (trig.flags & TRIGGER_ENERGY_DRAIN)
1097 else if (trig.flags & TRIGGER_EXIT)
1099 else if (trig.flags & TRIGGER_ONE_SHOT)
1101 else if (trig.flags & TRIGGER_MATCEN)
1103 else if (trig.flags & TRIGGER_ILLUSION_OFF)
1104 type = TT_ILLUSION_OFF;
1105 else if (trig.flags & TRIGGER_SECRET_EXIT)
1106 type = TT_SECRET_EXIT;
1107 else if (trig.flags & TRIGGER_ILLUSION_ON)
1108 type = TT_ILLUSION_ON;
1109 else if (trig.flags & TRIGGER_UNLOCK_DOORS)
1110 type = TT_UNLOCK_DOOR;
1111 else if (trig.flags & TRIGGER_OPEN_WALL)
1112 type = TT_OPEN_WALL;
1113 else if (trig.flags & TRIGGER_CLOSE_WALL)
1114 type = TT_CLOSE_WALL;
1115 else if (trig.flags & TRIGGER_ILLUSORY_WALL)
1116 type = TT_ILLUSORY_WALL;
1120 Triggers[i].type = type;
1121 Triggers[i].flags = 0;
1122 Triggers[i].num_links = trig.num_links;
1123 Triggers[i].num_links = trig.num_links;
1124 Triggers[i].value = trig.value;
1125 Triggers[i].time = trig.time;
1127 for (t=0;t<trig.num_links;t++) {
1128 Triggers[i].seg[t] = trig.seg[t];
1129 Triggers[i].side[t] = trig.side[t];
1133 trigger_read(&Triggers[i], LoadFile);
1137 //================ READ CONTROL CENTER TRIGGER INFO ===============
1139 if (game_fileinfo.control_offset > -1)
1140 if (!cfseek( LoadFile, game_fileinfo.control_offset,SEEK_SET ))
1141 for (i=0;i<game_fileinfo.control_howmany;i++)
1142 control_center_triggers_read(&ControlCenterTriggers, LoadFile);
1145 //================ READ MATERIALOGRIFIZATIONATORS INFO ===============
1147 if (game_fileinfo.matcen_offset > -1)
1150 if (!cfseek( LoadFile, game_fileinfo.matcen_offset,SEEK_SET )) {
1151 // mprintf((0, "Reading %i materialization centers.\n", game_fileinfo.matcen_howmany));
1152 for (i=0;i<game_fileinfo.matcen_howmany;i++) {
1153 if (game_top_fileinfo.fileinfo_version < 27) {
1155 Assert(game_fileinfo.matcen_sizeof == sizeof(m));
1156 if (cfread(&m, game_fileinfo.matcen_sizeof,1,LoadFile)!=1)
1157 Error( "Error reading RobotCenters in gamesave.c");
1158 RobotCenters[i].robot_flags[0] = m.robot_flags;
1159 RobotCenters[i].robot_flags[1] = 0;
1160 RobotCenters[i].hit_points = m.hit_points;
1161 RobotCenters[i].interval = m.interval;
1162 RobotCenters[i].segnum = m.segnum;
1163 RobotCenters[i].fuelcen_num = m.fuelcen_num;
1166 Assert(game_fileinfo.matcen_sizeof == sizeof(RobotCenters[i]));
1168 if (cfread(&RobotCenters[i], game_fileinfo.matcen_sizeof,1,LoadFile)!=1)
1169 Error( "Error reading RobotCenters in gamesave.c");
1171 RobotCenters[i].robot_flags[0] = cfile_read_int(LoadFile);
1172 RobotCenters[i].robot_flags[1] = cfile_read_int(LoadFile);
1173 RobotCenters[i].hit_points = cfile_read_fix(LoadFile);
1174 RobotCenters[i].interval = cfile_read_fix(LoadFile);
1175 RobotCenters[i].segnum = cfile_read_short(LoadFile);
1176 RobotCenters[i].fuelcen_num = cfile_read_short(LoadFile);
1180 // Set links in RobotCenters to Station array
1182 for (j=0; j<=Highest_segment_index; j++)
1183 if (Segment2s[j].special == SEGMENT_IS_ROBOTMAKER)
1184 if (Segment2s[j].matcen_num == i)
1185 RobotCenters[i].fuelcen_num = Segment2s[j].value;
1187 // mprintf((0, " %i: flags = %08x\n", i, RobotCenters[i].robot_flags));
1193 //================ READ DL_INDICES INFO ===============
1195 Num_static_lights = 0;
1197 if (game_fileinfo.dl_indices_offset > -1) {
1200 if (!cfseek( LoadFile, game_fileinfo.dl_indices_offset, SEEK_SET )) {
1201 Num_static_lights = game_fileinfo.dl_indices_howmany;
1202 for (i=0; i<game_fileinfo.dl_indices_howmany; i++) {
1203 if (game_top_fileinfo.fileinfo_version < 29) {
1204 mprintf((0, "Warning: Old mine version. Not reading Dl_indices info.\n"));
1205 Int3(); //shouldn't be here!!!
1208 if (cfread(&Dl_indices[i], game_fileinfo.dl_indices_sizeof, 1, LoadFile) != 1)
1209 Error( "Error reading Dl_indices in gamesave.c");
1211 Dl_indices[i].segnum = cfile_read_short(LoadFile);
1212 Dl_indices[i].sidenum = cfile_read_byte(LoadFile);
1213 Dl_indices[i].count = cfile_read_byte(LoadFile);
1214 Dl_indices[i].index = cfile_read_short(LoadFile);
1222 // Indicate that no light has been subtracted from any vertices.
1223 clear_light_subtracted();
1225 //================ READ DELTA LIGHT INFO ===============
1227 if (game_fileinfo.delta_light_offset > -1) {
1230 if (!cfseek( LoadFile, game_fileinfo.delta_light_offset, SEEK_SET )) {
1231 for (i=0; i<game_fileinfo.delta_light_howmany; i++) {
1232 if (game_top_fileinfo.fileinfo_version < 29) {
1233 mprintf((0, "Warning: Old mine version. Not reading delta light info.\n"));
1236 if (cfread(&Delta_lights[i], game_fileinfo.delta_light_sizeof, 1, LoadFile) != 1)
1237 Error( "Error reading Delta Lights in gamesave.c");
1239 Delta_lights[i].segnum = cfile_read_short(LoadFile);
1240 Delta_lights[i].sidenum = cfile_read_byte(LoadFile);
1241 Delta_lights[i].dummy = cfile_read_byte(LoadFile);
1242 Delta_lights[i].vert_light[0] = cfile_read_byte(LoadFile);
1243 Delta_lights[i].vert_light[1] = cfile_read_byte(LoadFile);
1244 Delta_lights[i].vert_light[2] = cfile_read_byte(LoadFile);
1245 Delta_lights[i].vert_light[3] = cfile_read_byte(LoadFile);
1253 //========================= UPDATE VARIABLES ======================
1255 reset_objects(game_fileinfo.object_howmany);
1257 for (i=0; i<MAX_OBJECTS; i++) {
1258 Objects[i].next = Objects[i].prev = -1;
1259 if (Objects[i].type != OBJ_NONE) {
1260 int objsegnum = Objects[i].segnum;
1262 if (objsegnum > Highest_segment_index) //bogus object
1263 Objects[i].type = OBJ_NONE;
1265 Objects[i].segnum = -1; //avoid Assert()
1266 obj_link(i,objsegnum);
1271 clear_transient_objects(1); //1 means clear proximity bombs
1273 // Make sure non-transparent doors are set correctly.
1274 for (i=0; i< Num_segments; i++)
1275 for (j=0;j<MAX_SIDES_PER_SEGMENT;j++) {
1276 side *sidep = &Segments[i].sides[j];
1277 if ((sidep->wall_num != -1) && (Walls[sidep->wall_num].clip_num != -1)) {
1278 //mprintf((0, "Checking Wall %d\n", Segments[i].sides[j].wall_num));
1279 if (WallAnims[Walls[sidep->wall_num].clip_num].flags & WCF_TMAP1) {
1280 //mprintf((0, "Fixing non-transparent door.\n"));
1281 sidep->tmap_num = WallAnims[Walls[sidep->wall_num].clip_num].frames[0];
1282 sidep->tmap_num2 = 0;
1288 Num_walls = game_fileinfo.walls_howmany;
1291 Num_open_doors = game_fileinfo.doors_howmany;
1292 Num_triggers = game_fileinfo.triggers_howmany;
1294 //go through all walls, killing references to invalid triggers
1295 for (i=0;i<Num_walls;i++)
1296 if (Walls[i].trigger >= Num_triggers) {
1297 mprintf((0,"Removing reference to invalid trigger %d from wall %d\n",Walls[i].trigger,i));
1298 Walls[i].trigger = -1; //kill trigger
1301 //go through all triggers, killing unused ones
1302 for (i=0;i<Num_triggers;) {
1305 // Find which wall this trigger is connected to.
1306 for (w=0; w<Num_walls; w++)
1307 if (Walls[w].trigger == i)
1311 if (w == Num_walls) {
1312 mprintf((0,"Removing unreferenced trigger %d\n",i));
1313 remove_trigger_num(i);
1320 // MK, 10/17/95: Make walls point back at the triggers that control them.
1321 // Go through all triggers, stuffing controlling_trigger field in Walls.
1324 for (i=0; i<Num_walls; i++)
1325 Walls[i].controlling_trigger = -1;
1327 for (t=0; t<Num_triggers; t++) {
1329 for (l=0; l<Triggers[t].num_links; l++) {
1330 int seg_num, side_num, wall_num;
1332 seg_num = Triggers[t].seg[l];
1333 side_num = Triggers[t].side[l];
1334 wall_num = Segments[seg_num].sides[side_num].wall_num;
1336 // -- if (Walls[wall_num].controlling_trigger != -1)
1339 //check to see that if a trigger requires a wall that it has one,
1340 //and if it requires a matcen that it has one
1342 if (Triggers[t].type == TT_MATCEN) {
1343 if (Segment2s[seg_num].special != SEGMENT_IS_ROBOTMAKER)
1344 Int3(); //matcen trigger doesn't point to matcen
1346 else if (Triggers[t].type != TT_LIGHT_OFF && Triggers[t].type != TT_LIGHT_ON) { //light triggers don't require walls
1348 Int3(); // This is illegal. This trigger requires a wall
1350 Walls[wall_num].controlling_trigger = t;
1356 Num_robot_centers = game_fileinfo.matcen_howmany;
1358 //fix old wall structs
1359 if (game_top_fileinfo.fileinfo_version < 17) {
1360 int segnum,sidenum,wallnum;
1362 for (segnum=0; segnum<=Highest_segment_index; segnum++)
1363 for (sidenum=0;sidenum<6;sidenum++)
1364 if ((wallnum=Segments[segnum].sides[sidenum].wall_num) != -1) {
1365 Walls[wallnum].segnum = segnum;
1366 Walls[wallnum].sidenum = sidenum;
1373 for (sidenum=0; sidenum<6; sidenum++) {
1374 int wallnum = Segments[Highest_segment_index].sides[sidenum].wall_num;
1376 if ((Walls[wallnum].segnum != Highest_segment_index) || (Walls[wallnum].sidenum != sidenum))
1377 Int3(); // Error. Bogus walls in this segment.
1378 // Consult Yuan or Mike.
1383 //create_local_segment_data();
1391 if (game_top_fileinfo.fileinfo_version < GAME_VERSION && !(game_top_fileinfo.fileinfo_version==25 && GAME_VERSION==26))
1392 return 1; //means old version
1398 int check_segment_connections(void);
1400 extern void set_ambient_sound_flags(void);
1402 // -----------------------------------------------------------------------------
1403 //loads from an already-open file
1404 // returns 0=everything ok, 1=old version, -1=error
1405 int load_mine_data(CFILE *LoadFile);
1406 int load_mine_data_compiled(CFILE *LoadFile);
1408 #define LEVEL_FILE_VERSION 8
1409 //1 -> 2 add palette name
1410 //2 -> 3 add control center explosion time
1411 //3 -> 4 add reactor strength
1412 //4 -> 5 killed hostage text stuff
1413 //5 -> 6 added Secret_return_segment and Secret_return_orient
1414 //6 -> 7 added flickering lights
1415 //7 -> 8 made version 8 to be not compatible with D2 1.0 & 1.1
1418 char *Level_being_loaded=NULL;
1422 extern void ncache_flush();
1425 extern int HoardEquipped();
1427 extern int Slide_segs_computed;
1429 int no_old_level_file_error=0;
1431 //loads a level (.LVL) file from disk
1432 //returns 0 if success, else error code
1433 int load_level(char * filename_passed)
1436 int use_compiled_level=1;
1440 int sig,version,minedata_offset,gamedata_offset;
1441 int mine_err,game_err;
1446 Slide_segs_computed = 0;
1449 if (Game_mode & GM_NETWORK)
1451 for (i=0;i<MAX_POWERUP_TYPES;i++)
1453 MaxPowerupsAllowed[i]=0;
1454 PowerupsInMine[i]=0;
1464 Level_being_loaded = filename_passed;
1467 strcpy(filename,filename_passed);
1471 //if we have the editor, try the LVL first, no matter what was passed.
1472 //if we don't have an LVL, try RDL
1473 //if we don't have the editor, we just use what was passed
1475 change_filename_extension(filename,filename_passed,".LVL");
1476 use_compiled_level = 0;
1478 if (!cfexist(filename)) {
1479 change_filename_extension(filename,filename,".RL2");
1480 use_compiled_level = 1;
1484 LoadFile = cfopen( filename, "rb" );
1488 mprintf((0,"Can't open level file <%s>\n", filename));
1491 Error("Can't open file <%s>\n",filename);
1495 strcpy( Gamesave_current_filename, filename );
1498 // if ( Newdemo_state == ND_STATE_RECORDING )
1499 // newdemo_record_start_demo();
1502 sig = cfile_read_int(LoadFile);
1503 version = cfile_read_int(LoadFile);
1504 minedata_offset = cfile_read_int(LoadFile);
1505 gamedata_offset = cfile_read_int(LoadFile);
1507 Assert(sig == MAKE_SIG('P','L','V','L'));
1509 if (version >= 8) { //read dummy data
1511 if (HoardEquipped())
1513 cfile_read_int(LoadFile);
1514 cfile_read_short(LoadFile);
1515 cfile_read_byte(LoadFile);
1519 // NOTE LINK TO ABOVE!
1520 Error("This level requires the Vertigo Enhanced version of D2.");
1525 cfile_read_int(LoadFile); //was hostagetext_offset
1528 cfgets(Current_level_palette,sizeof(Current_level_palette),LoadFile);
1529 if (Current_level_palette[strlen(Current_level_palette)-1] == '\n')
1530 Current_level_palette[strlen(Current_level_palette)-1] = 0;
1534 Base_control_center_explosion_time = cfile_read_int(LoadFile);
1536 Base_control_center_explosion_time = DEFAULT_CONTROL_CENTER_EXPLOSION_TIME;
1539 Reactor_strength = cfile_read_int(LoadFile);
1541 Reactor_strength = -1; //use old defaults
1544 Num_flickering_lights = cfile_read_int(LoadFile);
1546 Assert((Num_flickering_lights >= 0) && (Num_flickering_lights < MAX_FLICKERING_LIGHTS));
1547 for (i = 0; i < Num_flickering_lights; i++)
1549 Flickering_lights[i].segnum = cfile_read_short(LoadFile);
1550 Flickering_lights[i].sidenum = cfile_read_short(LoadFile);
1551 Flickering_lights[i].mask = cfile_read_int(LoadFile);
1552 Flickering_lights[i].timer = cfile_read_fix(LoadFile);
1553 Flickering_lights[i].delay = cfile_read_fix(LoadFile);
1556 cfread(Flickering_lights,sizeof(*Flickering_lights),Num_flickering_lights,LoadFile);
1560 Num_flickering_lights = 0;
1562 if (version <= 1 || Current_level_palette[0]==0)
1563 strcpy(Current_level_palette,"groupa.256");
1566 Secret_return_segment = 0;
1567 Secret_return_orient.rvec.x = F1_0; Secret_return_orient.rvec.y = 0; Secret_return_orient.rvec.z = 0;
1568 Secret_return_orient.fvec.x = 0; Secret_return_orient.fvec.y = F1_0; Secret_return_orient.fvec.z = 0;
1569 Secret_return_orient.uvec.x = 0; Secret_return_orient.uvec.y = 0; Secret_return_orient.uvec.z = F1_0;
1571 Secret_return_segment = cfile_read_int(LoadFile);
1572 Secret_return_orient.rvec.x = cfile_read_int(LoadFile);
1573 Secret_return_orient.rvec.y = cfile_read_int(LoadFile);
1574 Secret_return_orient.rvec.z = cfile_read_int(LoadFile);
1575 Secret_return_orient.fvec.x = cfile_read_int(LoadFile);
1576 Secret_return_orient.fvec.y = cfile_read_int(LoadFile);
1577 Secret_return_orient.fvec.z = cfile_read_int(LoadFile);
1578 Secret_return_orient.uvec.x = cfile_read_int(LoadFile);
1579 Secret_return_orient.uvec.y = cfile_read_int(LoadFile);
1580 Secret_return_orient.uvec.z = cfile_read_int(LoadFile);
1583 cfseek(LoadFile,minedata_offset,SEEK_SET);
1585 if (!use_compiled_level) {
1586 mine_err = load_mine_data(LoadFile);
1587 #if 0 //dunno - 3rd party stuff?
1588 // Compress all uv coordinates in mine, improves texmap precision. --MK, 02/19/96
1589 compress_uv_coordinates_all();
1593 //NOTE LINK TO ABOVE!!
1594 mine_err = load_mine_data_compiled(LoadFile);
1596 if (mine_err == -1) { //error!!
1601 cfseek(LoadFile,gamedata_offset,SEEK_SET);
1602 game_err = load_game_data(LoadFile);
1604 if (game_err == -1) { //error!!
1609 //======================== CLOSE FILE =============================
1611 cfclose( LoadFile );
1613 set_ambient_sound_flags();
1616 write_game_text_file(filename);
1617 if (Errors_in_mine) {
1618 if (is_real_level(filename)) {
1619 char ErrorMessage[200];
1621 sprintf( ErrorMessage, "Warning: %i errors in %s!\n", Errors_in_mine, Level_being_loaded );
1623 gr_palette_load(gr_palette);
1624 nm_messagebox( NULL, 1, "Continue", ErrorMessage );
1627 mprintf((1, "Error: %i errors in %s.\n", Errors_in_mine, Level_being_loaded));
1632 //If an old version, ask the use if he wants to save as new version
1633 if (!no_old_level_file_error && (Function_mode == FMODE_EDITOR) && (((LEVEL_FILE_VERSION>3) && version<LEVEL_FILE_VERSION) || mine_err==1 || game_err==1)) {
1634 char ErrorMessage[200];
1636 sprintf( ErrorMessage,
1637 "You just loaded a old version\n"
1638 "level. Would you like to save\n"
1639 "it as a current version level?");
1642 gr_palette_load(gr_palette);
1643 if (nm_messagebox( NULL, 2, "Don't Save", "Save", ErrorMessage )==1)
1644 save_level(filename);
1650 if (Function_mode == FMODE_EDITOR)
1651 editor_status("Loaded NEW mine %s, \"%s\"",filename,Current_level_name);
1655 if (check_segment_connections())
1656 nm_messagebox( "ERROR", 1, "Ok",
1657 "Connectivity errors detected in\n"
1658 "mine. See monochrome screen for\n"
1659 "details, and contact Matt or Mike." );
1666 void get_level_name()
1668 //NO_UI!!! UI_WINDOW *NameWindow = NULL;
1669 //NO_UI!!! UI_GADGET_INPUTBOX *NameText;
1670 //NO_UI!!! UI_GADGET_BUTTON *QuitButton;
1672 //NO_UI!!! // Open a window with a quit button
1673 //NO_UI!!! NameWindow = ui_open_window( 20, 20, 300, 110, WIN_DIALOG );
1674 //NO_UI!!! QuitButton = ui_add_gadget_button( NameWindow, 150-24, 60, 48, 40, "Done", NULL );
1676 //NO_UI!!! ui_wprintf_at( NameWindow, 10, 12,"Please enter a name for this mine:" );
1677 //NO_UI!!! NameText = ui_add_gadget_inputbox( NameWindow, 10, 30, LEVEL_NAME_LEN, LEVEL_NAME_LEN, Current_level_name );
1679 //NO_UI!!! NameWindow->keyboard_focus_gadget = (UI_GADGET *)NameText;
1680 //NO_UI!!! QuitButton->hotkey = KEY_ENTER;
1682 //NO_UI!!! ui_gadget_calc_keys(NameWindow);
1684 //NO_UI!!! while (!QuitButton->pressed && last_keypress!=KEY_ENTER) {
1685 //NO_UI!!! ui_mega_process();
1686 //NO_UI!!! ui_window_do_gadgets(NameWindow);
1689 //NO_UI!!! strcpy( Current_level_name, NameText->text );
1691 //NO_UI!!! if ( NameWindow!=NULL ) {
1692 //NO_UI!!! ui_close_window( NameWindow );
1693 //NO_UI!!! NameWindow = NULL;
1699 m[0].type = NM_TYPE_TEXT; m[0].text = "Please enter a name for this mine:";
1700 m[1].type = NM_TYPE_INPUT; m[1].text = Current_level_name; m[1].text_len = LEVEL_NAME_LEN;
1702 newmenu_do( NULL, "Enter mine name", 2, m, NULL );
1712 // -----------------------------------------------------------------------------
1713 int compute_num_delta_light_records(void)
1718 for (i=0; i<Num_static_lights; i++) {
1719 total += Dl_indices[i].count;
1726 // -----------------------------------------------------------------------------
1728 int save_game_data(FILE * SaveFile)
1730 int player_offset, object_offset, walls_offset, doors_offset, triggers_offset, control_offset, matcen_offset; //, links_offset;
1731 int dl_indices_offset, delta_light_offset;
1732 int start_offset,end_offset;
1734 start_offset = ftell(SaveFile);
1736 //===================== SAVE FILE INFO ========================
1738 game_fileinfo.fileinfo_signature = 0x6705;
1739 game_fileinfo.fileinfo_version = GAME_VERSION;
1740 game_fileinfo.level = Current_level_num;
1741 game_fileinfo.fileinfo_sizeof = sizeof(game_fileinfo);
1742 game_fileinfo.player_offset = -1;
1743 game_fileinfo.player_sizeof = sizeof(player);
1744 game_fileinfo.object_offset = -1;
1745 game_fileinfo.object_howmany = Highest_object_index+1;
1746 game_fileinfo.object_sizeof = sizeof(object);
1747 game_fileinfo.walls_offset = -1;
1748 game_fileinfo.walls_howmany = Num_walls;
1749 game_fileinfo.walls_sizeof = sizeof(wall);
1750 game_fileinfo.doors_offset = -1;
1751 game_fileinfo.doors_howmany = Num_open_doors;
1752 game_fileinfo.doors_sizeof = sizeof(active_door);
1753 game_fileinfo.triggers_offset = -1;
1754 game_fileinfo.triggers_howmany = Num_triggers;
1755 game_fileinfo.triggers_sizeof = sizeof(trigger);
1756 game_fileinfo.control_offset = -1;
1757 game_fileinfo.control_howmany = 1;
1758 game_fileinfo.control_sizeof = sizeof(control_center_triggers);
1759 game_fileinfo.matcen_offset = -1;
1760 game_fileinfo.matcen_howmany = Num_robot_centers;
1761 game_fileinfo.matcen_sizeof = sizeof(matcen_info);
1763 game_fileinfo.dl_indices_offset = -1;
1764 game_fileinfo.dl_indices_howmany = Num_static_lights;
1765 game_fileinfo.dl_indices_sizeof = sizeof(dl_index);
1767 game_fileinfo.delta_light_offset = -1;
1768 game_fileinfo.delta_light_howmany = compute_num_delta_light_records();
1769 game_fileinfo.delta_light_sizeof = sizeof(delta_light);
1771 // Write the fileinfo
1772 fwrite( &game_fileinfo, sizeof(game_fileinfo), 1, SaveFile );
1774 // Write the mine name
1775 fprintf(SaveFile,"%s\n",Current_level_name);
1777 fwrite(&N_polygon_models,2,1,SaveFile);
1778 fwrite(Pof_names,N_polygon_models,sizeof(*Pof_names),SaveFile);
1780 //==================== SAVE PLAYER INFO ===========================
1782 player_offset = ftell(SaveFile);
1783 fwrite( &Players[Player_num], sizeof(player), 1, SaveFile );
1785 //==================== SAVE OBJECT INFO ===========================
1787 object_offset = ftell(SaveFile);
1788 //fwrite( &Objects, sizeof(object), game_fileinfo.object_howmany, SaveFile );
1791 for (i=0;i<game_fileinfo.object_howmany;i++)
1792 write_object(&Objects[i],SaveFile);
1795 //==================== SAVE WALL INFO =============================
1797 walls_offset = ftell(SaveFile);
1798 fwrite( Walls, sizeof(wall), game_fileinfo.walls_howmany, SaveFile );
1800 //==================== SAVE DOOR INFO =============================
1802 doors_offset = ftell(SaveFile);
1803 fwrite( ActiveDoors, sizeof(active_door), game_fileinfo.doors_howmany, SaveFile );
1805 //==================== SAVE TRIGGER INFO =============================
1807 triggers_offset = ftell(SaveFile);
1808 fwrite( Triggers, sizeof(trigger), game_fileinfo.triggers_howmany, SaveFile );
1810 //================ SAVE CONTROL CENTER TRIGGER INFO ===============
1812 control_offset = ftell(SaveFile);
1813 fwrite( &ControlCenterTriggers, sizeof(control_center_triggers), 1, SaveFile );
1816 //================ SAVE MATERIALIZATION CENTER TRIGGER INFO ===============
1818 matcen_offset = ftell(SaveFile);
1819 // mprintf((0, "Writing %i materialization centers\n", game_fileinfo.matcen_howmany));
1821 // for (i=0; i<game_fileinfo.matcen_howmany; i++)
1822 // mprintf((0, " %i: robot_flags = %08x\n", i, RobotCenters[i].robot_flags));
1824 fwrite( RobotCenters, sizeof(matcen_info), game_fileinfo.matcen_howmany, SaveFile );
1826 //================ SAVE DELTA LIGHT INFO ===============
1827 dl_indices_offset = ftell(SaveFile);
1828 fwrite( Dl_indices, sizeof(dl_index), game_fileinfo.dl_indices_howmany, SaveFile );
1830 delta_light_offset = ftell(SaveFile);
1831 fwrite( Delta_lights, sizeof(delta_light), game_fileinfo.delta_light_howmany, SaveFile );
1833 //============= REWRITE FILE INFO, TO SAVE OFFSETS ===============
1835 // Update the offset fields
1836 game_fileinfo.player_offset = player_offset;
1837 game_fileinfo.object_offset = object_offset;
1838 game_fileinfo.walls_offset = walls_offset;
1839 game_fileinfo.doors_offset = doors_offset;
1840 game_fileinfo.triggers_offset = triggers_offset;
1841 game_fileinfo.control_offset = control_offset;
1842 game_fileinfo.matcen_offset = matcen_offset;
1843 game_fileinfo.dl_indices_offset = dl_indices_offset;
1844 game_fileinfo.delta_light_offset = delta_light_offset;
1847 end_offset = ftell(SaveFile);
1849 // Write the fileinfo
1850 fseek( SaveFile, start_offset, SEEK_SET ); // Move to TOF
1851 fwrite( &game_fileinfo, sizeof(game_fileinfo), 1, SaveFile );
1853 // Go back to end of data
1854 fseek(SaveFile,end_offset,SEEK_SET);
1859 int save_mine_data(FILE * SaveFile);
1861 // -----------------------------------------------------------------------------
1863 int save_level_sub(char * filename, int compiled_version)
1866 char temp_filename[128];
1867 int sig = MAKE_SIG('P','L','V','L'),version=LEVEL_FILE_VERSION;
1868 int minedata_offset=0,gamedata_offset=0;
1870 if ( !compiled_version ) {
1871 write_game_text_file(filename);
1873 if (Errors_in_mine) {
1874 if (is_real_level(filename)) {
1875 char ErrorMessage[200];
1877 sprintf( ErrorMessage, "Warning: %i errors in this mine!\n", Errors_in_mine );
1879 gr_palette_load(gr_palette);
1881 if (nm_messagebox( NULL, 2, "Cancel Save", "Save", ErrorMessage )!=1) {
1887 mprintf((1, "Error: %i errors in this mine. See the 'txm' file.\n", Errors_in_mine));
1889 change_filename_extension(temp_filename,filename,".LVL");
1893 // macs are using the regular hog/rl2 files for shareware
1894 #if defined(SHAREWARE) && !defined(MACINTOSH)
1895 change_filename_extension(temp_filename,filename,".SL2");
1897 change_filename_extension(temp_filename,filename,".RL2");
1901 SaveFile = fopen( temp_filename, "wb" );
1904 char ErrorMessage[256];
1907 _splitpath( temp_filename, NULL, NULL, fname, NULL );
1909 sprintf( ErrorMessage, \
1910 "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"
1911 , temp_filename, fname );
1913 gr_palette_load(gr_palette);
1914 nm_messagebox( NULL, 1, "Ok", ErrorMessage );
1919 if (Current_level_name[0] == 0)
1920 strcpy(Current_level_name,"Untitled");
1922 clear_transient_objects(1); //1 means clear proximity bombs
1924 compress_objects(); //after this, Highest_object_index == num objects
1926 //make sure player is in a segment
1927 if (update_object_seg(&Objects[Players[0].objnum]) == 0) {
1928 if (ConsoleObject->segnum > Highest_segment_index)
1929 ConsoleObject->segnum = 0;
1930 compute_segment_center(&ConsoleObject->pos,&(Segments[ConsoleObject->segnum]));
1937 gs_write_int(sig,SaveFile);
1938 gs_write_int(version,SaveFile);
1941 gs_write_int(minedata_offset,SaveFile);
1942 gs_write_int(gamedata_offset,SaveFile);
1944 //Now write the damn data
1946 //write the version 8 data (to make file unreadable by 1.0 & 1.1)
1947 gs_write_int(GameTime,SaveFile);
1948 gs_write_short(FrameCount,SaveFile);
1949 gs_write_byte(FrameTime,SaveFile);
1951 // Write the palette file name
1952 fprintf(SaveFile,"%s\n",Current_level_palette);
1954 gs_write_int(Base_control_center_explosion_time,SaveFile);
1955 gs_write_int(Reactor_strength,SaveFile);
1957 gs_write_int(Num_flickering_lights,SaveFile);
1958 fwrite(Flickering_lights,sizeof(*Flickering_lights),Num_flickering_lights,SaveFile);
1960 gs_write_int(Secret_return_segment, SaveFile);
1961 gs_write_int(Secret_return_orient.rvec.x, SaveFile);
1962 gs_write_int(Secret_return_orient.rvec.y, SaveFile);
1963 gs_write_int(Secret_return_orient.rvec.z, SaveFile);
1964 gs_write_int(Secret_return_orient.fvec.x, SaveFile);
1965 gs_write_int(Secret_return_orient.fvec.y, SaveFile);
1966 gs_write_int(Secret_return_orient.fvec.z, SaveFile);
1967 gs_write_int(Secret_return_orient.uvec.x, SaveFile);
1968 gs_write_int(Secret_return_orient.uvec.y, SaveFile);
1969 gs_write_int(Secret_return_orient.uvec.z, SaveFile);
1971 minedata_offset = ftell(SaveFile);
1972 if ( !compiled_version )
1973 save_mine_data(SaveFile);
1975 save_mine_data_compiled(SaveFile);
1976 gamedata_offset = ftell(SaveFile);
1977 save_game_data(SaveFile);
1979 fseek(SaveFile,sizeof(sig)+sizeof(version),SEEK_SET);
1980 gs_write_int(minedata_offset,SaveFile);
1981 gs_write_int(gamedata_offset,SaveFile);
1983 //==================== CLOSE THE FILE =============================
1986 if ( !compiled_version ) {
1987 if (Function_mode == FMODE_EDITOR)
1988 editor_status("Saved mine %s, \"%s\"",filename,Current_level_name);
1995 #if 0 //dunno - 3rd party stuff?
1996 extern void compress_uv_coordinates_all(void);
1999 int save_level(char * filename)
2003 // Save normal version...
2004 r1 = save_level_sub(filename, 0);
2006 // Save compiled version...
2007 save_level_sub(filename, 1);
2015 void dump_mine_info(void)
2017 int segnum, sidenum;
2018 fix min_u, max_u, min_v, max_v, min_l, max_l, max_sl;
2030 for (segnum=0; segnum<=Highest_segment_index; segnum++) {
2031 for (sidenum=0; sidenum<MAX_SIDES_PER_SEGMENT; sidenum++) {
2033 side *sidep = &Segments[segnum].sides[sidenum];
2035 if (Segment2s[segnum].static_light > max_sl)
2036 max_sl = Segment2s[segnum].static_light;
2038 for (vertnum=0; vertnum<4; vertnum++) {
2039 if (sidep->uvls[vertnum].u < min_u)
2040 min_u = sidep->uvls[vertnum].u;
2041 else if (sidep->uvls[vertnum].u > max_u)
2042 max_u = sidep->uvls[vertnum].u;
2044 if (sidep->uvls[vertnum].v < min_v)
2045 min_v = sidep->uvls[vertnum].v;
2046 else if (sidep->uvls[vertnum].v > max_v)
2047 max_v = sidep->uvls[vertnum].v;
2049 if (sidep->uvls[vertnum].l < min_l)
2050 min_l = sidep->uvls[vertnum].l;
2051 else if (sidep->uvls[vertnum].l > max_l)
2052 max_l = sidep->uvls[vertnum].l;
2058 // 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)));
2059 // mprintf((0, "Static light maximum = %7.3f\n", f2fl(max_sl)));
2060 // mprintf((0, "Number of walls: %i\n", Num_walls));
2068 //read in every level in mission and save out compiled version
2069 void save_all_compiled_levels(void)
2071 do_load_save_levels(1);
2074 //read in every level in mission
2075 void load_all_levels(void)
2077 do_load_save_levels(0);
2081 void do_load_save_levels(int save)
2085 if (! SafetyCheck())
2088 no_old_level_file_error=1;
2090 for (level_num=1;level_num<=Last_level;level_num++) {
2091 load_level(Level_names[level_num-1]);
2092 load_palette(Current_level_palette,1,1); //don't change screen
2094 save_level_sub(Level_names[level_num-1],1);
2097 for (level_num=-1;level_num>=Last_secret_level;level_num--) {
2098 load_level(Secret_level_names[-level_num-1]);
2099 load_palette(Current_level_palette,1,1); //don't change screen
2101 save_level_sub(Secret_level_names[-level_num-1],1);
2104 no_old_level_file_error=0;