mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-11 04:31:35 -10:00
This adds proper Unicode support by allowing the parse system to read UTF-8 encoded files. The new mode is controlled by a mod option which allows older mods to continue using the legacy text encoding. That option changes the behavior of several systems in FSO. It disallows the usage of Volition fonts since they don't support Unicode encoding and changes a few usages of special characters to their unicode counterparts of generic rendering solutions. This fixes #1415.
853 lines
21 KiB
C++
853 lines
21 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 <stdlib.h>
|
|
|
|
#include "cfile/cfile.h"
|
|
#include "freespace.h"
|
|
#include "gamesequence/gamesequence.h"
|
|
#include "gamesnd/eventmusic.h" /* for Master_event_music_volume */
|
|
#include "gamesnd/gamesnd.h"
|
|
#include "globalincs/alphacolors.h"
|
|
#include "graphics/font.h"
|
|
#include "io/key.h"
|
|
#include "io/timer.h"
|
|
#include "localization/localize.h"
|
|
#include "menuui/credits.h"
|
|
#include "missionui/missionscreencommon.h"
|
|
#include "parse/parselo.h"
|
|
#include "playerman/player.h"
|
|
#include "sound/audiostr.h"
|
|
#include "ui/ui.h"
|
|
|
|
#include <lua.h>
|
|
|
|
|
|
// This is the fs2_open credit list, please only add yourself if you have actually contributed code
|
|
// Rules!
|
|
const char *fs2_open_credit_text =
|
|
"SOURCE CODE PROJECT STAFF:\n"
|
|
"\n"
|
|
"Project Leader:\n"
|
|
"\n"
|
|
"Fabian \"The E\" Woltermann\n"
|
|
"\n"
|
|
"Senior Advisors:\n"
|
|
"\n"
|
|
"Ian \"Goober5000\" Warfield\n"
|
|
"Michael \"Zacam\" LaFleur\n"
|
|
"Taylor Richards\n"
|
|
"Edward \"Inquisitor\" Gardner\n"
|
|
"Cliff \"chief1983\" Gordon\n"
|
|
"\n"
|
|
"Programmers:\n"
|
|
"\n"
|
|
"Hassan \"Karajorma\" Kazmi\n"
|
|
"Derek \"Kazan\" Meek\n"
|
|
"Nick \"phreak\" Iannetta\n"
|
|
"Mike \"Bobboau\" Abegg\n"
|
|
"Backslash, Echelon9, Flaming_Sword\n"
|
|
"FUBAR, Iss Mneur, kkmic\n"
|
|
"Shade, Soulstorm, Sushi\n"
|
|
"Swifty, Wanderer, CommanderDJ\n"
|
|
"Valathil, MageKing17, Yarn\n"
|
|
"m!m, z64555, zookeeper\n"
|
|
"jg18, niffiwan\n"
|
|
"\n"
|
|
"\n"
|
|
"Readme Staff:\n"
|
|
"\n"
|
|
"Peter \"Flipside\" Dibble\n"
|
|
"\n"
|
|
"Web Support:\n"
|
|
"\n"
|
|
"http://www.hard-light.net/\n"
|
|
"http://scp.indiegames.us/\n"
|
|
"\n"
|
|
"Special thanks to:\n"
|
|
"\n"
|
|
"portej05\n"
|
|
"Hery\n"
|
|
"QuantumDelta\n"
|
|
"Fury\n"
|
|
"\n"
|
|
"Linux and OSX version with the support of\n"
|
|
"the icculus.org FreeSpace2 project:\n"
|
|
"Steven \"relnev\" Fuller\n"
|
|
"Ryan Gordon\n"
|
|
"Charles Mason\n"
|
|
"Dan Olson\n"
|
|
"Taylor Richards\n"
|
|
"\"tigital\"\n"
|
|
"\n"
|
|
"Very special thanks to:\n"
|
|
"\n"
|
|
"Volition for making FS2 such a great game\n"
|
|
"Dave Baranec for giving us the code and keeping us sanity checked\n"
|
|
"\n\n"
|
|
"FS2_Open makes use of the following technologies:\n"
|
|
"\n"
|
|
"Ogg Vorbis - (C) 2005, Xiph.Org Foundation\n"
|
|
"JPEG - Independent JPEG Group, (C) 1991-1998, Thomas G. Lane\n"
|
|
"libpng - Copyright (C) 1998-2010 Glenn Randers-Pehrson\n"
|
|
"liblua (" LUA_RELEASE ") - " LUA_COPYRIGHT "\n"
|
|
"zlib - Copyright (C) 1995-2005 Jean-loup Gailly and Mark Adler\n"
|
|
"FXAA - Copyright (c) 2010 NVIDIA Corporation. All rights reserved.\n"
|
|
"This software uses libraries from the FFmpeg project under the LGPLv2.1\n"
|
|
"\n"
|
|
"\n"
|
|
"\n";
|
|
|
|
const char *unmodified_credits = "ORIGINAL VOLITION STAFF:\n\n\n";
|
|
const char *mod_check = "Design:";
|
|
|
|
#define NUM_BUTTONS 5
|
|
#define DEFAULT_NUM_IMAGES 46
|
|
|
|
#define TECH_DATABASE_BUTTON 0
|
|
#define SIMULATOR_BUTTON 1
|
|
#define CUTSCENES_BUTTON 2
|
|
#define CREDITS_BUTTON 3
|
|
#define EXIT_BUTTON 4
|
|
|
|
// inidicies for coordinates
|
|
#define CREDITS_X_COORD 0
|
|
#define CREDITS_Y_COORD 1
|
|
#define CREDITS_W_COORD 2
|
|
#define CREDITS_H_COORD 3
|
|
|
|
static const char* Credits_bitmap_fname[GR_NUM_RESOLUTIONS] = {
|
|
"Credits", // GR_640
|
|
"2_Credits"
|
|
};
|
|
|
|
static const char* Credits_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
|
|
"Credits-M", // GR_640
|
|
"2_Credits-M"
|
|
};
|
|
|
|
int Credits_image_coords[GR_NUM_RESOLUTIONS][4] = {
|
|
{
|
|
219, 15, 394, 286 // GR_640
|
|
},
|
|
{
|
|
351, 25, 629, 455 // GR_1024
|
|
}
|
|
};
|
|
|
|
// x, y, w, h
|
|
int Credits_text_coords[GR_NUM_RESOLUTIONS][4] = {
|
|
{
|
|
26, 316, 482, 157 // GR_640
|
|
},
|
|
{
|
|
144, 507, 568, 249 // GR_640
|
|
}
|
|
};
|
|
|
|
struct credits_screen_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..
|
|
|
|
credits_screen_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 int Background_bitmap;
|
|
static UI_WINDOW Ui_window;
|
|
|
|
static credits_screen_buttons Buttons[NUM_BUTTONS][GR_NUM_RESOLUTIONS] = {
|
|
//XSTR:OFF
|
|
{
|
|
credits_screen_buttons("TDB_00", 7, 3, 37, 7, 0), // GR_640
|
|
credits_screen_buttons("2_TDB_00", 12, 5, 59, 12, 0) // GR_1024
|
|
},
|
|
{
|
|
credits_screen_buttons("TDB_01", 7, 18, 37, 23, 1), // GR_640
|
|
credits_screen_buttons("2_TDB_01", 12, 31, 59, 37, 1) // GR_1024
|
|
},
|
|
{
|
|
credits_screen_buttons("TDB_02", 7, 34, 37, 38, 2), // GR_640
|
|
credits_screen_buttons("2_TDB_02", 12, 56, 59, 62, 2) // GR_1024
|
|
},
|
|
{
|
|
credits_screen_buttons("TDB_03", 7, 49, 37, 54, 3), // GR_640
|
|
credits_screen_buttons("2_TDB_03", 12, 81, 59, 88, 3) // GR_1024
|
|
},
|
|
{
|
|
credits_screen_buttons("CRB_04", 571, 425, 588, 413, 4), // GR_640
|
|
credits_screen_buttons("2_CRB_04", 914, 681, 953, 668, 4) // GR_1024
|
|
}
|
|
//XSTR:ON
|
|
};
|
|
|
|
static char Credits_music_name[NAME_LENGTH];
|
|
static int Credits_music_handle = -1;
|
|
static int Credits_music_begin_timestamp;
|
|
|
|
static int Credits_frametime; // frametime of credits_do_frame() loop in ms
|
|
static int Credits_last_time; // timestamp used to calc frametime (in ms)
|
|
static float Credits_counter;
|
|
|
|
static int Credits_num_images;
|
|
static int Credits_artwork_index;
|
|
static SCP_vector<int> Credits_bmps;
|
|
|
|
// Positions for credits...
|
|
float Credit_start_pos, Credit_stop_pos, Credit_position = 0.0f;
|
|
|
|
static int Credits_music_delay = 2000;
|
|
static float Credits_scroll_rate = 15.0f;
|
|
static float Credits_artwork_display_time = 9.0f;
|
|
static float Credits_artwork_fade_time = 1.0f;
|
|
|
|
static SCP_vector<SCP_string> Credit_text_parts;
|
|
|
|
static bool Credits_parsed;
|
|
|
|
enum CreditsPosition
|
|
{
|
|
START,
|
|
END
|
|
};
|
|
|
|
static CreditsPosition SCP_credits_position = START;
|
|
|
|
void credits_stop_music(bool fade)
|
|
{
|
|
if ( Credits_music_handle != -1 ) {
|
|
audiostream_close_file(Credits_music_handle, fade);
|
|
Credits_music_handle = -1;
|
|
}
|
|
}
|
|
|
|
void credits_load_music(char* fname)
|
|
{
|
|
if ( Credits_music_handle != -1 ){
|
|
return;
|
|
}
|
|
|
|
if ( fname && *fname ){
|
|
Credits_music_handle = audiostream_open( fname, ASF_MENUMUSIC );
|
|
}
|
|
}
|
|
|
|
void credits_start_music()
|
|
{
|
|
if (Credits_music_handle != -1) {
|
|
if ( !audiostream_is_playing(Credits_music_handle) ){
|
|
audiostream_play(Credits_music_handle, Master_event_music_volume, 1);
|
|
}
|
|
} else {
|
|
nprintf(("Warning", "Cannot play credits music\n"));
|
|
}
|
|
}
|
|
|
|
int credits_screen_button_pressed(int n)
|
|
{
|
|
switch (n) {
|
|
case TECH_DATABASE_BUTTON:
|
|
gamesnd_play_iface(SND_SWITCH_SCREENS);
|
|
gameseq_post_event(GS_EVENT_TECH_MENU);
|
|
return 1;
|
|
|
|
case SIMULATOR_BUTTON:
|
|
gamesnd_play_iface(SND_SWITCH_SCREENS);
|
|
gameseq_post_event(GS_EVENT_SIMULATOR_ROOM);
|
|
return 1;
|
|
|
|
case CUTSCENES_BUTTON:
|
|
gamesnd_play_iface(SND_SWITCH_SCREENS);
|
|
gameseq_post_event(GS_EVENT_GOTO_VIEW_CUTSCENES_SCREEN);
|
|
return 1;
|
|
|
|
case EXIT_BUTTON:
|
|
gamesnd_play_iface(SND_COMMIT_PRESSED);
|
|
gameseq_post_event(GS_EVENT_MAIN_MENU);
|
|
game_flush();
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void credits_parse_table(const char* filename)
|
|
{
|
|
try
|
|
{
|
|
read_file_text(filename, CF_TYPE_TABLES);
|
|
reset_parse();
|
|
|
|
// any metadata?
|
|
if (optional_string("$Music:"))
|
|
{
|
|
stuff_string(Credits_music_name, F_NAME, NAME_LENGTH);
|
|
}
|
|
if (optional_string("$Number of Images:"))
|
|
{
|
|
int temp;
|
|
stuff_int(&temp);
|
|
if (temp > 0)
|
|
Credits_num_images = temp;
|
|
}
|
|
if (optional_string("$Start Image Index:"))
|
|
{
|
|
stuff_int(&Credits_artwork_index);
|
|
|
|
// bounds check
|
|
if (Credits_artwork_index < 0)
|
|
{
|
|
Credits_artwork_index = 0;
|
|
}
|
|
else if (Credits_artwork_index >= Credits_num_images)
|
|
{
|
|
Credits_artwork_index = Credits_num_images - 1;
|
|
}
|
|
}
|
|
if (optional_string("$Text scroll rate:"))
|
|
{
|
|
stuff_float(&Credits_scroll_rate);
|
|
if (Credits_scroll_rate < 0.01f)
|
|
Credits_scroll_rate = 0.01f;
|
|
}
|
|
if (optional_string("$Artworks display time:"))
|
|
{
|
|
stuff_float(&Credits_artwork_display_time);
|
|
if (Credits_artwork_display_time < 0.01f)
|
|
Credits_artwork_display_time = 0.01f;
|
|
}
|
|
if (optional_string("$Artworks fade time:"))
|
|
{
|
|
stuff_float(&Credits_artwork_fade_time);
|
|
if (Credits_artwork_fade_time < 0.01f)
|
|
Credits_artwork_fade_time = 0.01f;
|
|
}
|
|
if (optional_string("$SCP Credits position:"))
|
|
{
|
|
char mode[NAME_LENGTH];
|
|
|
|
stuff_string(mode, F_NAME, NAME_LENGTH);
|
|
|
|
if (!stricmp(mode, "Start"))
|
|
SCP_credits_position = START;
|
|
else if (!stricmp(mode, "End"))
|
|
SCP_credits_position = END;
|
|
else
|
|
Warning(LOCATION, "Unknown credits position mode \"%s\".", mode);
|
|
}
|
|
|
|
ignore_white_space();
|
|
|
|
SCP_string credits_text;
|
|
SCP_string line;
|
|
|
|
SCP_vector<int> charNum;
|
|
SCP_vector<const char*> lines;
|
|
int numLines = -1;
|
|
|
|
bool first_run = true;
|
|
while (!check_for_eof_raw() && !check_for_string_raw("#end"))
|
|
{
|
|
// Read in a line of text
|
|
stuff_string_line(line);
|
|
|
|
// This is a bit odd but it means if a total conversion uses different credits the
|
|
// Volition credit won't happen
|
|
// Also don't append the default credits anymore when there was already a parsed table
|
|
if (first_run && !Credits_parsed && !line.compare(mod_check))
|
|
{
|
|
credits_text.append(unmodified_credits);
|
|
}
|
|
|
|
first_run = false;
|
|
|
|
if (line.empty())
|
|
{
|
|
// If the line is empty then just append a newline, don't bother with splitting it first
|
|
credits_text.append("\n");
|
|
}
|
|
else
|
|
{
|
|
// split_str doesn't take care of this.
|
|
charNum.clear();
|
|
|
|
// Split the string into multiple lines if it's too long
|
|
numLines = split_str(line.c_str(), Credits_text_coords[gr_screen.res][2], charNum, lines);
|
|
|
|
// Make sure that we have valid data
|
|
Assertion(lines.size() == (size_t)numLines, "split_str reported %d lines but vector contains " SIZE_T_ARG " entries!", numLines, lines.size());
|
|
|
|
Assertion(lines.size() <= charNum.size(),
|
|
"Something has gone wrong while splitting strings. Got " SIZE_T_ARG " lines but only " SIZE_T_ARG " chacter lengths.",
|
|
lines.size(), charNum.size());
|
|
|
|
// Now add all splitted lines to the credit text and append a newline to the end
|
|
for (int i = 0; i < numLines; i++)
|
|
{
|
|
credits_text.append(SCP_string(lines[i], charNum[i]));
|
|
credits_text.append("\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
Credit_text_parts.push_back(credits_text);
|
|
|
|
Credits_parsed = true;
|
|
}
|
|
catch (const parse::ParseException& e)
|
|
{
|
|
mprintf(("TABLES: Unable to parse '%s'! Error message = %s.\n", filename, e.what()));
|
|
return;
|
|
}
|
|
}
|
|
|
|
void credits_parse()
|
|
{
|
|
// Parse main table
|
|
credits_parse_table("credits.tbl");
|
|
|
|
// Parse modular tables
|
|
parse_modular_table("*-crd.tbm", credits_parse_table);
|
|
}
|
|
|
|
void credits_init()
|
|
{
|
|
int i;
|
|
credits_screen_buttons *b;
|
|
|
|
// pre-initialize
|
|
Credits_num_images = DEFAULT_NUM_IMAGES;
|
|
Credits_artwork_index = -1;
|
|
|
|
// this is moved up here so we can override it if desired
|
|
strcpy_s(Credits_music_name, "Cinema");
|
|
|
|
// parse credits early so as to set up any overrides (for music and such)
|
|
Credits_parsed = false;
|
|
credits_parse();
|
|
|
|
// we could conceivably have specified a number of images but not an index,
|
|
// so if that's the case, set the value here
|
|
if (Credits_artwork_index < 0) {
|
|
Credits_artwork_index = rand() % Credits_num_images;
|
|
}
|
|
|
|
int credits_spooled_music_index = event_music_get_spooled_music_index(Credits_music_name);
|
|
if(credits_spooled_music_index != -1){
|
|
char *credits_wavfile_name = Spooled_music[credits_spooled_music_index].filename;
|
|
if(credits_wavfile_name != NULL){
|
|
credits_load_music(credits_wavfile_name);
|
|
}
|
|
}
|
|
|
|
// Use this id to trigger the start of music playing on the briefing screen
|
|
Credits_music_begin_timestamp = timestamp(Credits_music_delay);
|
|
|
|
Credits_frametime = 0;
|
|
Credits_last_time = timer_get_milliseconds();
|
|
|
|
if (!Credits_parsed)
|
|
{
|
|
Credit_text_parts.push_back(SCP_string("No credits available.\n"));
|
|
}
|
|
else
|
|
{
|
|
switch (SCP_credits_position)
|
|
{
|
|
case START:
|
|
Credit_text_parts.insert(Credit_text_parts.begin(), fs2_open_credit_text);
|
|
break;
|
|
|
|
case END:
|
|
Credit_text_parts.push_back(fs2_open_credit_text);
|
|
break;
|
|
|
|
default:
|
|
Error(LOCATION, "Unimplemented credits position %d. Get a coder!", (int) SCP_credits_position);
|
|
break;
|
|
}
|
|
}
|
|
|
|
int ch;
|
|
SCP_vector<SCP_string>::iterator iter;
|
|
|
|
for (iter = Credit_text_parts.begin(); iter != Credit_text_parts.end(); ++iter)
|
|
{
|
|
for (SCP_string::iterator ii = iter->begin(); ii != iter->end(); ++ii)
|
|
{
|
|
ch = *ii;
|
|
switch (ch)
|
|
{
|
|
case -4:
|
|
ch = 129;
|
|
break;
|
|
|
|
case -28:
|
|
ch = 132;
|
|
break;
|
|
|
|
case -10:
|
|
ch = 148;
|
|
break;
|
|
|
|
case -23:
|
|
ch = 130;
|
|
break;
|
|
|
|
case -30:
|
|
ch = 131;
|
|
break;
|
|
|
|
case -25:
|
|
ch = 135;
|
|
break;
|
|
|
|
case -21:
|
|
ch = 137;
|
|
break;
|
|
|
|
case -24:
|
|
ch = 138;
|
|
break;
|
|
|
|
case -17:
|
|
ch = 139;
|
|
break;
|
|
|
|
case -18:
|
|
ch = 140;
|
|
break;
|
|
|
|
case -60:
|
|
ch = 142;
|
|
break;
|
|
|
|
case -55:
|
|
ch = 144;
|
|
break;
|
|
|
|
case -12:
|
|
ch = 147;
|
|
break;
|
|
|
|
case -14:
|
|
ch = 149;
|
|
break;
|
|
|
|
case -5:
|
|
ch = 150;
|
|
break;
|
|
|
|
case -7:
|
|
ch = 151;
|
|
break;
|
|
|
|
case -42:
|
|
ch = 153;
|
|
break;
|
|
|
|
case -36:
|
|
ch = 154;
|
|
break;
|
|
|
|
case -31:
|
|
ch = 160;
|
|
break;
|
|
|
|
case -19:
|
|
ch = 161;
|
|
break;
|
|
|
|
case -13:
|
|
ch = 162;
|
|
break;
|
|
|
|
case -6:
|
|
ch = 163;
|
|
break;
|
|
|
|
case -32:
|
|
ch = 133;
|
|
break;
|
|
|
|
case -22:
|
|
ch = 136;
|
|
break;
|
|
|
|
case -20:
|
|
ch = 141;
|
|
break;
|
|
}
|
|
|
|
*ii = (char) ch;
|
|
}
|
|
}
|
|
|
|
int temp_h;
|
|
int h = 0;
|
|
|
|
for (iter = Credit_text_parts.begin(); iter != Credit_text_parts.end(); ++iter)
|
|
{
|
|
gr_get_string_size(NULL, &temp_h, iter->c_str(), (int)iter->length());
|
|
|
|
h = h + temp_h;
|
|
}
|
|
|
|
Credit_start_pos = i2fl(Credits_text_coords[gr_screen.res][CREDITS_H_COORD]);
|
|
Credit_stop_pos = -i2fl(h);
|
|
Credit_position = Credit_start_pos;
|
|
|
|
Ui_window.create(0, 0, gr_screen.max_w_unscaled, gr_screen.max_h_unscaled, 0);
|
|
Ui_window.set_mask_bmap(Credits_bitmap_mask_fname[gr_screen.res]);
|
|
common_set_interface_palette("InterfacePalette"); // set the interface palette
|
|
|
|
for (i=0; i<NUM_BUTTONS; i++) {
|
|
b = &Buttons[i][gr_screen.res];
|
|
|
|
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);
|
|
}
|
|
|
|
// add some text
|
|
Ui_window.add_XSTR("Technical Database", 1055, Buttons[TECH_DATABASE_BUTTON][gr_screen.res].xt, Buttons[TECH_DATABASE_BUTTON][gr_screen.res].yt, &Buttons[TECH_DATABASE_BUTTON][gr_screen.res].button, UI_XSTR_COLOR_GREEN);
|
|
Ui_window.add_XSTR("Mission Simulator", 1056, Buttons[SIMULATOR_BUTTON][gr_screen.res].xt, Buttons[SIMULATOR_BUTTON][gr_screen.res].yt, &Buttons[SIMULATOR_BUTTON][gr_screen.res].button, UI_XSTR_COLOR_GREEN);
|
|
Ui_window.add_XSTR("Cutscenes", 1057, Buttons[CUTSCENES_BUTTON][gr_screen.res].xt, Buttons[CUTSCENES_BUTTON][gr_screen.res].yt, &Buttons[CUTSCENES_BUTTON][gr_screen.res].button, UI_XSTR_COLOR_GREEN);
|
|
Ui_window.add_XSTR("Credits", 1058, Buttons[CREDITS_BUTTON][gr_screen.res].xt, Buttons[CREDITS_BUTTON][gr_screen.res].yt, &Buttons[CREDITS_BUTTON][gr_screen.res].button, UI_XSTR_COLOR_GREEN);
|
|
Ui_window.add_XSTR("Exit", 1420, Buttons[EXIT_BUTTON][gr_screen.res].xt, Buttons[EXIT_BUTTON][gr_screen.res].yt, &Buttons[EXIT_BUTTON][gr_screen.res].button, UI_XSTR_COLOR_PINK);
|
|
|
|
if (Player->flags & PLAYER_FLAGS_IS_MULTI) {
|
|
Buttons[SIMULATOR_BUTTON][gr_screen.res].button.disable();
|
|
Buttons[CUTSCENES_BUTTON][gr_screen.res].button.disable();
|
|
}
|
|
|
|
Buttons[EXIT_BUTTON][gr_screen.res].button.set_hotkey(KEY_CTRLED | KEY_ENTER);
|
|
|
|
Background_bitmap = bm_load(Credits_bitmap_fname[gr_screen.res]);
|
|
|
|
Credits_bmps.resize(Credits_num_images);
|
|
for (i=0; i<Credits_num_images; i++) {
|
|
Credits_bmps[i] = -1;
|
|
}
|
|
}
|
|
|
|
void credits_close()
|
|
{
|
|
int i;
|
|
|
|
for (i=0; i<Credits_num_images; i++) {
|
|
if (Credits_bmps[i] >= 0){
|
|
bm_release(Credits_bmps[i]);
|
|
}
|
|
}
|
|
Credits_bmps.clear();
|
|
|
|
credits_stop_music(true);
|
|
|
|
Credit_text_parts.clear();
|
|
|
|
if (Background_bitmap){
|
|
bm_release(Background_bitmap);
|
|
}
|
|
|
|
Ui_window.destroy();
|
|
common_free_interface_palette(); // restore game palette
|
|
}
|
|
|
|
void credits_do_frame(float frametime)
|
|
{
|
|
GR_DEBUG_SCOPE("Credits do frame");
|
|
|
|
int i, k, next, percent, bm1, bm2;
|
|
int bx1, by1, bw1, bh1;
|
|
int bx2, by2, bw2, bh2;
|
|
|
|
// Use this id to trigger the start of music playing on the credits screen
|
|
if ( timestamp_elapsed(Credits_music_begin_timestamp) ) {
|
|
Credits_music_begin_timestamp = 0;
|
|
credits_start_music();
|
|
}
|
|
|
|
k = Ui_window.process();
|
|
switch (k) {
|
|
case KEY_ESC:
|
|
gameseq_post_event(GS_EVENT_MAIN_MENU);
|
|
key_flush();
|
|
break;
|
|
|
|
case KEY_CTRLED | KEY_UP:
|
|
case KEY_SHIFTED | KEY_TAB:
|
|
if ( !(Player->flags & PLAYER_FLAGS_IS_MULTI) ) {
|
|
credits_screen_button_pressed(CUTSCENES_BUTTON);
|
|
break;
|
|
}
|
|
// else, react like tab key.
|
|
FALLTHROUGH;
|
|
|
|
case KEY_CTRLED | KEY_DOWN:
|
|
case KEY_TAB:
|
|
credits_screen_button_pressed(TECH_DATABASE_BUTTON);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
} // end switch
|
|
|
|
for (i=0; i<NUM_BUTTONS; i++){
|
|
if (Buttons[i][gr_screen.res].button.pressed()){
|
|
if (credits_screen_button_pressed(i)){
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
gr_reset_clip();
|
|
GR_MAYBE_CLEAR_RES(Background_bitmap);
|
|
if (Background_bitmap >= 0) {
|
|
gr_set_bitmap(Background_bitmap);
|
|
gr_bitmap(0, 0, GR_RESIZE_MENU);
|
|
}
|
|
|
|
percent = (int) (100.0f - (Credits_artwork_display_time - Credits_counter) * 100.0f / Credits_artwork_fade_time);
|
|
if (percent < 0){
|
|
percent = 0;
|
|
}
|
|
|
|
next = Credits_artwork_index + 1;
|
|
if (next >= Credits_num_images){
|
|
next = 0;
|
|
}
|
|
|
|
if (Credits_bmps[Credits_artwork_index] < 0) {
|
|
char buf[40];
|
|
|
|
if (gr_screen.res == GR_1024) {
|
|
sprintf(buf, NOX("2_CrIm%.2d"), Credits_artwork_index);
|
|
} else {
|
|
sprintf(buf, NOX("CrIm%.2d"), Credits_artwork_index);
|
|
}
|
|
Credits_bmps[Credits_artwork_index] = bm_load(buf);
|
|
}
|
|
|
|
if (Credits_bmps[next] < 0) {
|
|
char buf[40];
|
|
|
|
if (gr_screen.res == GR_1024) {
|
|
sprintf(buf, NOX("2_CrIm%.2d"), next);
|
|
} else {
|
|
sprintf(buf, NOX("CrIm%.2d"), next);
|
|
}
|
|
Credits_bmps[next] = bm_load(buf);
|
|
}
|
|
|
|
bm1 = Credits_bmps[Credits_artwork_index];
|
|
bm2 = Credits_bmps[next];
|
|
|
|
if((bm1 != -1) && (bm2 != -1)){
|
|
GR_DEBUG_SCOPE("Render credits bitmap");
|
|
|
|
Assert(percent >= 0 && percent <= 100);
|
|
|
|
// get width and height
|
|
bm_get_info(bm1, &bw1, &bh1, NULL, NULL, NULL);
|
|
bm_get_info(bm2, &bw2, &bh2, NULL, NULL, NULL);
|
|
|
|
// determine where to draw the coords
|
|
bx1 = Credits_image_coords[gr_screen.res][CREDITS_X_COORD] + ((Credits_image_coords[gr_screen.res][CREDITS_W_COORD] - bw1)/2);
|
|
by1 = Credits_image_coords[gr_screen.res][CREDITS_Y_COORD] + ((Credits_image_coords[gr_screen.res][CREDITS_H_COORD] - bh1)/2);
|
|
bx2 = Credits_image_coords[gr_screen.res][CREDITS_X_COORD] + ((Credits_image_coords[gr_screen.res][CREDITS_W_COORD] - bw2)/2);
|
|
by2 = Credits_image_coords[gr_screen.res][CREDITS_Y_COORD] + ((Credits_image_coords[gr_screen.res][CREDITS_H_COORD] - bh2)/2);
|
|
|
|
auto alpha = (float)percent / 100.0f;
|
|
|
|
gr_set_bitmap(bm1, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, 1.0f - alpha);
|
|
gr_bitmap(bx1, by1, GR_RESIZE_MENU);
|
|
|
|
gr_set_bitmap(bm2, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, alpha);
|
|
gr_bitmap(bx2, by2, GR_RESIZE_MENU);
|
|
}
|
|
|
|
Ui_window.draw();
|
|
|
|
for (i=TECH_DATABASE_BUTTON; i<=CREDITS_BUTTON; i++){
|
|
if (Buttons[i][gr_screen.res].button.button_down()){
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i > CREDITS_BUTTON){
|
|
Buttons[CREDITS_BUTTON][gr_screen.res].button.draw_forced(2);
|
|
}
|
|
|
|
gr_set_clip(Credits_text_coords[gr_screen.res][CREDITS_X_COORD], Credits_text_coords[gr_screen.res][CREDITS_Y_COORD], Credits_text_coords[gr_screen.res][CREDITS_W_COORD], Credits_text_coords[gr_screen.res][CREDITS_H_COORD], GR_RESIZE_MENU);
|
|
font::set_font(font::FONT1);
|
|
gr_set_color_fast(&Color_normal);
|
|
|
|
int y_offset = 0;
|
|
for (SCP_vector<SCP_string>::iterator iter = Credit_text_parts.begin(); iter != Credit_text_parts.end(); ++iter)
|
|
{
|
|
size_t currentPos = 0;
|
|
size_t lineEnd;
|
|
do
|
|
{
|
|
int height;
|
|
int width;
|
|
lineEnd = iter->find('\n', currentPos);
|
|
|
|
auto length = lineEnd - currentPos;
|
|
if (lineEnd == SCP_string::npos)
|
|
{
|
|
length = std::numeric_limits<size_t>::max();
|
|
}
|
|
|
|
gr_get_string_size(&width, &height, iter->c_str() + currentPos, static_cast<int>(length));
|
|
// Check if the text part is actually visible
|
|
if (Credit_position + y_offset + height > 0.0f)
|
|
{
|
|
float x = static_cast<float>((gr_screen.clip_width_unscaled - width) / 2);
|
|
gr_string(x, Credit_position + y_offset, iter->c_str() + currentPos, GR_RESIZE_MENU, static_cast<int>(length));
|
|
}
|
|
|
|
y_offset += height;
|
|
currentPos = lineEnd + 1;
|
|
} while (lineEnd < iter->length() && lineEnd != SCP_string::npos);
|
|
}
|
|
|
|
int temp_time;
|
|
temp_time = timer_get_milliseconds();
|
|
|
|
Credits_frametime = temp_time - Credits_last_time;
|
|
Credits_last_time = temp_time;
|
|
timestamp_inc(i2f(Credits_frametime) / TIMESTAMP_FREQUENCY);
|
|
|
|
float fl_frametime = i2fl(Credits_frametime) / 1000.f;
|
|
if (keyd_pressed[KEY_LSHIFT]) {
|
|
Credit_position -= fl_frametime * Credits_scroll_rate * 4.0f;
|
|
} else {
|
|
Credit_position -= fl_frametime * Credits_scroll_rate;
|
|
}
|
|
|
|
if (Credit_position < Credit_stop_pos){
|
|
Credit_position = Credit_start_pos;
|
|
}
|
|
|
|
Credits_counter += fl_frametime;
|
|
while (Credits_counter >= Credits_artwork_display_time) {
|
|
Credits_counter -= Credits_artwork_display_time;
|
|
Credits_artwork_index = next;
|
|
}
|
|
|
|
gr_flip();
|
|
}
|