2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
4 * All source code herein is the property of Volition, Inc. You may not sell
5 * or otherwise commercially exploit the source or things you created based on
10 * $Logfile: /Freespace2/code/MenuUI/Credits.cpp $
15 * C source file for displaying game credits
18 * Revision 1.9 2005/03/29 02:18:47 taylor
19 * Various 64-bit platform fixes
20 * Fix compiler errors with MAKE_FS1 and fix gr_set_bitmap() too
21 * Make sure that turrets can fire at asteroids for FS1 (needed for a couple missions)
22 * Streaming audio support (big thanks to Pierre Willenbrock!!)
23 * Removed dependance on strings.tbl for FS1 since we don't actually need it now
25 * Revision 1.8 2004/09/20 01:31:44 theoddone33
28 * Revision 1.7 2003/08/03 16:10:29 taylor
29 * cleanup; compile warning fixes
31 * Revision 1.6 2003/06/11 18:30:32 taylor
34 * Revision 1.5 2003/05/25 02:30:42 taylor
37 * Revision 1.4 2002/06/09 04:41:22 relnev
38 * added copyright header
40 * Revision 1.3 2002/05/27 22:43:02 theoddone33
41 * Fix more glide symbols
43 * Revision 1.2 2002/05/07 03:16:46 theoddone33
44 * The Great Newline Fix
46 * Revision 1.1.1.1 2002/05/03 03:28:09 root
50 * 20 9/14/99 5:14a Dave
51 * Fixed credits drawing in Glide.
53 * 19 9/13/99 1:53p Dave
54 * Fixed completely brain dead code in credits_init().
56 * 18 9/09/99 10:55a Jefff
58 * 17 9/03/99 11:45a Jefff
60 * 16 9/01/99 5:28p Jefff
61 * hi res art shows up now
63 * 15 9/01/99 4:20p Jefff
66 * 14 9/01/99 12:19p Jefff
67 * text splitting for long lines
69 * 13 7/19/99 2:13p Dave
70 * Added some new strings for Heiko.
72 * 12 2/03/99 6:06p Dave
73 * Groundwork for FS2 PXO usertracker support. Gametracker support next.
75 * 11 2/03/99 11:44a Dave
76 * Fixed d3d transparent textures.
78 * 10 2/01/99 5:55p Dave
79 * Removed the idea of explicit bitmaps for buttons. Fixed text
80 * highlighting for disabled gadgets.
82 * 9 1/30/99 9:01p Dave
85 * 8 1/30/99 5:08p Dave
86 * More new hi-res stuff.Support for nice D3D textures.
88 * 7 1/29/99 12:47a Dave
89 * Put in sounds for beam weapon. A bunch of interface screens (tech
92 * 6 1/28/99 1:46a Dave
93 * Updated coords and bitmaps.
95 * 5 1/14/99 5:15p Neilk
96 * changed credits, command debrief interfaces to high resolution support
98 * 4 11/20/98 4:08p Dave
99 * Fixed flak effect in multiplayer.
101 * 3 10/13/98 9:28a Dave
102 * Started neatening up freespace.h. Many variables renamed and
103 * reorganized. Added AlphaColors.[h,cpp]
105 * 2 10/07/98 10:53a Dave
108 * 1 10/07/98 10:49a Dave
110 * 18 6/19/98 3:51p Lawrance
111 * deal with foreign chars in the credits
113 * 17 6/01/98 11:43a John
114 * JAS & MK: Classified all strings for localization.
116 * 16 5/24/98 9:01p Lawrance
117 * Add commit sounds when accept is pressed
119 * 15 5/20/98 1:04p Hoffoss
120 * Made credits screen use new artwork and removed rating field usage from
121 * Fred (a goal struct member).
123 * 14 5/12/98 4:17p Hoffoss
124 * Make ctrl-arrows (up/down) switch between tech room screens.
126 * 13 5/12/98 11:21a Hoffoss
127 * Disabled cutscene screen and simulator room.
129 * 12 5/11/98 8:04p Hoffoss
132 * 11 4/22/98 3:35p John
133 * String externalization marking
135 * 10 4/22/98 10:46a Hoffoss
136 * Added images to credits screen.
138 * 9 4/21/98 7:07p Hoffoss
139 * Fixed problem where when switching screens flashes old tab hilight once
140 * before switching to new state.
142 * 8 4/17/98 3:28p Hoffoss
143 * Added new credits screen code.
145 * 7 3/05/98 11:15p Hoffoss
146 * Changed non-game key checking to use game_check_key() instead of
149 * 6 2/22/98 12:19p John
150 * Externalized some strings
152 * 5 1/05/98 2:30p John
153 * made credits.tbl display
155 * 4 9/19/97 5:14p Lawrance
156 * use new naming convention for spooled music
158 * 3 8/31/97 6:38p Lawrance
159 * pass in frametime to do_frame loop
161 * 2 4/22/97 11:06a Lawrance
162 * credits music playing, credits screen is a separate state
169 #include "gamesequence.h"
176 #include "audiostr.h"
177 #include "eventmusic.h" /* for Master_event_music_volume */
180 #include "missionscreencommon.h"
182 #include "freespace.h"
183 #include "alphacolors.h"
184 #include "localize.h"
186 #define CREDITS_MUSIC_DELAY 2000
187 #define CREDITS_SCROLL_RATE 15.0f
188 #define CREDITS_ARTWORK_DISPLAY_TIME 9.0f
189 #define CREDITS_ARTWORK_FADE_TIME 1.0f
191 #define NUM_BUTTONS 5
192 #define NUM_IMAGES 46
194 #define TECH_DATABASE_BUTTON 0
195 #define SIMULATOR_BUTTON 1
196 #define CUTSCENES_BUTTON 2
197 #define CREDITS_BUTTON 3
198 #define EXIT_BUTTON 4
200 // inidicies for coordinates
201 #define CREDITS_X_COORD 0
202 #define CREDITS_Y_COORD 1
203 #define CREDITS_W_COORD 2
204 #define CREDITS_H_COORD 3
206 static const char* Credits_bitmap_fname[GR_NUM_RESOLUTIONS] = {
211 static const char* Credits_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
212 "Credits-M", // GR_640
216 int Credits_image_coords[GR_NUM_RESOLUTIONS][4] = {
221 219, 15, 394, 286 // GR_640
225 351, 25, 629, 455 // GR_1024
230 int Credits_text_coords[GR_NUM_RESOLUTIONS][4] = {
235 26, 316, 482, 157 // GR_640
239 144, 507, 568, 249 // GR_640
243 struct credits_screen_buttons {
244 const char *filename;
247 UI_BUTTON button; // because we have a class inside this struct, we need the constructor below..
249 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) {}
252 static int Background_bitmap;
254 static int CreditsWin01 = -1;
255 static int CreditsWin02 = -1;
256 static int CreditsWin03 = -1;
257 static int CreditsWin04 = -1;
260 static UI_WINDOW Ui_window;
262 static credits_screen_buttons Buttons[NUM_BUTTONS][GR_NUM_RESOLUTIONS] = {
266 credits_screen_buttons("TDB_00", 0, 0, -1, -1, 0), // GR_640
267 credits_screen_buttons("2_TDB_00", 12, 5, 59, 12, 0) // GR_1024
270 credits_screen_buttons("TDB_01", 0, 19, -1, -1, 1), // GR_640
271 credits_screen_buttons("2_TDB_01", 12, 31, 59, 37, 1) // GR_1024
274 credits_screen_buttons("TDB_02", 0, 35, -1, -1, 2), // GR_640
275 credits_screen_buttons("2_TDB_02", 12, 56, 59, 62, 2) // GR_1024
278 credits_screen_buttons("TDB_03", 0, 56, -1, -1, 3), // GR_640
279 credits_screen_buttons("2_TDB_03", 12, 81, 59, 88, 3) // GR_1024
282 credits_screen_buttons("CRB_04", 561, 411, -1, -1, 4), // GR_640
283 credits_screen_buttons("2_CRB_04", 914, 681, 953, 68, 4) // GR_1024
287 credits_screen_buttons("TDB_00", 7, 3, 37, 7, 0), // GR_640
288 credits_screen_buttons("2_TDB_00", 12, 5, 59, 12, 0) // GR_1024
291 credits_screen_buttons("TDB_01", 7, 18, 37, 23, 1), // GR_640
292 credits_screen_buttons("2_TDB_01", 12, 31, 59, 37, 1) // GR_1024
295 credits_screen_buttons("TDB_02", 7, 34, 37, 38, 2), // GR_640
296 credits_screen_buttons("2_TDB_02", 12, 56, 59, 62, 2) // GR_1024
299 credits_screen_buttons("TDB_03", 7, 49, 37, 54, 3), // GR_640
300 credits_screen_buttons("2_TDB_03", 12, 81, 59, 88, 3) // GR_1024
303 credits_screen_buttons("CRB_04", 571, 425, 588, 413, 4), // GR_640
304 credits_screen_buttons("2_CRB_04", 914, 681, 953, 668, 4) // GR_1024
310 static int Credits_music_handle = -1;
311 static int Credits_music_begin_timestamp;
313 static int Credits_frametime; // frametime of credits_do_frame() loop in ms
314 static int Credits_last_time; // timestamp used to calc frametime (in ms)
315 static float Credits_counter;
316 static int Credits_artwork_index;
317 static int Credits_bmps[NUM_IMAGES];
319 char *Credit_text = NULL;
321 // Positions for credits...
322 float Credit_start_pos, Credit_stop_pos, Credit_position = 0.0f;
324 void credits_stop_music()
326 if ( Credits_music_handle != -1 ) {
327 audiostream_close_file(Credits_music_handle);
328 Credits_music_handle = -1;
332 void credits_load_music(const char* fname)
334 if ( Credits_music_handle != -1 ){
339 Credits_music_handle = audiostream_open( fname, ASF_EVENTMUSIC );
343 void credits_start_music()
345 if (Credits_music_handle != -1) {
346 if ( !audiostream_is_playing(Credits_music_handle) ){
347 audiostream_play(Credits_music_handle, Master_event_music_volume);
350 nprintf(("Warning", "Cannot play credits music\n"));
354 int credits_screen_button_pressed(int n)
357 case TECH_DATABASE_BUTTON:
358 gamesnd_play_iface(SND_SWITCH_SCREENS);
359 gameseq_post_event(GS_EVENT_TECH_MENU);
362 case SIMULATOR_BUTTON:
363 gamesnd_play_iface(SND_SWITCH_SCREENS);
364 gameseq_post_event(GS_EVENT_SIMULATOR_ROOM);
367 case CUTSCENES_BUTTON:
368 gamesnd_play_iface(SND_SWITCH_SCREENS);
369 gameseq_post_event(GS_EVENT_GOTO_VIEW_CUTSCENES_SCREEN);
373 gamesnd_play_iface(SND_COMMIT_PRESSED);
374 gameseq_post_event(GS_EVENT_MAIN_MENU);
385 credits_screen_buttons *b;
387 char *linep1, *linep2;
389 int credits_spooled_music_index = event_music_get_spooled_music_index("Cinema");
390 if(credits_spooled_music_index != -1){
391 char *credits_wavfile_name = Spooled_music[credits_spooled_music_index].filename;
392 if(credits_wavfile_name != NULL){
393 credits_load_music(credits_wavfile_name);
397 // Use this id to trigger the start of music playing on the briefing screen
398 Credits_music_begin_timestamp = timestamp(CREDITS_MUSIC_DELAY);
400 Credits_frametime = 0;
401 Credits_last_time = timer_get_milliseconds();
405 // allocate enough space for credits text
406 CFILE *fp = cfopen( NOX("credits.tbl"), "rb" );
409 size = cfilelength(fp);
410 Credit_text = (char *) malloc(size + 200);
413 // open localization and parse
417 read_file_text("credits.tbl");
420 // keep reading everything in
421 SDL_strlcpy(Credit_text, "", size+200);
423 while(!check_for_string_raw("#end")){
429 // get the line count, probably a crappy way to do it but it's the best way i've
430 // found to step through the credits without crashing problems since there's no
431 // definite end line in FS1
432 while (*ugh && *ugh != EOF_CHAR) {
441 while(line_count > 0){
444 stuff_string_line(line, 511);
448 linep2 = split_str_once(linep1, Credits_text_coords[gr_screen.res][2]);
449 SDL_strlcat(Credit_text, linep1, size+200);
450 SDL_strlcat(Credit_text, "\n", size+200);
452 } while (linep2 != NULL);
454 } catch (parse_error_t rval) {
455 (void)rval; // suppress unused warning in release build
456 mprintf(("Error parsing 'credits.tbl'\nError code = %i.\n", (int)rval));
459 // close localization
462 Credit_text = (char *) malloc(25 + 200);
463 SDL_strlcpy(Credit_text, NOX("No credits available.\n"), 25+200);
467 for ( i = 0; Credit_text[i]; i++ ) {
570 Credit_text[i] = (char)ch;
573 gr_get_string_size(&w, &h, Credit_text);
575 Credit_start_pos = i2fl(Credits_text_coords[gr_screen.res][CREDITS_H_COORD]);
576 Credit_stop_pos = -i2fl(h);
577 Credit_position = Credit_start_pos;
579 Ui_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
580 Ui_window.set_mask_bmap(Credits_bitmap_mask_fname[gr_screen.res]);
581 common_set_interface_palette("InterfacePalette"); // set the interface palette
583 for (i=0; i<NUM_BUTTONS; i++) {
584 b = &Buttons[i][gr_screen.res];
586 b->button.create(&Ui_window, "", b->x, b->y, 60, 30, (i < 2), 1);
587 // set up callback for when a mouse first goes over a button
588 b->button.set_highlight_action(common_play_highlight_sound);
589 b->button.set_bmaps(b->filename);
590 b->button.link_hotspot(b->hotspot);
595 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);
596 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);
597 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);
598 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);
599 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);
602 if (Player->flags & PLAYER_FLAGS_IS_MULTI) {
603 Buttons[SIMULATOR_BUTTON][gr_screen.res].button.disable();
604 Buttons[CUTSCENES_BUTTON][gr_screen.res].button.disable();
607 Buttons[EXIT_BUTTON][gr_screen.res].button.set_hotkey(KEY_CTRLED | SDLK_RETURN);
609 Background_bitmap = bm_load(Credits_bitmap_fname[gr_screen.res]);
610 Credits_artwork_index = rand() % NUM_IMAGES;
611 for (i=0; i<NUM_IMAGES; i++){
612 Credits_bmps[i] = -1;
616 CreditsWin01 = bm_load(NOX("CreditsWin01"));
617 CreditsWin02 = bm_load(NOX("CreditsWin02"));
618 CreditsWin03 = bm_load(NOX("CreditsWin03"));
619 CreditsWin04 = bm_load(NOX("CreditsWin04"));
628 if (CreditsWin01 != -1){
629 bm_unload(CreditsWin01);
632 if (CreditsWin02 != -1){
633 bm_unload(CreditsWin02);
636 if (CreditsWin03 != -1){
637 bm_unload(CreditsWin03);
640 if (CreditsWin04 != -1){
641 bm_unload(CreditsWin04);
646 for (i=0; i<NUM_IMAGES; i++){
647 if (Credits_bmps[i] >= 0){
648 bm_unload(Credits_bmps[i]);
649 Credits_bmps[i] = -1;
653 credits_stop_music();
660 if (Background_bitmap){
661 bm_unload(Background_bitmap);
665 common_free_interface_palette(); // restore game palette
668 void credits_do_frame(float frametime)
670 int i, k, next, percent, bm1, bm2;
671 int bx1, by1, bw1, bh1;
672 int bx2, by2, bw2, bh2;
674 // Use this id to trigger the start of music playing on the credits screen
675 if ( timestamp_elapsed(Credits_music_begin_timestamp) ) {
676 Credits_music_begin_timestamp = 0;
677 credits_start_music();
680 k = Ui_window.process();
683 gameseq_post_event(GS_EVENT_MAIN_MENU);
687 case KEY_CTRLED | SDLK_UP:
688 case KEY_SHIFTED | SDLK_TAB:
689 if ( !(Player->flags & PLAYER_FLAGS_IS_MULTI) ) {
690 credits_screen_button_pressed(CUTSCENES_BUTTON);
693 // else, react like tab key.
695 case KEY_CTRLED | SDLK_DOWN:
697 credits_screen_button_pressed(TECH_DATABASE_BUTTON);
704 for (i=0; i<NUM_BUTTONS; i++){
705 if (Buttons[i][gr_screen.res].button.pressed()){
706 if (credits_screen_button_pressed(i)){
713 GR_MAYBE_CLEAR_RES(Background_bitmap);
714 if (Background_bitmap >= 0) {
715 gr_set_bitmap(Background_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
719 percent = (int) (100.0f - (CREDITS_ARTWORK_DISPLAY_TIME - Credits_counter) * 100.0f / CREDITS_ARTWORK_FADE_TIME);
724 next = Credits_artwork_index + 1;
725 if (next >= NUM_IMAGES){
729 if (Credits_bmps[Credits_artwork_index] < 0) {
732 if (gr_screen.res == GR_1024) {
733 SDL_snprintf(buf, SDL_arraysize(buf), NOX("2_CrIm%.2d"), Credits_artwork_index);
735 SDL_snprintf(buf, SDL_arraysize(buf), NOX("CrIm%.2d"), Credits_artwork_index);
737 Credits_bmps[Credits_artwork_index] = bm_load(buf);
740 if (Credits_bmps[next] < 0) {
743 if (gr_screen.res == GR_1024) {
744 SDL_snprintf(buf, SDL_arraysize(buf), NOX("2_CrIm%.2d"), Credits_artwork_index);
746 SDL_snprintf(buf, SDL_arraysize(buf), NOX("CrIm%.2d"), next);
748 Credits_bmps[next] = bm_load(buf);
751 bm1 = Credits_bmps[Credits_artwork_index];
752 bm2 = Credits_bmps[next];
754 if((bm1 != -1) && (bm2 != -1)){
755 SDL_assert(percent >= 0 && percent <= 100);
757 // get width and height
758 bm_get_info(bm1, &bw1, &bh1, NULL, NULL, NULL);
759 bm_get_info(bm2, &bw2, &bh2, NULL, NULL, NULL);
761 // determine where to draw the coords
762 bx1 = Credits_image_coords[gr_screen.res][CREDITS_X_COORD] + ((Credits_image_coords[gr_screen.res][CREDITS_W_COORD] - bw1)/2);
763 by1 = Credits_image_coords[gr_screen.res][CREDITS_Y_COORD] + ((Credits_image_coords[gr_screen.res][CREDITS_H_COORD] - bh1)/2);
764 bx2 = Credits_image_coords[gr_screen.res][CREDITS_X_COORD] + ((Credits_image_coords[gr_screen.res][CREDITS_W_COORD] - bw2)/2);
765 by2 = Credits_image_coords[gr_screen.res][CREDITS_Y_COORD] + ((Credits_image_coords[gr_screen.res][CREDITS_H_COORD] - bh2)/2);
767 gr_cross_fade(bm1, bm2, bx1, by1, bx2, by2, (float)percent / 100.0f);
771 if (CreditsWin01 != -1) {
772 gr_set_bitmap(CreditsWin01, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
776 if (CreditsWin02 != -1) {
777 gr_set_bitmap(CreditsWin02, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
781 if (CreditsWin03 != -1) {
782 gr_set_bitmap(CreditsWin03, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
786 if (CreditsWin04 != -1) {
787 gr_set_bitmap(CreditsWin04, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
794 for (i=TECH_DATABASE_BUTTON; i<=CREDITS_BUTTON; i++){
795 if (Buttons[i][gr_screen.res].button.button_down()){
800 if (i > CREDITS_BUTTON){
801 Buttons[CREDITS_BUTTON][gr_screen.res].button.draw_forced(2);
804 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]);
806 gr_set_color_fast(&Color_normal);
809 if ( Credit_position > 0 ) {
810 sy = fl2i(Credit_position+0.5f);
812 sy = fl2i(Credit_position-0.5f);
815 gr_string(0x8000, sy, Credit_text);
818 temp_time = timer_get_milliseconds();
820 Credits_frametime = temp_time - Credits_last_time;
821 Credits_last_time = temp_time;
822 timestamp_inc(Credits_frametime / 1000.0f);
824 float fl_frametime = i2fl(Credits_frametime) / 1000.f;
825 if (key_pressed(SDLK_LSHIFT)) {
826 Credit_position -= fl_frametime * CREDITS_SCROLL_RATE * 4.0f;
828 Credit_position -= fl_frametime * CREDITS_SCROLL_RATE;
831 if (Credit_position < Credit_stop_pos){
832 Credit_position = Credit_start_pos;
835 Credits_counter += fl_frametime;
836 while (Credits_counter >= CREDITS_ARTWORK_DISPLAY_TIME) {
837 Credits_counter -= CREDITS_ARTWORK_DISPLAY_TIME;
838 Credits_artwork_index = next;