#include #include #include "hostage.h" #include "error.h" #include "cfile.h" #include "object.h" #include "wall.h" #include "fuelcen.h" #include "player.h" #include "loadrdl.h" #include "powerup.h" #include "polyobj.h" #include "textures.h" #include "mono.h" char Current_level_name[16]; char Level_palette[8]; int N_save_pof_names=25; #define MAX_POLYGON_MODELS_NEW 167 char Save_pof_names[MAX_POLYGON_MODELS_NEW][13]; int Gamesave_num_players=0; int Gamesave_num_org_robots = 0; static int convert_vclip(int vc) { if (vc < 0) return vc; if ((vc < VCLIP_MAXNUM) && (Vclip[vc].num_frames >= 0)) return vc; return 0; } static int convert_wclip(int wc) { return (wc < Num_wall_anims) ? wc : wc % Num_wall_anims; } static int convert_tmap(int tmap) { return (tmap >= NumTextures) ? tmap % NumTextures : tmap; } static int convert_polymod(int polymod) { return (polymod >= N_polygon_models) ? polymod % N_polygon_models : polymod; } #if 1 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 ) { obj->rtype.pobj_info.model_num = Robot_info[obj->id].model_num; obj->size = Polygon_models[obj->rtype.pobj_info.model_num].rad; //@@if (obj->control_type==CT_AI && Robot_info[obj->id].attack_type) //@@ obj->size = obj->size*3/4; } 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; } //if ( obj->type == OBJ_HOSTAGE ) { // if ( obj->id >= N_hostage_types ) { // obj->id = 0; // Assert( obj->render_type == RT_POLYOBJ ); // } //} if ( obj->type == OBJ_WEAPON ) { if ( obj->id >= N_weapon_types ) { obj->id = 0; Assert( obj->render_type != RT_POLYOBJ ); } } if ( obj->type == OBJ_CNTRLCEN ) { int i; obj->render_type = RT_POLYOBJ; obj->control_type = CT_CNTRLCEN; // Make model number is correct... for (i=0; irtype.pobj_info.model_num = ObjId[i]; obj->shields = ObjStrength[i]; break; } } 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; //obj->vclip_info.vclip_num = Hostage_vclip_num[Hostages[obj->id].type]; //obj->vclip_info.frametime = Vclip[obj->vclip_info.vclip_num].frame_time; //obj->vclip_info.framenum = 0; } } #endif /*static*/ int read_int(CFILE *file) { int i; if (cfread( &i, sizeof(i), 1, file) != 1) Error( "Error reading int in gamesave.c" ); return i; } /*static*/ fix read_fix(CFILE *file) { fix f; if (cfread( &f, sizeof(f), 1, file) != 1) Error( "Error reading fix in gamesave.c" ); return f; } /*static*/ short read_short(CFILE *file) { short s; if (cfread( &s, sizeof(s), 1, file) != 1) Error( "Error reading short in gamesave.c" ); return s; } static short read_fixang(CFILE *file) { fixang f; if (cfread( &f, sizeof(f), 1, file) != 1) Error( "Error reading fixang in gamesave.c" ); return f; } /*static*/ sbyte read_byte(CFILE *file) { sbyte b; if (cfread( &b, sizeof(b), 1, file) != 1) Error( "Error reading byte in gamesave.c" ); return b; } #if 0 static ubyte read_ubyte(CFILE *file) { byte b; if (cfread( &b, sizeof(b), 1, file) != 1) Error( "Error reading byte in gamesave.c" ); return b; } #endif /*static*/ void read_vector(vms_vector *v,CFILE *file) { v->x = read_fix(file); v->y = read_fix(file); v->z = read_fix(file); } static void read_matrix(vms_matrix *m,CFILE *file) { read_vector(&m->rvec,file); read_vector(&m->uvec,file); read_vector(&m->fvec,file); } static void read_angvec(vms_angvec *v,CFILE *file) { v->p = read_fixang(file); v->b = read_fixang(file); v->h = read_fixang(file); } //reads one object of the given version from the given file void read_object(object *obj,CFILE *f,int version) { obj->type = read_byte(f); obj->id = read_byte(f); if (obj->type == OBJ_ROBOT && obj->id > 23) { obj->id = obj->id % 24; } obj->control_type = read_byte(f); obj->movement_type = read_byte(f); obj->render_type = read_byte(f); obj->flags = read_byte(f); obj->segnum = read_short(f); obj->attached_obj = -1; read_vector(&obj->pos,f); read_matrix(&obj->orient,f); obj->size = read_fix(f); obj->shields = read_fix(f); read_vector(&obj->last_pos,f); obj->contains_type = read_byte(f); obj->contains_id = read_byte(f); obj->contains_count = read_byte(f); switch (obj->movement_type) { case MT_PHYSICS: read_vector(&obj->mtype.phys_info.velocity,f); read_vector(&obj->mtype.phys_info.thrust,f); obj->mtype.phys_info.mass = read_fix(f); obj->mtype.phys_info.drag = read_fix(f); obj->mtype.phys_info.brakes = read_fix(f); read_vector(&obj->mtype.phys_info.rotvel,f); read_vector(&obj->mtype.phys_info.rotthrust,f); obj->mtype.phys_info.turnroll = read_fixang(f); obj->mtype.phys_info.flags = read_short(f); break; case MT_SPINNING: 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 = read_byte(f); for (i=0;ictype.ai_info.flags[i] = read_byte(f); obj->ctype.ai_info.hide_segment = read_short(f); obj->ctype.ai_info.hide_index = read_short(f); obj->ctype.ai_info.path_length = read_short(f); obj->ctype.ai_info.cur_path_index = read_short(f); if (version <= 25) { obj->ctype.ai_info.follow_path_start_seg = read_short(f); obj->ctype.ai_info.follow_path_end_seg = read_short(f); } break; } case CT_EXPLOSION: obj->ctype.expl_info.spawn_time = read_fix(f); obj->ctype.expl_info.delete_time = read_fix(f); obj->ctype.expl_info.delete_objnum = 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 = read_short(f); obj->ctype.laser_info.parent_num = read_short(f); obj->ctype.laser_info.parent_signature = read_int(f); break; case CT_LIGHT: obj->ctype.light_info.intensity = read_fix(f); break; case CT_POWERUP: if (version >= 25) obj->ctype.powerup_info.count = 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; 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 = convert_polymod(read_int(f)); for (i=0;irtype.pobj_info.anim_angles[i],f); obj->rtype.pobj_info.subobj_flags = read_int(f); tmo = read_int(f); #ifndef EDITOR obj->rtype.pobj_info.tmap_override = convert_tmap(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 = convert_vclip(read_int(f)); obj->rtype.vclip_info.frametime = read_fix(f); obj->rtype.vclip_info.framenum = read_byte(f); break; case RT_LASER: break; default: Int3(); } } int load_mine_data_compiled_new(CFILE *LoadFile, int lvl_version) { int i,segnum,sidenum; ubyte version; short temp_short; ushort temp_ushort; ubyte bit_mask; // For compiled levels, textures map to themselves, prevent tmap_override always being gray, // bug which Matt and John refused to acknowledge, so here is Mike, fixing it. #ifdef EDITOR for (i=0; i>5, write as short, l>>1 write as short) for (i=0; i<4; i++ ) { cfread( &temp_short, sizeof(short), 1, LoadFile ); Segments[segnum].sides[sidenum].uvls[i].u = ((fix)temp_short) << 5; cfread( &temp_short, sizeof(short), 1, LoadFile ); Segments[segnum].sides[sidenum].uvls[i].v = ((fix)temp_short) << 5; cfread( &temp_ushort, sizeof(temp_ushort), 1, LoadFile ); Segments[segnum].sides[sidenum].uvls[i].l = ((fix)temp_ushort) << 1; //cfread( &Segments[segnum].sides[sidenum].uvls[i].l, sizeof(fix), 1, LoadFile ); } } else { Segments[segnum].sides[sidenum].tmap_num = 0; Segments[segnum].sides[sidenum].tmap_num2 = 0; for (i=0; i<4; i++ ) { Segments[segnum].sides[sidenum].uvls[i].u = 0; Segments[segnum].sides[sidenum].uvls[i].v = 0; Segments[segnum].sides[sidenum].uvls[i].l = 0; } } } } if (lvl_version > 5) { for (segnum=0; segnum= MAX_CENTER_TYPES) Segments[segnum].special = SEGMENT_IS_NOTHING; // remove goals etc. // Read byte Segments[segnum].matcen_num cfread( &Segments[segnum].matcen_num, sizeof(ubyte), 1, LoadFile ); // Read short Segments[segnum].value cfread( &Segments[segnum].value, sizeof(short), 1, LoadFile ); // Read fix Segments[segnum].static_light (shift down 5 bits, write as short) cfread( &temp_ushort, sizeof(temp_ushort), 1, LoadFile ); Segments[segnum].static_light = ((fix)temp_ushort) << 3; // Read short ??? cfread( &temp_ushort, sizeof(temp_ushort), 1, LoadFile ); /* if (temp_ushort != 7) printf("%03d ",temp_ushort); */ } } Highest_vertex_index = Num_vertices-1; Highest_segment_index = Num_segments-1; validate_segment_all(); // Fill in side type and normals. // Activate fuelcentes for (i=0; i< Num_segments; i++ ) { fuelcen_activate( &Segments[i], Segments[i].special ); } reset_objects(1); //one object, the player return 0; } // ----------------------------------------------------------------------------- // 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); // 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" ); // 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" ); game_fileinfo.fileinfo_signature = read_short(LoadFile); game_fileinfo.fileinfo_version = read_short(LoadFile); game_fileinfo.fileinfo_sizeof = read_int(LoadFile); for(i=0; i<15; i++) game_fileinfo.mine_filename[i] = read_byte(LoadFile); game_fileinfo.level = read_int(LoadFile); game_fileinfo.player_offset = read_int(LoadFile); // Player info game_fileinfo.player_sizeof = read_int(LoadFile); game_fileinfo.object_offset = read_int(LoadFile); // Object info game_fileinfo.object_howmany = read_int(LoadFile); game_fileinfo.object_sizeof = read_int(LoadFile); game_fileinfo.walls_offset = read_int(LoadFile); game_fileinfo.walls_howmany = read_int(LoadFile); game_fileinfo.walls_sizeof = read_int(LoadFile); game_fileinfo.doors_offset = read_int(LoadFile); game_fileinfo.doors_howmany = read_int(LoadFile); game_fileinfo.doors_sizeof = read_int(LoadFile); game_fileinfo.triggers_offset = read_int(LoadFile); game_fileinfo.triggers_howmany = read_int(LoadFile); game_fileinfo.triggers_sizeof = read_int(LoadFile); game_fileinfo.links_offset = read_int(LoadFile); game_fileinfo.links_howmany = read_int(LoadFile); game_fileinfo.links_sizeof = read_int(LoadFile); game_fileinfo.control_offset = read_int(LoadFile); game_fileinfo.control_howmany = read_int(LoadFile); game_fileinfo.control_sizeof = read_int(LoadFile); game_fileinfo.matcen_offset = read_int(LoadFile); game_fileinfo.matcen_howmany = read_int(LoadFile); game_fileinfo.matcen_sizeof = read_int(LoadFile); if (game_top_fileinfo.fileinfo_version > 25) cfseek(LoadFile,2*3*4,SEEK_CUR); // skip blastable light info if (game_top_fileinfo.fileinfo_version >= 14) { //load mine filename char *p=Current_level_name; //must do read one char at a time, since no cfgets() if (game_fileinfo.fileinfo_version > 25) { do *p = cfgetc(LoadFile); while ((*p++!=0x0a)&&(p-Current_level_name= 19) { //load pof names cfread(&N_save_pof_names,2,1,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_NEW); cfread(Save_pof_names,N_save_pof_names,13,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) { Assert(sizeof(Walls[i]) == game_fileinfo.walls_sizeof); if (cfread(&Walls[i], game_fileinfo.walls_sizeof, 1,LoadFile)!=1) Error( "Error reading Walls[%d] in gamesave.c", i); Walls[i].clip_num = convert_wclip(Walls[i].clip_num); } else if (game_top_fileinfo.fileinfo_version >= 17) { v19_wall w; Assert(sizeof(w) == game_fileinfo.walls_sizeof); if (cfread(&w, game_fileinfo.walls_sizeof, 1,LoadFile)!=1) Error( "Error reading Walls[%d] in gamesave.c", i); 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; Assert(sizeof(w) == game_fileinfo.walls_sizeof); if (cfread(&w, game_fileinfo.walls_sizeof, 1,LoadFile)!=1) Error( "Error reading Walls[%d] in gamesave.c", i); 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 % MAX_WALL_ANIMS; 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) { Assert(sizeof(ActiveDoors[i]) == game_fileinfo.doors_sizeof); if (cfread(&ActiveDoors[i], game_fileinfo.doors_sizeof,1,LoadFile)!=1) Error( "Error reading ActiveDoors[%d] in gamesave.c", i); } else { v19_door d; int p; Assert(sizeof(d) == game_fileinfo.doors_sizeof); if (cfread(&d, game_fileinfo.doors_sizeof, 1,LoadFile)!=1) Error( "Error reading Doors[%d] in gamesave.c", i); ActiveDoors[i].n_parts = d.n_parts; for (p=0;p -1) { if (!cfseek( LoadFile, game_fileinfo.triggers_offset,SEEK_SET )) { for (i=0;i -1) { if (!cfseek( LoadFile, game_fileinfo.control_offset,SEEK_SET )) { for (i=0;i -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 25) /*RobotCenters[i].robot_flags2 =*/ read_int(LoadFile); RobotCenters[i].hit_points = read_fix(LoadFile); RobotCenters[i].interval = read_fix(LoadFile); RobotCenters[i].segnum = read_short(LoadFile); RobotCenters[i].fuelcen_num = read_short(LoadFile); // Set links in RobotCenters to Station array for (j=0; j<=Highest_segment_index; j++) if (Segments[j].special == SEGMENT_IS_ROBOTMAKER) if (Segments[j].matcen_num == i) RobotCenters[i].fuelcen_num = Segments[j].value; // mprintf((0, " %i: flags = %08x\n", i, RobotCenters[i].robot_flags)); } } } //========================= UPDATE VARIABLES ====================== reset_objects(game_fileinfo.object_howmany); 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; Num_robot_centers = game_fileinfo.matcen_howmany; //fix old wall structs if (game_top_fileinfo.fileinfo_version < 17) { int segnum,sidenum,wallnum; for (segnum=0; segnum<=Highest_segment_index; segnum++) for (sidenum=0;sidenum<6;sidenum++) if ((wallnum=Segments[segnum].sides[sidenum].wall_num) != -1) { Walls[wallnum].segnum = segnum; Walls[wallnum].sidenum = sidenum; } } fix_object_segs(); if (game_top_fileinfo.fileinfo_version < GAME_VERSION) return 1; //means old version else return 0; } //loads a level (.RDL) file from disk int load_level(char * filename_passed) { CFILE * LoadFile; char filename[128]; int sig,version,minedata_offset,gamedata_offset,hostagetext_offset; int mine_err,game_err; strcpy(filename,filename_passed); LoadFile = cfopen( filename, "rb" ); if (!LoadFile) Error("Can't open file <%s>\n",filename); sig = read_int(LoadFile); Assert(sig == 0x504c564c); /* 'PLVL' */ version = read_int(LoadFile); minedata_offset = read_int(LoadFile); gamedata_offset = read_int(LoadFile); if (version == 1) hostagetext_offset = read_int(LoadFile); else { char *p = Level_palette; do *p = cfgetc(LoadFile); while (*p++!=0x0a); if (read_int(LoadFile) != 0x1e) Error("rl2 int 1 != 0x1e"); if (read_int(LoadFile) != -1) Error("rl2 int 2 != -1"); } cfseek(LoadFile,minedata_offset,SEEK_SET); mine_err = load_mine_data_compiled_new(LoadFile, version); if (mine_err == -1) //error!! return 1; cfseek(LoadFile,gamedata_offset,SEEK_SET); game_err = load_game_data(LoadFile); if (game_err == -1) //error!! return 1; //======================== CLOSE FILE ============================= cfclose( LoadFile ); return 0; }