diff --git a/code/menuui/playermenu.cpp b/code/menuui/playermenu.cpp index a9ef597620..1fc96d3190 100644 --- a/code/menuui/playermenu.cpp +++ b/code/menuui/playermenu.cpp @@ -128,7 +128,7 @@ UI_BUTTON Player_select_list_region; // button for detecting mouse clicks on th UI_INPUTBOX Player_select_input_box; // input box for adding new pilot names // #define PLAYER_SELECT_PALETTE_FNAME NOX("InterfacePalette") -int Player_select_background_bitmap; // bitmap for this screen +int Player_select_background_bitmap = -1; // bitmap for this screen // int Player_select_palette; // palette bitmap for this screen int Player_select_autoaccept = 0; // int Player_select_palette_set = 0; @@ -156,6 +156,8 @@ SCP_string Player_select_force_main_hall = ""; static int Player_select_no_save_pilot = 0; // to skip save of pilot in pilot_select_close() +bool Player_select_pilot_just_committed = false; + int Player_select_screen_active = 0; // for pilot savefile loading - taylor // notification text areas @@ -195,6 +197,8 @@ int player_select_get_last_pilot_info(); void player_select_eval_very_first_pilot(); void player_select_commit(); void player_select_cancel_create(); +bool player_select_commit_pilot(const char* callsign, bool is_multi); +void player_select_show_startup_warnings(); bool valid_pilot(const char* callsign, bool no_popup) { @@ -377,8 +381,8 @@ void player_select_init() player_select_init_player_stuff(PLAYER_SELECT_MODE_SINGLE); } - if (Cmdline_benchmark_mode || ((Player_select_num_pilots == 1) && Player_select_input_mode)) { - // When benchmarking, just accept automatically + if ((Player_select_num_pilots == 1) && Player_select_input_mode) { + // accept automatically once the very first pilot is created Player_select_autoaccept = 1; } } @@ -387,10 +391,8 @@ void player_select_init() static bool Startup_warning_dialog_displayed = false; static bool Save_file_warning_displayed = false; -void player_select_do() +void player_select_show_startup_warnings() { - int k; - // Goober5000 - display a popup warning about problems in the mod if ((Global_warning_count > 10 || Global_error_count > 0) && !Startup_warning_dialog_displayed) { char text[512]; @@ -403,7 +405,14 @@ void player_select_do() popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("A new settings file has been created for the current game or mod. You may want to check the options menu to ensure everything is set to your liking.", 1854)); Save_file_warning_displayed = true; } - +} + +void player_select_do() +{ + int k; + + player_select_show_startup_warnings(); + // set the input box at the "virtual" line 0 to be active so the player can enter a callsign if (Player_select_input_mode) { Player_select_input_box.set_focus(); @@ -460,12 +469,6 @@ void player_select_do() gr_set_bitmap(Player_select_background_bitmap); gr_bitmap(0,0,GR_RESIZE_MENU); - //skip this if pilot is given through cmdline, assuming single-player - if (Cmdline_pilot) { - player_finish_select(Cmdline_pilot, false); - return; - } - // press the accept button if (Player_select_autoaccept) { Player_select_buttons[gr_screen.res][ACCEPT_BUTTON].button.press_button(); @@ -515,66 +518,29 @@ void player_select_close() return; } - // actually set up the Player struct here - if ( (Player_select_pilot == -1) || (Player_select_num_pilots == 0) ) { - nprintf(("General","WARNING! No pilot selected! We should be exiting the game now!\n")); - return; + // a pilot may already have been selected some other way, e.g. by a script + if (!Player_select_pilot_just_committed) { + // actually set up the Player struct here + if ( (Player_select_pilot == -1) || (Player_select_num_pilots == 0) ) { + nprintf(("General","WARNING! No pilot selected! We should be exiting the game now!\n")); + return; + } + + if (!player_select_commit_pilot(Pilots[Player_select_pilot], Player_select_mode == PLAYER_SELECT_MODE_MULTI)) { + return; + } } // unload all bitmaps if(Player_select_background_bitmap >= 0) { bm_release(Player_select_background_bitmap); Player_select_background_bitmap = -1; - } + } // if(Player_select_palette >= 0){ // bm_release(Player_select_palette); //Player_select_palette = -1;GS_EVENT_MAIN_MENU // } - // setup the player struct - Player_num = 0; - Player = &Players[0]; - Player->flags |= PLAYER_FLAGS_STRUCTURE_IN_USE; - - // New pilot file makes no distinction between multi pilots and regular ones, so let's do this here. - if (Player_select_mode == PLAYER_SELECT_MODE_MULTI) { - Player->flags |= PLAYER_FLAGS_IS_MULTI; - } - - // WMC - Set appropriate game mode - if ( Player->flags & PLAYER_FLAGS_IS_MULTI ) { - Game_mode = GM_MULTIPLAYER; - } else { - Game_mode = GM_NORMAL; - } - - // now read in a the pilot data - if ( !Pilot.load_player(Pilots[Player_select_pilot], Player) ) { - Error(LOCATION,"Couldn't load pilot file, bailing"); - Player = NULL; - return; - } - - // set the local multi options from the player flags - multi_options_init_globals(); - - // read in the current campaign - // NOTE: this may fail if there is no current campaign, it's not fatal - Pilot.load_savefile(Player, Player->current_campaign); - - // Set singleplayer/multiplayer mode in Player - if (Player_select_mode == PLAYER_SELECT_MODE_MULTI) { - Player->player_was_multi = 1; - } else { - Player->player_was_multi = 0; - } - - // save the pilot file to a version that we work with - Pilot.save_player(Player); - - // Update the LastPlayer key in the registry - os_config_write_string(nullptr, "LastPlayer", Player->callsign); - // Maybe use a different main hall (debug console) if (Player_select_force_main_hall != "") { main_hall_init(Player_select_force_main_hall); @@ -870,20 +836,32 @@ void player_select_scroll_list_down() } } +// callsign of the last played pilot, or an empty string if there wasn't one +static SCP_string player_select_read_last_pilot() +{ + auto callsign = player_get_last_player(); + + // handle changing from pre-pilot code to post-pilot code, which appended M or S to the callsign; + // but only chop it off if the callsign isn't a valid pilot as-is + if (!callsign.empty() && (callsign.back() == 'M' || callsign.back() == 'S') && !Pilot.verify((callsign + ".json").c_str())) + { + auto chopped = callsign.substr(0, callsign.size() - 1); + if (Pilot.verify((chopped + ".json").c_str())) + callsign = chopped; + } + + return callsign; +} + // fill in the data on the last played pilot (callsign and is_multi or not) int player_select_get_last_pilot_info() { - const char *last_player = os_config_read_string( NULL, "LastPlayer", NULL); + auto last_pilot = player_select_read_last_pilot(); - if (last_player == NULL) { + if (last_pilot.empty()) { return 0; } else { - strcpy_s(Player_select_last_pilot, last_player); - } - - // handle changing from pre-pilot code to post-pilot code - if (Player_select_last_pilot[strlen(Player_select_last_pilot)-1] == 'M' || Player_select_last_pilot[strlen(Player_select_last_pilot)-1] == 'S') { - Player_select_last_pilot[strlen(Player_select_last_pilot)-1]='\0'; // chop off last char, M|P + strcpy_s(Player_select_last_pilot, last_pilot.c_str()); } if ( !Pilot.load_player(Player_select_last_pilot, Player) ) { @@ -895,56 +873,45 @@ int player_select_get_last_pilot_info() return 1; } -int player_select_get_last_pilot() +bool player_select_auto_select_requested() { - // if the player has the Cmdline_use_last_pilot command line option set, try and drop out quickly - if (Cmdline_use_last_pilot) { - int idx; + return Cmdline_pilot || Cmdline_use_last_pilot || Cmdline_benchmark_mode || Cmdline_start_mission; +} - if ( !player_select_get_last_pilot_info() ) { - return 0; - } +bool player_select_try_auto_select() +{ + if (!player_select_auto_select_requested()) + return false; + SCP_string callsign; + if (Cmdline_pilot) + callsign = Cmdline_pilot; + else + { + // pilots are sorted by file time, so if the last pilot isn't available, this picks the most recently saved one auto pilots = player_select_enumerate_pilots(); - // Copy the enumerated pilots into the appropriate local variables - Player_select_num_pilots = static_cast(pilots.size()); - for (auto i = 0; i < MAX_PILOTS; ++i) { - if (i < static_cast(pilots.size())) { - strcpy_s(Pilots_arr[i], pilots[i].c_str()); - } - Pilots[i] = Pilots_arr[i]; - } + if (pilots.empty()) + return false; - Player_select_pilot = -1; - idx = 0; - // pick the last player - for (idx=0;idxflags |= PLAYER_FLAGS_STRUCTURE_IN_USE; - - // New pilot file makes no distinction between multi pilots and regular ones, so let's do this here. - if (Player->player_was_multi) { - Player->flags |= PLAYER_FLAGS_IS_MULTI; - } - - return 1; - } } - return 0; + player_select_show_startup_warnings(); + + if (!valid_pilot(callsign.c_str())) + return false; + + return player_select_commit_pilot(callsign.c_str(), Cmdline_start_netgame || (Cmdline_connect_addr != nullptr)); } void player_select_init_player_stuff(int mode) @@ -1528,7 +1495,9 @@ SCP_string player_get_last_player() return SCP_string(last_player); } -void player_finish_select(const char* callsign, bool is_multi) { +bool player_select_commit_pilot(const char* callsign, bool is_multi) +{ + // setup the player struct Player_num = 0; Player = &Players[0]; Player->flags |= PLAYER_FLAGS_STRUCTURE_IN_USE; @@ -1549,23 +1518,33 @@ void player_finish_select(const char* callsign, bool is_multi) { if ( !Pilot.load_player(callsign, Player) ) { Error(LOCATION,"Couldn't load pilot file for pilot \"%s\", bailing", callsign); Player = nullptr; - } else { - // NOTE: this may fail if there is no current campaign, it's not fatal - Pilot.load_savefile(Player, Player->current_campaign); - } - - if (Player_select_mode == PLAYER_SELECT_MODE_MULTI) { - Player->player_was_multi = 1; - } else { - Player->player_was_multi = 0; + return false; } // set the local multi options from the player flags multi_options_init_globals(); + // read in the current campaign + // NOTE: this may fail if there is no current campaign, it's not fatal + Pilot.load_savefile(Player, Player->current_campaign); + + // Set singleplayer/multiplayer mode in Player + Player->player_was_multi = is_multi ? 1 : 0; + + // save the pilot file to a version that we work with + Pilot.save_player(Player); + + // Update the LastPlayer key in the registry os_config_write_string(nullptr, "LastPlayer", Player->callsign); - gameseq_post_event(GS_EVENT_MAIN_MENU); + Player_select_pilot_just_committed = true; + return true; +} + +void player_finish_select(const char* callsign, bool is_multi) { + if (player_select_commit_pilot(callsign, is_multi)) { + gameseq_post_event(GS_EVENT_MAIN_MENU); + } } bool player_create_new_pilot(const char* callsign, bool is_multi, const char* copy_from_callsign) { SCP_string buf = callsign; diff --git a/code/menuui/playermenu.h b/code/menuui/playermenu.h index f04e6d52e8..032e971fd4 100644 --- a/code/menuui/playermenu.h +++ b/code/menuui/playermenu.h @@ -35,8 +35,14 @@ void player_select_do(); */ void player_select_close(); -// function to check whether we found a "last pilot". loads this pilot in if possible and returns true, or false otherwise -int player_select_get_last_pilot(); +// set when a pilot is loaded as the active Player; cleared when the main menu state is entered +extern bool Player_select_pilot_just_committed; + +// whether the command line asks for a pilot to be selected without showing the pilot select screen +bool player_select_auto_select_requested(); + +// if requested by the command line, selects a pilot without the pilot select screen; returns true if a pilot was selected +bool player_select_try_auto_select(); // tooltips extern int Player_tips_start_index; diff --git a/freespace2/freespace.cpp b/freespace2/freespace.cpp index 46d0850fe2..be6ddf71d6 100644 --- a/freespace2/freespace.cpp +++ b/freespace2/freespace.cpp @@ -5292,8 +5292,8 @@ void game_process_event( int current_state, int event ) break; case GS_EVENT_GAME_INIT: - // see if the command line option has been set to use the last pilot, and act accordingly - if( player_select_get_last_pilot() ) { + // see if the command line has been set to select a pilot automatically, and act accordingly + if( player_select_try_auto_select() ) { // always enter the main menu -- do the automatic network startup stuff elsewhere // so that we still have valid checks for networking modes, etc. gameseq_set_state(GS_STATE_MAIN_MENU); @@ -5919,6 +5919,13 @@ void game_enter_state( int old_state, int new_state ) scripting::hook_param("OldState", 'o', l_GameState.Set(gamestate_h(old_state))), scripting::hook_param("NewState", 'o', l_GameState.Set(gamestate_h(new_state)))); + // consume the flag before any script override, so that it can't remain set for a later main hall entry + bool pilot_just_committed = false; + if (new_state == GS_STATE_MAIN_MENU) { + pilot_just_committed = Player_select_pilot_just_committed; + Player_select_pilot_just_committed = false; + } + if(scripting::hooks::OnStateStart->isActive()) { if (scripting::hooks::OnStateStart->isOverride(script_param_list)) { scripting::hooks::OnStateStart->run(std::move(script_param_list)); @@ -5947,8 +5954,8 @@ void game_enter_state( int old_state, int new_state ) } // determine which ship this guy is currently based on - // if we are seeing the main hall for the first time, allow falling back when the current campaign isn't available - const auto result = mission_load_up_campaign(old_state == GS_STATE_INITIAL_PLAYER_SELECT); + // if we have just selected a pilot, allow falling back when the current campaign isn't available + const auto result = mission_load_up_campaign(pilot_just_committed); // if there was a problem, pass an empty main hall which will set up appropriate defaults if (result != 0) { @@ -7053,7 +7060,7 @@ int game_main(int argc, char *argv[]) scripting::hooks::OnIntroAboutToPlay->run(); } - if (!Is_standalone && !skip_intro) { + if (!Is_standalone && !skip_intro && !player_select_auto_select_requested()) { movie::play("intro.mve"); }