/* $Id: gamesave.c,v 1.21 2003-06-16 07:15:59 btb Exp $ */ /* THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE. COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED. */ /* * * Save game information * * Old Log: * Revision 1.3 1996/02/21 13:59:17 allender * check Data folder when can't open a level file from a hog * * Revision 1.2 1995/10/31 10:23:23 allender * shareware stuff * * Revision 1.1 1995/05/16 15:25:37 allender * Initial revision * * Revision 2.2 1995/04/23 14:53:12 john * Made some mine structures read in with no structure packing problems. * * Revision 2.1 1995/03/20 18:15:43 john * Added code to not store the normals in the segment structure. * * Revision 2.0 1995/02/27 11:29:50 john * New version 2.0, which has no anonymous unions, builds with * Watcom 10.0, and doesn't require parsing BITMAPS.TBL. * * Revision 1.207 1995/02/23 10:17:36 allender * fixed parameter mismatch with compute_segment_center * * Revision 1.206 1995/02/22 14:51:17 allender * fixed some things that I missed * * Revision 1.205 1995/02/22 13:31:38 allender * remove anonymous unions from object structure * * Revision 1.204 1995/02/01 20:58:08 john * Made editor check hog. * * Revision 1.203 1995/01/28 17:40:34 mike * correct level names (use rdl, sdl) for dumpmine stuff. * * Revision 1.202 1995/01/25 20:03:46 matt * Moved matrix check to avoid orthogonalizing an uninitialize matrix * * Revision 1.201 1995/01/20 16:56:53 mike * remove some mprintfs. * * Revision 1.200 1995/01/15 19:42:13 matt * Ripped out hostage faces for registered version * * Revision 1.199 1995/01/05 16:59:09 yuan * Make it so if editor is loaded, don't get error from typo * in filename. * * Revision 1.198 1994/12/19 12:49:46 mike * Change fgets to cfgets. fgets was getting a pointer mismatch warning. * * Revision 1.197 1994/12/12 01:20:03 matt * Took out object size hack for green claw guys * * Revision 1.196 1994/12/11 13:19:37 matt * Restored calls to fix_object_segs() when debugging is turned off, since * it's not a big routine, and could fix some possibly bad problems. * * Revision 1.195 1994/12/10 16:17:24 mike * fix editor bug that was converting transparent walls into rock. * * Revision 1.194 1994/12/09 14:59:27 matt * Added system to attach a fireball to another object for rendering purposes, * so the fireball always renders on top of (after) the object. * * Revision 1.193 1994/12/08 17:19:02 yuan * Cfiling stuff. * * Revision 1.192 1994/12/02 20:01:05 matt * Always give vulcan cannon powerup same amount of ammo, regardless of * how much it was saved with * * Revision 1.191 1994/11/30 17:45:57 yuan * Saving files now creates RDL/SDLs instead of CDLs. * * Revision 1.190 1994/11/30 17:22:14 matt * Ripped out hostage faces in shareware version * * Revision 1.189 1994/11/28 00:09:30 allender * commented out call to newdemo_record_start_demo in load_level...what is * this doing here anyway????? * * Revision 1.188 1994/11/27 23:13:48 matt * Made changes for new mprintf calling convention * * Revision 1.187 1994/11/27 18:06:20 matt * Cleaned up LVL/CDL file loading * * Revision 1.186 1994/11/25 22:46:29 matt * Allow ESC out of compiled/normal menu (esc=compiled). * * Revision 1.185 1994/11/23 12:18:35 mike * move level names here...a more logical place than dumpmine. * * Revision 1.184 1994/11/21 20:29:19 matt * If hostage info is bad, fix it. * * Revision 1.183 1994/11/21 20:26:07 matt * Fixed bug, I hope * * Revision 1.182 1994/11/21 20:20:37 matt * Fixed stupid mistake * * Revision 1.181 1994/11/21 20:18:40 matt * Fixed (hopefully) totally bogus writing of hostage data * * Revision 1.180 1994/11/20 14:11:56 matt * Gracefully handle two hostages having same id * * Revision 1.179 1994/11/19 23:55:05 mike * remove Assert, put in comment for Matt. * * Revision 1.178 1994/11/19 19:53:24 matt * Added code to full support different hostage head clip & message for * each hostage. * * Revision 1.177 1994/11/19 15:15:21 mike * remove unused code and data * * Revision 1.176 1994/11/19 10:28:28 matt * Took out write routines when editor compiled out * * Revision 1.175 1994/11/17 20:38:25 john * Took out warning. * * Revision 1.174 1994/11/17 20:36:34 john * Made it so that saving a mine will write the .cdl even * if .lvl gets error. * * Revision 1.173 1994/11/17 20:26:19 john * Made the game load whichever of .cdl or .lvl exists, * and if they both exist, prompt the user for which one. * * Revision 1.172 1994/11/17 20:11:20 john * Fixed warning. * * Revision 1.171 1994/11/17 20:09:26 john * Added new compiled level format. * * Revision 1.170 1994/11/17 14:57:21 mike * moved segment validation functions from editor to main. * * Revision 1.169 1994/11/17 11:39:21 matt * Ripped out code to load old mines * * Revision 1.168 1994/11/16 11:24:53 matt * Made attack-type robots have smaller radius, so they get closer to player * * Revision 1.167 1994/11/15 21:42:47 mike * better error messages. * * Revision 1.166 1994/11/15 15:30:41 matt * Save ptr to name of level being loaded * * Revision 1.165 1994/11/14 20:47:46 john * Attempted to strip out all the code in the game * directory that uses any ui code. * * Revision 1.164 1994/11/14 14:34:23 matt * Fixed up handling when textures can't be found during remap * * Revision 1.163 1994/11/10 14:02:49 matt * Hacked in support for player ships with different textures * * Revision 1.162 1994/11/06 14:38:17 mike * Remove an apparently unnecessary mprintf. * * Revision 1.161 1994/10/30 14:11:28 mike * ripout local segments stuff. * * Revision 1.160 1994/10/28 12:10:41 matt * Check that was supposed to happen only when editor was in was happening * only when editor was out. * * Revision 1.159 1994/10/27 11:25:32 matt * Only do connectivity error check when editor in * * Revision 1.158 1994/10/27 10:54:00 matt * Made connectivity error checking put up warning if errors found * * Revision 1.157 1994/10/25 10:50:54 matt * Vulcan cannon powerups now contain ammo count * * Revision 1.156 1994/10/23 02:10:43 matt * Got rid of obsolete hostage_info stuff * * Revision 1.155 1994/10/22 18:57:26 matt * Added call to check_segment_connections() * * Revision 1.154 1994/10/21 12:19:23 matt * Clear transient objects when saving (& loading) games * * Revision 1.153 1994/10/21 11:25:10 mike * Use new constant IMMORTAL_TIME. * * Revision 1.152 1994/10/20 12:46:59 matt * Replace old save files (MIN/SAV/HOT) with new LVL files * * Revision 1.151 1994/10/19 19:26:32 matt * Fixed stupid bug * * Revision 1.150 1994/10/19 16:46:21 matt * Made tmap overrides for robots remap texture numbers * * Revision 1.149 1994/10/18 08:50:27 yuan * Fixed correct variable this time. * * Revision 1.148 1994/10/18 08:45:02 yuan * Oops. forgot load function. * * Revision 1.147 1994/10/18 08:42:10 yuan * Avoid the int3. * * Revision 1.146 1994/10/17 21:34:57 matt * Added support for new Control Center/Main Reactor * * Revision 1.145 1994/10/15 19:06:34 mike * Fix bug, maybe, having to do with something or other, ... * * Revision 1.144 1994/10/12 21:07:33 matt * Killed unused field in object structure * * Revision 1.143 1994/10/06 14:52:55 mike * Put check in to detect possibly bogus walls in last segment which leaked through an earlier check * due to misuse of Highest_segment_index. * * Revision 1.142 1994/10/05 22:12:44 mike * Put in cleanup for matcen/fuelcen links. * * Revision 1.141 1994/10/03 11:30:05 matt * Make sure player in a valid segment before saving * * Revision 1.140 1994/09/28 11:14:41 mike * Better error messaging on bogus mines: Only bring up dialog box if a "real" (level??.*) level. * * Revision 1.139 1994/09/28 09:22:58 mike * Comment out a mprintf. * * Revision 1.138 1994/09/27 17:08:36 mike * Message boxes when you load bogus mines. * * Revision 1.137 1994/09/27 15:43:45 mike * Move the dump stuff to dumpmine. * * Revision 1.136 1994/09/27 00:02:31 mike * Dump text files (".txm") when loading a mine, showing all kinds of useful mine info. * * Revision 1.135 1994/09/26 11:30:41 matt * Took out code which loaded bogus player structure * * Revision 1.134 1994/09/26 11:18:44 john * Fixed some conflicts with newseg. * * Revision 1.133 1994/09/26 10:56:58 matt * Fixed inconsistancies in lifeleft for immortal objects * * Revision 1.132 1994/09/25 23:41:10 matt * Changed the object load & save code to read/write the structure fields one * at a time (rather than the whole structure at once). This mean that the * object structure can be changed without breaking the load/save functions. * As a result of this change, the local_object data can be and has been * incorporated into the object array. Also, timeleft is now a property * of all objects, and the object structure has been otherwise cleaned up. * */ #ifdef HAVE_CONFIG_H #include #endif #ifdef RCS char gamesave_rcsid[] = "$Id: gamesave.c,v 1.21 2003-06-16 07:15:59 btb Exp $"; #endif #include #include #include "pstypes.h" #include "strutil.h" #include "mono.h" #include "key.h" #include "gr.h" #include "palette.h" #include "newmenu.h" #include "inferno.h" #ifdef EDITOR #include "editor/editor.h" #endif #include "error.h" #include "object.h" #include "game.h" #include "screens.h" #include "wall.h" #include "gamemine.h" #include "robot.h" #include "cfile.h" #include "bm.h" #include "menu.h" #include "switch.h" #include "fuelcen.h" #include "cntrlcen.h" #include "powerup.h" #include "weapon.h" #include "newdemo.h" #include "gameseq.h" #include "automap.h" #include "polyobj.h" #include "text.h" #include "gamefont.h" #include "gamesave.h" #include "gamepal.h" #include "laser.h" #include "byteswap.h" #include "multi.h" #include "makesig.h" char Gamesave_current_filename[128]; int Gamesave_current_version; #define GAME_VERSION 32 #define GAME_COMPATIBLE_VERSION 22 //version 28->29 add delta light support //version 27->28 controlcen id now is reactor number, not model number //version 28->29 ?? //version 29->30 changed trigger structure //version 30->31 changed trigger structure some more //version 31->32 change segment structure, make it 512 bytes w/o editor, add Segment2s array. #define MENU_CURSOR_X_MIN MENU_X #define MENU_CURSOR_X_MAX MENU_X+6 struct { ushort fileinfo_signature; ushort fileinfo_version; int fileinfo_sizeof; } game_top_fileinfo; // Should be same as first two fields below... struct { ushort fileinfo_signature; ushort fileinfo_version; int fileinfo_sizeof; char mine_filename[15]; int level; int player_offset; // Player info int player_sizeof; int object_offset; // Object info int object_howmany; int object_sizeof; int walls_offset; int walls_howmany; int walls_sizeof; int doors_offset; int doors_howmany; int doors_sizeof; int triggers_offset; int triggers_howmany; int triggers_sizeof; int links_offset; int links_howmany; int links_sizeof; int control_offset; int control_howmany; int control_sizeof; int matcen_offset; int matcen_howmany; int matcen_sizeof; int dl_indices_offset; int dl_indices_howmany; int dl_indices_sizeof; int delta_light_offset; int delta_light_howmany; int delta_light_sizeof; } game_fileinfo; // LINT: adding function prototypes void read_object(object *obj, CFILE *f, int version); #ifdef EDITOR void write_object(object *obj, FILE *f); void do_load_save_levels(int save); #endif #ifndef NDEBUG void dump_mine_info(void); #endif extern char MaxPowerupsAllowed[MAX_POWERUP_TYPES]; extern char PowerupsInMine[MAX_POWERUP_TYPES]; #ifdef EDITOR extern char mine_filename[]; extern int save_mine_data_compiled(FILE * SaveFile); //--unused-- #else //--unused-- char mine_filename[128]; #endif int Gamesave_num_org_robots = 0; //--unused-- grs_bitmap * Gamesave_saved_bitmap = NULL; #ifdef EDITOR // Return true if this level has a name of the form "level??" // Note that a pathspec can appear at the beginning of the filename. int is_real_level(char *filename) { int len = strlen(filename); if (len < 6) return 0; //mprintf((0, "String = [%s]\n", &filename[len-11])); return !strnicmp(&filename[len-11], "level", 5); } #endif void change_filename_extension( char *dest, char *src, char *new_ext ) { int i; strcpy (dest, src); if (new_ext[0]=='.') new_ext++; for (i=1; ip=(v)->b=(v)->h=0;} while (0) int Gamesave_num_players=0; int N_save_pof_names; char Save_pof_names[MAX_POLYGON_MODELS][FILENAME_LEN]; void check_and_fix_matrix(vms_matrix *m); void verify_object( object * obj ) { obj->lifeleft = IMMORTAL_TIME; //all loaded object are immortal, for now if ( obj->type == OBJ_ROBOT ) { Gamesave_num_org_robots++; // Make sure valid id... if ( obj->id >= N_robot_types ) obj->id = obj->id % N_robot_types; // Make sure model number & size are correct... if ( obj->render_type == RT_POLYOBJ ) { Assert(Robot_info[obj->id].model_num != -1); //if you fail this assert, it means that a robot in this level //hasn't been loaded, possibly because he's marked as //non-shareware. To see what robot number, print obj->id. Assert(Robot_info[obj->id].always_0xabcd == 0xabcd); //if you fail this assert, it means that the robot_ai for //a robot in this level hasn't been loaded, possibly because //it's marked as non-shareware. To see what robot number, //print obj->id. obj->rtype.pobj_info.model_num = Robot_info[obj->id].model_num; obj->size = Polygon_models[obj->rtype.pobj_info.model_num].rad; //@@Took out this ugly hack 1/12/96, because Mike has added code //@@that should fix it in a better way. //@@//this is a super-ugly hack. Since the baby stripe robots have //@@//their firing point on their bounding sphere, the firing points //@@//can poke through a wall if the robots are very close to it. So //@@//we make their radii bigger so the guns can't get too close to //@@//the walls //@@if (Robot_info[obj->id].flags & RIF_BIG_RADIUS) //@@ obj->size = (obj->size*3)/2; //@@if (obj->control_type==CT_AI && Robot_info[obj->id].attack_type) //@@ obj->size = obj->size*3/4; } if (obj->id == 65) //special "reactor" robots obj->movement_type = MT_NONE; if (obj->movement_type == MT_PHYSICS) { obj->mtype.phys_info.mass = Robot_info[obj->id].mass; obj->mtype.phys_info.drag = Robot_info[obj->id].drag; } } else { //Robots taken care of above if ( obj->render_type == RT_POLYOBJ ) { int i; char *name = Save_pof_names[obj->rtype.pobj_info.model_num]; for (i=0;i to %d (was %d)\n",name,i,obj->rtype.pobj_info.model_num)); obj->rtype.pobj_info.model_num = i; break; } } } if ( obj->type == OBJ_POWERUP ) { if ( obj->id >= N_powerup_types ) { obj->id = 0; Assert( obj->render_type != RT_POLYOBJ ); } obj->control_type = CT_POWERUP; obj->size = Powerup_info[obj->id].size; obj->ctype.powerup_info.creation_time = 0; #ifdef NETWORK if (Game_mode & GM_NETWORK) { if (multi_powerup_is_4pack(obj->id)) { PowerupsInMine[obj->id-1]+=4; MaxPowerupsAllowed[obj->id-1]+=4; } PowerupsInMine[obj->id]++; MaxPowerupsAllowed[obj->id]++; mprintf ((0,"PowerupLimiter: ID=%d\n",obj->id)); if (obj->id>MAX_POWERUP_TYPES) mprintf ((1,"POWERUP: Overwriting array bounds!! Get JL!\n")); } #endif } if ( obj->type == OBJ_WEAPON ) { if ( obj->id >= N_weapon_types ) { obj->id = 0; Assert( obj->render_type != RT_POLYOBJ ); } if (obj->id == PMINE_ID) { //make sure pmines have correct values obj->mtype.phys_info.mass = Weapon_info[obj->id].mass; obj->mtype.phys_info.drag = Weapon_info[obj->id].drag; obj->mtype.phys_info.flags |= PF_FREE_SPINNING; // Make sure model number & size are correct... Assert( obj->render_type == RT_POLYOBJ ); obj->rtype.pobj_info.model_num = Weapon_info[obj->id].model_num; obj->size = Polygon_models[obj->rtype.pobj_info.model_num].rad; } } if ( obj->type == OBJ_CNTRLCEN ) { obj->render_type = RT_POLYOBJ; obj->control_type = CT_CNTRLCEN; if (Gamesave_current_version <= 1) { // descent 1 reactor obj->id = 0; // used to be only one kind of reactor obj->rtype.pobj_info.model_num = Reactors[0].model_num;// descent 1 reactor } //@@// Make model number is correct... //@@for (i=0; irtype.pobj_info.model_num = ObjId[i]; //@@ obj->shields = ObjStrength[i]; //@@ break; //@@ } #ifdef EDITOR { int i; // Check, and set, strength of reactor for (i=0; iid ) { obj->shields = ObjStrength[i]; break; } Assert(i < Num_total_object_types); //make sure we found it } #endif } if ( obj->type == OBJ_PLAYER ) { //int i; //Assert(obj == Player); if ( obj == ConsoleObject ) init_player_object(); else if (obj->render_type == RT_POLYOBJ) //recover from Matt's pof file matchup bug obj->rtype.pobj_info.model_num = Player_ship->model_num; //Make sure orient matrix is orthogonal check_and_fix_matrix(&obj->orient); obj->id = Gamesave_num_players++; } if (obj->type == OBJ_HOSTAGE) { //@@if (obj->id > N_hostage_types) //@@ obj->id = 0; obj->render_type = RT_HOSTAGE; obj->control_type = CT_POWERUP; } } //static gs_skip(int len,CFILE *file) //{ // // cfseek(file,len,SEEK_CUR); //} #ifdef EDITOR static void gs_write_int(int i,FILE *file) { if (fwrite( &i, sizeof(i), 1, file) != 1) Error( "Error reading int in gamesave.c" ); } static void gs_write_fix(fix f,FILE *file) { if (fwrite( &f, sizeof(f), 1, file) != 1) Error( "Error reading fix in gamesave.c" ); } static void gs_write_short(short s,FILE *file) { if (fwrite( &s, sizeof(s), 1, file) != 1) Error( "Error reading short in gamesave.c" ); } static void gs_write_fixang(fixang f,FILE *file) { if (fwrite( &f, sizeof(f), 1, file) != 1) Error( "Error reading fixang in gamesave.c" ); } static void gs_write_byte(byte b,FILE *file) { if (fwrite( &b, sizeof(b), 1, file) != 1) Error( "Error reading byte in gamesave.c" ); } static void gr_write_vector(vms_vector *v,FILE *file) { gs_write_fix(v->x,file); gs_write_fix(v->y,file); gs_write_fix(v->z,file); } static void gs_write_matrix(vms_matrix *m,FILE *file) { gr_write_vector(&m->rvec,file); gr_write_vector(&m->uvec,file); gr_write_vector(&m->fvec,file); } static void gs_write_angvec(vms_angvec *v,FILE *file) { gs_write_fixang(v->p,file); gs_write_fixang(v->b,file); gs_write_fixang(v->h,file); } #endif extern int multi_powerup_is_4pack(int); //reads one object of the given version from the given file void read_object(object *obj,CFILE *f,int version) { obj->type = cfile_read_byte(f); obj->id = cfile_read_byte(f); obj->control_type = cfile_read_byte(f); obj->movement_type = cfile_read_byte(f); obj->render_type = cfile_read_byte(f); obj->flags = cfile_read_byte(f); obj->segnum = cfile_read_short(f); obj->attached_obj = -1; cfile_read_vector(&obj->pos,f); cfile_read_matrix(&obj->orient,f); obj->size = cfile_read_fix(f); obj->shields = cfile_read_fix(f); cfile_read_vector(&obj->last_pos,f); obj->contains_type = cfile_read_byte(f); obj->contains_id = cfile_read_byte(f); obj->contains_count = cfile_read_byte(f); switch (obj->movement_type) { case MT_PHYSICS: cfile_read_vector(&obj->mtype.phys_info.velocity,f); cfile_read_vector(&obj->mtype.phys_info.thrust,f); obj->mtype.phys_info.mass = cfile_read_fix(f); obj->mtype.phys_info.drag = cfile_read_fix(f); obj->mtype.phys_info.brakes = cfile_read_fix(f); cfile_read_vector(&obj->mtype.phys_info.rotvel,f); cfile_read_vector(&obj->mtype.phys_info.rotthrust,f); obj->mtype.phys_info.turnroll = cfile_read_fixang(f); obj->mtype.phys_info.flags = cfile_read_short(f); break; case MT_SPINNING: cfile_read_vector(&obj->mtype.spin_rate,f); break; case MT_NONE: break; default: Int3(); } switch (obj->control_type) { case CT_AI: { int i; obj->ctype.ai_info.behavior = cfile_read_byte(f); for (i=0;ictype.ai_info.flags[i] = cfile_read_byte(f); obj->ctype.ai_info.hide_segment = cfile_read_short(f); obj->ctype.ai_info.hide_index = cfile_read_short(f); obj->ctype.ai_info.path_length = cfile_read_short(f); obj->ctype.ai_info.cur_path_index = cfile_read_short(f); if (version <= 25) { cfile_read_short(f); // obj->ctype.ai_info.follow_path_start_seg = cfile_read_short(f); // obj->ctype.ai_info.follow_path_end_seg = } break; } case CT_EXPLOSION: obj->ctype.expl_info.spawn_time = cfile_read_fix(f); obj->ctype.expl_info.delete_time = cfile_read_fix(f); obj->ctype.expl_info.delete_objnum = cfile_read_short(f); obj->ctype.expl_info.next_attach = obj->ctype.expl_info.prev_attach = obj->ctype.expl_info.attach_parent = -1; break; case CT_WEAPON: //do I really need to read these? Are they even saved to disk? obj->ctype.laser_info.parent_type = cfile_read_short(f); obj->ctype.laser_info.parent_num = cfile_read_short(f); obj->ctype.laser_info.parent_signature = cfile_read_int(f); break; case CT_LIGHT: obj->ctype.light_info.intensity = cfile_read_fix(f); break; case CT_POWERUP: if (version >= 25) obj->ctype.powerup_info.count = cfile_read_int(f); else obj->ctype.powerup_info.count = 1; if (obj->id == POW_VULCAN_WEAPON) obj->ctype.powerup_info.count = VULCAN_WEAPON_AMMO_AMOUNT; if (obj->id == POW_GAUSS_WEAPON) obj->ctype.powerup_info.count = VULCAN_WEAPON_AMMO_AMOUNT; if (obj->id == POW_OMEGA_WEAPON) obj->ctype.powerup_info.count = MAX_OMEGA_CHARGE; break; case CT_NONE: case CT_FLYING: case CT_DEBRIS: break; case CT_SLEW: //the player is generally saved as slew break; case CT_CNTRLCEN: break; case CT_MORPH: case CT_FLYTHROUGH: case CT_REPAIRCEN: default: Int3(); } switch (obj->render_type) { case RT_NONE: break; case RT_MORPH: case RT_POLYOBJ: { int i,tmo; obj->rtype.pobj_info.model_num = cfile_read_int(f); for (i=0;irtype.pobj_info.anim_angles[i],f); obj->rtype.pobj_info.subobj_flags = cfile_read_int(f); tmo = cfile_read_int(f); #ifndef EDITOR obj->rtype.pobj_info.tmap_override = tmo; #else if (tmo==-1) obj->rtype.pobj_info.tmap_override = -1; else { int xlated_tmo = tmap_xlate_table[tmo]; if (xlated_tmo < 0) { mprintf( (0, "Couldn't find texture for demo object, model_num = %d\n", obj->rtype.pobj_info.model_num)); Int3(); xlated_tmo = 0; } obj->rtype.pobj_info.tmap_override = xlated_tmo; } #endif obj->rtype.pobj_info.alt_textures = 0; break; } case RT_WEAPON_VCLIP: case RT_HOSTAGE: case RT_POWERUP: case RT_FIREBALL: obj->rtype.vclip_info.vclip_num = cfile_read_int(f); obj->rtype.vclip_info.frametime = cfile_read_fix(f); obj->rtype.vclip_info.framenum = cfile_read_byte(f); break; case RT_LASER: break; default: Int3(); } } #ifdef EDITOR //writes one object to the given file void write_object(object *obj,FILE *f) { gs_write_byte(obj->type,f); gs_write_byte(obj->id,f); gs_write_byte(obj->control_type,f); gs_write_byte(obj->movement_type,f); gs_write_byte(obj->render_type,f); gs_write_byte(obj->flags,f); gs_write_short(obj->segnum,f); gr_write_vector(&obj->pos,f); gs_write_matrix(&obj->orient,f); gs_write_fix(obj->size,f); gs_write_fix(obj->shields,f); gr_write_vector(&obj->last_pos,f); gs_write_byte(obj->contains_type,f); gs_write_byte(obj->contains_id,f); gs_write_byte(obj->contains_count,f); switch (obj->movement_type) { case MT_PHYSICS: gr_write_vector(&obj->mtype.phys_info.velocity,f); gr_write_vector(&obj->mtype.phys_info.thrust,f); gs_write_fix(obj->mtype.phys_info.mass,f); gs_write_fix(obj->mtype.phys_info.drag,f); gs_write_fix(obj->mtype.phys_info.brakes,f); gr_write_vector(&obj->mtype.phys_info.rotvel,f); gr_write_vector(&obj->mtype.phys_info.rotthrust,f); gs_write_fixang(obj->mtype.phys_info.turnroll,f); gs_write_short(obj->mtype.phys_info.flags,f); break; case MT_SPINNING: gr_write_vector(&obj->mtype.spin_rate,f); break; case MT_NONE: break; default: Int3(); } switch (obj->control_type) { case CT_AI: { int i; gs_write_byte(obj->ctype.ai_info.behavior,f); for (i=0;ictype.ai_info.flags[i],f); gs_write_short(obj->ctype.ai_info.hide_segment,f); gs_write_short(obj->ctype.ai_info.hide_index,f); gs_write_short(obj->ctype.ai_info.path_length,f); gs_write_short(obj->ctype.ai_info.cur_path_index,f); // -- unused! mk, 08/13/95 -- gs_write_short(obj->ctype.ai_info.follow_path_start_seg,f); // -- unused! mk, 08/13/95 -- gs_write_short(obj->ctype.ai_info.follow_path_end_seg,f); break; } case CT_EXPLOSION: gs_write_fix(obj->ctype.expl_info.spawn_time,f); gs_write_fix(obj->ctype.expl_info.delete_time,f); gs_write_short(obj->ctype.expl_info.delete_objnum,f); break; case CT_WEAPON: //do I really need to write these objects? gs_write_short(obj->ctype.laser_info.parent_type,f); gs_write_short(obj->ctype.laser_info.parent_num,f); gs_write_int(obj->ctype.laser_info.parent_signature,f); break; case CT_LIGHT: gs_write_fix(obj->ctype.light_info.intensity,f); break; case CT_POWERUP: gs_write_int(obj->ctype.powerup_info.count,f); break; case CT_NONE: case CT_FLYING: case CT_DEBRIS: break; case CT_SLEW: //the player is generally saved as slew break; case CT_CNTRLCEN: break; //control center object. case CT_MORPH: case CT_REPAIRCEN: case CT_FLYTHROUGH: default: Int3(); } switch (obj->render_type) { case RT_NONE: break; case RT_MORPH: case RT_POLYOBJ: { int i; gs_write_int(obj->rtype.pobj_info.model_num,f); for (i=0;irtype.pobj_info.anim_angles[i],f); gs_write_int(obj->rtype.pobj_info.subobj_flags,f); gs_write_int(obj->rtype.pobj_info.tmap_override,f); break; } case RT_WEAPON_VCLIP: case RT_HOSTAGE: case RT_POWERUP: case RT_FIREBALL: gs_write_int(obj->rtype.vclip_info.vclip_num,f); gs_write_fix(obj->rtype.vclip_info.frametime,f); gs_write_byte(obj->rtype.vclip_info.framenum,f); break; case RT_LASER: break; default: Int3(); } } #endif extern int remove_trigger_num(int trigger_num); // ----------------------------------------------------------------------------- // Load game // Loads all the relevant data for a level. // If level != -1, it loads the filename with extension changed to .min // Otherwise it loads the appropriate level mine. // returns 0=everything ok, 1=old version, -1=error int load_game_data(CFILE *LoadFile) { int i,j; int start_offset; start_offset = cftell(LoadFile); //===================== READ FILE INFO ======================== // Set default values game_fileinfo.level = -1; game_fileinfo.player_offset = -1; game_fileinfo.player_sizeof = sizeof(player); game_fileinfo.object_offset = -1; game_fileinfo.object_howmany = 0; game_fileinfo.object_sizeof = sizeof(object); game_fileinfo.walls_offset = -1; game_fileinfo.walls_howmany = 0; game_fileinfo.walls_sizeof = sizeof(wall); game_fileinfo.doors_offset = -1; game_fileinfo.doors_howmany = 0; game_fileinfo.doors_sizeof = sizeof(active_door); game_fileinfo.triggers_offset = -1; game_fileinfo.triggers_howmany = 0; game_fileinfo.triggers_sizeof = sizeof(trigger); game_fileinfo.control_offset = -1; game_fileinfo.control_howmany = 0; game_fileinfo.control_sizeof = sizeof(control_center_triggers); game_fileinfo.matcen_offset = -1; game_fileinfo.matcen_howmany = 0; game_fileinfo.matcen_sizeof = sizeof(matcen_info); game_fileinfo.dl_indices_offset = -1; game_fileinfo.dl_indices_howmany = 0; game_fileinfo.dl_indices_sizeof = sizeof(dl_index); game_fileinfo.delta_light_offset = -1; game_fileinfo.delta_light_howmany = 0; game_fileinfo.delta_light_sizeof = sizeof(delta_light); // Read in game_top_fileinfo to get size of saved fileinfo. if (cfseek( LoadFile, start_offset, SEEK_SET )) Error( "Error seeking in gamesave.c" ); // if (cfread( &game_top_fileinfo, sizeof(game_top_fileinfo), 1, LoadFile) != 1) // Error( "Error reading game_top_fileinfo in gamesave.c" ); game_top_fileinfo.fileinfo_signature = cfile_read_short(LoadFile); game_top_fileinfo.fileinfo_version = cfile_read_short(LoadFile); game_top_fileinfo.fileinfo_sizeof = cfile_read_int(LoadFile); // Check signature if (game_top_fileinfo.fileinfo_signature != 0x6705) return -1; // Check version number if (game_top_fileinfo.fileinfo_version < GAME_COMPATIBLE_VERSION ) return -1; // Now, Read in the fileinfo if (cfseek( LoadFile, start_offset, SEEK_SET )) Error( "Error seeking to game_fileinfo in gamesave.c" ); // if (cfread( &game_fileinfo, game_top_fileinfo.fileinfo_sizeof, 1, LoadFile )!=1) // Error( "Error reading game_fileinfo in gamesave.c" ); game_fileinfo.fileinfo_signature = cfile_read_short(LoadFile); game_fileinfo.fileinfo_version = cfile_read_short(LoadFile); game_fileinfo.fileinfo_sizeof = cfile_read_int(LoadFile); for(i=0; i<15; i++) game_fileinfo.mine_filename[i] = cfile_read_byte(LoadFile); game_fileinfo.level = cfile_read_int(LoadFile); game_fileinfo.player_offset = cfile_read_int(LoadFile); // Player info game_fileinfo.player_sizeof = cfile_read_int(LoadFile); game_fileinfo.object_offset = cfile_read_int(LoadFile); // Object info game_fileinfo.object_howmany = cfile_read_int(LoadFile); game_fileinfo.object_sizeof = cfile_read_int(LoadFile); game_fileinfo.walls_offset = cfile_read_int(LoadFile); game_fileinfo.walls_howmany = cfile_read_int(LoadFile); game_fileinfo.walls_sizeof = cfile_read_int(LoadFile); game_fileinfo.doors_offset = cfile_read_int(LoadFile); game_fileinfo.doors_howmany = cfile_read_int(LoadFile); game_fileinfo.doors_sizeof = cfile_read_int(LoadFile); game_fileinfo.triggers_offset = cfile_read_int(LoadFile); game_fileinfo.triggers_howmany = cfile_read_int(LoadFile); game_fileinfo.triggers_sizeof = cfile_read_int(LoadFile); game_fileinfo.links_offset = cfile_read_int(LoadFile); game_fileinfo.links_howmany = cfile_read_int(LoadFile); game_fileinfo.links_sizeof = cfile_read_int(LoadFile); game_fileinfo.control_offset = cfile_read_int(LoadFile); game_fileinfo.control_howmany = cfile_read_int(LoadFile); game_fileinfo.control_sizeof = cfile_read_int(LoadFile); game_fileinfo.matcen_offset = cfile_read_int(LoadFile); game_fileinfo.matcen_howmany = cfile_read_int(LoadFile); game_fileinfo.matcen_sizeof = cfile_read_int(LoadFile); if (game_top_fileinfo.fileinfo_version >= 29) { game_fileinfo.dl_indices_offset = cfile_read_int(LoadFile); game_fileinfo.dl_indices_howmany = cfile_read_int(LoadFile); game_fileinfo.dl_indices_sizeof = cfile_read_int(LoadFile); game_fileinfo.delta_light_offset = cfile_read_int(LoadFile); game_fileinfo.delta_light_howmany = cfile_read_int(LoadFile); game_fileinfo.delta_light_sizeof = cfile_read_int(LoadFile); } if (game_top_fileinfo.fileinfo_version >= 31) { //load mine filename // read newline-terminated string, not sure what version this changed. cfgets(Current_level_name,sizeof(Current_level_name),LoadFile); if (Current_level_name[strlen(Current_level_name)-1] == '\n') Current_level_name[strlen(Current_level_name)-1] = 0; } else if (game_top_fileinfo.fileinfo_version >= 14) { //load mine filename // read null-terminated string char *p=Current_level_name; //must do read one char at a time, since no cfgets() do *p = cfgetc(LoadFile); while (*p++!=0); } else Current_level_name[0]=0; if (game_top_fileinfo.fileinfo_version >= 19) { //load pof names N_save_pof_names = cfile_read_short(LoadFile); if (N_save_pof_names != 0x614d && N_save_pof_names != 0x5547) { // "Ma"de w/DMB beta/"GU"ILE Assert(N_save_pof_names < MAX_POLYGON_MODELS); cfread(Save_pof_names,N_save_pof_names,FILENAME_LEN,LoadFile); } } //===================== READ PLAYER INFO ========================== Object_next_signature = 0; //===================== READ OBJECT INFO ========================== Gamesave_num_org_robots = 0; Gamesave_num_players = 0; if (game_fileinfo.object_offset > -1) { if (cfseek( LoadFile, game_fileinfo.object_offset, SEEK_SET )) Error( "Error seeking to object_offset in gamesave.c" ); for (i=0;i -1) { if (!cfseek( LoadFile, game_fileinfo.walls_offset,SEEK_SET )) { for (i=0;i= 20) wall_read(&Walls[i], LoadFile); // v20 walls and up. else if (game_top_fileinfo.fileinfo_version >= 17) { v19_wall w; v19_wall_read(&w, LoadFile); Walls[i].segnum = w.segnum; Walls[i].sidenum = w.sidenum; Walls[i].linked_wall = w.linked_wall; Walls[i].type = w.type; Walls[i].flags = w.flags; Walls[i].hps = w.hps; Walls[i].trigger = w.trigger; Walls[i].clip_num = w.clip_num; Walls[i].keys = w.keys; Walls[i].state = WALL_DOOR_CLOSED; } else { v16_wall w; v16_wall_read(&w, LoadFile); Walls[i].segnum = Walls[i].sidenum = Walls[i].linked_wall = -1; Walls[i].type = w.type; Walls[i].flags = w.flags; Walls[i].hps = w.hps; Walls[i].trigger = w.trigger; Walls[i].clip_num = w.clip_num; Walls[i].keys = w.keys; } } } } //===================== READ DOOR INFO ============================ if (game_fileinfo.doors_offset > -1) { if (!cfseek( LoadFile, game_fileinfo.doors_offset,SEEK_SET )) { for (i=0;i= 20) active_door_read(&ActiveDoors[i], LoadFile); // version 20 and up else { v19_door d; int p; v19_door_read(&d, LoadFile); ActiveDoors[i].n_parts = d.n_parts; for (p=0;p= verion 31 triggers. if (game_fileinfo.triggers_offset > -1) { if (!cfseek( LoadFile, game_fileinfo.triggers_offset,SEEK_SET )) { for (i=0;i -1) if (!cfseek(LoadFile, game_fileinfo.control_offset, SEEK_SET)) { Assert(game_fileinfo.control_sizeof == sizeof(control_center_triggers)); control_center_triggers_read_n(&ControlCenterTriggers, game_fileinfo.control_howmany, LoadFile); } //================ READ MATERIALOGRIFIZATIONATORS INFO =============== if (game_fileinfo.matcen_offset > -1) { int j; if (!cfseek( LoadFile, game_fileinfo.matcen_offset,SEEK_SET )) { // mprintf((0, "Reading %i materialization centers.\n", game_fileinfo.matcen_howmany)); for (i=0;i -1) { int i; if (!cfseek( LoadFile, game_fileinfo.dl_indices_offset, SEEK_SET )) { Num_static_lights = game_fileinfo.dl_indices_howmany; for (i=0; i -1) { int i; if (!cfseek( LoadFile, game_fileinfo.delta_light_offset, SEEK_SET )) { for (i=0; i Highest_segment_index) //bogus object Objects[i].type = OBJ_NONE; else { Objects[i].segnum = -1; //avoid Assert() obj_link(i,objsegnum); } } } clear_transient_objects(1); //1 means clear proximity bombs // Make sure non-transparent doors are set correctly. for (i=0; i< Num_segments; i++) for (j=0;jwall_num != -1) && (Walls[sidep->wall_num].clip_num != -1)) { //mprintf((0, "Checking Wall %d\n", Segments[i].sides[j].wall_num)); if (WallAnims[Walls[sidep->wall_num].clip_num].flags & WCF_TMAP1) { //mprintf((0, "Fixing non-transparent door.\n")); sidep->tmap_num = WallAnims[Walls[sidep->wall_num].clip_num].frames[0]; sidep->tmap_num2 = 0; } } } Num_walls = game_fileinfo.walls_howmany; reset_walls(); Num_open_doors = game_fileinfo.doors_howmany; Num_triggers = game_fileinfo.triggers_howmany; //go through all walls, killing references to invalid triggers for (i=0;i= Num_triggers) { mprintf((0,"Removing reference to invalid trigger %d from wall %d\n",Walls[i].trigger,i)); Walls[i].trigger = -1; //kill trigger } //go through all triggers, killing unused ones for (i=0;i 2 add palette name //2 -> 3 add control center explosion time //3 -> 4 add reactor strength //4 -> 5 killed hostage text stuff //5 -> 6 added Secret_return_segment and Secret_return_orient //6 -> 7 added flickering lights //7 -> 8 made version 8 to be not compatible with D2 1.0 & 1.1 #ifndef RELEASE char *Level_being_loaded=NULL; #endif #ifdef COMPACT_SEGS extern void ncache_flush(); #endif extern int Slide_segs_computed; int no_old_level_file_error=0; //loads a level (.LVL) file from disk //returns 0 if success, else error code int load_level(char * filename_passed) { #ifdef EDITOR int use_compiled_level=1; #endif CFILE * LoadFile; char filename[128]; int sig, minedata_offset, gamedata_offset; int mine_err, game_err; #ifdef NETWORK int i; #endif Slide_segs_computed = 0; #ifdef NETWORK if (Game_mode & GM_NETWORK) { for (i=0;i\n", filename)); return 1; #else Error("Can't open file <%s>\n",filename); #endif } strcpy( Gamesave_current_filename, filename ); // #ifdef NEWDEMO // if ( Newdemo_state == ND_STATE_RECORDING ) // newdemo_record_start_demo(); // #endif sig = cfile_read_int(LoadFile); Gamesave_current_version = cfile_read_int(LoadFile); mprintf((0, "Gamesave_current_version = %d\n", Gamesave_current_version)); minedata_offset = cfile_read_int(LoadFile); gamedata_offset = cfile_read_int(LoadFile); Assert(sig == MAKE_SIG('P','L','V','L')); if (Gamesave_current_version >= 8) { //read dummy data cfile_read_int(LoadFile); cfile_read_short(LoadFile); cfile_read_byte(LoadFile); } if (Gamesave_current_version < 5) cfile_read_int(LoadFile); //was hostagetext_offset if (Gamesave_current_version > 1) { cfgets(Current_level_palette,sizeof(Current_level_palette),LoadFile); if (Current_level_palette[strlen(Current_level_palette)-1] == '\n') Current_level_palette[strlen(Current_level_palette)-1] = 0; } if (Gamesave_current_version <= 1 || Current_level_palette[0]==0) // descent 1 level strcpy(Current_level_palette, DEFAULT_LEVEL_PALETTE); if (Gamesave_current_version >= 3) Base_control_center_explosion_time = cfile_read_int(LoadFile); else Base_control_center_explosion_time = DEFAULT_CONTROL_CENTER_EXPLOSION_TIME; if (Gamesave_current_version >= 4) Reactor_strength = cfile_read_int(LoadFile); else Reactor_strength = -1; //use old defaults if (Gamesave_current_version >= 7) { int i; Num_flickering_lights = cfile_read_int(LoadFile); Assert((Num_flickering_lights >= 0) && (Num_flickering_lights < MAX_FLICKERING_LIGHTS)); for (i = 0; i < Num_flickering_lights; i++) flickering_light_read(&Flickering_lights[i], LoadFile); } else Num_flickering_lights = 0; if (Gamesave_current_version < 6) { Secret_return_segment = 0; Secret_return_orient.rvec.x = F1_0; Secret_return_orient.rvec.y = 0; Secret_return_orient.rvec.z = 0; Secret_return_orient.fvec.x = 0; Secret_return_orient.fvec.y = F1_0; Secret_return_orient.fvec.z = 0; Secret_return_orient.uvec.x = 0; Secret_return_orient.uvec.y = 0; Secret_return_orient.uvec.z = F1_0; } else { Secret_return_segment = cfile_read_int(LoadFile); Secret_return_orient.rvec.x = cfile_read_int(LoadFile); Secret_return_orient.rvec.y = cfile_read_int(LoadFile); Secret_return_orient.rvec.z = cfile_read_int(LoadFile); Secret_return_orient.fvec.x = cfile_read_int(LoadFile); Secret_return_orient.fvec.y = cfile_read_int(LoadFile); Secret_return_orient.fvec.z = cfile_read_int(LoadFile); Secret_return_orient.uvec.x = cfile_read_int(LoadFile); Secret_return_orient.uvec.y = cfile_read_int(LoadFile); Secret_return_orient.uvec.z = cfile_read_int(LoadFile); } cfseek(LoadFile,minedata_offset,SEEK_SET); #ifdef EDITOR if (!use_compiled_level) { mine_err = load_mine_data(LoadFile); #if 0 // get from d1src if needed // Compress all uv coordinates in mine, improves texmap precision. --MK, 02/19/96 compress_uv_coordinates_all(); #endif } else #endif //NOTE LINK TO ABOVE!! mine_err = load_mine_data_compiled(LoadFile); if (mine_err == -1) { //error!! cfclose(LoadFile); return 2; } cfseek(LoadFile,gamedata_offset,SEEK_SET); game_err = load_game_data(LoadFile); if (game_err == -1) { //error!! cfclose(LoadFile); return 3; } //======================== CLOSE FILE ============================= cfclose( LoadFile ); set_ambient_sound_flags(); #ifdef EDITOR write_game_text_file(filename); if (Errors_in_mine) { if (is_real_level(filename)) { char ErrorMessage[200]; sprintf( ErrorMessage, "Warning: %i errors in %s!\n", Errors_in_mine, Level_being_loaded ); stop_time(); gr_palette_load(gr_palette); nm_messagebox( NULL, 1, "Continue", ErrorMessage ); start_time(); } else mprintf((1, "Error: %i errors in %s.\n", Errors_in_mine, Level_being_loaded)); } #endif #ifdef EDITOR //If an old version, ask the use if he wants to save as new version if (!no_old_level_file_error && (Function_mode == FMODE_EDITOR) && (((LEVEL_FILE_VERSION > 3) && Gamesave_current_version < LEVEL_FILE_VERSION) || mine_err == 1 || game_err == 1)) { char ErrorMessage[200]; sprintf( ErrorMessage, "You just loaded a old version\n" "level. Would you like to save\n" "it as a current version level?"); stop_time(); gr_palette_load(gr_palette); if (nm_messagebox( NULL, 2, "Don't Save", "Save", ErrorMessage )==1) save_level(filename); start_time(); } #endif #ifdef EDITOR if (Function_mode == FMODE_EDITOR) editor_status("Loaded NEW mine %s, \"%s\"",filename,Current_level_name); #endif #ifdef EDITOR if (check_segment_connections()) nm_messagebox( "ERROR", 1, "Ok", "Connectivity errors detected in\n" "mine. See monochrome screen for\n" "details, and contact Matt or Mike." ); #endif return 0; } #ifdef EDITOR void get_level_name() { //NO_UI!!! UI_WINDOW *NameWindow = NULL; //NO_UI!!! UI_GADGET_INPUTBOX *NameText; //NO_UI!!! UI_GADGET_BUTTON *QuitButton; //NO_UI!!! //NO_UI!!! // Open a window with a quit button //NO_UI!!! NameWindow = ui_open_window( 20, 20, 300, 110, WIN_DIALOG ); //NO_UI!!! QuitButton = ui_add_gadget_button( NameWindow, 150-24, 60, 48, 40, "Done", NULL ); //NO_UI!!! //NO_UI!!! ui_wprintf_at( NameWindow, 10, 12,"Please enter a name for this mine:" ); //NO_UI!!! NameText = ui_add_gadget_inputbox( NameWindow, 10, 30, LEVEL_NAME_LEN, LEVEL_NAME_LEN, Current_level_name ); //NO_UI!!! //NO_UI!!! NameWindow->keyboard_focus_gadget = (UI_GADGET *)NameText; //NO_UI!!! QuitButton->hotkey = KEY_ENTER; //NO_UI!!! //NO_UI!!! ui_gadget_calc_keys(NameWindow); //NO_UI!!! //NO_UI!!! while (!QuitButton->pressed && last_keypress!=KEY_ENTER) { //NO_UI!!! ui_mega_process(); //NO_UI!!! ui_window_do_gadgets(NameWindow); //NO_UI!!! } //NO_UI!!! //NO_UI!!! strcpy( Current_level_name, NameText->text ); //NO_UI!!! //NO_UI!!! if ( NameWindow!=NULL ) { //NO_UI!!! ui_close_window( NameWindow ); //NO_UI!!! NameWindow = NULL; //NO_UI!!! } //NO_UI!!! newmenu_item m[2]; m[0].type = NM_TYPE_TEXT; m[0].text = "Please enter a name for this mine:"; m[1].type = NM_TYPE_INPUT; m[1].text = Current_level_name; m[1].text_len = LEVEL_NAME_LEN; newmenu_do( NULL, "Enter mine name", 2, m, NULL ); } #endif #ifdef EDITOR int Errors_in_mine; // ----------------------------------------------------------------------------- int compute_num_delta_light_records(void) { int i; int total = 0; for (i=0; isegnum > Highest_segment_index) ConsoleObject->segnum = 0; compute_segment_center(&ConsoleObject->pos,&(Segments[ConsoleObject->segnum])); } fix_object_segs(); //Write the header gs_write_int(sig,SaveFile); gs_write_int(version,SaveFile); //save placeholders gs_write_int(minedata_offset,SaveFile); gs_write_int(gamedata_offset,SaveFile); //Now write the damn data //write the version 8 data (to make file unreadable by 1.0 & 1.1) gs_write_int(GameTime,SaveFile); gs_write_short(FrameCount,SaveFile); gs_write_byte(FrameTime,SaveFile); // Write the palette file name fprintf(SaveFile,"%s\n",Current_level_palette); gs_write_int(Base_control_center_explosion_time,SaveFile); gs_write_int(Reactor_strength,SaveFile); gs_write_int(Num_flickering_lights,SaveFile); fwrite(Flickering_lights,sizeof(*Flickering_lights),Num_flickering_lights,SaveFile); gs_write_int(Secret_return_segment, SaveFile); gs_write_int(Secret_return_orient.rvec.x, SaveFile); gs_write_int(Secret_return_orient.rvec.y, SaveFile); gs_write_int(Secret_return_orient.rvec.z, SaveFile); gs_write_int(Secret_return_orient.fvec.x, SaveFile); gs_write_int(Secret_return_orient.fvec.y, SaveFile); gs_write_int(Secret_return_orient.fvec.z, SaveFile); gs_write_int(Secret_return_orient.uvec.x, SaveFile); gs_write_int(Secret_return_orient.uvec.y, SaveFile); gs_write_int(Secret_return_orient.uvec.z, SaveFile); minedata_offset = ftell(SaveFile); if ( !compiled_version ) save_mine_data(SaveFile); else save_mine_data_compiled(SaveFile); gamedata_offset = ftell(SaveFile); save_game_data(SaveFile); fseek(SaveFile,sizeof(sig)+sizeof(version),SEEK_SET); gs_write_int(minedata_offset,SaveFile); gs_write_int(gamedata_offset,SaveFile); //==================== CLOSE THE FILE ============================= fclose(SaveFile); if ( !compiled_version ) { if (Function_mode == FMODE_EDITOR) editor_status("Saved mine %s, \"%s\"",filename,Current_level_name); } return 0; } #if 0 //dunno - 3rd party stuff? extern void compress_uv_coordinates_all(void); #endif int save_level(char * filename) { int r1; // Save normal version... r1 = save_level_sub(filename, 0); // Save compiled version... save_level_sub(filename, 1); return r1; } #endif //EDITOR #ifndef NDEBUG void dump_mine_info(void) { int segnum, sidenum; fix min_u, max_u, min_v, max_v, min_l, max_l, max_sl; min_u = F1_0*1000; min_v = min_u; min_l = min_u; max_u = -min_u; max_v = max_u; max_l = max_u; max_sl = 0; for (segnum=0; segnum<=Highest_segment_index; segnum++) { for (sidenum=0; sidenum max_sl) max_sl = Segment2s[segnum].static_light; for (vertnum=0; vertnum<4; vertnum++) { if (sidep->uvls[vertnum].u < min_u) min_u = sidep->uvls[vertnum].u; else if (sidep->uvls[vertnum].u > max_u) max_u = sidep->uvls[vertnum].u; if (sidep->uvls[vertnum].v < min_v) min_v = sidep->uvls[vertnum].v; else if (sidep->uvls[vertnum].v > max_v) max_v = sidep->uvls[vertnum].v; if (sidep->uvls[vertnum].l < min_l) min_l = sidep->uvls[vertnum].l; else if (sidep->uvls[vertnum].l > max_l) max_l = sidep->uvls[vertnum].l; } } } // 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))); // mprintf((0, "Static light maximum = %7.3f\n", f2fl(max_sl))); // mprintf((0, "Number of walls: %i\n", Num_walls)); } #endif #ifdef EDITOR //read in every level in mission and save out compiled version void save_all_compiled_levels(void) { do_load_save_levels(1); } //read in every level in mission void load_all_levels(void) { do_load_save_levels(0); } void do_load_save_levels(int save) { int level_num; if (! SafetyCheck()) return; no_old_level_file_error=1; for (level_num=1;level_num<=Last_level;level_num++) { load_level(Level_names[level_num-1]); load_palette(Current_level_palette,1,1); //don't change screen if (save) save_level_sub(Level_names[level_num-1],1); } for (level_num=-1;level_num>=Last_secret_level;level_num--) { load_level(Secret_level_names[-level_num-1]); load_palette(Current_level_palette,1,1); //don't change screen if (save) save_level_sub(Secret_level_names[-level_num-1],1); } no_old_level_file_error=0; } #endif