/* * 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 "cfile/cfile.h" #include "freespace.h" #include "gamehelp/contexthelp.h" #include "gamesequence/gamesequence.h" #include "gamesnd/gamesnd.h" #include "globalincs/alphacolors.h" #include "graphics/font.h" #include "io/key.h" #include "menuui/mainhallmenu.h" #include "menuui/readyroom.h" #include "menuui/techmenu.h" // for tech menu reset stuff #include "mission/missionload.h" #include "mission/missionparse.h" #include "mission/missioncampaign.h" #include "missionui/missionscreencommon.h" #include "parse/parselo.h" #include "pilotfile/pilotfile.h" #include "playerman/managepilot.h" #include "playerman/player.h" #include "popup/popup.h" #include "ui/ui.h" #include "ui/uidefs.h" #define MAX_MISSIONS 1024 int Mission_list_coords[GR_NUM_RESOLUTIONS][4] = { { // GR_640 33, 108, 402, 279 }, { // GR_1024 43, 175, 402, 459 } }; int Campaign_list_coords[GR_NUM_RESOLUTIONS][4] = { { // GR_640 491, 108, 115, 279 }, { // GR_1024 491, 175, 115, 459 } }; #define C_SUBTEXT_X 19 // x coordinate offsets for data when campaign tab active #define C_TEXT_X 0 // x coordinate offsets for data when mission tab active #define M_TEXT_X C_SUBTEXT_X //0 #define MODE_CAMPAIGNS 0 #define MODE_MISSIONS 1 #define MAX_LINES 200 // NOLINT(modernize-macro-to-enum) #define NUM_BUTTONS 11 #define LIST_BUTTONS_MAX 42 #define SCROLL_UP_BUTTON 0 #define SCROLL_DOWN_BUTTON 1 #define MISSION_TAB 2 #define CAMPAIGN_TAB 3 #define HELP_BUTTON 4 #define COMMIT_BUTTON 5 #define OPTIONS_BUTTON 6 #define TECH_DATABASE_BUTTON 7 #define SIMULATOR_BUTTON 8 #define CUTSCENES_BUTTON 9 #define CREDITS_BUTTON 10 #define X_COORD 0 #define Y_COORD 1 #define W_COORD 2 #define H_COORD 3 #define CAMPAIGN_MISSION_HASH_SIZE 307 struct sim_room_buttons { const char *filename; int x, y, xt, yt; int hotspot; UI_BUTTON button; // because we have a class inside this struct, we need the constructor below.. sim_room_buttons(const char *name, int x1, int y1, int xt1, int yt1, int h) : filename(name), x(x1), y(y1), xt(xt1), yt(yt1), hotspot(h) {} }; static sim_room_buttons Buttons[GR_NUM_RESOLUTIONS][NUM_BUTTONS] = { //XSTR:OFF { // GR_640 sim_room_buttons("LMB_04", 1, 99, -1, -1, 4), sim_room_buttons("LMB_05", 1, 381, -1, -1, 5), sim_room_buttons("LMB_06", 6, 438, 40, 445, 6), sim_room_buttons("LMB_07", 6, 457, 40, 462, 7), sim_room_buttons("LMB_08", 534, 426, 500, 440, 8), sim_room_buttons("LMB_09", 571, 426, 572, 413, 9), sim_room_buttons("LMB_10", 534, 455, 480, 462, 10), sim_room_buttons("TDB_00", 7, 3, 37, 7, 0), sim_room_buttons("TDB_01", 7, 18, 37, 23, 1), sim_room_buttons("TDB_02", 7, 34, 37, 38, 2), sim_room_buttons("TDB_03", 7, 49, 37, 54, 3), }, { // GR_1024 sim_room_buttons("2_LMB_04", 2, 159, -1, -1, 4), sim_room_buttons("2_LMB_05", 2, 609, -1, -1, 5), sim_room_buttons("2_LMB_06", 10, 701, 64, 712, 6), sim_room_buttons("2_LMB_07", 10, 732, 64, 739, 7), sim_room_buttons("2_LMB_08", 854, 681, 800, 704, 8), sim_room_buttons("2_LMB_09", 914, 681, 915, 660, 9), sim_room_buttons("2_LMB_10", 854, 728, 780, 736, 10), sim_room_buttons("2_TDB_00", 12, 5, 59, 12, 0), sim_room_buttons("2_TDB_01", 12, 31, 59, 37, 1), sim_room_buttons("2_TDB_02", 12, 56, 59, 62, 2), sim_room_buttons("2_TDB_03", 12, 81, 59, 88, 3), } //XSTR:ON }; SCP_vector Sim_Missions; SCP_vector Sim_CMissions; const char* Sim_filename[GR_NUM_RESOLUTIONS] = { "LoadMission", "2_LoadMission" }; const char* Sim_mask_filename[GR_NUM_RESOLUTIONS] = { "LoadMission-m", "2_LoadMission-m" }; const char* Campaign_filename[GR_NUM_RESOLUTIONS] = { "Campaign", "2_Campaign" }; const char* Campaign_mask_filename[GR_NUM_RESOLUTIONS] = { "Campaign-m", "2_Campaign-m" }; const char* Sim_room_slider_filename[GR_NUM_RESOLUTIONS] = { "slider", "2_slider" }; // misc text. ("Mission" and "Filename" #define NUM_SIM_MISC_TEXT 2 #define SIM_MISC_TEXT_MISSION 0 #define SIM_MISC_TEXT_FILENAME 1 int Sim_misc_text_coords[GR_NUM_RESOLUTIONS][NUM_SIM_MISC_TEXT][2] = { { // GR_640 {33, 95}, {491, 95} }, { // GR_1024 {43, 155}, {491, 155} } }; int Sim_room_slider_coords[GR_NUM_RESOLUTIONS][4] = { { // GR_640 4, 131, 20, 245 }, { // GR_1024 5, 209, 32, 392 } }; // readyroom text line stuff #define READYROOM_LINE_CAMPAIGN 1 #define READYROOM_LINE_CMISSION 2 #define READYROOM_LINE_MISSION 3 #define READYROOM_FLAG_FROM_VOLITION (1<<0) // volition made static struct { int type; // see READYROOM_LINE_* defines above const char *name; const char *filename; int x; // X coordinate of line int y; // Y coordinate of line int flags; // special flags, see READYROOM_FLAG_* defines above } sim_room_lines[MAX_LINES]; static char Cur_campaign[MAX_FILENAME_LEN]; static char *Mission_filenames[MAX_MISSIONS] = { NULL }; static char *Standalone_mission_names[MAX_MISSIONS] = { NULL }; static int Standalone_mission_flags[MAX_MISSIONS]; static char *Campaign_missions[MAX_MISSIONS] = { NULL }; static char *Campaign_mission_names[MAX_CAMPAIGN_MISSIONS] = { NULL }; static int Campaign_mission_flags[MAX_MISSIONS]; static int Simroom_show_all = 0; static int Standalone_mission_names_inited = 0; static int Campaign_mission_names_inited = 0; static int Num_standalone_missions; static int Num_campaign_missions; //static int Num_player_missions; static int Scroll_offset; static int Selected_line; static int Num_lines; static int Num_campaign_missions_with_info = 0; static int Num_standalone_missions_with_info = 0; static int list_x1; static int list_x2; static int list_y; static int list_w1; static int list_w2; static int list_h; static int Background_bitmap; static int Campaign_background_bitmap_mask; static UI_WINDOW Ui_window; static UI_BUTTON List_buttons[LIST_BUTTONS_MAX]; // buttons for each line of text in list int Sim_room_overlay_id; int Campaign_room_overlay_id; // globals bool Campaign_room_no_campaigns = false; // slider stuff static UI_SLIDER2 Sim_room_slider; typedef struct hash_node { hash_node *next; const char *filename; } hash_node; static hash_node *Campaign_mission_hash_table[CAMPAIGN_MISSION_HASH_SIZE]; static int Hash_table_inited; // special icons (1.04 + stuff) #define NUM_MISSION_ICONS 1 #define MISSION_ICON_VOLITION 0 // mini volition death's head :) // icon offsets (see LIST_ defines above //#define MISSION_ICON_VOLITION_X (46) #define MISSION_ICON_VOLITION_Y_OFFSET (-1) // icon offsets static int Sim_volition_icon_x[GR_NUM_RESOLUTIONS] = { 38, 49 }; // special icons themselves int Mission_icon_bitmaps[NUM_MISSION_ICONS]; //XSTR:OFF const char *Mission_icon_bitmap_filenames[NUM_MISSION_ICONS] = { "icon-volition" }; //XSTR:ON void sim_room_load_mission_icons(); void sim_room_unload_mission_icons(); void sim_room_blit_icons(int line_index, int y_start, fs_builtin_mission *fb = NULL, int is_md = 0); const std::shared_ptr> OnCampaignBeginHook = scripting::Hook<>::Factory( "On Campaign Begin", "Called when a campaign is started from the beginning or is reset", { { "Campaign", "string", "The campaign filename (without the extension)" }, }); // Finds a hash value for mission filename // // returns hash value int hash_filename(const char *filename) { ulonglong hash_val = 0; const char *ptr = filename; // Don't hash .fsm extension, convert all to upper case for (int i=0; i < ((signed int)(strlen(filename)) - 4); i++) { hash_val = (hash_val << 4) + SCP_toupper(*ptr++); } return int(hash_val % CAMPAIGN_MISSION_HASH_SIZE); } // insert filename into Campaign_mission_hash_table // // returns 1 if successful, 0 if could not allocate memory int hash_insert(const char *filename) { int hash_val = hash_filename(filename); hash_node *cur_node; // Check if table empty if (Campaign_mission_hash_table[hash_val] == NULL) { Campaign_mission_hash_table[hash_val] = new hash_node; cur_node = Campaign_mission_hash_table[hash_val]; if (cur_node == NULL) { // Unable to allocate memory return 0; } } else { // Walk down list to first empty node cur_node = Campaign_mission_hash_table[hash_val]; while (cur_node->next != NULL) { cur_node = cur_node->next; } // Create new node cur_node->next = new hash_node; if (cur_node->next == NULL) { // unable to allocate memory return 0; } else { cur_node = cur_node->next; } } // Initialize data cur_node->next = NULL; cur_node->filename = filename; // Return successs return 1; } // Checks if a filename already exitst in the hash table // // returns 1 if found (collision), 0 if no collision int campaign_mission_hash_collision(const char *filename) { int hash_val = hash_filename(filename); hash_node *cur_node = Campaign_mission_hash_table[hash_val]; if (cur_node == NULL) { return 0; } do { if (!stricmp(filename, cur_node->filename)) { return 1; } cur_node = cur_node->next; } while (cur_node != NULL); // Ran out of stuff to check return 0; } // builds hash table of campaign mission filenames // // returns 1 if successful, 0 if not successful int build_campaign_mission_filename_hash_table() { int rval; // Go through all campaign missions for (int i=0; inext != NULL) { hash_node *temp = cur_node->next; delete cur_node; cur_node = temp; } // Delete last node delete cur_node; Campaign_mission_hash_table[i] = NULL; } } } // add a line of sim_room smuck to end of list int sim_room_line_add(int type, const char *name, const char *filename, int x, int y, int flags) { if (Num_lines >= MAX_LINES) return 0; sim_room_lines[Num_lines].type = type; sim_room_lines[Num_lines].name = name; sim_room_lines[Num_lines].filename = filename; sim_room_lines[Num_lines].x = x; sim_room_lines[Num_lines].y = y; sim_room_lines[Num_lines].flags = flags; return Num_lines++; } // build up a list of all missions in all campaigns. int sim_room_campaign_mission_filter(const char *filename) { int num; num = mission_campaign_get_mission_list(filename, &Campaign_missions[Num_campaign_missions], MAX_MISSIONS - Num_campaign_missions); if (num < 0) return 0; Num_campaigns++; Num_campaign_missions += num; return 1; } // filter out all missions already used in existing campaigns int sim_room_standalone_mission_filter(const char *filename) { int type; char mission_name[255]; // Check if a campaign mission (single and multi) if (campaign_mission_hash_collision(filename)) { return 0; } // Check if a standalone multi mission OR Mdisk mission with data type = mission_parse_is_multi(filename, mission_name); if (type && !(type & MISSION_TYPE_SINGLE)) return 0; return 1; } // builds up list of standalone missions and adds them to missions simulator // processes one mission per frame // // returns 1 if finished with all missions, 0 otherwise // int build_standalone_mission_list_do_frame(bool API_Access) { int font_height = gr_get_font_height(); SCP_string popup_str; bool lcl_weirdness = false; // When no standalone missions in data directory if (Num_standalone_missions == 0) { Standalone_mission_names_inited = 1; return 1; } // Set global variable so we we'll have list available next time Standalone_mission_names[Num_standalone_missions_with_info] = NULL; Standalone_mission_flags[Num_standalone_missions_with_info] = 0; if (Num_standalone_missions > 0) { // sanity check if (strlen(Mission_filenames[Num_standalone_missions_with_info]) < MAX_FILENAME_LEN - 4) { // sanity check? // tack on an extension auto filename = cf_add_ext(Mission_filenames[Num_standalone_missions_with_info], FS_MISSION_FILE_EXT); // update popup sprintf(popup_str, XSTR("Single Mission\n\n%s", 989), filename); popup_change_text(popup_str.c_str()); // activate tstrings check Lcl_unexpected_tstring_check = &lcl_weirdness; // check if we can list the mission, if loading basic info didn't return an error code, and if we didn't find an XSTR mismatch bool condition = !mission_is_ignored(filename) && !get_mission_info(filename) && !lcl_weirdness; // maybe log if (lcl_weirdness) mprintf(("Skipping %s due to XSTR mismatch\n", filename)); // deactivate tstrings check Lcl_unexpected_tstring_check = nullptr; if (condition) { Standalone_mission_names[Num_standalone_missions_with_info] = vm_strdup(The_mission.name.c_str()); Standalone_mission_flags[Num_standalone_missions_with_info] = The_mission.game_type; int y = Num_lines * (font_height + 2); //Add mission data to the API if (API_Access) { sim_mission api_mission; api_mission.name = The_mission.name; api_mission.filename = filename; api_mission.mission_desc = The_mission.mission_desc; api_mission.author = The_mission.author; api_mission.visible = 1; Sim_Missions.push_back(std::move(api_mission)); } // determine some extra information int flags = 0; auto fb = game_find_builtin_mission(filename); if((fb != NULL) && (fb->flags & FSB_FROM_VOLITION)){ flags |= READYROOM_FLAG_FROM_VOLITION; } // add the line sim_room_line_add(READYROOM_LINE_MISSION, Standalone_mission_names[Num_standalone_missions_with_info], Mission_filenames[Num_standalone_missions_with_info], list_x1 + M_TEXT_X, y, flags); } } Num_standalone_missions_with_info++; } if (Num_standalone_missions_with_info == Num_standalone_missions) { Standalone_mission_names_inited = 1; return 1; } else { return 0; } } // builds up list of already played missions in a campaign and adds them to missions simulator // processes one mission per frame // // returns 1 if finished with all missions, 0 otherwise // int build_campaign_mission_list_do_frame(bool API_Access) { int font_height = gr_get_font_height(); SCP_string popup_str; static int valid_missions_with_info = 0; // we use this to avoid blank entries in the mission list // When no campaign files in data directory if (Campaign.num_missions == 0) { Campaign_mission_names_inited = 1; return 1; } // change popup sprintf(popup_str, XSTR("Campaign Mission\n\n%s", 990), Campaign.missions[Num_campaign_missions_with_info].name); popup_change_text(popup_str.c_str()); // Set global variable so we we'll have list available next time Campaign_mission_names[Num_campaign_missions_with_info] = NULL; Campaign_mission_flags[Num_campaign_missions_with_info] = 0; // Only allow missions already completed if (Campaign.missions[Num_campaign_missions_with_info].completed || (Simroom_show_all || API_Access)) { if (!get_mission_info(Campaign.missions[Num_campaign_missions_with_info].name)) { auto filename = Campaign.missions[Num_campaign_missions_with_info].name; // add to list Campaign_mission_names[Num_campaign_missions_with_info] = vm_strdup(The_mission.name.c_str()); Campaign_mission_flags[Num_campaign_missions_with_info] = The_mission.game_type; int y = valid_missions_with_info * (font_height + 2); // Add mission data to the API if (API_Access) { sim_mission api_mission; api_mission.name = The_mission.name; api_mission.filename = filename; api_mission.mission_desc = The_mission.mission_desc; api_mission.author = The_mission.author; api_mission.visible = Campaign.missions[Num_campaign_missions_with_info].completed; Sim_CMissions.push_back(std::move(api_mission)); } // determine some extra information int flags = 0; auto fb = game_find_builtin_mission(Campaign.missions[Num_campaign_missions_with_info].name); if((fb != NULL) && (fb->flags & FSB_FROM_VOLITION)) { flags |= READYROOM_FLAG_FROM_VOLITION; } sim_room_line_add(READYROOM_LINE_CMISSION, Campaign_mission_names[Num_campaign_missions_with_info], Campaign.missions[Num_campaign_missions_with_info].name, list_x1 + C_SUBTEXT_X, y, flags); valid_missions_with_info++; } } Num_campaign_missions_with_info++; if (Num_campaign_missions_with_info == Campaign.num_missions) { valid_missions_with_info = 0; Campaign_mission_names_inited = 1; return 1; } else { return 0; } } // Builds list of either (1) standalone missions or (2) already played missions in current campaign // First time through, it builds list, next time it uses precompiled list void sim_room_build_listing() { int i, y, max_num_entries_viewable; int font_height = gr_get_font_height(); Num_lines = y = 0; list_y = Mission_list_coords[gr_screen.res][1]; list_h = Mission_list_coords[gr_screen.res][3]; // Stand alone single player missions. if (Player->readyroom_listing_mode == MODE_MISSIONS) { if (Hash_table_inited) { if (!Standalone_mission_names_inited) { // Is this the first time through // build_list_do_frame builds list and adds sim room line and sets Standalone_mission_names_inited popup_till_condition([]() -> int { return build_standalone_mission_list_do_frame(false); }, POPUP_CANCEL, XSTR("Loading missions", 991)); } else { for (i=0; iflags & FSB_FROM_VOLITION)){ flags |= READYROOM_FLAG_FROM_VOLITION; } sim_room_line_add(READYROOM_LINE_MISSION, Standalone_mission_names[i], Mission_filenames[i], list_x1 + M_TEXT_X, y, flags); y += font_height + 2; } } } } } else { // Campaign missions list_y += font_height + 2; list_h -= font_height - 2; if (!Campaign_mission_names_inited) { // Is this the first time through // builds list, adds sim room line and sets Campaign_mission_names_inited popup_till_condition([]() -> int { return build_campaign_mission_list_do_frame(false); }, POPUP_CANCEL, XSTR("Loading campaign missions", 992)); } else { for (i=0; iflags & FSB_FROM_VOLITION)){ flags |= READYROOM_FLAG_FROM_VOLITION; } sim_room_line_add(READYROOM_LINE_CMISSION, Campaign_mission_names[i], Campaign.missions[i].name, list_x1 + C_SUBTEXT_X, y, flags); y += font_height + 2; // Goober5000 - added +2 to conform with above } } } } // free up memory from parsing all of those missions stop_parse(); // set appropriate slider size max_num_entries_viewable = list_h / (font_height + 2); Sim_room_slider.set_numberItems((Num_lines > max_num_entries_viewable) ? (Num_lines - max_num_entries_viewable) : 0); } void sim_room_reset_campaign_listing() { // only reset if we got fully inited in the first place if (!Campaign_mission_names_inited) return; for (int i=0; i= Num_lines)) return 0; y = sim_room_lines[n].y - sim_room_lines[Scroll_offset].y; if ((y < 0) || (y + gr_get_font_height() > list_h)) return 0; return 1; } void sim_room_scroll_screen_up() { if (Scroll_offset > 0) { Scroll_offset--; if (Player->readyroom_listing_mode == MODE_MISSIONS) { Assert(Selected_line > Scroll_offset); while (!sim_room_line_query_visible(Selected_line)) { Selected_line--; } } gamesnd_play_iface(InterfaceSounds::SCROLL); } else gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL); } void sim_room_scroll_line_up() { if (Selected_line) { Selected_line--; if (Selected_line < Scroll_offset) { Scroll_offset = Selected_line; Sim_room_slider.forceUp(); } gamesnd_play_iface(InterfaceSounds::SCROLL); } else gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL); } void sim_room_scroll_screen_down() { if (sim_room_lines[Num_lines - 1].y + gr_get_font_height() > sim_room_lines[Scroll_offset].y + list_h) { Scroll_offset++; if (Player->readyroom_listing_mode == MODE_MISSIONS) { while (!sim_room_line_query_visible(Selected_line)) { Selected_line++; Assert(Selected_line < Num_lines); } } gamesnd_play_iface(InterfaceSounds::SCROLL); } else gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL); } void sim_room_scroll_line_down() { if (Selected_line < Num_lines - 1) { Selected_line++; Assert(Selected_line > Scroll_offset); while (!sim_room_line_query_visible(Selected_line)) { Scroll_offset++; Sim_room_slider.forceDown(); } gamesnd_play_iface(InterfaceSounds::SCROLL); } else gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL); } /* Goober5000 - why are there two nearly identical functions? (campaign_room_reset_campaign and sim_room_reset_campaign) Looks like this function should be deprecated... // returns: 0 = success, !0 = aborted or failed int sim_room_reset_campaign() { int z, rval = 1; z = popup(PF_TITLE_BIG | PF_TITLE_RED, 2, POPUP_CANCEL, POPUP_OK, XSTR( "Warning\nThis will cause all progress in your\ncurrent campaign to be lost", 110) ); if (z) { mission_campaign_savefile_delete(Campaign.filename); mission_campaign_load(Campaign.filename); rval = 0; } return rval; } */ // Decide if we should offer choice to resume this savegame int sim_room_can_resume_savegame(char * /*savegame_filename*/) { #ifdef FREESPACE_SAVERESTORE_SYSTEM char savegame_mission[MAX_FILENAME_LEN]; if (state_read_description(savegame_filename, NULL, savegame_mission)) { return 0; } if (stricmp(Game_current_mission_filename, savegame_mission)) { return 0; } return 1; #else return 0; #endif } int readyroom_continue_campaign() { // check for possible mission loop, need to do this before calling mission_campaign_next_mission()! if ( !(Game_mode & GM_MULTIPLAYER) && !Campaign.loop_enabled && (Campaign.current_mission == -1) && (Campaign.prev_mission != -1) && (Campaign.next_mission != -1) ) { if (Campaign.missions[Campaign.prev_mission].flags & CMISSION_FLAG_HAS_LOOP) { // NOTE: the order of these calls is *very* important // we must manually set the current mission to the *previous* mission and the mission name. // this isn't the same thing that is done in mission_campaign_next_mission(), but it's // cleaner to do this here than it is to hack that function to do the same thing Campaign.current_mission = Campaign.prev_mission; strcpy_s( Game_current_mission_filename, Campaign.missions[Campaign.current_mission].name ); // set the bit for campaign mode Game_mode |= GM_CAMPAIGN_MODE; // eval the mission (may be needed to setup loop mission stuff) if (Campaign.loop_mission == CAMPAIGN_LOOP_MISSION_UNINITIALIZED) mission_campaign_eval_next_mission(); // only proceed to loop if we have a loop if (Campaign.loop_mission > 0) { gameseq_post_event(GS_EVENT_LOOP_BRIEF); return 0; } } } int mc_rval = mission_campaign_next_mission(); if (mc_rval == -1) { // is campaign and next mission valid? gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL); popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR( "The campaign is over. To replay the campaign, either create a new pilot or restart the campaign in the campaign room.", 112) ); return -1; } else if(mc_rval == -2) { gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL); popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, NOX("The current campaign has no missions") ); return -1; } // set the bit for campaign mode Game_mode |= GM_CAMPAIGN_MODE; gameseq_post_event( GS_EVENT_START_GAME ); return 0; } void sim_room_commit() { if ((Selected_line >= Num_lines) || !sim_room_lines[Selected_line].filename) { gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL); return; } strcpy_s(Game_current_mission_filename, sim_room_lines[Selected_line].filename); Game_mode &= ~(GM_CAMPAIGN_MODE); // be sure this bit is clear gameseq_post_event(GS_EVENT_START_GAME); gamesnd_play_iface(InterfaceSounds::COMMIT_PRESSED); } int sim_room_button_pressed(int n) { switch (n) { case SCROLL_UP_BUTTON: sim_room_scroll_screen_up(); Sim_room_slider.forceUp(); break; case SCROLL_DOWN_BUTTON: sim_room_scroll_screen_down(); Sim_room_slider.forceDown(); break; case MISSION_TAB: Simroom_show_all = 0; Player->readyroom_listing_mode = MODE_MISSIONS; Selected_line = Scroll_offset = 0; gamesnd_play_iface(InterfaceSounds::USER_SELECT); sim_room_build_listing(); break; case CAMPAIGN_TAB: if ( !strlen(Campaign.filename) ) { 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; } if (Player->readyroom_listing_mode != MODE_CAMPAIGNS) Simroom_show_all = 0; Player->readyroom_listing_mode = MODE_CAMPAIGNS; Scroll_offset = 0; gamesnd_play_iface(InterfaceSounds::USER_SELECT); sim_room_build_listing(); break; case COMMIT_BUTTON: sim_room_commit(); break; case HELP_BUTTON: launch_context_help(); gamesnd_play_iface(InterfaceSounds::HELP_PRESSED); break; case OPTIONS_BUTTON: gamesnd_play_iface(InterfaceSounds::SWITCH_SCREENS); gameseq_post_event(GS_EVENT_OPTIONS_MENU); return 1; case TECH_DATABASE_BUTTON: gamesnd_play_iface(InterfaceSounds::SWITCH_SCREENS); gameseq_post_event(GS_EVENT_TECH_MENU); return 1; case CUTSCENES_BUTTON: gamesnd_play_iface(InterfaceSounds::SWITCH_SCREENS); gameseq_post_event(GS_EVENT_GOTO_VIEW_CUTSCENES_SCREEN); return 1; case CREDITS_BUTTON: gamesnd_play_iface(InterfaceSounds::SWITCH_SCREENS); gameseq_post_event(GS_EVENT_CREDITS); return 1; } return 0; } // do nothing void sim_room_scroll_capture() { } // --------------------------------------------------------------------- // mission_sim_room_init() // // Initialize the sim_room assignment screen system. Called when GS_STATE_sim_room_SCREEN // is entered. // void sim_room_init() { int i; sim_room_buttons *b; char wild_card[8]; list_x1 = Mission_list_coords[gr_screen.res][0]; list_x2 = Campaign_list_coords[gr_screen.res][0]; list_y = Mission_list_coords[gr_screen.res][1]; list_w1 = Mission_list_coords[gr_screen.res][2]; list_w2 = Campaign_list_coords[gr_screen.res][2]; list_h = Mission_list_coords[gr_screen.res][3]; // force mode to valid range. I once had a bogus value here. Don't know how that happened, though. if (Player->readyroom_listing_mode != MODE_MISSIONS) Player->readyroom_listing_mode = MODE_CAMPAIGNS; *Game_current_mission_filename = 0; common_set_interface_palette("InterfacePalette"); // set the interface palette Ui_window.create(0, 0, gr_screen.max_w_unscaled, gr_screen.max_h_unscaled, 0); Ui_window.set_mask_bmap(Sim_mask_filename[gr_screen.res]); for (i=0; ibutton.create(&Ui_window, "", b->x, b->y, 60, 30, (i < 2), 1); // set up callback for when a mouse first goes over a button b->button.set_highlight_action(common_play_highlight_sound); b->button.set_bmaps(b->filename); b->button.link_hotspot(b->hotspot); } // screen/button specific text Ui_window.add_XSTR("Single Missions", 1060, Buttons[gr_screen.res][MISSION_TAB].xt, Buttons[gr_screen.res][MISSION_TAB].yt, &Buttons[gr_screen.res][MISSION_TAB].button, UI_XSTR_COLOR_GREEN); Ui_window.add_XSTR("Campaign Missions", 1061, Buttons[gr_screen.res][CAMPAIGN_TAB].xt, Buttons[gr_screen.res][CAMPAIGN_TAB].yt, &Buttons[gr_screen.res][CAMPAIGN_TAB].button, UI_XSTR_COLOR_GREEN); Ui_window.add_XSTR("Help", 928, Buttons[gr_screen.res][HELP_BUTTON].xt, Buttons[gr_screen.res][HELP_BUTTON].yt, &Buttons[gr_screen.res][HELP_BUTTON].button, UI_XSTR_COLOR_GREEN); Ui_window.add_XSTR("Commit", 1062, Buttons[gr_screen.res][COMMIT_BUTTON].xt, Buttons[gr_screen.res][COMMIT_BUTTON].yt, &Buttons[gr_screen.res][COMMIT_BUTTON].button, UI_XSTR_COLOR_PINK); Ui_window.add_XSTR("Options", 1036, Buttons[gr_screen.res][OPTIONS_BUTTON].xt, Buttons[gr_screen.res][OPTIONS_BUTTON].yt, &Buttons[gr_screen.res][OPTIONS_BUTTON].button, UI_XSTR_COLOR_GREEN); // common tab button text Ui_window.add_XSTR("Technical Database", 1055, Buttons[gr_screen.res][TECH_DATABASE_BUTTON].xt, Buttons[gr_screen.res][TECH_DATABASE_BUTTON].yt, &Buttons[gr_screen.res][TECH_DATABASE_BUTTON].button, UI_XSTR_COLOR_GREEN); Ui_window.add_XSTR("Mission Simulator", 1056, Buttons[gr_screen.res][SIMULATOR_BUTTON].xt, Buttons[gr_screen.res][SIMULATOR_BUTTON].yt, &Buttons[gr_screen.res][SIMULATOR_BUTTON].button, UI_XSTR_COLOR_GREEN); Ui_window.add_XSTR("Cutscenes", 1057, Buttons[gr_screen.res][CUTSCENES_BUTTON].xt, Buttons[gr_screen.res][CUTSCENES_BUTTON].yt, &Buttons[gr_screen.res][CUTSCENES_BUTTON].button, UI_XSTR_COLOR_GREEN); Ui_window.add_XSTR("Credits", 1058, Buttons[gr_screen.res][CREDITS_BUTTON].xt, Buttons[gr_screen.res][CREDITS_BUTTON].yt, &Buttons[gr_screen.res][CREDITS_BUTTON].button, UI_XSTR_COLOR_GREEN); // misc text - not associated with any buttons Ui_window.add_XSTR("Mission", 1063, Sim_misc_text_coords[gr_screen.res][SIM_MISC_TEXT_MISSION][0], Sim_misc_text_coords[gr_screen.res][SIM_MISC_TEXT_MISSION][1], NULL, UI_XSTR_COLOR_GREEN); Ui_window.add_XSTR("Filename", 1064, Sim_misc_text_coords[gr_screen.res][SIM_MISC_TEXT_FILENAME][0], Sim_misc_text_coords[gr_screen.res][SIM_MISC_TEXT_FILENAME][1], NULL, UI_XSTR_COLOR_GREEN); for (i=0; icurrent_campaign); if ( !mission_load_up_campaign() ) { mission_campaign_next_mission(); } else { Campaign.filename[0] = 0; Campaign.num_missions = 0; // 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; Get_file_list_filter = sim_room_campaign_mission_filter; mission_campaign_build_list(false, false); // no descs, no sorting Hash_table_inited = 0; if (build_campaign_mission_filename_hash_table()) { Hash_table_inited = 1; } // if there is no campaign available then force the use of mode_missions only if ( !strlen(Campaign.filename) ) Player->readyroom_listing_mode = MODE_MISSIONS; // HACK GR_MAYBE_CLEAR_RES(Background_bitmap); if(Background_bitmap != -1){ gr_set_bitmap(Background_bitmap); gr_bitmap(0, 0, GR_RESIZE_MENU); } Ui_window.draw(); gr_flip(); Get_file_list_filter = sim_room_standalone_mission_filter; strcpy_s(wild_card, NOX("*")); strcat_s(wild_card, FS_MISSION_FILE_EXT); #ifndef NDEBUG // Activate the check while we fill the mission list so that we don't potentially end up with dozens of string length warnings. // We don't actually remove mismatched missions from the list here; that will happen in build_standalone_mission_list_do_frame(). bool dummy_buffer; Lcl_unexpected_tstring_check = &dummy_buffer; #endif Num_standalone_missions = cf_get_file_list(MAX_MISSIONS, Mission_filenames, CF_TYPE_MISSIONS, wild_card, CF_SORT_NAME); #ifndef NDEBUG Lcl_unexpected_tstring_check = nullptr; #endif // set up slider with 0 items to start Sim_room_slider.create(&Ui_window, Sim_room_slider_coords[gr_screen.res][X_COORD], Sim_room_slider_coords[gr_screen.res][Y_COORD], Sim_room_slider_coords[gr_screen.res][W_COORD], Sim_room_slider_coords[gr_screen.res][H_COORD], 0, Sim_room_slider_filename[gr_screen.res], &sim_room_scroll_screen_up, &sim_room_scroll_screen_down, &sim_room_scroll_capture); Num_campaign_missions_with_info = Num_standalone_missions_with_info = Standalone_mission_names_inited = Campaign_mission_names_inited = 0; Simroom_show_all = 0; sim_room_build_listing(); // load special mission icons sim_room_load_mission_icons(); } // --------------------------------------------------------------------- // sim_room_close() // // Cleanup the sim_room assignment screen system. Called when GS_STATE_sim_room_SCREEN // is left. // void sim_room_close() { int i; for (i=0; i= 0) bm_release(Background_bitmap); if (Standalone_mission_names_inited){ for (i=0; i 0) || B1_JUST_RELEASED ) { if ( help_overlay_active(Sim_room_overlay_id) ) { help_overlay_set_state(Sim_room_overlay_id, gr_screen.res, 0); Ui_window.set_ignore_gadgets(0); k = 0; } } if ( !help_overlay_active(Sim_room_overlay_id) ) { Ui_window.set_ignore_gadgets(0); } switch (k) { case KEY_DOWN: // scroll list down sim_room_scroll_line_down(); break; case KEY_UP: // scroll list up sim_room_scroll_line_up(); break; case KEY_ESC: gameseq_post_event(GS_EVENT_MAIN_MENU); break; case KEY_CTRLED | KEY_UP: sim_room_button_pressed(TECH_DATABASE_BUTTON); break; case KEY_CTRLED | KEY_DOWN: sim_room_button_pressed(CUTSCENES_BUTTON); break; case KEY_TAB: if (Player->readyroom_listing_mode == MODE_CAMPAIGNS) Player->readyroom_listing_mode = MODE_MISSIONS; else if ( strlen(Campaign.filename) ) Player->readyroom_listing_mode = MODE_CAMPAIGNS; else Player->readyroom_listing_mode = MODE_MISSIONS; Selected_line = Scroll_offset = Simroom_show_all = 0; gamesnd_play_iface(InterfaceSounds::USER_SELECT); sim_room_build_listing(); break; case KEY_F2: gamesnd_play_iface(InterfaceSounds::SWITCH_SCREENS); gameseq_post_event(GS_EVENT_OPTIONS_MENU); break; case KEY_CTRLED | KEY_SHIFTED | KEY_S: if (Player->readyroom_listing_mode == MODE_CAMPAIGNS) { Simroom_show_all = !Simroom_show_all; sim_room_reset_campaign_listing(); } break; } // end switch for (i=0; i= 0) { gr_set_bitmap(Background_bitmap); gr_bitmap(0, 0, GR_RESIZE_MENU); } Ui_window.draw(); for (i=TECH_DATABASE_BUTTON; i<=CREDITS_BUTTON; i++){ if (Buttons[gr_screen.res][i].button.button_down()){ break; } } if (i > CREDITS_BUTTON){ Buttons[gr_screen.res][SIMULATOR_BUTTON].button.draw_forced(2); } if (!Buttons[gr_screen.res][CAMPAIGN_TAB].button.button_down() && !Buttons[gr_screen.res][MISSION_TAB].button.button_down()) { if (Player->readyroom_listing_mode == MODE_CAMPAIGNS){ Buttons[gr_screen.res][CAMPAIGN_TAB].button.draw_forced(2); } else if (Player->readyroom_listing_mode == MODE_MISSIONS){ Buttons[gr_screen.res][MISSION_TAB].button.draw_forced(2); } } font::set_font(font::FONT1); if (Player->readyroom_listing_mode == MODE_CAMPAIGNS) { gr_set_color_fast(&Color_text_heading); strcpy_s(buf, Campaign.name); font::force_fit_string(buf, 255, list_w1); gr_printf_menu(list_x1, Mission_list_coords[gr_screen.res][1], "%s", buf); if (Campaign.filename[0] != '\0') { sprintf(buf, NOX("%s%s"), Campaign.filename, FS_CAMPAIGN_FILE_EXT); font::force_fit_string(buf, 255, list_w2); gr_printf_menu(list_x2, Mission_list_coords[gr_screen.res][1], "%s", buf); // blit the proper icons if necessary auto full_name = cf_add_ext(Campaign.filename,FS_CAMPAIGN_FILE_EXT); auto fb = game_find_builtin_mission(full_name); if(fb != NULL){ // sim_room_blit_icons(0, Mission_list_coords[gr_screen.res][1], fb, 0); } } } line = Scroll_offset; while (sim_room_line_query_visible(line) && (line - Scroll_offset < LIST_BUTTONS_MAX)) { y = list_y + sim_room_lines[line].y - sim_room_lines[Scroll_offset].y; if (sim_room_lines[line].type != READYROOM_LINE_CAMPAIGN) { List_buttons[line - Scroll_offset].update_dimensions(list_x1, y, list_x2 + list_w2 - list_x1, font_height); List_buttons[line - Scroll_offset].enable(); } else List_buttons[line - Scroll_offset].disable(); if (line == Selected_line) gr_set_color_fast(&Color_text_selected); else if (line == select_tease_line) gr_set_color_fast(&Color_text_subselected); else gr_set_color_fast(&Color_text_normal); strcpy_s(buf, sim_room_lines[line].name); font::force_fit_string(buf, 255, list_x1 + list_w1 - sim_room_lines[line].x); gr_printf_menu(sim_room_lines[line].x, y, "%s", buf); if (sim_room_lines[line].filename) { strcpy_s(buf, sim_room_lines[line].filename); font::force_fit_string(buf, 255, list_w2); gr_printf_menu(list_x2, y, "%s", buf); } // blit additional icon information sim_room_blit_icons(line, y); line++; } i = line - Scroll_offset; while (i < LIST_BUTTONS_MAX) List_buttons[i++].disable(); // blit help overlay if active help_overlay_maybe_blit(Sim_room_overlay_id, gr_screen.res); gr_flip(); } void sim_room_blit_icons(int line_index, int y_start, fs_builtin_mission *fb, int /*is_md*/) { int is_from_volition = 0; // determine icon status if(fb == NULL){ is_from_volition = (sim_room_lines[line_index].flags & READYROOM_FLAG_FROM_VOLITION) ? 1 : 0; } else { is_from_volition = (fb->flags & FSB_FROM_VOLITION) ? 1 : 0; } // if the line is flagged as a volition file if(is_from_volition && (Mission_icon_bitmaps[MISSION_ICON_VOLITION] >= 0)){ gr_set_bitmap(Mission_icon_bitmaps[MISSION_ICON_VOLITION]); gr_bitmap(Sim_volition_icon_x[gr_screen.res], y_start + MISSION_ICON_VOLITION_Y_OFFSET, GR_RESIZE_MENU); } } /// Campaign room stuff below int Cr_list_coords[GR_NUM_RESOLUTIONS][4] = { { // GR_640 47, 21, 565, 233 }, { // GR_1024 64, 34, 916, 459 } }; int Cr_info_coords[GR_NUM_RESOLUTIONS][4] = { { // GR_640 28, 267, 476, 103 }, { // GR_1024 45, 427, 761, 165 }, }; #define CR_NUM_BUTTONS 6 #define CR_SCROLL_UP_BUTTON 0 #define CR_SCROLL_DOWN_BUTTON 1 #define CR_SCROLL_INFO_UP_BUTTON 2 #define CR_SCROLL_INFO_DOWN_BUTTON 3 #define CR_RESET_BUTTON 4 #define CR_COMMIT_BUTTON 5 #define MAX_INFO_LINES 20 #define MAX_INFO_LINE_LEN 256 ui_button_info Cr_buttons[GR_NUM_RESOLUTIONS][CR_NUM_BUTTONS] = { { // GR_640 ui_button_info("CAB_00", 2, 42, -1, -1, 0), ui_button_info("CAB_01", 2, 89, -1, -1, 1), ui_button_info("CAB_02", 2, 279, -1, -1, 2), ui_button_info("CAB_03", 2, 325, -1, -1, 3), ui_button_info("CAB_04", 579, 353, -1, -1, 4), ui_button_info("CAB_05", 575, 434, -1, -1, 5), }, { // GR_1024 ui_button_info("2_CAB_00", 3, 68, -1, -1, 0), ui_button_info("2_CAB_01", 3, 142, -1, -1, 1), ui_button_info("2_CAB_02", 3, 446, -1, -1, 2), ui_button_info("2_CAB_03", 3, 520, -1, -1, 3), ui_button_info("2_CAB_04", 927, 565, -1, -1, 4), ui_button_info("2_CAB_05", 920, 694, -1, -1, 5), } }; // text #define CR_NUM_TEXT 3 UI_XSTR Cr_text[GR_NUM_RESOLUTIONS][CR_NUM_TEXT] = { { // GR_640 { "Restart", 1403, 569, 326, UI_XSTR_COLOR_GREEN, -1, &Cr_buttons[0][CR_RESET_BUTTON].button }, { "Campaign", 1404, 569, 337, UI_XSTR_COLOR_GREEN, -1, &Cr_buttons[0][CR_RESET_BUTTON].button }, { "Select", 1409, 568, 413, UI_XSTR_COLOR_PINK, -1, &Cr_buttons[0][CR_COMMIT_BUTTON].button }, }, { // GR_1024 { "Restart", 1403, 922, 523, UI_XSTR_COLOR_GREEN, -1, &Cr_buttons[1][CR_RESET_BUTTON].button }, { "Campaign", 1404, 922, 538, UI_XSTR_COLOR_GREEN, -1, &Cr_buttons[1][CR_RESET_BUTTON].button }, { "Select", 1409, 921, 665, UI_XSTR_COLOR_PINK, -1, &Cr_buttons[1][CR_COMMIT_BUTTON].button }, } }; static int Desc_scroll_offset; static int Selected_campaign_index; static int Active_campaign_index; const char *Info_text_ptrs[MAX_INFO_LINES]; int Num_info_lines, Info_text_line_size[MAX_INFO_LINES]; void campaign_room_build_listing() { font::set_font(font::FONT1); int font_height = gr_get_font_height(); int i, y; Num_lines = y = 0; for (i = 0; i < Num_campaigns; i++) { if (Campaign_names[i] != NULL) { // determine some extra information int flags = 0; auto fb = game_find_builtin_mission(Campaign_file_names[i]); if (fb != NULL) { if (fb->flags & FSB_FROM_VOLITION) { flags |= READYROOM_FLAG_FROM_VOLITION; } } sim_room_line_add(READYROOM_LINE_CAMPAIGN, Campaign_names[i], Campaign_file_names[i], Cr_list_coords[gr_screen.res][0], y, flags); y += font_height + 2; } } } void set_new_campaign_line(int n) { char *str; Selected_campaign_index = n; str = Campaign_descs[Selected_campaign_index]; Num_info_lines = 0; if (str) { Num_info_lines = split_str(str, Cr_info_coords[gr_screen.res][2], Info_text_line_size, Info_text_ptrs, MAX_INFO_LINES, MAX_INFO_LINE_LEN); Assert(Num_info_lines >= 0); } Desc_scroll_offset = 0; } void campaign_room_scroll_info_up() { if (Desc_scroll_offset) { Desc_scroll_offset--; gamesnd_play_iface(InterfaceSounds::SCROLL); } else gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL); } void campaign_room_scroll_info_down() { if ( (Num_info_lines - Desc_scroll_offset) * gr_get_font_height() > Cr_info_coords[gr_screen.res][3]) { Desc_scroll_offset++; gamesnd_play_iface(InterfaceSounds::SCROLL); } else gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL); } void campaign_reset(const SCP_string& campaign_file) { // note: we do not toss all-time stats from player's performance in campaign up till now mission_campaign_savefile_delete(campaign_file.c_str()); const int load_status = mission_campaign_load(campaign_file.c_str(), nullptr, nullptr, 1); // see if we successfully loaded this campaign if (load_status == 0) { // Goober5000 - reinitialize tech database if needed if ((Campaign.flags & CF_CUSTOM_TECH_DATABASE) || !stricmp(Campaign.filename, "freespace2")) { // reset tech database to what's in the tables tech_reset_to_default(); // write the savefile so that we don't later load a stale techroom Pilot.save_savefile(); } if (OnCampaignBeginHook->isActive()) { OnCampaignBeginHook->run(scripting::hook_param_list(scripting::hook_param("Campaign", 's', Campaign.filename))); } } mission_campaign_next_mission(); } // returns: 0 = success, !0 = aborted or failed int campaign_room_reset_campaign(int n) { int z = popup(PF_TITLE_BIG | PF_TITLE_RED, 2, POPUP_CANCEL, POPUP_OK, XSTR( "Warning\nThis will cause all progress in your\ncurrent campaign to be lost", 110)); if (z == 1) { campaign_reset(Campaign_file_names[n]); return 0; } return 1; } void campaign_room_commit() { if (Selected_campaign_index < 0) { gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL); return; } campaign_select_campaign(Campaign_file_names[Selected_campaign_index]); gameseq_post_event(GS_EVENT_MAIN_MENU); gamesnd_play_iface(InterfaceSounds::COMMIT_PRESSED); } int campaign_room_button_pressed(int n) { switch (n) { case CR_SCROLL_UP_BUTTON: sim_room_scroll_screen_up(); break; case CR_SCROLL_DOWN_BUTTON: sim_room_scroll_screen_down(); break; case CR_SCROLL_INFO_UP_BUTTON: campaign_room_scroll_info_up(); break; case CR_SCROLL_INFO_DOWN_BUTTON: campaign_room_scroll_info_down(); break; case CR_COMMIT_BUTTON: campaign_room_commit(); break; /* case CR_HELP_BUTTON: launch_context_help(); gamesnd_play_iface(SND_HELP_PRESSED); break; case CR_OPTIONS_BUTTON: gamesnd_play_iface(SND_SWITCH_SCREENS); gameseq_post_event(GS_EVENT_OPTIONS_MENU); return 1; */ case CR_RESET_BUTTON: if ( (Active_campaign_index < 0) || (Active_campaign_index >= Num_campaigns) ) gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL); else if (campaign_room_reset_campaign(Active_campaign_index)) gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL); else gamesnd_play_iface(InterfaceSounds::USER_SELECT); break; } return 0; } bool campaign_build_campaign_list() { const auto load_failed = mission_load_up_campaign(); if (!load_failed) { mission_campaign_next_mission(); } else { Campaign.filename[0] = 0; Campaign.num_missions = 0; mission_campaign_load_failure_popup(); } // we need descriptions too, so "true" it mission_campaign_build_list(true); // convert to proper bool return load_failed != 0; } void campaign_select_campaign(const SCP_string& campaign_file) { // new campaign selected? if (stricmp(campaign_file.c_str(), Campaign.filename) != 0) { strcpy_s(Player->current_campaign, campaign_file.c_str()); // track new campaign for player // attempt to load the campaign const int load_status = mission_campaign_load(campaign_file.c_str()); // see if we successfully loaded this campaign and it's at the beginning if (load_status == 0 && Campaign.prev_mission < 0) { // Goober5000 - reinitialize tech database if needed if ((Campaign.flags & CF_CUSTOM_TECH_DATABASE) || !stricmp(Campaign.filename, "freespace2")) { // reset tech database to what's in the tables tech_reset_to_default(); // write the savefile so that we don't later load a stale techroom Pilot.save_savefile(); } if (OnCampaignBeginHook->isActive()) { OnCampaignBeginHook->run(scripting::hook_param_list(scripting::hook_param("Campaign", 's', Campaign.filename))); } } // that's all we need to do for now; the campaign loading status will be checked again when we try to load the // campaign in the ready room } } void campaign_room_init() { int i; ui_button_info *b; list_h = Mission_list_coords[gr_screen.res][3]; Ui_window.create(0, 0, gr_screen.max_w_unscaled, gr_screen.max_h_unscaled, 0); Campaign_background_bitmap_mask = bm_load(Campaign_mask_filename[gr_screen.res]); if (Campaign_background_bitmap_mask < 0) { Warning(LOCATION, "Error loading campaign room mask %s", Campaign_mask_filename[gr_screen.res]); } else { Ui_window.set_mask_bmap(Campaign_mask_filename[gr_screen.res]); } for (i=0; ibutton.create(&Ui_window, "", b->x, b->y, 60, 30, (i < 2), 1); // set up callback for when a mouse first goes over a button b->button.set_highlight_action(common_play_highlight_sound); b->button.set_bmaps(b->filename); b->button.link_hotspot(b->hotspot); } for (i=0; i= Num_campaigns)) { // Player is trying to use a regular pilot in the demo. if (Num_campaigns < 1) { // If there are no campaigns loaded, there's really nothing left to do. popup_game_feature_not_in_demo(); return; } int select_default = popup(PF_USE_AFFIRMATIVE_ICON|PF_BODY_BIG, 2, "Exit", "Select first campaign", "Campaign Room only available in full version. However, you may select the first campaign found."); if (select_default == 1) { campaign_select_campaign(Campaign_file_names[0]); } } else { int reset_campaign = popup(PF_USE_AFFIRMATIVE_ICON|PF_BODY_BIG, 2, "Exit", "Restart Campaign", "Campaign Room only available in full version. However, you may restart the campaign."); if (reset_campaign == 1) { // Rather than hardcoding the reset, let's reuse this. campaign_room_reset_campaign(Active_campaign_index); } } gameseq_post_event(GS_EVENT_MAIN_MENU); return; } if ( help_overlay_active(Campaign_room_overlay_id) ) { // Cr_buttons[gr_screen.res][CR_HELP_BUTTON].button.reset_status(); Ui_window.set_ignore_gadgets(1); } k = Ui_window.process() & ~KEY_DEBUGGED; if ( (k > 0) || B1_JUST_RELEASED ) { if ( help_overlay_active(Campaign_room_overlay_id) ) { help_overlay_set_state(Campaign_room_overlay_id, gr_screen.res, 0); Ui_window.set_ignore_gadgets(0); k = 0; } } if ( !help_overlay_active(Campaign_room_overlay_id) ) { Ui_window.set_ignore_gadgets(0); } switch (k) { case KEY_DOWN: // scroll list down if (Selected_campaign_index < Num_campaigns - 1) { set_new_campaign_line(Selected_campaign_index + 1); gamesnd_play_iface(InterfaceSounds::SCROLL); } else gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL); break; case KEY_UP: // scroll list up if (Selected_campaign_index < 0) Selected_campaign_index = 1; if (Selected_campaign_index) { set_new_campaign_line(Selected_campaign_index - 1); gamesnd_play_iface(InterfaceSounds::SCROLL); } else gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL); break; case KEY_ESC: gameseq_post_event(GS_EVENT_MAIN_MENU); break; } // end switch for (i=0; i= 0) { gr_set_bitmap(Background_bitmap); gr_bitmap(0, 0, GR_RESIZE_MENU); } Ui_window.draw(); font::set_font(font::FONT1); int font_height = gr_get_font_height(); line = Scroll_offset; while (sim_room_line_query_visible(line) && (line - Scroll_offset < LIST_BUTTONS_MAX)) { y = Cr_list_coords[gr_screen.res][1] + sim_room_lines[line].y - sim_room_lines[Scroll_offset].y; List_buttons[line - Scroll_offset].update_dimensions(Cr_list_coords[gr_screen.res][0], y, Cr_list_coords[gr_screen.res][2], font_height); List_buttons[line - Scroll_offset].enable(); if (!stricmp(sim_room_lines[line].filename, Campaign.filename)) { gr_set_color_fast(&Color_white); i = y + font_height / 2 - 1; gr_circle(Cr_list_coords[gr_screen.res][0] - 6, i, 5, GR_RESIZE_MENU); gr_set_color_fast(&Color_bright_white); gr_line(Cr_list_coords[gr_screen.res][0] - 10, i, Cr_list_coords[gr_screen.res][0] - 8, i, GR_RESIZE_MENU); gr_line(Cr_list_coords[gr_screen.res][0] - 6, i - 4, Cr_list_coords[gr_screen.res][0] - 6, i - 2, GR_RESIZE_MENU); gr_line(Cr_list_coords[gr_screen.res][0] - 4, i, Cr_list_coords[gr_screen.res][0] - 2, i, GR_RESIZE_MENU); gr_line(Cr_list_coords[gr_screen.res][0] - 6, i + 2, Cr_list_coords[gr_screen.res][0] - 6, i + 4, GR_RESIZE_MENU); } if (line == Selected_campaign_index) gr_set_color_fast(&Color_text_selected); else if (line == select_tease_line) gr_set_color_fast(&Color_text_subselected); else gr_set_color_fast(&Color_text_normal); strcpy_s(buf, sim_room_lines[line].name); font::force_fit_string(buf, 255, Cr_list_coords[gr_screen.res][0] + Cr_list_coords[gr_screen.res][2] - sim_room_lines[line].x); gr_printf_menu(sim_room_lines[line].x, y, "%s", buf); line++; } i = line - Scroll_offset; while (i < LIST_BUTTONS_MAX) List_buttons[i++].disable(); y = 0; i = Desc_scroll_offset; gr_set_color_fast(&Color_text_normal); while (y + font_height <= Cr_info_coords[gr_screen.res][3]) { if (i >= Num_info_lines) break; Assert(Info_text_line_size[i] < MAX_INFO_LINE_LEN); strncpy(line_text, Info_text_ptrs[i], Info_text_line_size[i]); line_text[Info_text_line_size[i]] = 0; drop_white_space(line_text); gr_string(Cr_info_coords[gr_screen.res][0], Cr_info_coords[gr_screen.res][1] + y, line_text, GR_RESIZE_MENU); y += font_height; i++; } if (Num_campaigns < 1) { popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR( "No campaigns are available!", 1613)); Campaign_room_no_campaigns = true; gameseq_post_event(GS_EVENT_MAIN_MENU); } // blit help overlay if active help_overlay_maybe_blit(Campaign_room_overlay_id, gr_screen.res); gr_flip(); } void sim_room_load_mission_icons() { int idx; // load all bitmaps for(idx=0; idx= 0){ // don't bm_release() here, used in multiple places - taylor bm_unload(Mission_icon_bitmaps[idx]); Mission_icon_bitmaps[idx] = -1; } } }