2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
4 * All source code herein is the property of Volition, Inc. You may not sell
5 * or otherwise commercially exploit the source or things you created based on
10 * $Logfile: /Freespace2/code/Playerman/ManagePilot.cpp $
15 * ManagePilot.cpp has code to load and save pilot files, and to select and
19 * Revision 1.4 2002/06/09 04:41:25 relnev
20 * added copyright header
22 * Revision 1.3 2002/06/09 03:16:05 relnev
25 * removed unneeded asm, old sdl 2d setup.
27 * fixed crash caused by opengl_get_region.
29 * Revision 1.2 2002/05/07 03:16:50 theoddone33
30 * The Great Newline Fix
32 * Revision 1.1.1.1 2002/05/03 03:28:11 root
36 * 41 9/13/99 4:52p Dave
39 * 40 9/01/99 10:09a Dave
42 * 39 8/26/99 8:49p Jefff
43 * old player file compatibility with new medal stuff
45 * 38 8/22/99 5:53p Dave
46 * Scoring fixes. Added self destruct key. Put callsigns in the logfile
47 * instead of ship designations for multiplayer players.
49 * 37 8/16/99 4:06p Dave
50 * Big honking checkin.
52 * 36 8/11/99 11:36a Jefff
53 * added compatibility w/ fs2 demo plr version
55 * 35 8/10/99 3:46p Jefff
56 * changes for Intelligence section of new tech room
58 * 34 8/04/99 11:38p Andsager
59 * make new pilot detail level match registry info.
61 * 33 8/02/99 9:55p Dave
62 * Hardcode a nice hud color config for the demo.
64 * 32 8/02/99 9:13p Dave
67 * 31 8/01/99 12:39p Dave
68 * Added HUD contrast control key (for nebula).
70 * 30 7/30/99 10:31p Dave
71 * Added comm menu to the configurable hud files.
73 * 29 7/29/99 10:47p Dave
74 * Standardized D3D fogging using vertex fog. Shook out Savage 4 bugs.
76 * 28 7/29/99 12:05a Dave
77 * Nebula speed optimizations.
79 * 27 7/24/99 1:54p Dave
80 * Hud text flash gauge. Reworked dead popup to use 4 buttons in red-alert
83 * 26 6/22/99 7:03p Dave
84 * New detail options screen.
86 * 25 6/16/99 4:06p Dave
87 * New pilot info popup. Added new draw-bitmap-as-poly function.
89 * 24 6/11/99 11:13a Dave
90 * last minute changes before press tour build.
92 * 23 6/08/99 1:14a Dave
93 * Multi colored hud test.
95 * 22 5/03/99 8:33p Dave
96 * New version of multi host options screen.
98 * 21 3/24/99 4:05p Dave
99 * Put in support for assigning the player to a specific squadron with a
100 * specific logo. Preliminary work for doing pos/orient checksumming in
101 * multiplayer to reduce bandwidth.
103 * 20 1/30/99 1:29a Dave
104 * Fixed nebula thumbnail problem. Full support for 1024x768 choose pilot
105 * screen. Fixed beam weapon death messages.
107 * 19 1/29/99 2:08a Dave
108 * Fixed beam weapon collisions with players. Reduced size of scoring
109 * struct for multiplayer. Disabled PXO.
111 * 18 1/21/99 2:06p Dave
112 * Final checkin for multiplayer testing.
114 * 17 1/15/99 2:49p Dave
115 * Fixed creation of pilots.
117 * 16 1/14/99 6:06p Dave
118 * 100% full squad logo support for single player and multiplayer.
120 * 15 1/12/99 3:15a Dave
121 * Barracks screen support for selecting squad logos. We need real artwork
124 * 14 1/06/99 2:24p Dave
125 * Stubs and release build fixes.
127 * 13 12/14/98 12:13p Dave
128 * Spiffed up xfer system a bit. Put in support for squad logo file xfer.
131 * 12 11/20/98 11:16a Dave
132 * Fixed up IPX support a bit. Making sure that switching modes and
133 * loading/saving pilot files maintains proper state.
135 * 11 11/19/98 4:51p Dave
136 * Ignore multiplayer protocol settings in the pilot file for now.
138 * 10 11/19/98 4:19p Dave
139 * Put IPX sockets back in psnet. Consolidated all multiplayer config
142 * 9 10/13/98 2:47p Andsager
143 * Remove reference to Tech_shivan_species_avail
145 * 8 10/13/98 9:29a Dave
146 * Started neatening up freespace.h. Many variables renamed and
147 * reorganized. Added AlphaColors.[h,cpp]
149 * 7 10/12/98 9:30a Andsager
150 * Clean up barracks.cpp. Remove unneeded ".h" files from ManagePilot
152 * 6 10/09/98 5:17p Andsager
153 * move barracks screen into barracks.cpp
155 * 5 10/09/98 2:57p Dave
156 * Starting splitting up OS stuff.
158 * 4 10/08/98 9:19a Andsager
159 * Clean up pilot player read and write, starting with new version.
161 * 3 10/07/98 6:27p Dave
162 * Globalized mission and campaign file extensions. Removed Silent Threat
163 * special code. Moved \cache \players and \multidata into the \data
166 * 2 10/07/98 10:53a Dave
169 * 1 10/07/98 10:50a Dave
171 * 279 9/21/98 10:02p Dave
172 * Last minute changes to techroom weapon/ship/species stuff.
174 * 278 9/08/98 12:10p Andsager
175 * Fixed a bug with saving ship and weapon info techroom flags.
177 * 277 9/01/98 4:25p Dave
178 * Put in total (I think) backwards compatibility between mission disk
179 * freespace and non mission disk freespace, including pilot files and
180 * campaign savefiles.
182 * 276 6/09/98 10:31a Hoffoss
183 * Created index numbers for all xstr() references. Any new xstr() stuff
184 * added from here on out should be added to the end if the list. The
185 * current list count can be found in FreeSpace.cpp (search for
188 * 275 6/05/98 9:49a Lawrance
191 * 274 6/01/98 11:43a John
192 * JAS & MK: Classified all strings for localization.
194 * 273 5/26/98 11:53a Allender
195 * fix multiplayer problems and sexpression crash
197 * 272 5/24/98 2:46p Lawrance
198 * Fix bug where skill level would be reset to default when switching
201 * 271 5/23/98 4:02p Allender
204 * 270 5/23/98 2:41p Mike
205 * Make Easy the default skill level and prevent old pilot's skill level
206 * from carrying into new pilot.
211 #include "managepilot.h"
213 #include "freespace.h"
214 #include "hudsquadmsg.h"
217 #include "eventmusic.h"
218 #include "audiostr.h"
219 #include "osregistry.h"
221 #include "playermenu.h"
222 #include "missionshipchoice.h"
223 #include "hudconfig.h"
225 #include "redalert.h"
226 #include "techmenu.h"
229 #include "cutscenes.h"
232 // update this when altering data that is read/written to .PLR file
233 #define CURRENT_PLAYER_FILE_VERSION 140
234 #define FS2_DEMO_PLAYER_FILE_VERSION 135
235 #define LOWEST_COMPATIBLE_PLAYER_FILE_VERSION CURRENT_PLAYER_FILE_VERSION // demo plr files should work in final
237 // keep track of pilot file changes here
238 // version 2 : Added squad logo filename
239 // version 3 : Changed size of scoring struct. use ushort instead of ints for storing alltime kills by ship type
240 // version 4/5 : Added squadron name field
241 // version 6 : changed max length on a multiplayer options field
242 // version 130 : changed size of hud config struct
243 // version 133 : misc changes. new hud gauge
244 // version 134 : added HUD contrast toggle key
245 // version 135 : added tips flag (THIS IS THE DEMO VERSION - RETAIN COMPATIBILITY FROM HERE ON OUT)
246 // version 136 : added intelligence flags to tech room visibility data
247 // version 137 : 2 new HUD gauges.
248 // version 138 : new multiplayer config
249 // version 139 : # medals increased - added compatibility with old plr file versions
250 // version 140 : ships table reordered. clear out old pilot files
251 // search for PLAYER INIT for new pilot initialization stuff. I _think_ its in the right spot for now
252 #define PLR_FILE_ID 'FPSF' // unique signiture to identify a .PLR file (FreeSpace Player File) // FPSF appears as FSPF in file.
254 // Current content of a .PLR file
260 // pilot pic image list stuff ( call pilot_load_pic_list() to make these valid )
261 char Pilot_images_arr[MAX_PILOT_IMAGES][MAX_FILENAME_LEN];
262 char *Pilot_image_names[MAX_PILOT_IMAGES];
263 int Num_pilot_images = 0;
265 // squad logo list stuff (call pilot_load_squad_pic_list() to make these valid )
266 char Pilot_squad_images_arr[MAX_PILOT_IMAGES][MAX_FILENAME_LEN];
267 char *Pilot_squad_image_names[MAX_PILOT_IMAGES];
268 int Num_pilot_squad_images = 0;
270 static uint Player_file_version;
272 // forward declarations
273 void read_stats_block(CFILE *file, int Player_file_version, scoring_struct *stats);
274 void write_stats_block(CFILE *file, scoring_struct *stats);
275 void read_multiplayer_options(player *p,CFILE *file);
276 void write_multiplayer_options(player *p,CFILE *file);
278 // internal function to delete a player file. Called after a pilot is obsoleted, and when a pilot is deleted
279 // used in barracks and player_select
280 void delete_pilot_file( char *pilot_name, int single )
282 char filename[MAX_FILENAME_LEN];
283 char basename[MAX_FILENAME_LEN];
285 // get the player file.
287 _splitpath(pilot_name, NULL, NULL, basename, NULL);
289 strcpy( filename, basename );
290 strcat( filename, NOX(".plr") );
291 if (Player_sel_mode == PLAYER_SELECT_MODE_SINGLE){
292 cf_delete(filename, CF_TYPE_SINGLE_PLAYERS);
294 cf_delete(filename, CF_TYPE_MULTI_PLAYERS);
297 // we must try and delete the campaign save files for a pilot as well.
298 mission_campaign_delete_all_savefiles( basename, !single );
301 // check if a pilot file is valid or not (i.e. is usable, not out of date, etc)
302 // used in barracks and player_select
303 int verify_pilot_file(char *filename, int single, int *rank)
306 uint id, file_version;
309 filename = cf_add_ext(filename, NOX(".plr"));
312 file = cfopen(filename, "rb", CFILE_NORMAL, CF_TYPE_SINGLE_PLAYERS);
314 file = cfopen(filename, "rb", CFILE_NORMAL, CF_TYPE_MULTI_PLAYERS);
321 id = cfread_uint(file);
322 if (id != PLR_FILE_ID) {
323 nprintf(("Warning", "Player file has invalid signature\n"));
325 delete_pilot_file( filename, single );
329 // check for compatibility here
330 file_version = cfread_uint(file);
331 /* if (file_version < INITIAL_RELEASE_FILE_VERSION) { */
332 // if (file_version != CURRENT_PLAYER_FILE_VERSION) {
333 if (file_version < LOWEST_COMPATIBLE_PLAYER_FILE_VERSION) {
334 nprintf(("Warning", "WARNING => Player file is outdated and not compatible...\n"));
336 delete_pilot_file( filename, single );
340 type = !cfread_ubyte(file);
346 *rank = cfread_int(file);
359 void pilot_write_techroom_data(CFILE *file)
364 // write the ship and weapon count
365 cfwrite_int(Num_ship_types, file);
366 cfwrite_int(Num_weapon_types, file);
367 cfwrite_int(Intel_info_size, file);
369 // write all ship flags out
370 for (idx=0; idx<Num_ship_types; idx++) {
371 out = (Ship_info[idx].flags & SIF_IN_TECH_DATABASE) ? (ubyte)1 : (ubyte)0;
372 cfwrite_ubyte(out, file);
375 // write all weapon types out
376 for (idx=0; idx<Num_weapon_types; idx++) {
377 out = (Weapon_info[idx].wi_flags & WIF_IN_TECH_DATABASE) ? (ubyte)1 : (ubyte)0;
378 cfwrite_ubyte(out, file);
381 // write all intel entry flags out
382 for (idx=0; idx<Intel_info_size; idx++) {
383 cfwrite_ubyte((ubyte)Intel_info[idx].in_tech_db, file);
387 void pilot_read_techroom_data(CFILE *file)
390 int ship_count, weapon_count, intel_count;
393 // read in ship and weapon counts
394 ship_count = cfread_int(file);
395 weapon_count = cfread_int(file);
396 Assert(ship_count <= MAX_SHIP_TYPES);
397 Assert(weapon_count <= MAX_WEAPON_TYPES);
399 // maintain compatibility w/ demo version
400 if (Player_file_version < 136) {
401 // skip over all this data, because the lack of tech room in the demo
402 // left this all hosed in the demo .plr files
403 // this will all get initialized as if this fella was a new pilot
404 for (idx=0; idx<ship_count+weapon_count; idx++) {
405 in = cfread_ubyte(file);
410 intel_count = cfread_int(file);
411 Assert(intel_count <= MAX_INTEL_ENTRIES);
414 for (idx=0; idx<ship_count; idx++) {
415 in = cfread_ubyte(file);
417 Ship_info[idx].flags |= SIF_IN_TECH_DATABASE | SIF_IN_TECH_DATABASE_M;
419 Ship_info[idx].flags &= ~SIF_IN_TECH_DATABASE;
423 // read all weapons in
424 for (idx=0; idx<weapon_count; idx++) {
425 in = cfread_ubyte(file);
427 Weapon_info[idx].wi_flags |= WIF_IN_TECH_DATABASE;
429 Weapon_info[idx].wi_flags &= ~WIF_IN_TECH_DATABASE;
433 // read all intel entries in
434 for (idx=0; idx<intel_count; idx++) {
435 in = cfread_ubyte(file);
437 Intel_info[idx].in_tech_db = 1;
439 Intel_info[idx].in_tech_db = 0;
445 // write out the player ship selection
446 void pilot_write_loadout(CFILE *file)
451 cfwrite_string_len(Player_loadout.filename, file);
452 cfwrite_string_len(Player_loadout.last_modified, file);
454 // write ship and weapon counts
455 cfwrite_int(Num_ship_types, file);
456 cfwrite_int(Num_weapon_types, file);
459 for ( i = 0; i < Num_ship_types; i++ ) {
460 cfwrite_int(Player_loadout.ship_pool[i], file);
463 // write weapons pool
464 for ( i = 0; i < Num_weapon_types; i++ ) {
465 cfwrite_int(Player_loadout.weapon_pool[i], file);
468 // write ship loadouts
469 for ( i = 0; i < MAX_WSS_SLOTS; i++ ) {
470 slot = &Player_loadout.unit_data[i];
471 cfwrite_int(slot->ship_class, file);
472 for ( j = 0; j < MAX_WL_WEAPONS; j++ ) {
473 cfwrite_int(slot->wep[j], file);
474 cfwrite_int(slot->wep_count[j], file);
479 // read in the ship selection for the pilot
480 void pilot_read_loadout(CFILE *file)
484 int ship_count, weapon_count;
486 memset(Player_loadout.filename, 0, MAX_FILENAME_LEN);
487 cfread_string_len(Player_loadout.filename, MAX_FILENAME_LEN, file);
489 memset(Player_loadout.last_modified, 0, DATE_TIME_LENGTH);
490 cfread_string_len(Player_loadout.last_modified, DATE_TIME_LENGTH, file);
492 // read in ship and weapon counts
493 ship_count = cfread_int(file);
494 weapon_count = cfread_int(file);
495 Assert(ship_count <= MAX_SHIP_TYPES);
496 Assert(weapon_count <= MAX_WEAPON_TYPES);
499 for ( i = 0; i < ship_count; i++ ) {
500 Player_loadout.ship_pool[i] = cfread_int(file);
503 // read in weapons pool
504 for ( i = 0; i < weapon_count; i++ ) {
505 Player_loadout.weapon_pool[i] = cfread_int(file);
508 // read in loadout info
509 for ( i = 0; i < MAX_WSS_SLOTS; i++ ) {
510 slot = &Player_loadout.unit_data[i];
511 slot->ship_class = cfread_int(file);
512 for ( j = 0; j < MAX_WL_WEAPONS; j++ ) {
513 slot->wep[j] = cfread_int(file);
514 slot->wep_count[j] = cfread_int(file);
521 // returns 0 - file read in correctly
522 // -1 - .PLR file doesn't exist or file not compatible
523 // >0 - errno from fopen error
524 // if single == 1, look for players in the single players directory, otherwise look in the
525 // multiplayers directory
526 int read_pilot_file(char *callsign, int single, player *p)
530 char filename[MAX_FILENAME_LEN], ship_name[NAME_LENGTH];
537 Assert((Player_num >= 0) && (Player_num < MAX_PLAYERS));
538 p = &Players[Player_num];
541 //sprintf(filename, "%-.8s.plr",Players[Player_num].callsign);
542 Assert(strlen(callsign) < MAX_FILENAME_LEN - 4); // ensure we won't overrun the buffer
543 strcpy( filename, callsign );
544 strcat( filename, NOX(".plr") );
546 // if we're a standalone server in multiplayer, just fill in some bogus values since we don't have a pilot file
547 if ((Game_mode & GM_MULTIPLAYER) && (Game_mode & GM_STANDALONE_SERVER)) {
548 memset(Player, 0, sizeof(player));
549 strcpy(Player->callsign, NOX("Standalone"));
550 strcpy(Player->short_callsign, NOX("Standalone"));
554 // see comments at the beginning of function
556 file = cfopen(filename, "rb", CFILE_NORMAL, CF_TYPE_SINGLE_PLAYERS);
558 file = cfopen(filename, "rb", CFILE_NORMAL, CF_TYPE_MULTI_PLAYERS);
565 id = cfread_uint(file);
566 if (id != PLR_FILE_ID) {
567 Warning(LOCATION, "Player file has invalid signature");
572 // check for compatibility here
573 Player_file_version = cfread_uint(file);
574 cf_set_version( file, Player_file_version );
576 if (Player_file_version < LOWEST_COMPATIBLE_PLAYER_FILE_VERSION) {
577 nprintf(("Warning", "WARNING => Player file is outdated and not compatible...\n"));
579 delete_pilot_file( filename, single );
583 // read in whether he's a multiplayer or not
584 is_multi = cfread_ubyte(file);
586 p->flags |= PLAYER_FLAGS_IS_MULTI;
588 p->flags &= ~PLAYER_FLAGS_IS_MULTI; // this takes care of unsetting any leftover bits from a (possibly) previously selected pilot
594 // get player location
595 p->on_bastion = cfread_ubyte(file);
598 p->tips = cfread_int(file);
600 // write out the image file name
601 cfread_string_len(p->image_filename, MAX_FILENAME_LEN - 1, file);
603 // write out the image file name
604 p->insignia_texture = -1;
605 cfread_string_len(p->squad_name, NAME_LENGTH, file);
606 cfread_string_len(p->squad_filename, MAX_FILENAME_LEN - 1, file);
607 player_set_squad_bitmap(p, p->squad_filename);
609 // deal with campaign stuff. The way we store the information in the file is to first store the
610 // name of the current campaign that the player is playing. Next we store the info regarding the campaigns
611 // that the player is currently playing
612 memset(p->current_campaign, 0, MAX_FILENAME_LEN);
613 cfread_string_len(p->current_campaign, MAX_FILENAME_LEN, file);
615 // read in the ship name for last ship flown by the player
616 memset(ship_name, 0, NAME_LENGTH);
617 cfread_string_len(ship_name, NAME_LENGTH, file);
618 p->last_ship_flown_si_index = ship_info_lookup(ship_name);
619 if ( p->last_ship_flown_si_index < 0 ) {
620 nprintf(("Warning","WARNING => Ship class %s not located in Ship_info[] in player file\n",ship_name));
621 p->last_ship_flown_si_index = ship_info_lookup(default_player_ship);
624 // set all the entries in the control config arrays to -1 (undefined)
625 control_config_clear();
627 // ---------------------------------------------
628 // read in the keyboard/joystick mapping
629 // ---------------------------------------------
630 num_ctrls = cfread_ubyte(file);
631 for (i=0; i<num_ctrls; i++) {
632 key_value = cfread_short(file);
633 // NOTE: next two lines are only here for transitioning from 255 to -1 as undefined key items
634 if (key_value == 255)
637 Control_config[i].key_id = (short) key_value;
639 key_value = cfread_short(file);
640 // NOTE: next two lines are only here for transitioning from 255 to -1 as undefined key items
641 if (key_value == 255)
644 Control_config[i].joy_id = (short) key_value;
647 HUD_config.show_flags = cfread_int(file);
648 HUD_config.show_flags2 = cfread_int(file);
650 HUD_config.popup_flags = cfread_int(file);
651 HUD_config.popup_flags2 = cfread_int(file);
653 HUD_config.num_msg_window_lines = cfread_ubyte(file);
654 HUD_config.rp_flags = cfread_int(file);
655 HUD_config.rp_dist = cfread_int(file);
656 // HUD_config.color = cfread_int( file );
657 // HUD_color_alpha = cfread_int( file );
658 // if ( HUD_color_alpha < HUD_COLOR_ALPHA_USER_MIN ) {
659 // HUD_color_alpha = HUD_COLOR_ALPHA_DEFAULT;
661 // hud_config_record_color(HUD_config.color);
663 // added 2 gauges with version 137
664 if(Player_file_version < 137){
665 for(idx=0; idx<NUM_HUD_GAUGES-2; idx++){
666 cfread(&HUD_config.clr[idx], sizeof(color), 1, file);
669 // set the 2 new gauges to be radar color
670 memcpy(&HUD_config.clr[NUM_HUD_GAUGES-2], &HUD_config.clr[HUD_RADAR], sizeof(color));
671 memcpy(&HUD_config.clr[NUM_HUD_GAUGES-1], &HUD_config.clr[HUD_RADAR], sizeof(color));
673 for(idx=0; idx<NUM_HUD_GAUGES; idx++){
674 cfread(&HUD_config.clr[idx], sizeof(color), 1, file);
678 // read in the cutscenes which have been viewed
679 Cutscenes_viewable = cfread_int(file);
681 Master_sound_volume = cfread_float(file);
682 Master_event_music_volume = cfread_float(file);
683 Master_voice_volume = cfread_float(file);
685 audiostream_set_volume_all(Master_voice_volume, ASF_VOICE);
686 audiostream_set_volume_all(Master_event_music_volume, ASF_EVENTMUSIC);
688 if ( Master_event_music_volume > 0.0f ) {
689 Event_music_enabled = 1;
691 Event_music_enabled = 0;
694 cfread( &Detail, sizeof(detail_levels), 1, file );
696 // restore list of most recently played missions
697 Num_recent_missions = cfread_int( file );
698 Assert(Num_recent_missions <= MAX_RECENT_MISSIONS);
699 for ( i = 0; i < Num_recent_missions; i++ ) {
702 cfread_string_len( Recent_missions[i], MAX_FILENAME_LEN, file);
703 // Remove the extension
704 p = strchr(Recent_missions[i], '.');
709 // use this block of stats from now on
710 read_stats_block(file, Player_file_version, &p->stats);
712 Game_skill_level = cfread_int(file);
714 for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++) {
715 Axis_map_to[i] = cfread_int(file);
716 Invert_axis[i] = cfread_int(file);
719 // restore some player flags
720 Player[Player_num].save_flags = cfread_int(file);
722 // restore the most recent ship selection
723 pilot_read_loadout(file);
725 // read in multiplayer options
726 read_multiplayer_options(p,file);
728 p->readyroom_listing_mode = cfread_int(file);
729 Briefing_voice_enabled = cfread_int(file);
731 // restore the default netgame protocol mode
732 int protocol_temp = cfread_int(file);
733 switch(protocol_temp){
737 Multi_options_g.protocol = NET_TCP;
741 Multi_options_g.protocol = NET_IPX;
745 // restore wingman status used by red alert missions
746 red_alert_read_wingman_status(file, Player_file_version);
748 // read techroom data
749 pilot_read_techroom_data(file);
751 // restore auto-advance pref
752 Player->auto_advance = cfread_int(file);
754 Use_mouse_to_fly = cfread_int(file);
755 Mouse_sensitivity = cfread_int(file);
756 Joy_sensitivity = cfread_int(file);
757 Dead_zone_size = cfread_int(file);
762 // restore the callsign into the Player structure
763 strcpy(p->callsign, callsign);
765 // restore the truncated callsign into Player structure
766 pilot_set_short_callsign(p, SHORT_CALLSIGN_PIXEL_W);
768 // when we store the LastPlayer key, we have to mark it as being single or multiplayer, so we know where to look for him
769 // (since we could have a single and a multiplayer pilot with the same callsign)
770 // 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
773 strcpy(cat, p->callsign);
775 strcat(cat, NOX("M"));
777 strcat(cat, NOX("S"));
779 os_config_write_string( NULL, "LastPlayer", cat );
781 // if he's not a multiplayer pilot, then load in the campaign file at this point!
783 if (mission_campaign_load_by_name(campaign_fname)) {
784 strcpy(campaign_fname, BUILTIN_CAMPAIGN);
785 if (mission_campaign_load_by_name(campaign_fname))
789 //Campaign.current_mission = mission_num;*/
791 hud_squadmsg_save_keys(); // when new pilot read in, must save info for squadmate messaging
796 void read_stats_block(CFILE *file, int Player_file_version, scoring_struct *stats)
800 init_scoring_element(stats);
801 stats->score = cfread_int(file);
802 stats->rank = cfread_int(file);
803 stats->assists = cfread_int(file);
805 if (Player_file_version < 139) {
806 // support for FS2_DEMO pilots that still have FS1 medal info in the .plr files
807 for (i=0; i < NUM_MEDALS_FS1; i++) {
808 total = cfread_int(file); // dummy read
811 // read the usual way
812 for (i=0; i < NUM_MEDALS; i++) {
813 stats->medals[i] = cfread_int(file);
817 total = cfread_int(file);
818 if (total > MAX_SHIP_TYPES){
819 Warning(LOCATION, "Some ship kill information will be lost due to MAX_SHIP_TYPES decrease");
822 for (i=0; i<total && i<MAX_SHIP_TYPES; i++){
823 stats->kills[i] = cfread_ushort(file);
826 stats->kill_count = cfread_int(file);
827 stats->kill_count_ok = cfread_int(file);
829 stats->p_shots_fired = cfread_uint(file);
830 stats->s_shots_fired = cfread_uint(file);
831 stats->p_shots_hit = cfread_uint(file);
832 stats->s_shots_hit = cfread_uint(file);
834 stats->p_bonehead_hits = cfread_uint(file);
835 stats->s_bonehead_hits = cfread_uint(file);
836 stats->bonehead_kills = cfread_uint(file);
839 // Will write the pilot file in the most current format
841 // if single == 1, save into the single players directory, else save into the multiplayers directory
842 int write_pilot_file_core(player *p)
844 char filename[MAX_FILENAME_LEN + 1];
845 int i, si_index, idx;
849 // never save a pilot file for the standalone server in multiplayer
850 if ((Game_mode & GM_MULTIPLAYER) && (Game_mode & GM_STANDALONE_SERVER)) {
855 Assert((Player_num >= 0) && (Player_num < MAX_PLAYERS));
856 p = &Players[Player_num];
859 i = strlen(p->callsign);
861 return 0; // This means there is no player, probably meaning he was deleted and game exited from same screen.
863 Assert((i > 0) && (i <= MAX_FILENAME_LEN - 4)); // ensure we won't overrun the buffer
864 strcpy( filename, p->callsign);
865 strcat( filename, NOX(".plr") );
867 // determine if this pilot is a multiplayer pilot or not
868 if (p->flags & PLAYER_FLAGS_IS_MULTI){
876 file = cfopen(filename, "wb", CFILE_NORMAL, CF_TYPE_SINGLE_PLAYERS);
878 file = cfopen(filename, "wb", CFILE_NORMAL, CF_TYPE_MULTI_PLAYERS);
885 // Write out player's info
886 cfwrite_uint(PLR_FILE_ID, file);
887 cfwrite_uint(CURRENT_PLAYER_FILE_VERSION, file);
889 cfwrite_ubyte(is_multi, file);
890 cfwrite_int(p->stats.rank, file);
891 cfwrite_ubyte((ubyte) p->on_bastion, file);
893 cfwrite_int(p->tips, file);
895 // write out the image file name
896 cfwrite_string_len(p->image_filename, file);
898 // write out the image file name
899 cfwrite_string_len(p->squad_name, file);
900 cfwrite_string_len(p->squad_filename, file);
902 // write out the name of the player's active campaign.
903 cfwrite_string_len(p->current_campaign, file);
905 // write the ship name for last ship flown by the player
906 si_index = p->last_ship_flown_si_index;
907 if((si_index < 0) || (si_index >= Num_ship_types)){
911 cfwrite_string_len(Ship_info[si_index].name, file);
913 // ---------------------------------------------
914 // write the keyboard/joystick configuration
915 // ---------------------------------------------
916 cfwrite_ubyte( CCFG_MAX, file );
917 for (i=0; i<CCFG_MAX; i++) {
918 cfwrite_short( Control_config[i].key_id, file );
919 cfwrite_short( Control_config[i].joy_id, file );
922 // if this hud is an observer, the player ended the last mission as an observer, so we should
923 // restore his "real" ship HUD.
924 HUD_CONFIG_TYPE hc_temp;
925 hc_temp.show_flags = 0;
926 int stored_observer = 0;
927 if ( HUD_config.is_observer ){
928 // if we're in mission, copy the HUD we're currently using
929 if(Game_mode & GM_IN_MISSION){
930 memcpy(&hc_temp,&HUD_config,sizeof(HUD_CONFIG_TYPE));
934 hud_config_restore();
937 // write the hud configuration
938 cfwrite_int(HUD_config.show_flags, file);
939 cfwrite_int(HUD_config.show_flags2, file);
940 cfwrite_int(HUD_config.popup_flags, file);
941 cfwrite_int(HUD_config.popup_flags2, file);
942 cfwrite_ubyte( (ubyte) HUD_config.num_msg_window_lines, file );
943 cfwrite_int( HUD_config.rp_flags, file );
944 cfwrite_int( HUD_config.rp_dist, file );
945 // cfwrite_int( HUD_config.color, file );
946 // cfwrite_int( HUD_color_alpha, file );
947 for(idx=0; idx<NUM_HUD_GAUGES; idx++){
948 cfwrite(&HUD_config.clr[idx], sizeof(color), 1, file);
951 // restore the HUD we backed up
952 if( (Game_mode & GM_IN_MISSION) && stored_observer ){
953 memcpy(&HUD_config,&hc_temp,sizeof(HUD_CONFIG_TYPE));
956 // write the cutscenes which have been viewed
957 cfwrite_int(Cutscenes_viewable, file);
959 // store the digital sound fx volume, and music volume
960 cfwrite_float(Master_sound_volume, file);
961 cfwrite_float(Master_event_music_volume, file);
962 cfwrite_float(Master_voice_volume, file);
965 cfwrite( &Detail, sizeof(detail_levels), 1, file );
967 // store list of most recently played missions
968 cfwrite_int(Num_recent_missions, file);
969 for (i=0; i<Num_recent_missions; i++) {
970 cfwrite_string_len(Recent_missions[i], file);
973 // write the player stats
974 write_stats_block(file, &p->stats);
975 cfwrite_int(Game_skill_level, file);
977 for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++) {
978 cfwrite_int(Axis_map_to[i], file);
979 cfwrite_int(Invert_axis[i], file);
982 // store some player flags
983 cfwrite_int(Player->save_flags, file);
985 // store ship selection for most recent mission
986 pilot_write_loadout(file);
988 // read in multiplayer options
989 write_multiplayer_options(p, file);
991 cfwrite_int(p->readyroom_listing_mode, file);
992 cfwrite_int(Briefing_voice_enabled, file);
994 // store the default netgame protocol mode for this pilot
995 if (Multi_options_g.protocol == NET_TCP) {
996 cfwrite_int(NET_TCP, file);
998 cfwrite_int(NET_IPX, file);
1001 red_alert_write_wingman_status(file);
1002 pilot_write_techroom_data(file);
1004 // store auto-advance pref
1005 cfwrite_int(Player->auto_advance, file);
1007 cfwrite_int(Use_mouse_to_fly, file);
1008 cfwrite_int(Mouse_sensitivity, file);
1009 cfwrite_int(Joy_sensitivity, file);
1010 cfwrite_int(Dead_zone_size, file);
1018 int write_pilot_file(player *the_player)
1020 int pilot_write_rval;
1022 // write_pilot_file_core returns 0 if ok, non-zero for error
1023 pilot_write_rval = write_pilot_file_core(the_player);
1025 // check with user if write not successful
1026 if (pilot_write_rval) {
1027 int popup_rval = popup(PF_TITLE_RED | PF_TITLE_BIG, 3, XSTR( "&Retry", 41), XSTR( "&Ignore", 42), XSTR( "&Quit Game", 43),
1028 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) );
1030 // quit game popup return value (2)
1031 if (popup_rval == 2) {
1035 // _ignore_ popup return value (1) - don't save the file
1037 return pilot_write_rval;
1040 // retry popup return value (0) - try again
1043 } while (pilot_write_rval);
1049 void write_stats_block(CFILE *file,scoring_struct *stats)
1054 cfwrite_int(stats->score, file);
1055 cfwrite_int(stats->rank, file);
1056 cfwrite_int(stats->assists, file);
1057 for (i=0; i<NUM_MEDALS; i++){
1058 cfwrite_int(stats->medals[i], file);
1061 total = MAX_SHIP_TYPES;
1062 while (total && !stats->kills[total - 1]){ // find last used element
1066 cfwrite_int(total, file);
1067 for (i=0; i<total; i++){
1068 cfwrite_ushort(stats->kills[i], file);
1071 cfwrite_int(stats->kill_count,file);
1072 cfwrite_int(stats->kill_count_ok,file);
1074 cfwrite_uint(stats->p_shots_fired,file);
1075 cfwrite_uint(stats->s_shots_fired,file);
1076 cfwrite_uint(stats->p_shots_hit,file);
1077 cfwrite_uint(stats->s_shots_hit,file);
1078 cfwrite_uint(stats->p_bonehead_hits,file);
1079 cfwrite_uint(stats->s_bonehead_hits,file);
1080 cfwrite_uint(stats->bonehead_kills,file);
1083 // write multiplayer information
1084 void write_multiplayer_options(player *p,CFILE *file)
1086 // write the netgame options
1087 cfwrite_ubyte(p->m_server_options.squad_set,file);
1088 cfwrite_ubyte(p->m_server_options.endgame_set,file);
1089 cfwrite_int(p->m_server_options.flags,file);
1090 cfwrite_uint(p->m_server_options.respawn,file);
1091 cfwrite_ubyte(p->m_server_options.max_observers,file);
1092 cfwrite_ubyte(p->m_server_options.skill_level,file);
1093 cfwrite_ubyte(p->m_server_options.voice_qos,file);
1094 cfwrite_int(p->m_server_options.voice_token_wait,file);
1095 cfwrite_int(p->m_server_options.voice_record_time,file);
1096 cfwrite(&p->m_server_options.mission_time_limit,sizeof(fix),1,file);
1097 cfwrite_int(p->m_server_options.kill_limit,file);
1099 // write the local options
1100 cfwrite_int(p->m_local_options.flags,file);
1101 cfwrite_int(p->m_local_options.obj_update_level,file);
1104 // read multiplayer options
1105 void read_multiplayer_options(player *p,CFILE *file)
1107 // read the netgame options
1108 p->m_server_options.squad_set = cfread_ubyte(file);
1109 p->m_server_options.endgame_set = cfread_ubyte(file);
1110 p->m_server_options.flags = cfread_int(file);
1111 p->m_server_options.respawn = cfread_uint(file);
1112 p->m_server_options.max_observers = cfread_ubyte(file);
1113 p->m_server_options.skill_level = cfread_ubyte(file);
1114 p->m_server_options.voice_qos = cfread_ubyte(file);
1115 p->m_server_options.voice_token_wait = cfread_int(file);
1116 p->m_server_options.voice_record_time = cfread_int(file);
1117 cfread(&p->m_server_options.mission_time_limit,sizeof(fix),1,file);
1118 p->m_server_options.kill_limit = cfread_int(file);
1120 // read the local options
1121 p->m_local_options.flags = cfread_int(file);
1122 p->m_local_options.obj_update_level = cfread_int(file);
1125 // run thorough an open file (at the beginning) and see if this pilot is a multiplayer pilot
1126 int is_pilot_multi(CFILE *fp)
1128 uint id,file_version;
1131 id = cfread_uint(fp);
1132 if (id != PLR_FILE_ID) {
1133 Warning(LOCATION, "Player file has invalid signature");
1138 // check for compatibility here
1139 file_version = cfread_uint(fp);
1140 if (file_version < LOWEST_COMPATIBLE_PLAYER_FILE_VERSION) {
1141 nprintf(("Warning", "WARNING => Player file is outdated and not compatible...\n"));
1146 // read in whether he's a multiplayer or not
1147 is_multi = cfread_ubyte(fp);
1154 int is_pilot_multi(player *p)
1156 return (p->flags & PLAYER_FLAGS_IS_MULTI) ? 1 : 0;
1159 // this works on barracks and player_select interface screens
1160 void init_new_pilot(player *p, int reset)
1165 hud_set_default_hud_config(p); // use a default hud config
1167 // in the demo, load up the hardcoded hcf file
1169 hud_config_color_load("hud_1.hcf");
1171 hud_config_color_load("hud_3.hcf");
1174 control_config_reset_defaults(); // get a default keyboard config
1175 player_set_pilot_defaults(p); // set up any player struct defaults
1177 cur_speed = os_config_read_uint(NULL, NOX("ComputerSpeed"), 2 );
1178 if ( cur_speed < 0 ) {
1180 } else if ( cur_speed >= NUM_DEFAULT_DETAIL_LEVELS ) {
1181 cur_speed = NUM_DEFAULT_DETAIL_LEVELS-1;
1183 // always set to high
1184 // DKA: 8/4/99 USE speed from registry
1185 // cur_speed = NUM_DEFAULT_DETAIL_LEVELS-2;
1187 #if NUM_DEFAULT_DETAIL_LEVELS != 4
1188 #error Code in ManagePilot assumes NUM_DEFAULT_DETAIL_LEVELS = 4
1191 detail_level_set(cur_speed);
1193 Game_skill_level = game_get_default_skill_level();
1195 mprintf(( "Setting detail level to %d because of new pilot\n", cur_speed ));
1196 Use_mouse_to_fly = 0;
1197 Mouse_sensitivity = 4;
1198 Joy_sensitivity = 9;
1199 Dead_zone_size = 10;
1202 // unassigned squadron
1203 strcpy(p->squad_name, XSTR("Unassigned", 1255));
1204 strcpy(p->squad_filename, "");
1206 // set him to be a single player pilot by default (the actual creation routines will change this if necessary)
1207 p->flags &= ~PLAYER_FLAGS_IS_MULTI;
1209 // effectively sets the length return by strlen() to 0
1210 Campaign.filename[0] = 0;
1213 // pick a random pilot image for this guy
1215 pilot_set_random_pic(p);
1216 p->insignia_texture = -1;
1217 pilot_set_random_squad_pic(p);
1220 init_scoring_element(&p->stats);
1223 p->stats.rank = RANK_ENSIGN;
1227 Multi_options_g.protocol = NET_TCP;
1229 // initialize default multiplayer options
1230 multi_options_set_netgame_defaults(&p->m_server_options);
1231 multi_options_set_local_defaults(&p->m_local_options);
1233 Player_loadout.filename[0] = 0;
1235 // reset the cutscenes which can be viewed
1237 Cutscenes_viewable = INTRO_CUTSCENE_FLAG;
1241 void pilot_set_short_callsign(player *p, int max_width)
1243 strcpy(p->short_callsign, p->callsign);
1245 gr_force_fit_string(p->short_callsign, CALLSIGN_LEN - 1, max_width);
1246 gr_get_string_size( &(p->short_callsign_width), NULL, p->short_callsign );
1249 // pick a random image for the passed player
1250 void pilot_set_random_pic(player *p)
1252 // if there are no available pilot pics, set the image filename to null
1253 if (Num_pilot_images <= 0) {
1254 strcpy(p->image_filename, "");
1256 // pick a random name from the list
1257 int random_index = rand() % Num_pilot_images;
1258 Assert((random_index >= 0) && (random_index < Num_pilot_images));
1259 strcpy(p->image_filename, Pilot_images_arr[random_index]);
1263 // pick a random image for the passed player
1264 void pilot_set_random_squad_pic(player *p)
1266 // if there are no available pilot pics, set the image filename to null
1267 if (Num_pilot_squad_images <= 0) {
1268 player_set_squad_bitmap(p, "");
1269 // strcpy(p->squad_filename, "");
1271 // pick a random name from the list
1272 int random_index = rand() % Num_pilot_squad_images;
1273 Assert((random_index >= 0) && (random_index < Num_pilot_squad_images));
1274 player_set_squad_bitmap(p, Pilot_squad_images_arr[random_index]);
1275 // strcpy(p->squad_filename, Pilot_squad_images_arr[random_index]);
1279 // format a pilot's callsign into a "personal" form - ie, adding a 's or just an ' as appropriate
1280 void pilot_format_callsign_personal(char *in_callsign,char *out_callsign)
1282 // don't do anything if we've got invalid strings
1283 if((in_callsign == NULL) || (out_callsign == NULL)){
1287 // copy the original string
1288 strcpy(out_callsign,in_callsign);
1290 // tack on the appropriate postfix
1291 if(in_callsign[strlen(in_callsign) - 1] == 's'){
1292 strcat(out_callsign,XSTR( "\'", 45));
1294 strcat(out_callsign,XSTR( "\'s", 46));
1298 // throw up a popup asking the user to verify the overwrite of an existing pilot name
1299 // 1 == ok to overwrite, 0 == not ok
1300 int pilot_verify_overwrite()
1302 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));
1305 extern int Skip_packfile_search; // located in CFileList.cpp
1308 // load up the list of pilot image filenames (do this at game startup as well as barracks startup)
1309 void pilot_load_pic_list()
1311 Num_pilot_images = 0;
1313 // load pilot images from the player images directory
1314 Num_pilot_images = cf_get_file_list_preallocated(MAX_PILOT_IMAGES, Pilot_images_arr, Pilot_image_names, CF_TYPE_PLAYER_IMAGES_MAIN, NOX("*.pcx"));
1316 // sort all filenames
1317 cf_sort_filenames(Num_pilot_images, Pilot_image_names, CF_SORT_NAME);
1320 // load up the list of pilot squad filenames
1321 void pilot_load_squad_pic_list()
1323 Num_pilot_squad_images = 0;
1325 // load pilot images from the player images directory
1326 Num_pilot_squad_images = cf_get_file_list_preallocated(MAX_PILOT_IMAGES, Pilot_squad_images_arr, Pilot_squad_image_names, CF_TYPE_SQUAD_IMAGES_MAIN, NOX("*.pcx"));
1328 // sort all filenames
1329 cf_sort_filenames(Num_pilot_squad_images, Pilot_squad_image_names, CF_SORT_NAME);
1332 // will attempt to load an insignia bitmap and set it as active for the player
1333 void player_set_squad_bitmap(player *p, char *fname)
1340 // if he has another bitmap already - unload it
1341 if(p->insignia_texture >= 0){
1342 bm_unload(p->insignia_texture);
1345 p->insignia_texture = -1;
1347 // try and set the new one
1348 strncpy(p->squad_filename, fname, MAX_FILENAME_LEN);
1349 if(strlen(p->squad_filename) > 0){
1350 p->insignia_texture = bm_load_duplicate(fname);
1352 // lock is as a transparent texture
1353 if(p->insignia_texture != -1){
1354 bm_lock(p->insignia_texture, 16, BMP_TEX_XPARENT);
1355 bm_unlock(p->insignia_texture);
1360 flen = strlen(filename);
1362 Assert(flen < MAX_PATH_LEN);
1363 strcpy(path, filename);
1364 if ((flen < 4) || stricmp(path + flen - elen, ext)) {
1365 Assert(flen + elen < MAX_PATH_LEN);
1372 void player_set_squad(player *p, char *squad_name)
1379 strncpy(p->squad_name, squad_name, NAME_LENGTH+1);
1382 DCF(pilot,"Changes pilot stats. (Like reset campaign)" )
1385 dc_get_arg(ARG_STRING);
1386 if (!strcmp(Dc_arg, NOX("reset"))) {
1387 if (strlen(Campaign.filename)) {
1388 mission_campaign_savefile_delete(Campaign.filename);
1389 mission_campaign_load(Campaign.filename);
1393 Dc_help = 1; // print usage, not stats
1398 dc_printf( "Usage: pilot keyword\nWhere keyword can be in the following forms:\n" );
1399 dc_printf( "pilot reset Resets campaign stats.\n" );
1400 Dc_status = 0; // don't print status if help is printed. Too messy.