/* * Copyright (C) Volition, Inc. 1999. All rights reserved. * * All source code herein is the property of Volition, Inc. You may not sell * or otherwise commercially exploit the source or things you created based on the * source. * */ #include #include "playerman/managepilot.h" #include "freespace2/freespace.h" #include "hud/hudsquadmsg.h" #include "sound/sound.h" #include "gamesnd/eventmusic.h" #include "sound/audiostr.h" #include "osapi/osregistry.h" #include "graphics/font.h" #include "menuui/playermenu.h" #include "missionui/missionshipchoice.h" #include "hud/hudconfig.h" #include "popup/popup.h" #include "missionui/redalert.h" #include "menuui/techmenu.h" #include "io/joy.h" #include "io/mouse.h" #include "cutscene/cutscenes.h" #include "missionui/missionscreencommon.h" #include "mission/missionbriefcommon.h" #include "mission/missioncampaign.h" #include "mission/missionload.h" #include "playerman/player.h" #include "ship/ship.h" #include "weapon/weapon.h" #include "cfile/cfile.h" #include "network/multi.h" // update this when altering data that is read/written to .PLR file #define CURRENT_PLAYER_FILE_VERSION 242 #define CURRENT_MULTI_PLAYER_FILE_VERSION 142 #define FS2_DEMO_PLAYER_FILE_VERSION 135 #define LOWEST_COMPATIBLE_PLAYER_FILE_VERSION 140 // compatible with release - Goober5000 // it used to be "demo plr files should work in final", but I guess this was cleared out // when version 140 came along - see below // keep track of pilot file changes here // version 2 : Added squad logo filename // version 3 : Changed size of scoring struct. use ushort instead of ints for storing alltime kills by ship type // version 4/5 : Added squadron name field // version 6 : changed max length on a multiplayer options field // version 130 : changed size of hud config struct // version 133 : misc changes. new hud gauge // version 134 : added HUD contrast toggle key // version 135 : added tips flag (THIS IS THE DEMO VERSION - RETAIN COMPATIBILITY FROM HERE ON OUT) // version 136 : added intelligence flags to tech room visibility data // version 137 : 2 new HUD gauges. // version 138 : new multiplayer config // version 139 : # medals increased - added compatibility with old plr file versions // version 140 : ships table reordered. clear out old pilot files // HERE ONWARD ARE SCP CHANGES - MAINTAIN COMPATIBILITY WITH RELEASE -- VERSION 140 // version 141 : player-persistent variables - Goober5000 // multi player file version changes - no savefiles, need to keep everything here // version 142 : handle new ship/weapon tables properly - multi only - taylor // single player file version changes // version 242 : move anything that might be campaign specific to the savefile - single only - taylor // search for PLAYER INIT for new pilot initialization stuff. I _think_ its in the right spot for now //#define PLR_FILE_ID 'FPSF' // unique signiture to identify a .PLR file (FreeSpace Player File) // FPSF appears as FSPF in file. #define PLR_FILE_ID 0x46505346 // "FPSF" - unique signiture to identify a .PLR file (FreeSpace Player File), appears as FSPF in file. // Current content of a .PLR file // // Global variables int Player_sel_mode; // pilot pic image list stuff ( call pilot_load_pic_list() to make these valid ) char Pilot_images_arr[MAX_PILOT_IMAGES][MAX_FILENAME_LEN]; char *Pilot_image_names[MAX_PILOT_IMAGES]; int Num_pilot_images = 0; // squad logo list stuff (call pilot_load_squad_pic_list() to make these valid ) char Pilot_squad_images_arr[MAX_PILOT_IMAGES][MAX_FILENAME_LEN]; char *Pilot_squad_image_names[MAX_PILOT_IMAGES]; int Num_pilot_squad_images = 0; static uint Player_file_version; // forward declarations void read_detail_settings(CFILE *file, int Player_file_version); void write_detail_settings(CFILE *file); void read_stats_block(CFILE *file, int Player_file_version, scoring_struct *stats); void write_stats_block(CFILE *file, scoring_struct *stats, int multi); void read_multiplayer_options(player *p,CFILE *file); void write_multiplayer_options(player *p,CFILE *file); // internal function to delete a player file. Called after a pilot is obsoleted, and when a pilot is deleted // used in barracks and player_select //returns 0 on failure, 1 on success int delete_pilot_file( char *pilot_name, int single ) { int delreturn; char filename[MAX_FILENAME_LEN]; char basename[MAX_FILENAME_LEN]; // get the player file. _splitpath(pilot_name, NULL, NULL, basename, NULL); strcpy_s( filename, basename ); if (Player_sel_mode == PLAYER_SELECT_MODE_SINGLE){ strcat_s( filename, NOX(".pl2") ); // we only support the new format now - taylor delreturn = cf_delete(filename, CF_TYPE_SINGLE_PLAYERS); } else { strcat_s( filename, NOX(".plr") ); // multi pilots use modified old format delreturn = cf_delete(filename, CF_TYPE_MULTI_PLAYERS); } // we must try and delete the campaign save files for a pilot as well. if(delreturn) { mission_campaign_delete_all_savefiles( basename, !single ); return 1; } else { return 0; } } // same as delete_pilot_file() but deletes the old .plr files only // we don't delete campaign files here though, do it in missioncampaign.cpp instead int delete_pilot_file_old( char *pilot_name, int single ) { int delreturn = 0; char filename[MAX_FILENAME_LEN]; char basename[MAX_FILENAME_LEN]; // get the player file. _splitpath(pilot_name, NULL, NULL, basename, NULL); strcpy_s( filename, basename ); strcat_s( filename, NOX(".plr") ); // this is for single players only if (Player_sel_mode == PLAYER_SELECT_MODE_SINGLE){ delreturn = cf_delete(filename, CF_TYPE_SINGLE_PLAYERS); } if (delreturn) return 1; return 0; } // check if a pilot file is valid or not (i.e. is usable, not out of date, etc) // used in barracks and player_select int verify_pilot_file(char *filename, int single, int *rank) { CFILE *file; uint id, file_version; int type; char pname[MAX_FILENAME_LEN]; Assert( strlen(filename) < MAX_FILENAME_LEN - 4 ); strcpy_s(pname, filename); char *p = strchr( pname, '.' ); if ( p ) *p = 0; if (single) strcat_s(pname, ".pl2"); else strcat_s(pname, ".plr"); if (single){ file = cfopen(pname, "rb", CFILE_NORMAL, CF_TYPE_SINGLE_PLAYERS); } else { file = cfopen(pname, "rb", CFILE_NORMAL, CF_TYPE_MULTI_PLAYERS); } // if we didn't fine the file try the old version if (!file) { if (single){ strcpy_s(pname, filename); strcat_s(pname, ".plr"); file = cfopen(pname, "rb", CFILE_NORMAL, CF_TYPE_SINGLE_PLAYERS); } if (!file) return -1; } id = cfread_uint(file); if (id != PLR_FILE_ID) { nprintf(("Warning", "Player file ('%s') has invalid signature\n", pname)); cfclose(file); delete_pilot_file( pname, single ); return -1; } // check for compatibility here file_version = cfread_uint(file); /* if (file_version < INITIAL_RELEASE_FILE_VERSION) { */ // if (file_version != CURRENT_PLAYER_FILE_VERSION) { if (file_version < LOWEST_COMPATIBLE_PLAYER_FILE_VERSION) { nprintf(("Warning", "WARNING => Player file ('%s') is outdated and not compatible...\n", pname)); // Goober5000 - warn them char warning_text[256]; sprintf(warning_text, "ATTENTION: Detected out-of-date player file \"%s\". If you press OK, this file will be deleted! Back this file up now if you do not want to lose it. Contact a coder to resolve this error.", pname); Error(LOCATION, warning_text); cfclose(file); delete_pilot_file( pname, single ); return -1; } else if ( single && (file_version > CURRENT_PLAYER_FILE_VERSION) ) { nprintf(("Warning", "WARNING => Player file ('%s') is too new and not compatible...\n", pname)); cfclose(file); return -1; } else if ( !single && (file_version > CURRENT_MULTI_PLAYER_FILE_VERSION) ) { nprintf(("Warning", "WARNING => Player file ('%s') is too new and not compatible...\n", pname)); cfclose(file); return -1; } type = !cfread_ubyte(file); if (rank){ *rank = 0; } if (rank){ *rank = cfread_int(file); } else { cfread_int(file); } cfclose(file); if (type != single){ return -1; } return 0; } // check to see if a pilot file is going to be updated to the new pl2 format int pilot_file_upgrade_check(char *callsign, int single) { int rc; char pname[MAX_FILENAME_LEN]; // not for multi if (!single) return 0; // we only look for old pilot files here so if it's not found then it's assumed to be upgraded already Assert(strlen(callsign) < MAX_FILENAME_LEN - 4); // ensure we won't overrun the buffer strcpy_s( pname, callsign ); strcat_s( pname, NOX(".plr") ); // check if we've actually got an old file and make sure the user knows what's going to happen if (cf_exists(pname, CF_TYPE_SINGLE_PLAYERS)) { // give a popup warning about the conversion process before proceeding - taylor char confirm_string[300]; #ifndef _WIN32 snprintf(confirm_string, 300, "This will cause the pilot '%s' to be converted to FSO format. The conversion process is not reversible and you will no longer be able to use this pilot in earlier versions of FreeSpace (including FS2 retail). Do you want to continue?", callsign); #else // FIXME: really need to fix this crap system-wide (snprintf) // The Windows implementation if snprintf is not safe sprintf(confirm_string, "This will cause the pilot '%s' to be converted to FSO format. The conversion process is not reversible and you will no longer be able to use this pilot in earlier versions of FreeSpace (including FS2 retail). Do you want to continue?", callsign); #endif rc = popup(PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON, 2, POPUP_NO, POPUP_YES, confirm_string, -1); if (rc != 1) return 1; } return 0; } void pilot_write_techroom_data(CFILE *file, int multi) { int idx; ubyte out; /******** MULTI ONLY ********/ if ( !multi ) return; // write the ship and weapon count cfwrite_int(Num_ship_classes, file); cfwrite_int(Num_weapon_types, file); cfwrite_int(Intel_info_size, file); // write all ship flags out for (idx=0; idxship_class, file); cfwrite_string_len(Ship_info[slot->ship_class].name, file); for ( j = 0; j < MAX_SHIP_WEAPONS; j++ ) { cfwrite_int(slot->wep[j], file); cfwrite_int(slot->wep_count[j], file); cfwrite_string_len(Weapon_info[slot->wep[j]].name, file); } } } // read in the ship selection for the pilot void pilot_read_loadout(CFILE *file, int pfile_version) { int i, j; wss_unit *slot; int ship_count, weapon_count; char sw_name[NAME_LENGTH]; int pool_count = -1; if (pfile_version < 242) { memset(Player_loadout.filename, 0, MAX_FILENAME_LEN); memset(Player_loadout.last_modified, 0, DATE_TIME_LENGTH); cfread_string_len(Player_loadout.filename, MAX_FILENAME_LEN, file); cfread_string_len(Player_loadout.last_modified, DATE_TIME_LENGTH, file); // read in ship and weapon counts ship_count = cfread_int(file); weapon_count = cfread_int(file); Assert(ship_count <= MAX_SHIP_CLASSES); Assert(weapon_count <= MAX_WEAPON_TYPES); // read in ship pool for ( i = 0; i < ship_count; i++ ) { if (pfile_version >= 142) { pool_count = cfread_int(file); cfread_string_len(sw_name, NAME_LENGTH, file); Player_loadout.ship_pool[ship_info_lookup(sw_name)] = pool_count; } else { Player_loadout.ship_pool[i] = cfread_int(file); } } // read in weapons pool for ( i = 0; i < weapon_count; i++ ) { if (pfile_version >= 142) { pool_count = cfread_int(file); cfread_string_len(sw_name, NAME_LENGTH, file); Player_loadout.weapon_pool[weapon_info_lookup(sw_name)] = pool_count; } else { Player_loadout.weapon_pool[i] = cfread_int(file); } } // read in loadout info for ( i = 0; i < MAX_WSS_SLOTS; i++ ) { slot = &Player_loadout.unit_data[i]; slot->ship_class = cfread_int(file); if (pfile_version >= 142) { cfread_string_len(sw_name, NAME_LENGTH, file); slot->ship_class = ship_info_lookup(sw_name); } for ( j = 0; j < MAX_SHIP_WEAPONS; j++ ) { slot->wep[j] = cfread_int(file); slot->wep_count[j] = cfread_int(file); if (pfile_version >= 142) { cfread_string_len(sw_name, NAME_LENGTH, file); slot->wep[j] = weapon_info_lookup(sw_name); } } } } } // read_pilot_file() // // returns 0 - file read in correctly // -1 - .PLR file doesn't exist or file not compatible // >0 - errno from fopen error // if single == 1, look for players in the single players directory, otherwise look in the // multiplayers directory int read_pilot_file(char *callsign, int single, player *p) { ubyte num_ctrls; ubyte is_multi = 0; char filename[MAX_FILENAME_LEN], ship_name[NAME_LENGTH]; CFILE *file; uint id; int idx; int i, key_value; int pfile_upgrade = 0; char *ext; // if we're a standalone server in multiplayer, just fill in some bogus values since we don't have a pilot file if ((Game_mode & GM_MULTIPLAYER) && (Game_mode & GM_STANDALONE_SERVER)) { Player->insignia_texture = -1; strcpy_s(Player->callsign, NOX("Standalone")); strcpy_s(Player->short_callsign, NOX("Standalone")); return 0; } if (!p) { Assert((Player_num >= 0) && (Player_num < MAX_PLAYERS)); p = &Players[Player_num]; } // multi doesn't have a campaign savefile so we have to still use the old format // for all multi pilots. Do use the new 142 version though for table values. if (single) { ext = NOX(".pl2"); } else { ext = NOX(".plr"); } //sprintf(filename, "%-.8s.plr",Players[Player_num].callsign); Assert(strlen(callsign) < MAX_FILENAME_LEN - 4); // ensure we won't overrun the buffer strcpy_s( filename, callsign ); strcat_s( filename, ext ); // see comments at the beginning of function if (single) { file = cfopen(filename, "rb", CFILE_NORMAL, CF_TYPE_SINGLE_PLAYERS); } else { file = cfopen(filename, "rb", CFILE_NORMAL, CF_TYPE_MULTI_PLAYERS); } // if we couldn't open the new filetype try the old one and upgrade if (!file) { if (single) { strcpy_s( filename, callsign ); strcat_s( filename, NOX(".plr") ); file = cfopen(filename, "rb", CFILE_NORMAL, CF_TYPE_SINGLE_PLAYERS); pfile_upgrade = 1; } if (!file) return errno; } id = cfread_uint(file); if (id != PLR_FILE_ID) { Warning(LOCATION, "Player file has invalid signature"); cfclose(file); return -1; } // check for compatibility here Player_file_version = cfread_uint(file); cf_set_version( file, Player_file_version ); if (Player_file_version < LOWEST_COMPATIBLE_PLAYER_FILE_VERSION) { nprintf(("Warning", "WARNING => Player file is outdated and not compatible...\n")); // Goober5000 - warn them char warning_text[256]; sprintf(warning_text, "ATTENTION: Detected out-of-date player file \"%s\". If you press OK, this file will be deleted! Back this file up now if you do not want to lose it. Contact a coder to resolve this error.", filename); Error(LOCATION, warning_text); cfclose(file); delete_pilot_file( filename, single ); return -1; } // read in whether he's a multiplayer or not is_multi = cfread_ubyte(file); if (is_multi){ p->flags |= PLAYER_FLAGS_IS_MULTI; } else { p->flags &= ~PLAYER_FLAGS_IS_MULTI; // this takes care of unsetting any leftover bits from a (possibly) previously selected pilot } // read in rank. cfread_int(file); // get player location // moved to campaign file in 242 - taylor if ( Player_file_version < 242 ) { p->main_hall = cfread_ubyte(file); } // tips? p->tips = cfread_int(file); // write out the image file name cfread_string_len(p->image_filename, MAX_FILENAME_LEN - 1, file); // write out the image file name p->insignia_texture = -1; cfread_string_len(p->squad_name, NAME_LENGTH, file); // squad image filename char fname[MAX_FILENAME_LEN]; cfread_string_len(fname, MAX_FILENAME_LEN - 1, file); player_set_squad_bitmap(p, fname); // deal with campaign stuff. The way we store the information in the file is to first store the // name of the current campaign that the player is playing. Next we store the info regarding the campaigns // that the player is currently playing memset(p->current_campaign, 0, MAX_FILENAME_LEN); cfread_string_len(p->current_campaign, MAX_FILENAME_LEN, file); // read in the ship name for last ship flown by the player memset(ship_name, 0, NAME_LENGTH); cfread_string_len(ship_name, NAME_LENGTH, file); p->last_ship_flown_si_index = ship_info_lookup(ship_name); if ( p->last_ship_flown_si_index < 0 ) { nprintf(("Warning","WARNING => Ship class %s not located in Ship_info[] in player file\n",ship_name)); p->last_ship_flown_si_index = ship_info_lookup(default_player_ship); } // set all the entries in the control config arrays to -1 (undefined) control_config_clear(); // --------------------------------------------- // read in the keyboard/joystick mapping // --------------------------------------------- num_ctrls = cfread_ubyte(file); for (i=0; i 0.0f ) { Event_music_enabled = 1; } else { Event_music_enabled = 0; } read_detail_settings(file, Player_file_version); // restore list of most recently played missions Num_recent_missions = cfread_int( file ); Assert(Num_recent_missions <= MAX_RECENT_MISSIONS); for ( i = 0; i < Num_recent_missions; i++ ) { char *cp; cfread_string_len( Recent_missions[i], MAX_FILENAME_LEN, file); // Remove the extension cp = strchr(Recent_missions[i], '.'); if (cp) *cp = 0; } // use this block of stats from now on read_stats_block(file, Player_file_version, &p->stats); Game_skill_level = cfread_int(file); for (i=0; ireadyroom_listing_mode = cfread_int(file); Briefing_voice_enabled = cfread_int(file); // restore the default netgame protocol mode int protocol_temp = cfread_int(file); switch(protocol_temp){ // plain TCP case NET_VMT: case NET_TCP: Multi_options_g.protocol = NET_TCP; break; // IPX case NET_IPX: Multi_options_g.protocol = NET_IPX; break; } // restore wingman status used by red alert missions // moved to campaign file in 242 - taylor if ( Player_file_version < 242 ) { red_alert_read_wingman_status(file, Player_file_version); } // read techroom data pilot_read_techroom_data(file, Player_file_version); // restore auto-advance pref Player->auto_advance = cfread_int(file); Use_mouse_to_fly = cfread_int(file); Mouse_sensitivity = cfread_int(file); Joy_sensitivity = cfread_int(file); Dead_zone_size = cfread_int(file); // Goober5000 - read in player-persistent variables // maintain compatibility with previous file versions if (Player_file_version >= 141) { Player->num_variables = cfread_int( file ); for ( i = 0; i < Player->num_variables; i++ ) { Player->player_variables[i].type = cfread_int( file ); cfread_string_len( Player->player_variables[i].text, TOKEN_LENGTH, file ); cfread_string_len( Player->player_variables[i].variable_name, TOKEN_LENGTH, file ); } } if (cfclose(file)) return errno; // restore the callsign into the Player structure strcpy_s(p->callsign, callsign); // restore the truncated callsign into Player structure pilot_set_short_callsign(p, SHORT_CALLSIGN_PIXEL_W); // when we store the LastPlayer key, we have to mark it as being single or multiplayer, so we know where to look for him // (since we could have a single and a multiplayer pilot with the same callsign) // we'll distinguish them by putting an M and the end of the multiplayer callsign and a P at the end of a single player char cat[35]; strcpy_s(cat, p->callsign); if (is_multi) strcat_s(cat, NOX("M")); else strcat_s(cat, NOX("S")); os_config_write_string( NULL, "LastPlayer", cat ); /* // if he's not a multiplayer pilot, then load in the campaign file at this point! if (!is_multi) { if (mission_campaign_load_by_name(campaign_fname)) { strcpy_s(campaign_fname, BUILTIN_CAMPAIGN); if (mission_campaign_load_by_name(campaign_fname)) Assert(0); } } //Campaign.current_mission = mission_num;*/ hud_squadmsg_save_keys(); // when new pilot read in, must save info for squadmate messaging // if we had to upgrade formats force a save to be sure we don't lose anything if (pfile_upgrade) { write_pilot_file(p); // now delete old .plr file if it exists delete_pilot_file_old(callsign, single); } return 0; } void read_stats_block(CFILE *file, int pfile_version, scoring_struct *stats) { int i, total; int k_count; char kname[NAME_LENGTH]; init_scoring_element(stats); if (pfile_version < 242) { stats->score = cfread_int(file); stats->rank = cfread_int(file); stats->assists = cfread_int(file); if (pfile_version < 139) { // support for FS2_DEMO pilots that still have FS1 medal info in the .plr files for (i=0; i < NUM_MEDALS_FS1; i++) { total = cfread_int(file); // dummy read } } else { // read the usual way for (i=0; i < MAX_MEDALS; i++) { stats->medals[i] = cfread_int(file); } } total = cfread_int(file); if (total > MAX_SHIP_CLASSES){ Warning(LOCATION, "Some ship kill information will be lost due to MAX_SHIP_CLASSES decrease"); } for (i=0; i= 142) { k_count = cfread_ushort(file); cfread_string_len(kname, NAME_LENGTH, file); stats->kills[ship_info_lookup(kname)] = k_count; } else { stats->kills[i] = cfread_ushort(file); } } stats->kill_count = cfread_int(file); stats->kill_count_ok = cfread_int(file); stats->p_shots_fired = cfread_uint(file); stats->s_shots_fired = cfread_uint(file); stats->p_shots_hit = cfread_uint(file); stats->s_shots_hit = cfread_uint(file); stats->p_bonehead_hits = cfread_uint(file); stats->s_bonehead_hits = cfread_uint(file); stats->bonehead_kills = cfread_uint(file); } } // grab the various detail settings void read_detail_settings(CFILE *file, int pfile_version) { // mass read the Detail struct cfread( &Detail, sizeof(detail_levels), 1, file ); // swap, swap, swap Detail.setting = INTEL_INT(Detail.setting); Detail.nebula_detail = INTEL_INT(Detail.nebula_detail); Detail.detail_distance = INTEL_INT(Detail.detail_distance); Detail.hardware_textures = INTEL_INT(Detail.hardware_textures); Detail.num_small_debris = INTEL_INT(Detail.num_small_debris); Detail.num_particles = INTEL_INT(Detail.num_particles); Detail.num_stars = INTEL_INT(Detail.num_stars); Detail.shield_effects = INTEL_INT(Detail.shield_effects); Detail.lighting = INTEL_INT(Detail.lighting); Detail.targetview_model = INTEL_INT(Detail.targetview_model); Detail.planets_suns = INTEL_INT(Detail.planets_suns); Detail.weapon_extras = INTEL_INT(Detail.weapon_extras); } // Will write the pilot file in the most current format // // if single == 1, save into the single players directory, else save into the multiplayers directory int write_pilot_file_core(player *p) { char filename[MAX_FILENAME_LEN + 1]; int i, si_index, idx; ubyte is_multi; CFILE *file; // never save a pilot file for the standalone server in multiplayer if ((Game_mode & GM_MULTIPLAYER) && (Game_mode & GM_STANDALONE_SERVER)) { return 0; } if (!p) { Assert((Player_num >= 0) && (Player_num < MAX_PLAYERS)); p = &Players[Player_num]; } i = strlen(p->callsign); if (i == 0) return 0; // This means there is no player, probably meaning he was deleted and game exited from same screen. Assert((i > 0) && (i <= MAX_FILENAME_LEN - 4)); // ensure we won't overrun the buffer strcpy_s( filename, p->callsign); // determine if this pilot is a multiplayer pilot or not if (p->flags & PLAYER_FLAGS_IS_MULTI){ is_multi = 1; } else { is_multi = 0; } // see above if ( !is_multi ){ strcat_s( filename, NOX(".pl2") ); // support only new format on save - taylor file = cfopen(filename, "wb", CFILE_NORMAL, CF_TYPE_SINGLE_PLAYERS); } else { strcat_s( filename, NOX(".plr") ); file = cfopen(filename, "wb", CFILE_NORMAL, CF_TYPE_MULTI_PLAYERS); } if (!file){ return errno; } // Write out player's info cfwrite_uint(PLR_FILE_ID, file); if ( !is_multi ) cfwrite_uint(CURRENT_PLAYER_FILE_VERSION, file); else cfwrite_uint(CURRENT_MULTI_PLAYER_FILE_VERSION, file); cfwrite_ubyte(is_multi, file); cfwrite_int(p->stats.rank, file); if ( is_multi ) { cfwrite_ubyte((ubyte) p->main_hall, file); } cfwrite_int(p->tips, file); // write out the image file name cfwrite_string_len(p->image_filename, file); // write out the image file name cfwrite_string_len(p->squad_name, file); cfwrite_string_len(p->squad_filename, file); // write out the name of the player's active campaign. cfwrite_string_len(p->current_campaign, file); // write the ship name for last ship flown by the player si_index = p->last_ship_flown_si_index; if((si_index < 0) || (si_index >= Num_ship_classes)){ si_index = 0; } cfwrite_string_len(Ship_info[si_index].name, file); // --------------------------------------------- // write the keyboard/joystick configuration // --------------------------------------------- cfwrite_ubyte( CCFG_MAX, file ); for (i=0; istats, is_multi); cfwrite_int(Game_skill_level, file); for (i=0; isave_flags, file); // store ship selection for most recent mission pilot_write_loadout(file, is_multi); // read in multiplayer options write_multiplayer_options(p, file); cfwrite_int(p->readyroom_listing_mode, file); cfwrite_int(Briefing_voice_enabled, file); // store the default netgame protocol mode for this pilot if (Multi_options_g.protocol == NET_TCP) { cfwrite_int(NET_TCP, file); } else { cfwrite_int(NET_IPX, file); } if ( is_multi ) { red_alert_write_wingman_status(file); } pilot_write_techroom_data(file, is_multi); // store auto-advance pref cfwrite_int(Player->auto_advance, file); cfwrite_int(Use_mouse_to_fly, file); cfwrite_int(Mouse_sensitivity, file); cfwrite_int(Joy_sensitivity, file); cfwrite_int(Dead_zone_size, file); // write out player-persistent variables - Goober5000 cfwrite_int( Player->num_variables, file ); for ( i = 0; i < Player->num_variables; i++ ) { cfwrite_int( Player->player_variables[i].type, file ); cfwrite_string_len( Player->player_variables[i].text, file ); cfwrite_string_len( Player->player_variables[i].variable_name, file ); } if (!cfclose(file)) return 0; return errno; } int write_pilot_file(player *the_player) { int pilot_write_rval; do { // write_pilot_file_core returns 0 if ok, non-zero for error pilot_write_rval = write_pilot_file_core(the_player); // check with user if write not successful if (pilot_write_rval) { int popup_rval = popup(PF_TITLE_RED | PF_TITLE_BIG, 3, XSTR( "&Retry", 41), XSTR( "&Ignore", 42), XSTR( "&Quit Game", 43), XSTR( "Warning\nFailed to save pilot file. You may be out of disk space. If so, you should press Alt-Tab, free up some disk space, then come back and choose retry.\n", 44) ); // quit game popup return value (2) if (popup_rval == 2) { exit(1); } // _ignore_ popup return value (1) - don't save the file if (popup_rval) { return pilot_write_rval; } // retry popup return value (0) - try again } } while (pilot_write_rval); // write successful return 0; } void write_stats_block(CFILE *file,scoring_struct *stats, int multi) { int i; int total; if ( !multi ) return; cfwrite_int(stats->score, file); cfwrite_int(stats->rank, file); cfwrite_int(stats->assists, file); //WMC - pilot files don't support different numbers of medals Assert(Num_medals == MAX_MEDALS); for (i=0; imedals[i], file); } for(;ikills[total - 1]){ // find last used element total--; } cfwrite_int(total, file); for (i=0; ikills[i], file); cfwrite_string_len(Ship_info[i].name, file); } cfwrite_int(stats->kill_count,file); cfwrite_int(stats->kill_count_ok,file); cfwrite_uint(stats->p_shots_fired,file); cfwrite_uint(stats->s_shots_fired,file); cfwrite_uint(stats->p_shots_hit,file); cfwrite_uint(stats->s_shots_hit,file); cfwrite_uint(stats->p_bonehead_hits,file); cfwrite_uint(stats->s_bonehead_hits,file); cfwrite_uint(stats->bonehead_kills,file); } // write the various detail settings void write_detail_settings(CFILE *file) { // we still need sane values in the Detail struct so create // a temporary one to value swap and write to file detail_levels Detail_tmp; memset(&Detail_tmp, 0, sizeof(detail_levels)); memcpy(&Detail_tmp, &Detail, sizeof(detail_levels)); // swap, swap, swap - on big-endian this will convert back to little-endian Detail_tmp.setting = INTEL_INT(Detail_tmp.setting); Detail_tmp.nebula_detail = INTEL_INT(Detail_tmp.nebula_detail); Detail_tmp.detail_distance = INTEL_INT(Detail_tmp.detail_distance); Detail_tmp.hardware_textures = INTEL_INT(Detail_tmp.hardware_textures); Detail_tmp.num_small_debris = INTEL_INT(Detail_tmp.num_small_debris); Detail_tmp.num_particles = INTEL_INT(Detail_tmp.num_particles); Detail_tmp.num_stars = INTEL_INT(Detail_tmp.num_stars); Detail_tmp.shield_effects = INTEL_INT(Detail_tmp.shield_effects); Detail_tmp.lighting = INTEL_INT(Detail_tmp.lighting); Detail_tmp.targetview_model = INTEL_INT(Detail_tmp.targetview_model); Detail_tmp.planets_suns = INTEL_INT(Detail_tmp.planets_suns); Detail_tmp.weapon_extras = INTEL_INT(Detail_tmp.weapon_extras); cfwrite( &Detail_tmp, sizeof(detail_levels), 1, file ); } // write multiplayer information void write_multiplayer_options(player *p,CFILE *file) { // write the netgame options cfwrite_ubyte(p->m_server_options.squad_set,file); cfwrite_ubyte(p->m_server_options.endgame_set,file); cfwrite_int(p->m_server_options.flags,file); cfwrite_uint(p->m_server_options.respawn,file); cfwrite_ubyte(p->m_server_options.max_observers,file); cfwrite_ubyte(p->m_server_options.skill_level,file); cfwrite_ubyte(p->m_server_options.voice_qos,file); cfwrite_int(p->m_server_options.voice_token_wait,file); cfwrite_int(p->m_server_options.voice_record_time,file); cfwrite(&p->m_server_options.mission_time_limit,sizeof(fix),1,file); cfwrite_int(p->m_server_options.kill_limit,file); // write the local options cfwrite_int(p->m_local_options.flags,file); cfwrite_int(p->m_local_options.obj_update_level,file); } // read multiplayer options void read_multiplayer_options(player *p,CFILE *file) { // read the netgame options p->m_server_options.squad_set = cfread_ubyte(file); p->m_server_options.endgame_set = cfread_ubyte(file); p->m_server_options.flags = cfread_int(file); p->m_server_options.respawn = cfread_uint(file); p->m_server_options.max_observers = cfread_ubyte(file); p->m_server_options.skill_level = cfread_ubyte(file); p->m_server_options.voice_qos = cfread_ubyte(file); p->m_server_options.voice_token_wait = cfread_int(file); p->m_server_options.voice_record_time = cfread_int(file); cfread(&p->m_server_options.mission_time_limit,sizeof(fix),1,file); p->m_server_options.kill_limit = cfread_int(file); // read the local options p->m_local_options.flags = cfread_int(file); p->m_local_options.obj_update_level = cfread_int(file); } // run thorough an open file (at the beginning) and see if this pilot is a multiplayer pilot int is_pilot_multi(CFILE *fp) { uint id,file_version; ubyte is_multi; id = cfread_uint(fp); if (id != PLR_FILE_ID) { Warning(LOCATION, "Player file has invalid signature"); cfclose(fp); return -1; } // check for compatibility here file_version = cfread_uint(fp); if (file_version < LOWEST_COMPATIBLE_PLAYER_FILE_VERSION) { nprintf(("Warning", "WARNING => Player file is outdated and not compatible...\n")); cfclose(fp); return -1; } // read in whether he's a multiplayer or not is_multi = cfread_ubyte(fp); if (is_multi) return 1; return 0; } int is_pilot_multi(player *p) { return (p->flags & PLAYER_FLAGS_IS_MULTI) ? 1 : 0; } // this works on barracks and player_select interface screens void init_new_pilot(player *p, int reset) { int cur_speed; if (reset) { hud_set_default_hud_config(p); // use a default hud config // in the demo, load up the hardcoded hcf file #ifdef FS2_DEMO hud_config_color_load("hud_1.hcf"); #else hud_config_color_load("hud_3.hcf"); #endif control_config_reset_defaults(); // get a default keyboard config player_set_pilot_defaults(p); // set up any player struct defaults cur_speed = os_config_read_uint(NULL, NOX("ComputerSpeed"), 2 ); if ( cur_speed < 0 ) { cur_speed = 0; } else if ( cur_speed >= NUM_DEFAULT_DETAIL_LEVELS ) { cur_speed = NUM_DEFAULT_DETAIL_LEVELS-1; } // always set to high // DKA: 8/4/99 USE speed from registry // cur_speed = NUM_DEFAULT_DETAIL_LEVELS-2; #if NUM_DEFAULT_DETAIL_LEVELS != 4 #error Code in ManagePilot assumes NUM_DEFAULT_DETAIL_LEVELS = 4 #endif detail_level_set(cur_speed); Game_skill_level = game_get_default_skill_level(); mprintf(( "Setting detail level to %d because of new pilot\n", cur_speed )); Use_mouse_to_fly = 1; Mouse_sensitivity = 4; Joy_sensitivity = 9; Dead_zone_size = 10; } // unassigned squadron strcpy_s(p->squad_name, XSTR("Unassigned", 1255)); strcpy_s(p->squad_filename, ""); // set him to be a single player pilot by default (the actual creation routines will change this if necessary) p->flags &= ~PLAYER_FLAGS_IS_MULTI; // effectively sets the length return by strlen() to 0 Campaign.filename[0] = 0; p->main_hall = 0; // pick a random pilot image for this guy if (reset){ pilot_set_random_pic(p); p->insignia_texture = -1; pilot_set_random_squad_pic(p); } init_scoring_element(&p->stats); p->stats.score = 0; p->stats.rank = RANK_ENSIGN; p->tips = 1; Multi_options_g.protocol = NET_TCP; // initialize default multiplayer options multi_options_set_netgame_defaults(&p->m_server_options); multi_options_set_local_defaults(&p->m_local_options); Player_loadout.filename[0] = 0; // reset the cutscenes which can be viewed if ( reset ){ Cutscenes_viewable = INTRO_CUTSCENE_FLAG; } } void pilot_set_short_callsign(player *p, int max_width) { strcpy_s(p->short_callsign, p->callsign); gr_set_font(FONT1); gr_force_fit_string(p->short_callsign, CALLSIGN_LEN - 1, max_width); gr_get_string_size( &(p->short_callsign_width), NULL, p->short_callsign ); } // pick a random image for the passed player void pilot_set_random_pic(player *p) { // if there are no available pilot pics, set the image filename to null if (Num_pilot_images <= 0) { strcpy_s(p->image_filename, ""); } else { // pick a random name from the list int random_index = rand() % Num_pilot_images; Assert((random_index >= 0) && (random_index < Num_pilot_images)); strcpy_s(p->image_filename, Pilot_images_arr[random_index]); } } // pick a random image for the passed player void pilot_set_random_squad_pic(player *p) { // if there are no available pilot pics, set the image filename to null if (Num_pilot_squad_images <= 0) { player_set_squad_bitmap(p, ""); // strcpy_s(p->squad_filename, ""); } else { // pick a random name from the list int random_index = rand() % Num_pilot_squad_images; Assert((random_index >= 0) && (random_index < Num_pilot_squad_images)); player_set_squad_bitmap(p, Pilot_squad_images_arr[random_index]); // strcpy_s(p->squad_filename, Pilot_squad_images_arr[random_index]); } } // format a pilot's callsign into a "personal" form - ie, adding a 's or just an ' as appropriate void pilot_format_callsign_personal(char *in_callsign,char *out_callsign) { // don't do anything if we've got invalid strings if((in_callsign == NULL) || (out_callsign == NULL)){ return; } // copy the original string strcpy(out_callsign,in_callsign); // tack on the appropriate postfix if(in_callsign[strlen(in_callsign) - 1] == 's'){ strcat(out_callsign,XSTR( "\'", 45)); } else { strcat(out_callsign,XSTR( "\'s", 46)); } } // throw up a popup asking the user to verify the overwrite of an existing pilot name // 1 == ok to overwrite, 0 == not ok int pilot_verify_overwrite() { return popup(PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG, 2, XSTR( "&No", 47), XSTR( "&Yes", 48), XSTR( "Warning\nA duplicate pilot exists\nOverwrite?", 49)); } extern int Skip_packfile_search; // located in CFileList.cpp // load up the list of pilot image filenames (do this at game startup as well as barracks startup) void pilot_load_pic_list() { Num_pilot_images = 0; // load pilot images from the player images directory Num_pilot_images = cf_get_file_list_preallocated(MAX_PILOT_IMAGES, Pilot_images_arr, Pilot_image_names, CF_TYPE_PLAYER_IMAGES, NOX("*.pcx")); // sort all filenames cf_sort_filenames(Num_pilot_images, Pilot_image_names, CF_SORT_NAME); } // load up the list of pilot squad filenames void pilot_load_squad_pic_list() { Num_pilot_squad_images = 0; // load pilot images from the player images directory Num_pilot_squad_images = cf_get_file_list_preallocated(MAX_PILOT_IMAGES, Pilot_squad_images_arr, Pilot_squad_image_names, CF_TYPE_SQUAD_IMAGES, NOX("*.pcx")); // sort all filenames cf_sort_filenames(Num_pilot_squad_images, Pilot_squad_image_names, CF_SORT_NAME); } // will attempt to load an insignia bitmap and set it as active for the player void player_set_squad_bitmap(player *p, char *fname) { // sanity check if(p == NULL){ return; } // if he has another bitmap already - unload it if (p->insignia_texture >= 0) { bm_release(p->insignia_texture); } p->insignia_texture = -1; // try and set the new one if (fname != p->squad_filename) { strncpy(p->squad_filename, fname, MAX_FILENAME_LEN); } if (strlen(p->squad_filename) > 0) { p->insignia_texture = bm_load_duplicate(fname); // lock is as a transparent texture if (p->insignia_texture != -1) { bm_lock(p->insignia_texture, 16, BMP_TEX_XPARENT); bm_unlock(p->insignia_texture); } } /* flen = strlen(filename); elen = strlen(ext); Assert(flen < MAX_PATH_LEN); strcpy_s(path, filename); if ((flen < 4) || stricmp(path + flen - elen, ext)) { Assert(flen + elen < MAX_PATH_LEN); strcat_s(path, ext); } */ } // set squadron void player_set_squad(player *p, char *squad_name) { // sanity check if(p == NULL){ return; } strncpy(p->squad_name, squad_name, NAME_LENGTH+1); } DCF(pilot,"Changes pilot stats. (Like reset campaign)" ) { if (Dc_command) { dc_get_arg(ARG_STRING); if (!strcmp(Dc_arg, NOX("reset"))) { if (strlen(Campaign.filename)) { mission_campaign_savefile_delete(Campaign.filename); mission_campaign_load(Campaign.filename); } } else { Dc_help = 1; // print usage, not stats } } if (Dc_help) { dc_printf( "Usage: pilot keyword\nWhere keyword can be in the following forms:\n" ); dc_printf( "pilot reset Resets campaign stats.\n" ); Dc_status = 0; // don't print status if help is printed. Too messy. } if (Dc_status) { // no stats } }