Skip to content

Commit

Permalink
Merge pull request scp-fs2open#5910 from Goober5000/campaign_load_ref…
Browse files Browse the repository at this point in the history
…actor

campaign error message improvement
  • Loading branch information
Goober5000 authored Jan 5, 2024
2 parents 9a45c9f + ff0b79b commit e619c0c
Show file tree
Hide file tree
Showing 10 changed files with 85 additions and 44 deletions.
2 changes: 1 addition & 1 deletion code/localization/localize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ bool *Lcl_unexpected_tstring_check = nullptr;
// NOTE: with map storage of XSTR strings, the indexes no longer need to be contiguous,
// but internal strings should still increment XSTR_SIZE to avoid collisions.
// retail XSTR_SIZE = 1570
// #define XSTR_SIZE 1816 // This is the next available ID
// #define XSTR_SIZE 1817 // This is the next available ID


// struct to allow for strings.tbl-determined x offset
Expand Down
6 changes: 6 additions & 0 deletions code/menuui/barracks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1070,6 +1070,9 @@ void barracks_button_pressed(int n)
} else {
gamesnd_play_iface(InterfaceSounds::SCROLL);

// we now have a new pilot, so try to load its current campaign, falling back if necessary
mission_load_up_campaign(true);

if (Campaign_file_missing) {
popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR( "The currently active campaign cannot be found. Please select another...", 1600));
gameseq_post_event(GS_EVENT_CAMPAIGN_ROOM);
Expand All @@ -1081,6 +1084,9 @@ void barracks_button_pressed(int n)
if (Num_pilots && !barracks_pilot_accepted()) {
gamesnd_play_iface(InterfaceSounds::COMMIT_PRESSED);

// we now have a new pilot, so try to load its current campaign, falling back if necessary
mission_load_up_campaign(true);

if (Campaign_file_missing) {
popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR( "The currently active campaign cannot be found. Please select another...", 1600));
gameseq_post_event(GS_EVENT_CAMPAIGN_ROOM);
Expand Down
4 changes: 1 addition & 3 deletions code/menuui/mainhallmenu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -906,8 +906,7 @@ void main_hall_do(float frametime)
Game_mode = GM_NORMAL;

// see if we have a missing campaign and force the player to select a new campaign if so
if (!(Player->flags & PLAYER_FLAGS_IS_MULTI) && Campaign_file_missing &&
!Campaign_room_no_campaigns) {
if (!(Player->flags & PLAYER_FLAGS_IS_MULTI) && Campaign_file_missing && !Campaign_room_no_campaigns) {
int rc = popup(0,
3,
XSTR("Go to Campaign Room", 1607),
Expand All @@ -933,7 +932,6 @@ void main_hall_do(float frametime)
break;
}
} else {

gameseq_post_event(GS_EVENT_NEW_CAMPAIGN);
gamesnd_play_iface(InterfaceSounds::IFACE_MOUSE_CLICK);
}
Expand Down
10 changes: 8 additions & 2 deletions code/menuui/readyroom.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -917,7 +917,11 @@ int sim_room_button_pressed(int n)

case CAMPAIGN_TAB:
if ( !strlen(Campaign.filename) ) {
popup( PF_USE_AFFIRMATIVE_ICON | PF_NO_NETWORKING, 1, POPUP_OK, XSTR( "The currently active campaign cannot be found, unable to switch to campaign mode!", 1612));
if (Campaign_load_failure == CAMPAIGN_ERROR_MISSING) {
popup(PF_USE_AFFIRMATIVE_ICON | PF_NO_NETWORKING, 1, POPUP_OK, XSTR("The currently active campaign cannot be found. Unable to switch to campaign mode.", 1612));
} else {
popup(PF_USE_AFFIRMATIVE_ICON | PF_NO_NETWORKING, 1, POPUP_OK, XSTR("The currently active campaign cannot be loaded. Unable to switch to campaign mode.", 1816));
}
break;
}

Expand Down Expand Up @@ -1050,7 +1054,9 @@ void sim_room_init()
Campaign.filename[0] = 0;
Campaign.num_missions = 0;

mission_campaign_load_failure_popup();
// don't display the popup in the sim room - first because there is already logic to prevent listing campaign missions,
// second because there's not much the player can do about it in the sim room, and third because displaying the popup
// clears the error code
}

Num_campaign_missions = 0;
Expand Down
53 changes: 32 additions & 21 deletions code/mission/missioncampaign.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ char *Campaign_names[MAX_CAMPAIGNS] = { NULL };
char *Campaign_file_names[MAX_CAMPAIGNS] = { NULL };
char *Campaign_descs[MAX_CAMPAIGNS] = { NULL };
int Num_campaigns;
int Campaign_file_missing;
bool Campaign_file_missing = false;
int Campaign_load_failure = 0;
int Campaign_names_inited = 0;
SCP_vector<SCP_string> Ignored_campaigns;
Expand Down Expand Up @@ -428,7 +428,7 @@ int mission_campaign_load(const char* filename, const char* full_path, player* p
char name[NAME_LENGTH], type[NAME_LENGTH], temp[NAME_LENGTH];

if (campaign_is_ignored(filename)) {
Campaign_file_missing = 1;
Campaign_file_missing = true;
Campaign_load_failure = CAMPAIGN_ERROR_IGNORED;
return CAMPAIGN_ERROR_IGNORED;
}
Expand Down Expand Up @@ -634,19 +634,25 @@ int mission_campaign_load(const char* filename, const char* full_path, player* p
if ((Game_mode & GM_MULTIPLAYER) && Campaign.num_missions > UINT8_MAX)
throw parse::ParseException("Number of campaign missions is too high and breaks multi!");
}
catch (const parse::ParseException& e)
catch (const parse::FileOpenException& foe)
{
mprintf(("Error parsing '%s'\r\nError message = %s.\r\n", filename, e.what()));
mprintf(("Error opening '%s'\r\nError message = %s.\r\n", filename, foe.what()));

Campaign.filename[0] = 0;
Campaign.num_missions = 0;

if (!Fred_running && !(Game_mode & GM_MULTIPLAYER)) {
Campaign_file_missing = 1;
Campaign_load_failure = CAMPAIGN_ERROR_MISSING;
return CAMPAIGN_ERROR_MISSING;
}
Campaign_file_missing = true;
Campaign_load_failure = CAMPAIGN_ERROR_MISSING;
return CAMPAIGN_ERROR_MISSING;
}
catch (const parse::ParseException& pe)
{
mprintf(("Error parsing '%s'\r\nError message = %s.\r\n", filename, pe.what()));

Campaign.filename[0] = 0;
Campaign.num_missions = 0;

Campaign_file_missing = true;
Campaign_load_failure = CAMPAIGN_ERROR_CORRUPT;
return CAMPAIGN_ERROR_CORRUPT;
}
Expand Down Expand Up @@ -681,7 +687,7 @@ int mission_campaign_load(const char* filename, const char* full_path, player* p
}

// all is good here, move along
Campaign_file_missing = 0;
Campaign_file_missing = false;

return 0;
}
Expand Down Expand Up @@ -721,7 +727,7 @@ void mission_campaign_init()
{
mission_campaign_clear();

Campaign_file_missing = 0;
Campaign_file_missing = false;

player_loadout_init();
}
Expand Down Expand Up @@ -1539,34 +1545,39 @@ bool campaign_is_ignored(const char *filename)
return false;
}

static bool campaign_reportable_error(int val)
{
return val != 0 && val != CAMPAIGN_ERROR_MISSING && val != CAMPAIGN_ERROR_IGNORED;
}

// returns 0: loaded, !0: error
int mission_load_up_campaign( player *pl )
int mission_load_up_campaign(bool fall_back_from_current)
{
int rc = -1, idx;

if ( pl == NULL )
pl = Player;
auto pl = Player;

// find best campaign to use:
// 1) last used
// 2) builtin
// 3) anything else
// Note that in each step we only fall back when the error is benign, e.g. ignored or missing;
// if there's some other real error with the campaign file, we report it.
// Also note that if we *have* a current campaign, we shouldn't fall back *at all*, lest we repeatedly
// reset what the current campaign is, *unless* we are starting a brand new session or loading a new pilot.

// last used...
if ( strlen(pl->current_campaign) ) {
if (!campaign_is_ignored(pl->current_campaign)) {
return mission_campaign_load(pl->current_campaign, nullptr, pl);
}
else {
Campaign_file_missing = 1;
rc = mission_campaign_load(pl->current_campaign, nullptr, pl);
if (rc == 0 || !fall_back_from_current || campaign_reportable_error(rc)) {
return rc;
}
}

// builtin...
rc = mission_campaign_load(Default_campaign_file_name, nullptr, pl);

// everything else...
if (rc < 0) {
if (rc < 0 && !campaign_reportable_error(rc)) {
// no descriptions, no sorting
mission_campaign_build_list(false, false);

Expand Down
6 changes: 3 additions & 3 deletions code/mission/missioncampaign.h
Original file line number Diff line number Diff line change
Expand Up @@ -161,8 +161,8 @@ extern SCP_vector<SCP_string> Ignored_campaigns;

extern char Default_campaign_file_name[MAX_FILENAME_LEN - 4];

// if the campaign file is missing this will get set for us to check against
extern int Campaign_file_missing;
extern bool Campaign_file_missing; // if the campaign file is missing this will get set for us to check against
extern int Campaign_load_failure;

/*
* initialise Player_loadout with default values
Expand Down Expand Up @@ -218,7 +218,7 @@ int mission_campaign_get_info(const char *filename, char *name, int *type, int *
int mission_campaign_get_mission_list(const char *filename, char **list, int max);

// load up a campaign for the current player.
int mission_load_up_campaign( player *p = NULL );
int mission_load_up_campaign(bool fall_back_from_current = false);

// stores mission goals and events in Campaign struct
void mission_campaign_store_goals_and_events();
Expand Down
22 changes: 13 additions & 9 deletions code/parse/parselo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2104,10 +2104,11 @@ int parse_get_line(char *lineout, int max_line_len, const char *start, int max_s
// Goober5000 - added ability to read somewhere other than Parse_text
void read_file_text(const char *filename, int mode, char *processed_text, char *raw_text)
{
// copy the filename
Assertion(filename, "Filename must not be null!");
if (!filename)
throw parse::ParseException("Invalid filename");
throw parse::FileOpenException("Filename must not be null!");

// copy the filename
strcpy_s(Current_filename_sub, filename);

// if we are paused then processed_text and raw_text must not be NULL!!
Expand Down Expand Up @@ -2231,21 +2232,23 @@ void read_raw_file_text(const char *filename, int mode, char *raw_text)
CFILE *mf;
int file_is_encrypted;

Assert(filename);
Assertion(filename, "Filename must not be null!");
if (!filename)
throw parse::FileOpenException("Filename must not be null!");

mf = cfopen(filename, "rb", CFILE_NORMAL, mode);
if (mf == NULL)
{
nprintf(("Error", "Wokka! Error opening file (%s)!\n", filename));
throw parse::ParseException("Failed to open file");
throw parse::FileOpenException("Failed to open file");
}

// read the entire file in
int file_len = cfilelength(mf);

if(!file_len) {
nprintf(("Error", "Oh noes!! File is empty! (%s)!\n", filename));
throw parse::ParseException("Failed to open file");
throw parse::ParseException("File is empty");
}

// For the possible Latin1 -> UTF-8 conversion we need to reallocate the raw_text at some point and we can only do
Expand Down Expand Up @@ -2469,10 +2472,11 @@ void read_file_bytes(const char *filename, int mode, char *raw_bytes)
{
CFILE *mf;

// copy the filename
Assertion(filename, "Filename must not be null!");
if (!filename)
throw parse::ParseException("Invalid filename");
throw parse::FileOpenException("Filename must not be null!");

// copy the filename
strcpy_s(Current_filename_sub, filename);

// if we are paused then raw_bytes must not be NULL!!
Expand All @@ -2484,15 +2488,15 @@ void read_file_bytes(const char *filename, int mode, char *raw_bytes)
if (mf == nullptr)
{
nprintf(("Error", "Wokka! Error opening file (%s)!\n", filename));
throw parse::ParseException("Failed to open file");
throw parse::FileOpenException("Failed to open file");
}

// read the entire file in
int file_len = cfilelength(mf);

if(!file_len) {
nprintf(("Error", "Oh noes!! File is empty! (%s)!\n", filename));
throw parse::ParseException("Failed to open file");
throw parse::ParseException("File is empty");
}

if (raw_bytes == nullptr) {
Expand Down
7 changes: 7 additions & 0 deletions code/parse/parselo.h
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,13 @@ namespace parse
~ParseException() noexcept override = default;
};

class FileOpenException : public ParseException
{
public:
explicit FileOpenException(const std::string& msg) : ParseException(msg) {}
~FileOpenException() noexcept override = default;
};

class VersionException : public std::runtime_error
{
private:
Expand Down
2 changes: 2 additions & 0 deletions fred2/campaigneditordlg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,8 @@ void campaign_editor::load_campaign(const char *filename, const char *full_path)
MessageBox("Requested campaign file is corrupt.", "Could not load campaign file");
else if (result == CAMPAIGN_ERROR_SEXP_EXHAUSTED)
MessageBox("Requested campaign requires too many SEXPs.", "Could not load campaign file");
else if (result == CAMPAIGN_ERROR_MISSING)
MessageBox("Requested campaign file could not be found.", "Could not load campaign file");
else if (result == CAMPAIGN_ERROR_SAVEFILE)
MessageBox("The pilot savefile for this campaign is invalid for the current mod.", "Could not load campaign file");
else if (result == CAMPAIGN_ERROR_IGNORED)
Expand Down
17 changes: 12 additions & 5 deletions freespace2/freespace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5691,7 +5691,7 @@ void game_enter_state( int old_state, int new_state )
}

switch (new_state) {
case GS_STATE_MAIN_MENU:
case GS_STATE_MAIN_MENU: {
// in multiplayer mode, be sure that we are not doing networking anymore.
if ( Game_mode & GM_MULTIPLAYER ) {
Assert( Net_player != nullptr );
Expand All @@ -5711,13 +5711,19 @@ void game_enter_state( int old_state, int new_state )
}

// determine which ship this guy is currently based on
mission_load_up_campaign(Player);
// 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 there was a problem, pass an empty main hall which will set up appropriate defaults
if (result != 0) {
main_hall_init("");
}
// if we're coming from the end of a campaign, we want to load the first mainhall of the campaign
// otherwise load the mainhall for the mission the player's up to
if (Campaign.next_mission == -1) {
else if (Campaign.next_mission == -1) {
main_hall_init(Campaign.missions[0].main_hall);
} else {
}
// otherwise load the mainhall for the mission the player's up to
else {
main_hall_init(Campaign.missions[Campaign.next_mission].main_hall);
}

Expand Down Expand Up @@ -5745,6 +5751,7 @@ void game_enter_state( int old_state, int new_state )
Cmdline_start_mission = nullptr;
}
break;
}

case GS_STATE_START_GAME:
main_hall_stop_music(true);
Expand Down

0 comments on commit e619c0c

Please sign in to comment.