Files
fs2open.github.com/code/menuui/readyroom.cpp
T
Goober5000 5e7a6ece52 log when missions are skipped due to XSTR mismatch
As a follow-up to #2343, users might wonder why their mission isn't showing up in the tech room, so log a friendly message.
2021-07-16 22:17:36 -04:00

1957 lines
56 KiB
C++

/*
* 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 <cctype>
#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
#define MAX_DESC_LINES 200
#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
};
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
char *name;
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;
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);
// 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(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; i<Num_campaign_missions; i++) {
rval = hash_insert(Campaign_missions[i]);
if (rval == 0) {
return 0;
}
}
// successful
return 1;
}
// deletes hash table nodes
//
void campaign_mission_hash_table_delete()
{
hash_node *cur_node;
for (int i=0; i<CAMPAIGN_MISSION_HASH_SIZE; i++) {
// Look for entries into array
if (Campaign_mission_hash_table[i] != NULL) {
cur_node = Campaign_mission_hash_table[i];
// Walk down the list deleting self
while (cur_node->next != 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, char *name, 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()
{
int font_height = gr_get_font_height();
char filename[MAX_FILENAME_LEN];
char str[256];
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?
strcpy_s(filename, Mission_filenames[Num_standalone_missions_with_info]);
// update popup
memset(str, 0, 256);
sprintf(str, XSTR("Single Mission\n\n%s",989), filename);
popup_change_text(str);
// tack on an extension
strcat_s(filename, FS_MISSION_FILE_EXT);
// 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);
Standalone_mission_flags[Num_standalone_missions_with_info] = The_mission.game_type;
int y = Num_lines * (font_height + 2);
// determine some extra information
int flags = 0;
fs_builtin_mission *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()
{
int font_height = gr_get_font_height();
char str[256];
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
memset(str, 0, 256);
sprintf(str, XSTR("Campaign Mission\n\n%s",990), Campaign.missions[Num_campaign_missions_with_info].name);
popup_change_text(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)
{
if (!get_mission_info(Campaign.missions[Num_campaign_missions_with_info].name))
{
// add to list
Campaign_mission_names[Num_campaign_missions_with_info] = vm_strdup(The_mission.name);
Campaign_mission_flags[Num_campaign_missions_with_info] = The_mission.game_type;
int y = valid_missions_with_info * (font_height + 2);
// determine some extra information
int flags = 0;
fs_builtin_mission *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();
char full_filename[256];
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(build_standalone_mission_list_do_frame, POPUP_CANCEL, XSTR("Loading missions", 991) );
} else {
for (i=0; i<Num_standalone_missions_with_info; i++) {
if (Standalone_mission_names[i]) {
// determine some extra information
int flags = 0;
memset(full_filename, 0, 256);
strcpy_s(full_filename, cf_add_ext(Mission_filenames[i], FS_MISSION_FILE_EXT));
fs_builtin_mission *fb = game_find_builtin_mission(full_filename);
if((fb != NULL) && (fb->flags & 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(build_campaign_mission_list_do_frame, POPUP_CANCEL, XSTR("Loading campaign missions",992));
} else {
for (i=0; i<Num_campaign_missions_with_info; i++) {
if (Campaign_mission_names[i]) {
// determine some extra information
int flags = 0;
memset(full_filename, 0, 256);
strcpy_s(full_filename, cf_add_ext(Campaign.missions[i].name, FS_MISSION_FILE_EXT));
fs_builtin_mission *fb = game_find_builtin_mission(full_filename);
if((fb != NULL) && (fb->flags & 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<Campaign.num_missions; i++) {
if (Campaign_mission_names[i]) {
vm_free(Campaign_mission_names[i]);
Campaign_mission_names[i] = NULL;
}
}
Campaign_mission_names_inited = 0;
Num_campaign_missions_with_info = 0;
sim_room_build_listing();
}
int sim_room_line_query_visible(int n)
{
int y;
if ((n < 0) || (n >= 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
}
// Decide wether to resume a save game or not
// exit: 1 => savegame has been restored
// 0 => no restore, proceed to briefing
// -1 => don't start mission at all
int sim_room_maybe_resume_savegame()
{
// MWA -- 3/26/98 -- removed all savegame references in game
return 0;
/*
char savegame_filename[_MAX_FNAME];
int popup_rval = -1, resume_savegame = 0;
// Generate the save-game filename for this campaign
memset(savegame_filename, 0, _MAX_FNAME);
mission_campaign_savefile_generate_root(savegame_filename);
strcat_s(savegame_filename, NOX("svg"));
// Decide if we should offer choice to resume this savegame
if ( sim_room_can_resume_savegame(savegame_filename) ) {
popup_rval = popup(0, 3, XSTR("&Cancel",-1), XSTR("&Overwrite",-1), XSTR("&Resume",-1), XSTR("A save game for this mission exists.", -1));
switch ( popup_rval ) {
case 0:
case -1:
resume_savegame = -1;
break;
case 1:
resume_savegame = 0;
break;
case 2:
resume_savegame = 1;
break;
default:
Int3();
resume_savegame = -1;
break;
}
} else {
resume_savegame = 0;
}
if (resume_savegame == 1) {
if ( state_restore_all(savegame_filename) == -1 ) {
popup_rval = popup(PF_TITLE_BIG | PF_TITLE_RED, 2, POPUP_NO, POPUP_YES, XSTR("Error\nSaved misison could not be loaded.\nDo you wish to start this mission from the beginning?", -1));
if (popup_rval == 1) {
resume_savegame = 0;
} else {
resume_savegame = -1;
}
} else {
resume_savegame = 1;
}
}
// If we are resuming this savegame, then delete the file
if (resume_savegame == 1) {
cf_delete(savegame_filename);
}
return resume_savegame;
*/
}
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) ) {
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));
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[256];
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; i<NUM_BUTTONS; i++) {
b = &Buttons[gr_screen.res][i];
b->button.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; i<LIST_BUTTONS_MAX; i++) {
List_buttons[i].create(&Ui_window, "", 0, 0, 60, 30, 0, 1);
List_buttons[i].hide();
List_buttons[i].disable();
}
// set up sim_rooms for buttons so we draw the correct animation frame when a key is pressed
Buttons[gr_screen.res][SCROLL_UP_BUTTON].button.set_hotkey(KEY_PAGEUP);
Buttons[gr_screen.res][SCROLL_DOWN_BUTTON].button.set_hotkey(KEY_PAGEDOWN);
Buttons[gr_screen.res][COMMIT_BUTTON].button.set_hotkey(KEY_CTRLED | KEY_ENTER);
Background_bitmap = bm_load(Sim_filename[gr_screen.res]);
// load in help overlay bitmap
Sim_room_overlay_id = help_overlay_get_index(SIM_ROOM_OVERLAY);
help_overlay_set_state(Sim_room_overlay_id,gr_screen.res,0);
Scroll_offset = Selected_line = 0;
strcpy_s(Cur_campaign, Player->current_campaign);
if ( !mission_load_up_campaign() ) {
mission_campaign_next_mission();
} else {
Campaign.filename[0] = 0;
Campaign.num_missions = 0;
mission_campaign_load_failure_popup();
}
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;
memset(wild_card, 0, 256);
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<Num_campaign_missions; i++) {
if (Campaign_missions[i]) {
vm_free(Campaign_missions[i]);
Campaign_missions[i] = NULL;
}
}
if (Background_bitmap >= 0)
bm_release(Background_bitmap);
if (Standalone_mission_names_inited){
for (i=0; i<Num_standalone_missions; i++){
if (Standalone_mission_names[i] != NULL){
vm_free(Standalone_mission_names[i]);
Standalone_mission_names[i] = NULL;
}
Standalone_mission_flags[i] = 0;
}
}
if (Campaign_mission_names_inited) {
for (i=0; i<Campaign.num_missions; i++) {
if (Campaign_mission_names[i]) {
vm_free(Campaign_mission_names[i]);
Campaign_mission_names[i] = NULL;
}
}
}
for (i=0; i<Num_standalone_missions; i++) {
vm_free(Mission_filenames[i]);
Mission_filenames[i] = NULL;
}
// free global Campaign_* list stuff
mission_campaign_free_list();
campaign_mission_hash_table_delete();
Ui_window.destroy();
common_free_interface_palette(); // restore game palette
Pilot.save_player();
// unload special mission icons
sim_room_unload_mission_icons();
}
// ---------------------------------------------------------------------
// sim_room_do_frame()
//
// Called once per frame to process user input for the sim_room Assignment Screen
//
void sim_room_do_frame(float /*frametime*/)
{
char buf[256];
int i, k, y, line;
int font_height = gr_get_font_height();
int select_tease_line = -1; // line mouse is down on, but won't be selected until button released
if ( help_overlay_active(Sim_room_overlay_id) ) {
Buttons[gr_screen.res][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(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<NUM_BUTTONS; i++){
if (Buttons[gr_screen.res][i].button.pressed()){
if (sim_room_button_pressed(i)){
return;
}
}
}
for (i=0; i<LIST_BUTTONS_MAX; i++) {
if (List_buttons[i].is_mouse_on())
select_tease_line = i + Scroll_offset;
if (List_buttons[i].pressed()) {
Selected_line = i + Scroll_offset;
gamesnd_play_iface(InterfaceSounds::USER_SELECT);
}
}
GR_MAYBE_CLEAR_RES(Background_bitmap);
if (Background_bitmap >= 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
char full_name[256];
memset(full_name, 0, 256);
strcpy_s(full_name, cf_add_ext(Campaign.filename,FS_CAMPAIGN_FILE_EXT));
fs_builtin_mission *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)) {
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 struct {
char *text;
int len;
} campaign_desc_lines[MAX_DESC_LINES];
*/
static int Num_desc_lines;
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;
fs_builtin_mission *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) {
auto filename = campaign_file + FS_CAMPAIGN_FILE_EXT;
mission_campaign_savefile_delete(filename.c_str());
mission_campaign_load(filename.c_str(), nullptr, 1 , false); // retail doesn't reset stats when resetting the campaign
mission_campaign_next_mission();
// 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();
}
}
// 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
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();
}
}
// 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; i<CR_NUM_BUTTONS; i++) {
b = &Cr_buttons[gr_screen.res][i];
b->button.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<LIST_BUTTONS_MAX; i++) {
List_buttons[i].create(&Ui_window, "", 0, 0, 60, 30, 0, 1);
List_buttons[i].hide();
List_buttons[i].disable();
}
// add xstrs
for(i=0; i<CR_NUM_TEXT; i++){
Ui_window.add_XSTR(&Cr_text[gr_screen.res][i]);
}
// set up sim_rooms for buttons so we draw the correct animation frame when a key is pressed
Cr_buttons[gr_screen.res][CR_SCROLL_UP_BUTTON].button.set_hotkey(KEY_PAGEUP);
Cr_buttons[gr_screen.res][CR_SCROLL_DOWN_BUTTON].button.set_hotkey(KEY_PAGEDOWN);
Cr_buttons[gr_screen.res][CR_RESET_BUTTON].button.set_hotkey(KEY_DELETE);
Cr_buttons[gr_screen.res][CR_COMMIT_BUTTON].button.set_hotkey(KEY_CTRLED | KEY_ENTER);
// Cr_buttons[gr_screen.res][CR_HELP_BUTTON].button.set_hotkey(KEY_F2);
Background_bitmap = bm_load(Campaign_filename[gr_screen.res]);
if (Background_bitmap < 0) {
Warning(LOCATION, "Error loading campaign room background %s", Campaign_filename[gr_screen.res]);
}
// load in help overlay bitmap
Campaign_room_overlay_id = help_overlay_get_index(CAMPAIGN_ROOM_OVERLAY);
help_overlay_set_state(Campaign_room_overlay_id,gr_screen.res,0);
Num_desc_lines = 0;
Desc_scroll_offset = Scroll_offset = 0;
// this stuff needs to happen before the mission_campaign_build_list() call
const auto load_failed = campaign_build_campaign_list();
campaign_room_build_listing();
Selected_campaign_index = Active_campaign_index = -1;
if (!load_failed) {
for (i=0; i<Num_campaigns; i++)
if (!stricmp(Campaign_file_names[i], Campaign.filename)) {
set_new_campaign_line(i);
Active_campaign_index = i;
break;
}
}
Campaign_room_no_campaigns = false;
}
void campaign_room_close()
{
if (bm_is_valid(Background_bitmap))
bm_release(Background_bitmap);
// Reset info text pointers and size since they may contain pointers into the campaign description which will be
// freed soon.
memset(Info_text_line_size, 0, sizeof(Info_text_line_size));
memset(Info_text_ptrs, 0, sizeof(Info_text_ptrs));
// free the global Campaign_* list stuff
mission_campaign_free_list();
Ui_window.destroy();
if (bm_is_valid(Campaign_background_bitmap_mask)) {
bm_release(Campaign_background_bitmap_mask);
}
common_free_interface_palette(); // restore game palette
Pilot.save_player();
}
void campaign_room_do_frame(float /*frametime*/)
{
char buf[256];
char line_text[MAX_INFO_LINE_LEN];
int i, k, y, line;
int select_tease_line = -1; // line mouse is down on, but won't be selected until button released
// If we don't have a mask, we don't have enough data to do anything with this screen.
if (Campaign_background_bitmap_mask == -1) {
{
//popup_game_feature_not_in_demo();
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<CR_NUM_BUTTONS; i++){
if (Cr_buttons[gr_screen.res][i].button.pressed()){
if (campaign_room_button_pressed(i)){
return;
}
}
}
for (i=0; i<LIST_BUTTONS_MAX; i++) {
if (List_buttons[i].is_mouse_on()){
select_tease_line = i + Scroll_offset;
}
if (List_buttons[i].pressed()) {
set_new_campaign_line(i + Scroll_offset);
gamesnd_play_iface(InterfaceSounds::USER_SELECT);
}
}
GR_MAYBE_CLEAR_RES(Background_bitmap);
if (Background_bitmap >= 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)) {
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<NUM_MISSION_ICONS; idx++){
Mission_icon_bitmaps[idx] = -1;
Mission_icon_bitmaps[idx] = bm_load(Mission_icon_bitmap_filenames[idx]);
}
}
void sim_room_unload_mission_icons()
{
int idx;
// unload all bitmaps
for(idx=0; idx<NUM_MISSION_ICONS; idx++){
if(Mission_icon_bitmaps[idx] >= 0){
// don't bm_release() here, used in multiple places - taylor
bm_unload(Mission_icon_bitmaps[idx]);
Mission_icon_bitmaps[idx] = -1;
}
}
}