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.
15 * $Source: /cvs/cvsroot/d2x/main/gamesave.c,v $
18 * $Date: 2001-11-14 09:34:32 $
20 * Save game information
22 * $Log: not supported by cvs2svn $
34 char gamesave_rcsid[] = "$Id: gamesave.c,v 1.6 2001-11-14 09:34:32 bradleyb Exp $";
50 #include "editor/editor.h"
82 char Gamesave_current_filename[128];
84 #define GAME_VERSION 32
85 #define GAME_COMPATIBLE_VERSION 22
87 //version 28->29 add delta light support
88 //version 27->28 controlcen id now is reactor number, not model number
90 //version 29->30 changed trigger structure
91 //version 30->31 changed trigger structure some more
92 //version 31->32 change segment structure, make it 512 bytes w/o editor, add Segment2s array.
94 #define MENU_CURSOR_X_MIN MENU_X
95 #define MENU_CURSOR_X_MAX MENU_X+6
97 //Start old wall structures
99 typedef struct v16_wall {
100 byte type; // What kind of special wall.
101 byte flags; // Flags for the wall.
102 fix hps; // "Hit points" of the wall.
103 byte trigger; // Which trigger is associated with the wall.
104 byte clip_num; // Which animation associated with the wall.
108 typedef struct v19_wall {
109 int segnum,sidenum; // Seg & side for this wall
110 byte type; // What kind of special wall.
111 byte flags; // Flags for the wall.
112 fix hps; // "Hit points" of the wall.
113 byte trigger; // Which trigger is associated with the wall.
114 byte clip_num; // Which animation associated with the wall.
116 int linked_wall; // number of linked wall
119 typedef struct v19_door {
120 int n_parts; // for linked walls
121 short seg[2]; // Segment pointer of door.
122 short side[2]; // Side number of door.
123 short type[2]; // What kind of door animation.
124 fix open; // How long it has been open.
127 //End old wall structures
129 //old trigger structs
131 typedef struct v29_trigger {
138 short seg[MAX_WALLS_PER_LINK];
139 short side[MAX_WALLS_PER_LINK];
142 typedef struct v30_trigger {
145 byte pad; //keep alignment
148 short seg[MAX_WALLS_PER_LINK];
149 short side[MAX_WALLS_PER_LINK];
152 //flags for V30 & below triggers
153 #define TRIGGER_CONTROL_DOORS 1 // Control Trigger
154 #define TRIGGER_SHIELD_DAMAGE 2 // Shield Damage Trigger
155 #define TRIGGER_ENERGY_DRAIN 4 // Energy Drain Trigger
156 #define TRIGGER_EXIT 8 // End of level Trigger
157 #define TRIGGER_ON 16 // Whether Trigger is active
158 #define TRIGGER_ONE_SHOT 32 // If Trigger can only be triggered once
159 #define TRIGGER_MATCEN 64 // Trigger for materialization centers
160 #define TRIGGER_ILLUSION_OFF 128 // Switch Illusion OFF trigger
161 #define TRIGGER_SECRET_EXIT 256 // Exit to secret level
162 #define TRIGGER_ILLUSION_ON 512 // Switch Illusion ON trigger
163 #define TRIGGER_UNLOCK_DOORS 1024 // Unlocks a door
164 #define TRIGGER_OPEN_WALL 2048 // Makes a wall open
165 #define TRIGGER_CLOSE_WALL 4096 // Makes a wall closed
166 #define TRIGGER_ILLUSORY_WALL 8192 // Makes a wall illusory
169 ushort fileinfo_signature;
170 ushort fileinfo_version;
172 } game_top_fileinfo; // Should be same as first two fields below...
175 ushort fileinfo_signature;
176 ushort fileinfo_version;
178 char mine_filename[15];
180 int player_offset; // Player info
182 int object_offset; // Object info
192 int triggers_howmany;
203 int dl_indices_offset;
204 int dl_indices_howmany;
205 int dl_indices_sizeof;
206 int delta_light_offset;
207 int delta_light_howmany;
208 int delta_light_sizeof;
211 // LINT: adding function prototypes
212 void read_object(object *obj, CFILE *f, int version);
213 void write_object(object *obj, FILE *f);
214 void do_load_save_levels(int save);
215 void dump_mine_info(void);
217 extern char MaxPowerupsAllowed[MAX_POWERUP_TYPES];
218 extern char PowerupsInMine[MAX_POWERUP_TYPES];
221 extern char mine_filename[];
222 extern int save_mine_data_compiled(FILE * SaveFile);
224 //--unused-- char mine_filename[128];
227 int Gamesave_num_org_robots = 0;
228 //--unused-- grs_bitmap * Gamesave_saved_bitmap = NULL;
231 // Return true if this level has a name of the form "level??"
232 // Note that a pathspec can appear at the beginning of the filename.
233 int is_real_level(char *filename)
235 int len = strlen(filename);
240 //mprintf((0, "String = [%s]\n", &filename[len-11]));
241 return !strnicmp(&filename[len-11], "level", 5);
246 void change_filename_extension( char *dest, char *src, char *new_ext )
255 for (i=1; i<strlen(dest); i++ )
256 if (dest[i]=='.'||dest[i]==' '||dest[i]==0)
261 dest[i+1]=new_ext[0];
262 dest[i+2]=new_ext[1];
263 dest[i+3]=new_ext[2];
269 //--unused-- vms_angvec zero_angles={0,0,0};
271 #define vm_angvec_zero(v) do {(v)->p=(v)->b=(v)->h=0;} while (0)
273 int Gamesave_num_players=0;
275 int N_save_pof_names;
276 char Save_pof_names[MAX_POLYGON_MODELS][FILENAME_LEN];
278 void check_and_fix_matrix(vms_matrix *m);
280 void verify_object( object * obj ) {
282 obj->lifeleft = IMMORTAL_TIME; //all loaded object are immortal, for now
284 if ( obj->type == OBJ_ROBOT ) {
285 Gamesave_num_org_robots++;
287 // Make sure valid id...
288 if ( obj->id >= N_robot_types )
289 obj->id = obj->id % N_robot_types;
291 // Make sure model number & size are correct...
292 if ( obj->render_type == RT_POLYOBJ ) {
293 Assert(Robot_info[obj->id].model_num != -1);
294 //if you fail this assert, it means that a robot in this level
295 //hasn't been loaded, possibly because he's marked as
296 //non-shareware. To see what robot number, print obj->id.
298 Assert(Robot_info[obj->id].always_0xabcd == 0xabcd);
299 //if you fail this assert, it means that the robot_ai for
300 //a robot in this level hasn't been loaded, possibly because
301 //it's marked as non-shareware. To see what robot number,
304 obj->rtype.pobj_info.model_num = Robot_info[obj->id].model_num;
305 obj->size = Polygon_models[obj->rtype.pobj_info.model_num].rad;
307 //@@Took out this ugly hack 1/12/96, because Mike has added code
308 //@@that should fix it in a better way.
309 //@@//this is a super-ugly hack. Since the baby stripe robots have
310 //@@//their firing point on their bounding sphere, the firing points
311 //@@//can poke through a wall if the robots are very close to it. So
312 //@@//we make their radii bigger so the guns can't get too close to
314 //@@if (Robot_info[obj->id].flags & RIF_BIG_RADIUS)
315 //@@ obj->size = (obj->size*3)/2;
317 //@@if (obj->control_type==CT_AI && Robot_info[obj->id].attack_type)
318 //@@ obj->size = obj->size*3/4;
321 if (obj->id == 65) //special "reactor" robots
322 obj->movement_type = MT_NONE;
324 if (obj->movement_type == MT_PHYSICS) {
325 obj->mtype.phys_info.mass = Robot_info[obj->id].mass;
326 obj->mtype.phys_info.drag = Robot_info[obj->id].drag;
329 else { //Robots taken care of above
331 if ( obj->render_type == RT_POLYOBJ ) {
333 char *name = Save_pof_names[obj->rtype.pobj_info.model_num];
335 for (i=0;i<N_polygon_models;i++)
336 if (!stricmp(Pof_names[i],name)) { //found it!
337 // mprintf((0,"Mapping <%s> to %d (was %d)\n",name,i,obj->rtype.pobj_info.model_num));
338 obj->rtype.pobj_info.model_num = i;
344 if ( obj->type == OBJ_POWERUP ) {
345 if ( obj->id >= N_powerup_types ) {
347 Assert( obj->render_type != RT_POLYOBJ );
349 obj->control_type = CT_POWERUP;
350 obj->size = Powerup_info[obj->id].size;
351 obj->ctype.powerup_info.creation_time = 0;
354 if (Game_mode & GM_NETWORK)
356 if (multi_powerup_is_4pack(obj->id))
358 PowerupsInMine[obj->id-1]+=4;
359 MaxPowerupsAllowed[obj->id-1]+=4;
361 PowerupsInMine[obj->id]++;
362 MaxPowerupsAllowed[obj->id]++;
363 mprintf ((0,"PowerupLimiter: ID=%d\n",obj->id));
364 if (obj->id>MAX_POWERUP_TYPES)
365 mprintf ((1,"POWERUP: Overwriting array bounds!! Get JL!\n"));
371 if ( obj->type == OBJ_WEAPON ) {
372 if ( obj->id >= N_weapon_types ) {
374 Assert( obj->render_type != RT_POLYOBJ );
377 if (obj->id == PMINE_ID) { //make sure pmines have correct values
379 obj->mtype.phys_info.mass = Weapon_info[obj->id].mass;
380 obj->mtype.phys_info.drag = Weapon_info[obj->id].drag;
381 obj->mtype.phys_info.flags |= PF_FREE_SPINNING;
383 // Make sure model number & size are correct...
384 Assert( obj->render_type == RT_POLYOBJ );
386 obj->rtype.pobj_info.model_num = Weapon_info[obj->id].model_num;
387 obj->size = Polygon_models[obj->rtype.pobj_info.model_num].rad;
391 if ( obj->type == OBJ_CNTRLCEN ) {
393 obj->render_type = RT_POLYOBJ;
394 obj->control_type = CT_CNTRLCEN;
396 //@@// Make model number is correct...
397 //@@for (i=0; i<Num_total_object_types; i++ )
398 //@@ if ( ObjType[i] == OL_CONTROL_CENTER ) {
399 //@@ obj->rtype.pobj_info.model_num = ObjId[i];
400 //@@ obj->shields = ObjStrength[i];
407 // Check, and set, strength of reactor
408 for (i=0; i<Num_total_object_types; i++ )
409 if ( ObjType[i]==OL_CONTROL_CENTER && ObjId[i] == obj->id ) {
410 obj->shields = ObjStrength[i];
413 Assert(i < Num_total_object_types); //make sure we found it
418 if ( obj->type == OBJ_PLAYER ) {
421 //Assert(obj == Player);
423 if ( obj == ConsoleObject )
424 init_player_object();
426 if (obj->render_type == RT_POLYOBJ) //recover from Matt's pof file matchup bug
427 obj->rtype.pobj_info.model_num = Player_ship->model_num;
429 //Make sure orient matrix is orthogonal
430 check_and_fix_matrix(&obj->orient);
432 obj->id = Gamesave_num_players++;
435 if (obj->type == OBJ_HOSTAGE) {
437 //@@if (obj->id > N_hostage_types)
440 obj->render_type = RT_HOSTAGE;
441 obj->control_type = CT_POWERUP;
446 static int read_int(CFILE *file)
450 if (cfread( &i, sizeof(i), 1, file) != 1)
451 Error( "Error reading int in gamesave.c" );
457 static fix read_fix(CFILE *file)
461 if (cfread( &f, sizeof(f), 1, file) != 1)
462 Error( "Error reading fix in gamesave.c" );
464 f = (fix)INTEL_INT((int)f);
468 static short read_short(CFILE *file)
472 if (cfread( &s, sizeof(s), 1, file) != 1)
473 Error( "Error reading short in gamesave.c" );
479 static short read_fixang(CFILE *file)
483 if (cfread( &f, sizeof(f), 1, file) != 1)
484 Error( "Error reading fixang in gamesave.c" );
486 f = (fixang)INTEL_SHORT((short)f);
490 static byte read_byte(CFILE *file)
494 if (cfread( &b, sizeof(b), 1, file) != 1)
495 Error( "Error reading byte in gamesave.c" );
500 static void read_vector(vms_vector *v,CFILE *file)
502 v->x = read_fix(file);
503 v->y = read_fix(file);
504 v->z = read_fix(file);
507 static void read_matrix(vms_matrix *m,CFILE *file)
509 read_vector(&m->rvec,file);
510 read_vector(&m->uvec,file);
511 read_vector(&m->fvec,file);
514 static void read_angvec(vms_angvec *v,CFILE *file)
516 v->p = read_fixang(file);
517 v->b = read_fixang(file);
518 v->h = read_fixang(file);
521 //static gs_skip(int len,CFILE *file)
524 // cfseek(file,len,SEEK_CUR);
528 static void gs_write_int(int i,FILE *file)
530 if (fwrite( &i, sizeof(i), 1, file) != 1)
531 Error( "Error reading int in gamesave.c" );
535 static void gs_write_fix(fix f,FILE *file)
537 if (fwrite( &f, sizeof(f), 1, file) != 1)
538 Error( "Error reading fix in gamesave.c" );
542 static void gs_write_short(short s,FILE *file)
544 if (fwrite( &s, sizeof(s), 1, file) != 1)
545 Error( "Error reading short in gamesave.c" );
549 static void gs_write_fixang(fixang f,FILE *file)
551 if (fwrite( &f, sizeof(f), 1, file) != 1)
552 Error( "Error reading fixang in gamesave.c" );
556 static void gs_write_byte(byte b,FILE *file)
558 if (fwrite( &b, sizeof(b), 1, file) != 1)
559 Error( "Error reading byte in gamesave.c" );
563 static void gr_write_vector(vms_vector *v,FILE *file)
565 gs_write_fix(v->x,file);
566 gs_write_fix(v->y,file);
567 gs_write_fix(v->z,file);
570 static void gs_write_matrix(vms_matrix *m,FILE *file)
572 gr_write_vector(&m->rvec,file);
573 gr_write_vector(&m->uvec,file);
574 gr_write_vector(&m->fvec,file);
577 static void gs_write_angvec(vms_angvec *v,FILE *file)
579 gs_write_fixang(v->p,file);
580 gs_write_fixang(v->b,file);
581 gs_write_fixang(v->h,file);
587 extern int multi_powerup_is_4pack(int);
588 //reads one object of the given version from the given file
589 void read_object(object *obj,CFILE *f,int version)
592 obj->type = read_byte(f);
593 obj->id = read_byte(f);
595 if (obj->type == OBJ_CNTRLCEN && version<28)
596 obj->id = 0; //used to be only one kind of reactor
598 obj->control_type = read_byte(f);
599 obj->movement_type = read_byte(f);
600 obj->render_type = read_byte(f);
601 obj->flags = read_byte(f);
603 obj->segnum = read_short(f);
604 obj->attached_obj = -1;
606 read_vector(&obj->pos,f);
607 read_matrix(&obj->orient,f);
609 obj->size = read_fix(f);
610 obj->shields = read_fix(f);
612 read_vector(&obj->last_pos,f);
614 obj->contains_type = read_byte(f);
615 obj->contains_id = read_byte(f);
616 obj->contains_count = read_byte(f);
618 switch (obj->movement_type) {
622 read_vector(&obj->mtype.phys_info.velocity,f);
623 read_vector(&obj->mtype.phys_info.thrust,f);
625 obj->mtype.phys_info.mass = read_fix(f);
626 obj->mtype.phys_info.drag = read_fix(f);
627 obj->mtype.phys_info.brakes = read_fix(f);
629 read_vector(&obj->mtype.phys_info.rotvel,f);
630 read_vector(&obj->mtype.phys_info.rotthrust,f);
632 obj->mtype.phys_info.turnroll = read_fixang(f);
633 obj->mtype.phys_info.flags = read_short(f);
639 read_vector(&obj->mtype.spin_rate,f);
649 switch (obj->control_type) {
654 obj->ctype.ai_info.behavior = read_byte(f);
656 for (i=0;i<MAX_AI_FLAGS;i++)
657 obj->ctype.ai_info.flags[i] = read_byte(f);
659 obj->ctype.ai_info.hide_segment = read_short(f);
660 obj->ctype.ai_info.hide_index = read_short(f);
661 obj->ctype.ai_info.path_length = read_short(f);
662 obj->ctype.ai_info.cur_path_index = read_short(f);
665 read_short(f); // obj->ctype.ai_info.follow_path_start_seg =
666 read_short(f); // obj->ctype.ai_info.follow_path_end_seg =
674 obj->ctype.expl_info.spawn_time = read_fix(f);
675 obj->ctype.expl_info.delete_time = read_fix(f);
676 obj->ctype.expl_info.delete_objnum = read_short(f);
677 obj->ctype.expl_info.next_attach = obj->ctype.expl_info.prev_attach = obj->ctype.expl_info.attach_parent = -1;
683 //do I really need to read these? Are they even saved to disk?
685 obj->ctype.laser_info.parent_type = read_short(f);
686 obj->ctype.laser_info.parent_num = read_short(f);
687 obj->ctype.laser_info.parent_signature = read_int(f);
693 obj->ctype.light_info.intensity = read_fix(f);
699 obj->ctype.powerup_info.count = read_int(f);
701 obj->ctype.powerup_info.count = 1;
703 if (obj->id == POW_VULCAN_WEAPON)
704 obj->ctype.powerup_info.count = VULCAN_WEAPON_AMMO_AMOUNT;
706 if (obj->id == POW_GAUSS_WEAPON)
707 obj->ctype.powerup_info.count = VULCAN_WEAPON_AMMO_AMOUNT;
709 if (obj->id == POW_OMEGA_WEAPON)
710 obj->ctype.powerup_info.count = MAX_OMEGA_CHARGE;
720 case CT_SLEW: //the player is generally saved as slew
734 switch (obj->render_type) {
743 obj->rtype.pobj_info.model_num = read_int(f);
745 for (i=0;i<MAX_SUBMODELS;i++)
746 read_angvec(&obj->rtype.pobj_info.anim_angles[i],f);
748 obj->rtype.pobj_info.subobj_flags = read_int(f);
753 obj->rtype.pobj_info.tmap_override = tmo;
756 obj->rtype.pobj_info.tmap_override = -1;
758 int xlated_tmo = tmap_xlate_table[tmo];
759 if (xlated_tmo < 0) {
760 mprintf( (0, "Couldn't find texture for demo object, model_num = %d\n", obj->rtype.pobj_info.model_num));
764 obj->rtype.pobj_info.tmap_override = xlated_tmo;
768 obj->rtype.pobj_info.alt_textures = 0;
773 case RT_WEAPON_VCLIP:
778 obj->rtype.vclip_info.vclip_num = read_int(f);
779 obj->rtype.vclip_info.frametime = read_fix(f);
780 obj->rtype.vclip_info.framenum = read_byte(f);
796 //writes one object to the given file
797 void write_object(object *obj,FILE *f)
799 gs_write_byte(obj->type,f);
800 gs_write_byte(obj->id,f);
802 gs_write_byte(obj->control_type,f);
803 gs_write_byte(obj->movement_type,f);
804 gs_write_byte(obj->render_type,f);
805 gs_write_byte(obj->flags,f);
807 gs_write_short(obj->segnum,f);
809 gr_write_vector(&obj->pos,f);
810 gs_write_matrix(&obj->orient,f);
812 gs_write_fix(obj->size,f);
813 gs_write_fix(obj->shields,f);
815 gr_write_vector(&obj->last_pos,f);
817 gs_write_byte(obj->contains_type,f);
818 gs_write_byte(obj->contains_id,f);
819 gs_write_byte(obj->contains_count,f);
821 switch (obj->movement_type) {
825 gr_write_vector(&obj->mtype.phys_info.velocity,f);
826 gr_write_vector(&obj->mtype.phys_info.thrust,f);
828 gs_write_fix(obj->mtype.phys_info.mass,f);
829 gs_write_fix(obj->mtype.phys_info.drag,f);
830 gs_write_fix(obj->mtype.phys_info.brakes,f);
832 gr_write_vector(&obj->mtype.phys_info.rotvel,f);
833 gr_write_vector(&obj->mtype.phys_info.rotthrust,f);
835 gs_write_fixang(obj->mtype.phys_info.turnroll,f);
836 gs_write_short(obj->mtype.phys_info.flags,f);
842 gr_write_vector(&obj->mtype.spin_rate,f);
852 switch (obj->control_type) {
857 gs_write_byte(obj->ctype.ai_info.behavior,f);
859 for (i=0;i<MAX_AI_FLAGS;i++)
860 gs_write_byte(obj->ctype.ai_info.flags[i],f);
862 gs_write_short(obj->ctype.ai_info.hide_segment,f);
863 gs_write_short(obj->ctype.ai_info.hide_index,f);
864 gs_write_short(obj->ctype.ai_info.path_length,f);
865 gs_write_short(obj->ctype.ai_info.cur_path_index,f);
867 // -- unused! mk, 08/13/95 -- gs_write_short(obj->ctype.ai_info.follow_path_start_seg,f);
868 // -- unused! mk, 08/13/95 -- gs_write_short(obj->ctype.ai_info.follow_path_end_seg,f);
875 gs_write_fix(obj->ctype.expl_info.spawn_time,f);
876 gs_write_fix(obj->ctype.expl_info.delete_time,f);
877 gs_write_short(obj->ctype.expl_info.delete_objnum,f);
883 //do I really need to write these objects?
885 gs_write_short(obj->ctype.laser_info.parent_type,f);
886 gs_write_short(obj->ctype.laser_info.parent_num,f);
887 gs_write_int(obj->ctype.laser_info.parent_signature,f);
893 gs_write_fix(obj->ctype.light_info.intensity,f);
898 gs_write_int(obj->ctype.powerup_info.count,f);
906 case CT_SLEW: //the player is generally saved as slew
910 break; //control center object.
920 switch (obj->render_type) {
929 gs_write_int(obj->rtype.pobj_info.model_num,f);
931 for (i=0;i<MAX_SUBMODELS;i++)
932 gs_write_angvec(&obj->rtype.pobj_info.anim_angles[i],f);
934 gs_write_int(obj->rtype.pobj_info.subobj_flags,f);
936 gs_write_int(obj->rtype.pobj_info.tmap_override,f);
941 case RT_WEAPON_VCLIP:
946 gs_write_int(obj->rtype.vclip_info.vclip_num,f);
947 gs_write_fix(obj->rtype.vclip_info.frametime,f);
948 gs_write_byte(obj->rtype.vclip_info.framenum,f);
964 int robot_flags; // Up to 32 different robots
965 fix hit_points; // How hard it is to destroy this particular matcen
966 fix interval; // Interval between materialogrifizations
967 short segnum; // Segment this is attached to.
968 short fuelcen_num; // Index in fuelcen array.
971 extern int remove_trigger_num(int trigger_num);
973 // -----------------------------------------------------------------------------
975 // Loads all the relevant data for a level.
976 // If level != -1, it loads the filename with extension changed to .min
977 // Otherwise it loads the appropriate level mine.
978 // returns 0=everything ok, 1=old version, -1=error
979 int load_game_data(CFILE *LoadFile)
984 start_offset = cftell(LoadFile);
986 //===================== READ FILE INFO ========================
988 // Set default values
989 game_fileinfo.level = -1;
990 game_fileinfo.player_offset = -1;
991 game_fileinfo.player_sizeof = sizeof(player);
992 game_fileinfo.object_offset = -1;
993 game_fileinfo.object_howmany = 0;
994 game_fileinfo.object_sizeof = sizeof(object);
995 game_fileinfo.walls_offset = -1;
996 game_fileinfo.walls_howmany = 0;
997 game_fileinfo.walls_sizeof = sizeof(wall);
998 game_fileinfo.doors_offset = -1;
999 game_fileinfo.doors_howmany = 0;
1000 game_fileinfo.doors_sizeof = sizeof(active_door);
1001 game_fileinfo.triggers_offset = -1;
1002 game_fileinfo.triggers_howmany = 0;
1003 game_fileinfo.triggers_sizeof = sizeof(trigger);
1004 game_fileinfo.control_offset = -1;
1005 game_fileinfo.control_howmany = 0;
1006 game_fileinfo.control_sizeof = sizeof(control_center_triggers);
1007 game_fileinfo.matcen_offset = -1;
1008 game_fileinfo.matcen_howmany = 0;
1009 game_fileinfo.matcen_sizeof = sizeof(matcen_info);
1011 game_fileinfo.dl_indices_offset = -1;
1012 game_fileinfo.dl_indices_howmany = 0;
1013 game_fileinfo.dl_indices_sizeof = sizeof(dl_index);
1015 game_fileinfo.delta_light_offset = -1;
1016 game_fileinfo.delta_light_howmany = 0;
1017 game_fileinfo.delta_light_sizeof = sizeof(delta_light);
1019 // Read in game_top_fileinfo to get size of saved fileinfo.
1021 if (cfseek( LoadFile, start_offset, SEEK_SET ))
1022 Error( "Error seeking in gamesave.c" );
1024 // if (cfread( &game_top_fileinfo, sizeof(game_top_fileinfo), 1, LoadFile) != 1)
1025 // Error( "Error reading game_top_fileinfo in gamesave.c" );
1027 game_top_fileinfo.fileinfo_signature = read_short(LoadFile);
1028 game_top_fileinfo.fileinfo_version = read_short(LoadFile);
1029 game_top_fileinfo.fileinfo_sizeof = read_int(LoadFile);
1032 if (game_top_fileinfo.fileinfo_signature != 0x6705)
1035 // Check version number
1036 if (game_top_fileinfo.fileinfo_version < GAME_COMPATIBLE_VERSION )
1039 // Now, Read in the fileinfo
1040 if (cfseek( LoadFile, start_offset, SEEK_SET ))
1041 Error( "Error seeking to game_fileinfo in gamesave.c" );
1043 // if (cfread( &game_fileinfo, game_top_fileinfo.fileinfo_sizeof, 1, LoadFile )!=1)
1044 // Error( "Error reading game_fileinfo in gamesave.c" );
1046 game_fileinfo.fileinfo_signature = read_short(LoadFile);
1047 game_fileinfo.fileinfo_version = read_short(LoadFile);
1048 game_fileinfo.fileinfo_sizeof = read_int(LoadFile);
1050 game_fileinfo.mine_filename[i] = read_byte(LoadFile);
1051 game_fileinfo.level = read_int(LoadFile);
1052 game_fileinfo.player_offset = read_int(LoadFile); // Player info
1053 game_fileinfo.player_sizeof = read_int(LoadFile);
1054 game_fileinfo.object_offset = read_int(LoadFile); // Object info
1055 game_fileinfo.object_howmany = read_int(LoadFile);
1056 game_fileinfo.object_sizeof = read_int(LoadFile);
1057 game_fileinfo.walls_offset = read_int(LoadFile);
1058 game_fileinfo.walls_howmany = read_int(LoadFile);
1059 game_fileinfo.walls_sizeof = read_int(LoadFile);
1060 game_fileinfo.doors_offset = read_int(LoadFile);
1061 game_fileinfo.doors_howmany = read_int(LoadFile);
1062 game_fileinfo.doors_sizeof = read_int(LoadFile);
1063 game_fileinfo.triggers_offset = read_int(LoadFile);
1064 game_fileinfo.triggers_howmany = read_int(LoadFile);
1065 game_fileinfo.triggers_sizeof = read_int(LoadFile);
1066 game_fileinfo.links_offset = read_int(LoadFile);
1067 game_fileinfo.links_howmany = read_int(LoadFile);
1068 game_fileinfo.links_sizeof = read_int(LoadFile);
1069 game_fileinfo.control_offset = read_int(LoadFile);
1070 game_fileinfo.control_howmany = read_int(LoadFile);
1071 game_fileinfo.control_sizeof = read_int(LoadFile);
1072 game_fileinfo.matcen_offset = read_int(LoadFile);
1073 game_fileinfo.matcen_howmany = read_int(LoadFile);
1074 game_fileinfo.matcen_sizeof = read_int(LoadFile);
1076 if (game_top_fileinfo.fileinfo_version >= 29) {
1077 game_fileinfo.dl_indices_offset = read_int(LoadFile);
1078 game_fileinfo.dl_indices_howmany = read_int(LoadFile);
1079 game_fileinfo.dl_indices_sizeof = read_int(LoadFile);
1081 game_fileinfo.delta_light_offset = read_int(LoadFile);
1082 game_fileinfo.delta_light_howmany = read_int(LoadFile);
1083 game_fileinfo.delta_light_sizeof = read_int(LoadFile);
1086 if (game_top_fileinfo.fileinfo_version >= 14) { //load mine filename
1087 //@@char *p=Current_level_name;
1088 //@@//must do read one char at a time, since no cfgets()
1089 //@@do *p = cfgetc(LoadFile); while (*p++!=0);
1091 cfgets(Current_level_name,sizeof(Current_level_name),LoadFile);
1093 if (Current_level_name[strlen(Current_level_name)-1] == '\n')
1094 Current_level_name[strlen(Current_level_name)-1] = 0;
1097 Current_level_name[0]=0;
1099 if (game_top_fileinfo.fileinfo_version >= 19) { //load pof names
1100 // cfread(&N_save_pof_names,2,1,LoadFile);
1101 N_save_pof_names = read_short(LoadFile);
1102 cfread(Save_pof_names,N_save_pof_names,FILENAME_LEN,LoadFile);
1105 //===================== READ PLAYER INFO ==========================
1106 Object_next_signature = 0;
1108 //===================== READ OBJECT INFO ==========================
1110 Gamesave_num_org_robots = 0;
1111 Gamesave_num_players = 0;
1113 if (game_fileinfo.object_offset > -1) {
1114 if (cfseek( LoadFile, game_fileinfo.object_offset, SEEK_SET ))
1115 Error( "Error seeking to object_offset in gamesave.c" );
1117 for (i=0;i<game_fileinfo.object_howmany;i++) {
1119 read_object(&Objects[i],LoadFile,game_top_fileinfo.fileinfo_version);
1121 Objects[i].signature = Object_next_signature++;
1122 verify_object( &Objects[i] );
1127 //===================== READ WALL INFO ============================
1129 if (game_fileinfo.walls_offset > -1)
1132 if (!cfseek( LoadFile, game_fileinfo.walls_offset,SEEK_SET )) {
1133 for (i=0;i<game_fileinfo.walls_howmany;i++) {
1135 if (game_top_fileinfo.fileinfo_version >= 20) {
1137 Assert(sizeof(Walls[i]) == game_fileinfo.walls_sizeof);
1139 // code to correctly read wall structure on mac. I'm assuming only v20 walls
1142 if (cfread(&Walls[i], game_fileinfo.walls_sizeof, 1,LoadFile)!=1)
1143 Error( "Error reading Walls[%d] in gamesave.c", i);
1145 Walls[i].segnum = read_int(LoadFile);
1146 Walls[i].sidenum = read_int(LoadFile);
1147 Walls[i].hps = read_fix(LoadFile);
1148 Walls[i].linked_wall = read_int(LoadFile);
1149 Walls[i].type = read_byte(LoadFile);
1150 Walls[i].flags = read_byte(LoadFile);
1151 Walls[i].state = read_byte(LoadFile);
1152 Walls[i].trigger = read_byte(LoadFile);
1153 Walls[i].clip_num = read_byte(LoadFile);
1154 Walls[i].keys = read_byte(LoadFile);
1155 Walls[i].controlling_trigger = read_byte(LoadFile);
1156 Walls[i].cloak_value = read_byte(LoadFile);
1159 else if (game_top_fileinfo.fileinfo_version >= 17) {
1162 Assert(sizeof(w) == game_fileinfo.walls_sizeof);
1164 if (cfread(&w, game_fileinfo.walls_sizeof, 1,LoadFile)!=1)
1165 Error( "Error reading Walls[%d] in gamesave.c", i);
1167 Walls[i].segnum = w.segnum;
1168 Walls[i].sidenum = w.sidenum;
1169 Walls[i].linked_wall = w.linked_wall;
1171 Walls[i].type = w.type;
1172 Walls[i].flags = w.flags;
1173 Walls[i].hps = w.hps;
1174 Walls[i].trigger = w.trigger;
1175 Walls[i].clip_num = w.clip_num;
1176 Walls[i].keys = w.keys;
1178 Walls[i].state = WALL_DOOR_CLOSED;
1183 Assert(sizeof(w) == game_fileinfo.walls_sizeof);
1185 if (cfread(&w, game_fileinfo.walls_sizeof, 1,LoadFile)!=1)
1186 Error( "Error reading Walls[%d] in gamesave.c", i);
1188 Walls[i].segnum = Walls[i].sidenum = Walls[i].linked_wall = -1;
1190 Walls[i].type = w.type;
1191 Walls[i].flags = w.flags;
1192 Walls[i].hps = w.hps;
1193 Walls[i].trigger = w.trigger;
1194 Walls[i].clip_num = w.clip_num;
1195 Walls[i].keys = w.keys;
1202 //===================== READ DOOR INFO ============================
1204 if (game_fileinfo.doors_offset > -1)
1206 if (!cfseek( LoadFile, game_fileinfo.doors_offset,SEEK_SET )) {
1208 for (i=0;i<game_fileinfo.doors_howmany;i++) {
1210 if (game_top_fileinfo.fileinfo_version >= 20) {
1212 Assert(sizeof(ActiveDoors[i]) == game_fileinfo.doors_sizeof);
1214 // code to read doors for mac -- assume version 20 and greater for doors
1216 if (cfread(&ActiveDoors[i], game_fileinfo.doors_sizeof,1,LoadFile)!=1)
1217 Error( "Error reading ActiveDoors[%d] in gamesave.c", i);
1219 ActiveDoors[i].n_parts = read_int(LoadFile);
1220 ActiveDoors[i].front_wallnum[0] = read_short(LoadFile);
1221 ActiveDoors[i].front_wallnum[1] = read_short(LoadFile);
1222 ActiveDoors[i].back_wallnum[0] = read_short(LoadFile);
1223 ActiveDoors[i].back_wallnum[1] = read_short(LoadFile);
1224 ActiveDoors[i].time = read_fix(LoadFile);
1231 Assert(sizeof(d) == game_fileinfo.doors_sizeof);
1233 if (cfread(&d, game_fileinfo.doors_sizeof, 1,LoadFile)!=1)
1234 Error( "Error reading Doors[%d] in gamesave.c", i);
1236 ActiveDoors[i].n_parts = d.n_parts;
1238 for (p=0;p<d.n_parts;p++) {
1241 cseg = Segments[d.seg[p]].children[d.side[p]];
1242 cside = find_connect_side(&Segments[d.seg[p]],&Segments[cseg]);
1244 ActiveDoors[i].front_wallnum[p] = Segments[d.seg[p]].sides[d.side[p]].wall_num;
1245 ActiveDoors[i].back_wallnum[p] = Segments[cseg].sides[cside].wall_num;
1253 //==================== READ TRIGGER INFO ==========================
1256 // for MACINTOSH -- assume all triggers >= verion 31 triggers.
1258 if (game_fileinfo.triggers_offset > -1)
1260 if (!cfseek( LoadFile, game_fileinfo.triggers_offset,SEEK_SET )) {
1261 for (i=0;i<game_fileinfo.triggers_howmany;i++)
1262 if (game_top_fileinfo.fileinfo_version < 31) {
1268 if (game_top_fileinfo.fileinfo_version < 30) {
1272 if (cfread(&trig29, game_fileinfo.triggers_sizeof,1,LoadFile)!=1)
1273 Error( "Error reading Triggers[%d] in gamesave.c", i);
1275 trig.flags = trig29.flags;
1276 trig.num_links = trig29.num_links;
1277 trig.num_links = trig29.num_links;
1278 trig.value = trig29.value;
1279 trig.time = trig29.time;
1281 for (t=0;t<trig.num_links;t++) {
1282 trig.seg[t] = trig29.seg[t];
1283 trig.side[t] = trig29.side[t];
1287 if (cfread(&trig, game_fileinfo.triggers_sizeof,1,LoadFile)!=1)
1288 Error( "Error reading Triggers[%d] in gamesave.c", i);
1290 //Assert(trig.flags & TRIGGER_ON);
1291 trig.flags &= ~TRIGGER_ON;
1293 if (trig.flags & TRIGGER_CONTROL_DOORS)
1294 type = TT_OPEN_DOOR;
1295 else if (trig.flags & TRIGGER_SHIELD_DAMAGE)
1297 else if (trig.flags & TRIGGER_ENERGY_DRAIN)
1299 else if (trig.flags & TRIGGER_EXIT)
1301 else if (trig.flags & TRIGGER_ONE_SHOT)
1303 else if (trig.flags & TRIGGER_MATCEN)
1305 else if (trig.flags & TRIGGER_ILLUSION_OFF)
1306 type = TT_ILLUSION_OFF;
1307 else if (trig.flags & TRIGGER_SECRET_EXIT)
1308 type = TT_SECRET_EXIT;
1309 else if (trig.flags & TRIGGER_ILLUSION_ON)
1310 type = TT_ILLUSION_ON;
1311 else if (trig.flags & TRIGGER_UNLOCK_DOORS)
1312 type = TT_UNLOCK_DOOR;
1313 else if (trig.flags & TRIGGER_OPEN_WALL)
1314 type = TT_OPEN_WALL;
1315 else if (trig.flags & TRIGGER_CLOSE_WALL)
1316 type = TT_CLOSE_WALL;
1317 else if (trig.flags & TRIGGER_ILLUSORY_WALL)
1318 type = TT_ILLUSORY_WALL;
1322 Triggers[i].type = type;
1323 Triggers[i].flags = 0;
1324 Triggers[i].num_links = trig.num_links;
1325 Triggers[i].num_links = trig.num_links;
1326 Triggers[i].value = trig.value;
1327 Triggers[i].time = trig.time;
1329 for (t=0;t<trig.num_links;t++) {
1330 Triggers[i].seg[t] = trig.seg[t];
1331 Triggers[i].side[t] = trig.side[t];
1336 if (cfread(&Triggers[i], game_fileinfo.triggers_sizeof,1,LoadFile)!=1)
1337 Error( "Error reading Triggers[%d] in gamesave.c", i);
1339 Triggers[i].type = read_byte(LoadFile);
1340 Triggers[i].flags = read_byte(LoadFile);
1341 Triggers[i].num_links = read_byte(LoadFile);
1342 Triggers[i].pad = read_byte(LoadFile);
1343 Triggers[i].value = read_fix(LoadFile);
1344 Triggers[i].time = read_fix(LoadFile);
1345 for (j=0; j<MAX_WALLS_PER_LINK; j++ )
1346 Triggers[i].seg[j] = read_short(LoadFile);
1347 for (j=0; j<MAX_WALLS_PER_LINK; j++ )
1348 Triggers[i].side[j] = read_short(LoadFile);
1354 //================ READ CONTROL CENTER TRIGGER INFO ===============
1356 if (game_fileinfo.control_offset > -1)
1358 if (!cfseek( LoadFile, game_fileinfo.control_offset,SEEK_SET )) {
1359 for (i=0;i<game_fileinfo.control_howmany;i++)
1361 if (cfread(&ControlCenterTriggers, game_fileinfo.control_sizeof,1,LoadFile)!=1)
1362 Error( "Error reading ControlCenterTriggers in gamesave.c");
1364 ControlCenterTriggers.num_links = read_short(LoadFile);
1365 for (j=0; j<MAX_CONTROLCEN_LINKS; j++ )
1366 ControlCenterTriggers.seg[j] = read_short( LoadFile );
1367 for (j=0; j<MAX_CONTROLCEN_LINKS; j++ )
1368 ControlCenterTriggers.side[j] = read_short( LoadFile );
1374 //================ READ MATERIALOGRIFIZATIONATORS INFO ===============
1376 if (game_fileinfo.matcen_offset > -1)
1379 if (!cfseek( LoadFile, game_fileinfo.matcen_offset,SEEK_SET )) {
1380 // mprintf((0, "Reading %i materialization centers.\n", game_fileinfo.matcen_howmany));
1381 for (i=0;i<game_fileinfo.matcen_howmany;i++) {
1382 if (game_top_fileinfo.fileinfo_version < 27) {
1384 Assert(game_fileinfo.matcen_sizeof == sizeof(m));
1385 if (cfread(&m, game_fileinfo.matcen_sizeof,1,LoadFile)!=1)
1386 Error( "Error reading RobotCenters in gamesave.c");
1387 RobotCenters[i].robot_flags[0] = m.robot_flags;
1388 RobotCenters[i].robot_flags[1] = 0;
1389 RobotCenters[i].hit_points = m.hit_points;
1390 RobotCenters[i].interval = m.interval;
1391 RobotCenters[i].segnum = m.segnum;
1392 RobotCenters[i].fuelcen_num = m.fuelcen_num;
1395 Assert(game_fileinfo.matcen_sizeof == sizeof(RobotCenters[i]));
1397 if (cfread(&RobotCenters[i], game_fileinfo.matcen_sizeof,1,LoadFile)!=1)
1398 Error( "Error reading RobotCenters in gamesave.c");
1400 RobotCenters[i].robot_flags[0] = read_int(LoadFile);
1401 RobotCenters[i].robot_flags[1] = read_int(LoadFile);
1402 RobotCenters[i].hit_points = read_fix(LoadFile);
1403 RobotCenters[i].interval = read_fix(LoadFile);
1404 RobotCenters[i].segnum = read_short(LoadFile);
1405 RobotCenters[i].fuelcen_num = read_short(LoadFile);
1409 // Set links in RobotCenters to Station array
1411 for (j=0; j<=Highest_segment_index; j++)
1412 if (Segment2s[j].special == SEGMENT_IS_ROBOTMAKER)
1413 if (Segment2s[j].matcen_num == i)
1414 RobotCenters[i].fuelcen_num = Segment2s[j].value;
1416 // mprintf((0, " %i: flags = %08x\n", i, RobotCenters[i].robot_flags));
1422 //================ READ DL_INDICES INFO ===============
1424 Num_static_lights = 0;
1426 if (game_fileinfo.dl_indices_offset > -1) {
1429 if (!cfseek( LoadFile, game_fileinfo.dl_indices_offset, SEEK_SET )) {
1430 Num_static_lights = game_fileinfo.dl_indices_howmany;
1431 for (i=0; i<game_fileinfo.dl_indices_howmany; i++) {
1432 if (game_top_fileinfo.fileinfo_version < 29) {
1433 mprintf((0, "Warning: Old mine version. Not reading Dl_indices info.\n"));
1434 Int3(); //shouldn't be here!!!
1437 if (cfread(&Dl_indices[i], game_fileinfo.dl_indices_sizeof, 1, LoadFile) != 1)
1438 Error( "Error reading Dl_indices in gamesave.c");
1440 Dl_indices[i].segnum = read_short(LoadFile);
1441 Dl_indices[i].sidenum = read_byte(LoadFile);
1442 Dl_indices[i].count = read_byte(LoadFile);
1443 Dl_indices[i].index = read_short(LoadFile);
1451 // Indicate that no light has been subtracted from any vertices.
1452 clear_light_subtracted();
1454 //================ READ DELTA LIGHT INFO ===============
1456 if (game_fileinfo.delta_light_offset > -1) {
1459 if (!cfseek( LoadFile, game_fileinfo.delta_light_offset, SEEK_SET )) {
1460 for (i=0; i<game_fileinfo.delta_light_howmany; i++) {
1461 if (game_top_fileinfo.fileinfo_version < 29) {
1462 mprintf((0, "Warning: Old mine version. Not reading delta light info.\n"));
1465 if (cfread(&Delta_lights[i], game_fileinfo.delta_light_sizeof, 1, LoadFile) != 1)
1466 Error( "Error reading Delta Lights in gamesave.c");
1468 Delta_lights[i].segnum = read_short(LoadFile);
1469 Delta_lights[i].sidenum = read_byte(LoadFile);
1470 Delta_lights[i].dummy = read_byte(LoadFile);
1471 Delta_lights[i].vert_light[0] = read_byte(LoadFile);
1472 Delta_lights[i].vert_light[1] = read_byte(LoadFile);
1473 Delta_lights[i].vert_light[2] = read_byte(LoadFile);
1474 Delta_lights[i].vert_light[3] = read_byte(LoadFile);
1482 //========================= UPDATE VARIABLES ======================
1484 reset_objects(game_fileinfo.object_howmany);
1486 for (i=0; i<MAX_OBJECTS; i++) {
1487 Objects[i].next = Objects[i].prev = -1;
1488 if (Objects[i].type != OBJ_NONE) {
1489 int objsegnum = Objects[i].segnum;
1491 if (objsegnum > Highest_segment_index) //bogus object
1492 Objects[i].type = OBJ_NONE;
1494 Objects[i].segnum = -1; //avoid Assert()
1495 obj_link(i,objsegnum);
1500 clear_transient_objects(1); //1 means clear proximity bombs
1502 // Make sure non-transparent doors are set correctly.
1503 for (i=0; i< Num_segments; i++)
1504 for (j=0;j<MAX_SIDES_PER_SEGMENT;j++) {
1505 side *sidep = &Segments[i].sides[j];
1506 if ((sidep->wall_num != -1) && (Walls[sidep->wall_num].clip_num != -1)) {
1507 //mprintf((0, "Checking Wall %d\n", Segments[i].sides[j].wall_num));
1508 if (WallAnims[Walls[sidep->wall_num].clip_num].flags & WCF_TMAP1) {
1509 //mprintf((0, "Fixing non-transparent door.\n"));
1510 sidep->tmap_num = WallAnims[Walls[sidep->wall_num].clip_num].frames[0];
1511 sidep->tmap_num2 = 0;
1517 Num_walls = game_fileinfo.walls_howmany;
1520 Num_open_doors = game_fileinfo.doors_howmany;
1521 Num_triggers = game_fileinfo.triggers_howmany;
1523 //go through all walls, killing references to invalid triggers
1524 for (i=0;i<Num_walls;i++)
1525 if (Walls[i].trigger >= Num_triggers) {
1526 mprintf((0,"Removing reference to invalid trigger %d from wall %d\n",Walls[i].trigger,i));
1527 Walls[i].trigger = -1; //kill trigger
1530 //go through all triggers, killing unused ones
1531 for (i=0;i<Num_triggers;) {
1534 // Find which wall this trigger is connected to.
1535 for (w=0; w<Num_walls; w++)
1536 if (Walls[w].trigger == i)
1540 if (w == Num_walls) {
1541 mprintf((0,"Removing unreferenced trigger %d\n",i));
1542 remove_trigger_num(i);
1549 // MK, 10/17/95: Make walls point back at the triggers that control them.
1550 // Go through all triggers, stuffing controlling_trigger field in Walls.
1553 for (i=0; i<Num_walls; i++)
1554 Walls[i].controlling_trigger = -1;
1556 for (t=0; t<Num_triggers; t++) {
1558 for (l=0; l<Triggers[t].num_links; l++) {
1559 int seg_num, side_num, wall_num;
1561 seg_num = Triggers[t].seg[l];
1562 side_num = Triggers[t].side[l];
1563 wall_num = Segments[seg_num].sides[side_num].wall_num;
1565 // -- if (Walls[wall_num].controlling_trigger != -1)
1568 //check to see that if a trigger requires a wall that it has one,
1569 //and if it requires a matcen that it has one
1571 if (Triggers[t].type == TT_MATCEN) {
1572 if (Segment2s[seg_num].special != SEGMENT_IS_ROBOTMAKER)
1573 Int3(); //matcen trigger doesn't point to matcen
1575 else if (Triggers[t].type != TT_LIGHT_OFF && Triggers[t].type != TT_LIGHT_ON) { //light triggers don't require walls
1577 Int3(); // This is illegal. This trigger requires a wall
1579 Walls[wall_num].controlling_trigger = t;
1585 Num_robot_centers = game_fileinfo.matcen_howmany;
1587 //fix old wall structs
1588 if (game_top_fileinfo.fileinfo_version < 17) {
1589 int segnum,sidenum,wallnum;
1591 for (segnum=0; segnum<=Highest_segment_index; segnum++)
1592 for (sidenum=0;sidenum<6;sidenum++)
1593 if ((wallnum=Segments[segnum].sides[sidenum].wall_num) != -1) {
1594 Walls[wallnum].segnum = segnum;
1595 Walls[wallnum].sidenum = sidenum;
1602 for (sidenum=0; sidenum<6; sidenum++) {
1603 int wallnum = Segments[Highest_segment_index].sides[sidenum].wall_num;
1605 if ((Walls[wallnum].segnum != Highest_segment_index) || (Walls[wallnum].sidenum != sidenum))
1606 Int3(); // Error. Bogus walls in this segment.
1607 // Consult Yuan or Mike.
1612 //create_local_segment_data();
1620 if (game_top_fileinfo.fileinfo_version < GAME_VERSION && !(game_top_fileinfo.fileinfo_version==25 && GAME_VERSION==26))
1621 return 1; //means old version
1627 int check_segment_connections(void);
1629 extern void set_ambient_sound_flags(void);
1631 // -----------------------------------------------------------------------------
1632 //loads from an already-open file
1633 // returns 0=everything ok, 1=old version, -1=error
1634 int load_mine_data(CFILE *LoadFile);
1635 int load_mine_data_compiled(CFILE *LoadFile);
1637 #define LEVEL_FILE_VERSION 8
1638 //1 -> 2 add palette name
1639 //2 -> 3 add control center explosion time
1640 //3 -> 4 add reactor strength
1641 //4 -> 5 killed hostage text stuff
1642 //5 -> 6 added Secret_return_segment and Secret_return_orient
1643 //6 -> 7 added flickering lights
1644 //7 -> 8 made version 8 to be not compatible with D2 1.0 & 1.1
1647 char *Level_being_loaded=NULL;
1651 extern void ncache_flush();
1654 extern int HoardEquipped();
1656 extern int Slide_segs_computed;
1658 int no_old_level_file_error=0;
1660 //loads a level (.LVL) file from disk
1661 //returns 0 if success, else error code
1662 int load_level(char * filename_passed)
1665 int use_compiled_level=1;
1669 int sig,version,minedata_offset,gamedata_offset;
1670 int mine_err,game_err;
1675 Slide_segs_computed = 0;
1678 if (Game_mode & GM_NETWORK)
1680 for (i=0;i<MAX_POWERUP_TYPES;i++)
1682 MaxPowerupsAllowed[i]=0;
1683 PowerupsInMine[i]=0;
1693 Level_being_loaded = filename_passed;
1696 strcpy(filename,filename_passed);
1700 //if we have the editor, try the LVL first, no matter what was passed.
1701 //if we don't have an LVL, try RDL
1702 //if we don't have the editor, we just use what was passed
1704 change_filename_extension(filename,filename_passed,".LVL");
1705 use_compiled_level = 0;
1707 if (!cfexist(filename)) {
1708 change_filename_extension(filename,filename,".RL2");
1709 use_compiled_level = 1;
1713 LoadFile = cfopen( filename, "rb" );
1717 mprintf((0,"Can't open level file <%s>\n", filename));
1720 Error("Can't open file <%s>\n",filename);
1724 strcpy( Gamesave_current_filename, filename );
1727 // if ( Newdemo_state == ND_STATE_RECORDING )
1728 // newdemo_record_start_demo();
1731 sig = read_int(LoadFile);
1732 version = read_int(LoadFile);
1733 minedata_offset = read_int(LoadFile);
1734 gamedata_offset = read_int(LoadFile);
1736 Assert(sig == MAKE_SIG('P','L','V','L'));
1738 if (version >= 8) { //read dummy data
1740 if (HoardEquipped())
1743 read_short(LoadFile);
1744 read_byte(LoadFile);
1748 // NOTE LINK TO ABOVE!
1749 Error("This level requires the Vertigo Enhanced version of D2.");
1754 read_int(LoadFile); //was hostagetext_offset
1757 cfgets(Current_level_palette,sizeof(Current_level_palette),LoadFile);
1758 if (Current_level_palette[strlen(Current_level_palette)-1] == '\n')
1759 Current_level_palette[strlen(Current_level_palette)-1] = 0;
1763 Base_control_center_explosion_time = read_int(LoadFile);
1765 Base_control_center_explosion_time = DEFAULT_CONTROL_CENTER_EXPLOSION_TIME;
1768 Reactor_strength = read_int(LoadFile);
1770 Reactor_strength = -1; //use old defaults
1773 Num_flickering_lights = read_int(LoadFile);
1775 Assert((Num_flickering_lights >= 0) && (Num_flickering_lights < MAX_FLICKERING_LIGHTS));
1776 for (i = 0; i < Num_flickering_lights; i++)
1778 Flickering_lights[i].segnum = read_short(LoadFile);
1779 Flickering_lights[i].sidenum = read_short(LoadFile);
1780 Flickering_lights[i].mask = read_int(LoadFile);
1781 Flickering_lights[i].timer = read_fix(LoadFile);
1782 Flickering_lights[i].delay = read_fix(LoadFile);
1785 cfread(Flickering_lights,sizeof(*Flickering_lights),Num_flickering_lights,LoadFile);
1789 Num_flickering_lights = 0;
1791 if (version <= 1 || Current_level_palette[0]==0)
1792 strcpy(Current_level_palette,"groupa.256");
1795 Secret_return_segment = 0;
1796 Secret_return_orient.rvec.x = F1_0; Secret_return_orient.rvec.y = 0; Secret_return_orient.rvec.z = 0;
1797 Secret_return_orient.fvec.x = 0; Secret_return_orient.fvec.y = F1_0; Secret_return_orient.fvec.z = 0;
1798 Secret_return_orient.uvec.x = 0; Secret_return_orient.uvec.y = 0; Secret_return_orient.uvec.z = F1_0;
1800 Secret_return_segment = read_int(LoadFile);
1801 Secret_return_orient.rvec.x = read_int(LoadFile);
1802 Secret_return_orient.rvec.y = read_int(LoadFile);
1803 Secret_return_orient.rvec.z = read_int(LoadFile);
1804 Secret_return_orient.fvec.x = read_int(LoadFile);
1805 Secret_return_orient.fvec.y = read_int(LoadFile);
1806 Secret_return_orient.fvec.z = read_int(LoadFile);
1807 Secret_return_orient.uvec.x = read_int(LoadFile);
1808 Secret_return_orient.uvec.y = read_int(LoadFile);
1809 Secret_return_orient.uvec.z = read_int(LoadFile);
1812 cfseek(LoadFile,minedata_offset,SEEK_SET);
1814 if (!use_compiled_level) {
1815 mine_err = load_mine_data(LoadFile);
1816 #if 0 //dunno - 3rd party stuff?
1817 // Compress all uv coordinates in mine, improves texmap precision. --MK, 02/19/96
1818 compress_uv_coordinates_all();
1822 //NOTE LINK TO ABOVE!!
1823 mine_err = load_mine_data_compiled(LoadFile);
1825 if (mine_err == -1) { //error!!
1830 cfseek(LoadFile,gamedata_offset,SEEK_SET);
1831 game_err = load_game_data(LoadFile);
1833 if (game_err == -1) { //error!!
1838 //======================== CLOSE FILE =============================
1840 cfclose( LoadFile );
1842 set_ambient_sound_flags();
1845 write_game_text_file(filename);
1846 if (Errors_in_mine) {
1847 if (is_real_level(filename)) {
1848 char ErrorMessage[200];
1850 sprintf( ErrorMessage, "Warning: %i errors in %s!\n", Errors_in_mine, Level_being_loaded );
1852 gr_palette_load(gr_palette);
1853 nm_messagebox( NULL, 1, "Continue", ErrorMessage );
1856 mprintf((1, "Error: %i errors in %s.\n", Errors_in_mine, Level_being_loaded));
1861 //If an old version, ask the use if he wants to save as new version
1862 if (!no_old_level_file_error && (Function_mode == FMODE_EDITOR) && (((LEVEL_FILE_VERSION>3) && version<LEVEL_FILE_VERSION) || mine_err==1 || game_err==1)) {
1863 char ErrorMessage[200];
1865 sprintf( ErrorMessage,
1866 "You just loaded a old version\n"
1867 "level. Would you like to save\n"
1868 "it as a current version level?");
1871 gr_palette_load(gr_palette);
1872 if (nm_messagebox( NULL, 2, "Don't Save", "Save", ErrorMessage )==1)
1873 save_level(filename);
1879 if (Function_mode == FMODE_EDITOR)
1880 editor_status("Loaded NEW mine %s, \"%s\"",filename,Current_level_name);
1884 if (check_segment_connections())
1885 nm_messagebox( "ERROR", 1, "Ok",
1886 "Connectivity errors detected in\n"
1887 "mine. See monochrome screen for\n"
1888 "details, and contact Matt or Mike." );
1895 void get_level_name()
1897 //NO_UI!!! UI_WINDOW *NameWindow = NULL;
1898 //NO_UI!!! UI_GADGET_INPUTBOX *NameText;
1899 //NO_UI!!! UI_GADGET_BUTTON *QuitButton;
1901 //NO_UI!!! // Open a window with a quit button
1902 //NO_UI!!! NameWindow = ui_open_window( 20, 20, 300, 110, WIN_DIALOG );
1903 //NO_UI!!! QuitButton = ui_add_gadget_button( NameWindow, 150-24, 60, 48, 40, "Done", NULL );
1905 //NO_UI!!! ui_wprintf_at( NameWindow, 10, 12,"Please enter a name for this mine:" );
1906 //NO_UI!!! NameText = ui_add_gadget_inputbox( NameWindow, 10, 30, LEVEL_NAME_LEN, LEVEL_NAME_LEN, Current_level_name );
1908 //NO_UI!!! NameWindow->keyboard_focus_gadget = (UI_GADGET *)NameText;
1909 //NO_UI!!! QuitButton->hotkey = KEY_ENTER;
1911 //NO_UI!!! ui_gadget_calc_keys(NameWindow);
1913 //NO_UI!!! while (!QuitButton->pressed && last_keypress!=KEY_ENTER) {
1914 //NO_UI!!! ui_mega_process();
1915 //NO_UI!!! ui_window_do_gadgets(NameWindow);
1918 //NO_UI!!! strcpy( Current_level_name, NameText->text );
1920 //NO_UI!!! if ( NameWindow!=NULL ) {
1921 //NO_UI!!! ui_close_window( NameWindow );
1922 //NO_UI!!! NameWindow = NULL;
1928 m[0].type = NM_TYPE_TEXT; m[0].text = "Please enter a name for this mine:";
1929 m[1].type = NM_TYPE_INPUT; m[1].text = Current_level_name; m[1].text_len = LEVEL_NAME_LEN;
1931 newmenu_do( NULL, "Enter mine name", 2, m, NULL );
1941 // -----------------------------------------------------------------------------
1942 int compute_num_delta_light_records(void)
1947 for (i=0; i<Num_static_lights; i++) {
1948 total += Dl_indices[i].count;
1955 // -----------------------------------------------------------------------------
1957 int save_game_data(FILE * SaveFile)
1959 int player_offset, object_offset, walls_offset, doors_offset, triggers_offset, control_offset, matcen_offset; //, links_offset;
1960 int dl_indices_offset, delta_light_offset;
1961 int start_offset,end_offset;
1963 start_offset = ftell(SaveFile);
1965 //===================== SAVE FILE INFO ========================
1967 game_fileinfo.fileinfo_signature = 0x6705;
1968 game_fileinfo.fileinfo_version = GAME_VERSION;
1969 game_fileinfo.level = Current_level_num;
1970 game_fileinfo.fileinfo_sizeof = sizeof(game_fileinfo);
1971 game_fileinfo.player_offset = -1;
1972 game_fileinfo.player_sizeof = sizeof(player);
1973 game_fileinfo.object_offset = -1;
1974 game_fileinfo.object_howmany = Highest_object_index+1;
1975 game_fileinfo.object_sizeof = sizeof(object);
1976 game_fileinfo.walls_offset = -1;
1977 game_fileinfo.walls_howmany = Num_walls;
1978 game_fileinfo.walls_sizeof = sizeof(wall);
1979 game_fileinfo.doors_offset = -1;
1980 game_fileinfo.doors_howmany = Num_open_doors;
1981 game_fileinfo.doors_sizeof = sizeof(active_door);
1982 game_fileinfo.triggers_offset = -1;
1983 game_fileinfo.triggers_howmany = Num_triggers;
1984 game_fileinfo.triggers_sizeof = sizeof(trigger);
1985 game_fileinfo.control_offset = -1;
1986 game_fileinfo.control_howmany = 1;
1987 game_fileinfo.control_sizeof = sizeof(control_center_triggers);
1988 game_fileinfo.matcen_offset = -1;
1989 game_fileinfo.matcen_howmany = Num_robot_centers;
1990 game_fileinfo.matcen_sizeof = sizeof(matcen_info);
1992 game_fileinfo.dl_indices_offset = -1;
1993 game_fileinfo.dl_indices_howmany = Num_static_lights;
1994 game_fileinfo.dl_indices_sizeof = sizeof(dl_index);
1996 game_fileinfo.delta_light_offset = -1;
1997 game_fileinfo.delta_light_howmany = compute_num_delta_light_records();
1998 game_fileinfo.delta_light_sizeof = sizeof(delta_light);
2000 // Write the fileinfo
2001 fwrite( &game_fileinfo, sizeof(game_fileinfo), 1, SaveFile );
2003 // Write the mine name
2004 fprintf(SaveFile,"%s\n",Current_level_name);
2006 fwrite(&N_polygon_models,2,1,SaveFile);
2007 fwrite(Pof_names,N_polygon_models,sizeof(*Pof_names),SaveFile);
2009 //==================== SAVE PLAYER INFO ===========================
2011 player_offset = ftell(SaveFile);
2012 fwrite( &Players[Player_num], sizeof(player), 1, SaveFile );
2014 //==================== SAVE OBJECT INFO ===========================
2016 object_offset = ftell(SaveFile);
2017 //fwrite( &Objects, sizeof(object), game_fileinfo.object_howmany, SaveFile );
2020 for (i=0;i<game_fileinfo.object_howmany;i++)
2021 write_object(&Objects[i],SaveFile);
2024 //==================== SAVE WALL INFO =============================
2026 walls_offset = ftell(SaveFile);
2027 fwrite( Walls, sizeof(wall), game_fileinfo.walls_howmany, SaveFile );
2029 //==================== SAVE DOOR INFO =============================
2031 doors_offset = ftell(SaveFile);
2032 fwrite( ActiveDoors, sizeof(active_door), game_fileinfo.doors_howmany, SaveFile );
2034 //==================== SAVE TRIGGER INFO =============================
2036 triggers_offset = ftell(SaveFile);
2037 fwrite( Triggers, sizeof(trigger), game_fileinfo.triggers_howmany, SaveFile );
2039 //================ SAVE CONTROL CENTER TRIGGER INFO ===============
2041 control_offset = ftell(SaveFile);
2042 fwrite( &ControlCenterTriggers, sizeof(control_center_triggers), 1, SaveFile );
2045 //================ SAVE MATERIALIZATION CENTER TRIGGER INFO ===============
2047 matcen_offset = ftell(SaveFile);
2048 // mprintf((0, "Writing %i materialization centers\n", game_fileinfo.matcen_howmany));
2050 // for (i=0; i<game_fileinfo.matcen_howmany; i++)
2051 // mprintf((0, " %i: robot_flags = %08x\n", i, RobotCenters[i].robot_flags));
2053 fwrite( RobotCenters, sizeof(matcen_info), game_fileinfo.matcen_howmany, SaveFile );
2055 //================ SAVE DELTA LIGHT INFO ===============
2056 dl_indices_offset = ftell(SaveFile);
2057 fwrite( Dl_indices, sizeof(dl_index), game_fileinfo.dl_indices_howmany, SaveFile );
2059 delta_light_offset = ftell(SaveFile);
2060 fwrite( Delta_lights, sizeof(delta_light), game_fileinfo.delta_light_howmany, SaveFile );
2062 //============= REWRITE FILE INFO, TO SAVE OFFSETS ===============
2064 // Update the offset fields
2065 game_fileinfo.player_offset = player_offset;
2066 game_fileinfo.object_offset = object_offset;
2067 game_fileinfo.walls_offset = walls_offset;
2068 game_fileinfo.doors_offset = doors_offset;
2069 game_fileinfo.triggers_offset = triggers_offset;
2070 game_fileinfo.control_offset = control_offset;
2071 game_fileinfo.matcen_offset = matcen_offset;
2072 game_fileinfo.dl_indices_offset = dl_indices_offset;
2073 game_fileinfo.delta_light_offset = delta_light_offset;
2076 end_offset = ftell(SaveFile);
2078 // Write the fileinfo
2079 fseek( SaveFile, start_offset, SEEK_SET ); // Move to TOF
2080 fwrite( &game_fileinfo, sizeof(game_fileinfo), 1, SaveFile );
2082 // Go back to end of data
2083 fseek(SaveFile,end_offset,SEEK_SET);
2088 int save_mine_data(FILE * SaveFile);
2090 // -----------------------------------------------------------------------------
2092 int save_level_sub(char * filename, int compiled_version)
2095 char temp_filename[128];
2096 int sig = MAKE_SIG('P','L','V','L'),version=LEVEL_FILE_VERSION;
2097 int minedata_offset=0,gamedata_offset=0;
2099 if ( !compiled_version ) {
2100 write_game_text_file(filename);
2102 if (Errors_in_mine) {
2103 if (is_real_level(filename)) {
2104 char ErrorMessage[200];
2106 sprintf( ErrorMessage, "Warning: %i errors in this mine!\n", Errors_in_mine );
2108 gr_palette_load(gr_palette);
2110 if (nm_messagebox( NULL, 2, "Cancel Save", "Save", ErrorMessage )!=1) {
2116 mprintf((1, "Error: %i errors in this mine. See the 'txm' file.\n", Errors_in_mine));
2118 change_filename_extension(temp_filename,filename,".LVL");
2122 // macs are using the regular hog/rl2 files for shareware
2123 #if defined(SHAREWARE) && !defined(MACINTOSH)
2124 change_filename_extension(temp_filename,filename,".SL2");
2126 change_filename_extension(temp_filename,filename,".RL2");
2130 SaveFile = fopen( temp_filename, "wb" );
2133 char ErrorMessage[256];
2136 _splitpath( temp_filename, NULL, NULL, fname, NULL );
2138 sprintf( ErrorMessage, \
2139 "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"
2140 , temp_filename, fname );
2142 gr_palette_load(gr_palette);
2143 nm_messagebox( NULL, 1, "Ok", ErrorMessage );
2148 if (Current_level_name[0] == 0)
2149 strcpy(Current_level_name,"Untitled");
2151 clear_transient_objects(1); //1 means clear proximity bombs
2153 compress_objects(); //after this, Highest_object_index == num objects
2155 //make sure player is in a segment
2156 if (update_object_seg(&Objects[Players[0].objnum]) == 0) {
2157 if (ConsoleObject->segnum > Highest_segment_index)
2158 ConsoleObject->segnum = 0;
2159 compute_segment_center(&ConsoleObject->pos,&(Segments[ConsoleObject->segnum]));
2166 gs_write_int(sig,SaveFile);
2167 gs_write_int(version,SaveFile);
2170 gs_write_int(minedata_offset,SaveFile);
2171 gs_write_int(gamedata_offset,SaveFile);
2173 //Now write the damn data
2175 //write the version 8 data (to make file unreadable by 1.0 & 1.1)
2176 gs_write_int(GameTime,SaveFile);
2177 gs_write_short(FrameCount,SaveFile);
2178 gs_write_byte(FrameTime,SaveFile);
2180 // Write the palette file name
2181 fprintf(SaveFile,"%s\n",Current_level_palette);
2183 gs_write_int(Base_control_center_explosion_time,SaveFile);
2184 gs_write_int(Reactor_strength,SaveFile);
2186 gs_write_int(Num_flickering_lights,SaveFile);
2187 fwrite(Flickering_lights,sizeof(*Flickering_lights),Num_flickering_lights,SaveFile);
2189 gs_write_int(Secret_return_segment, SaveFile);
2190 gs_write_int(Secret_return_orient.rvec.x, SaveFile);
2191 gs_write_int(Secret_return_orient.rvec.y, SaveFile);
2192 gs_write_int(Secret_return_orient.rvec.z, SaveFile);
2193 gs_write_int(Secret_return_orient.fvec.x, SaveFile);
2194 gs_write_int(Secret_return_orient.fvec.y, SaveFile);
2195 gs_write_int(Secret_return_orient.fvec.z, SaveFile);
2196 gs_write_int(Secret_return_orient.uvec.x, SaveFile);
2197 gs_write_int(Secret_return_orient.uvec.y, SaveFile);
2198 gs_write_int(Secret_return_orient.uvec.z, SaveFile);
2200 minedata_offset = ftell(SaveFile);
2201 if ( !compiled_version )
2202 save_mine_data(SaveFile);
2204 save_mine_data_compiled(SaveFile);
2205 gamedata_offset = ftell(SaveFile);
2206 save_game_data(SaveFile);
2208 fseek(SaveFile,sizeof(sig)+sizeof(version),SEEK_SET);
2209 gs_write_int(minedata_offset,SaveFile);
2210 gs_write_int(gamedata_offset,SaveFile);
2212 //==================== CLOSE THE FILE =============================
2215 if ( !compiled_version ) {
2216 if (Function_mode == FMODE_EDITOR)
2217 editor_status("Saved mine %s, \"%s\"",filename,Current_level_name);
2224 #if 0 //dunno - 3rd party stuff?
2225 extern void compress_uv_coordinates_all(void);
2228 int save_level(char * filename)
2232 // Save normal version...
2233 r1 = save_level_sub(filename, 0);
2235 // Save compiled version...
2236 save_level_sub(filename, 1);
2244 void dump_mine_info(void)
2246 int segnum, sidenum;
2247 fix min_u, max_u, min_v, max_v, min_l, max_l, max_sl;
2259 for (segnum=0; segnum<=Highest_segment_index; segnum++) {
2260 for (sidenum=0; sidenum<MAX_SIDES_PER_SEGMENT; sidenum++) {
2262 side *sidep = &Segments[segnum].sides[sidenum];
2264 if (Segment2s[segnum].static_light > max_sl)
2265 max_sl = Segment2s[segnum].static_light;
2267 for (vertnum=0; vertnum<4; vertnum++) {
2268 if (sidep->uvls[vertnum].u < min_u)
2269 min_u = sidep->uvls[vertnum].u;
2270 else if (sidep->uvls[vertnum].u > max_u)
2271 max_u = sidep->uvls[vertnum].u;
2273 if (sidep->uvls[vertnum].v < min_v)
2274 min_v = sidep->uvls[vertnum].v;
2275 else if (sidep->uvls[vertnum].v > max_v)
2276 max_v = sidep->uvls[vertnum].v;
2278 if (sidep->uvls[vertnum].l < min_l)
2279 min_l = sidep->uvls[vertnum].l;
2280 else if (sidep->uvls[vertnum].l > max_l)
2281 max_l = sidep->uvls[vertnum].l;
2287 // 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)));
2288 // mprintf((0, "Static light maximum = %7.3f\n", f2fl(max_sl)));
2289 // mprintf((0, "Number of walls: %i\n", Num_walls));
2297 //read in every level in mission and save out compiled version
2298 void save_all_compiled_levels(void)
2300 do_load_save_levels(1);
2303 //read in every level in mission
2304 void load_all_levels(void)
2306 do_load_save_levels(0);
2310 void do_load_save_levels(int save)
2314 if (! SafetyCheck())
2317 no_old_level_file_error=1;
2319 for (level_num=1;level_num<=Last_level;level_num++) {
2320 load_level(Level_names[level_num-1]);
2321 load_palette(Current_level_palette,1,1); //don't change screen
2323 save_level_sub(Level_names[level_num-1],1);
2326 for (level_num=-1;level_num>=Last_secret_level;level_num--) {
2327 load_level(Secret_level_names[-level_num-1]);
2328 load_palette(Current_level_palette,1,1); //don't change screen
2330 save_level_sub(Secret_level_names[-level_num-1],1);
2333 no_old_level_file_error=0;