]> icculus.org git repositories - taylor/freespace2.git/blob - src/menuui/credits.cpp
Freespace 1 support
[taylor/freespace2.git] / src / menuui / credits.cpp
1 /*
2  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3  *
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
6  * the source.
7  */
8
9 /*
10  * $Logfile: /Freespace2/code/MenuUI/Credits.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  *
15  * C source file for displaying game credits
16  *
17  * $Log$
18  * Revision 1.5  2003/05/25 02:30:42  taylor
19  * Freespace 1 support
20  *
21  * Revision 1.4  2002/06/09 04:41:22  relnev
22  * added copyright header
23  *
24  * Revision 1.3  2002/05/27 22:43:02  theoddone33
25  * Fix more glide symbols
26  *
27  * Revision 1.2  2002/05/07 03:16:46  theoddone33
28  * The Great Newline Fix
29  *
30  * Revision 1.1.1.1  2002/05/03 03:28:09  root
31  * Initial import.
32  *
33  * 
34  * 20    9/14/99 5:14a Dave
35  * Fixed credits drawing in Glide.
36  * 
37  * 19    9/13/99 1:53p Dave
38  * Fixed completely brain dead code in credits_init().
39  * 
40  * 18    9/09/99 10:55a Jefff
41  * 
42  * 17    9/03/99 11:45a Jefff
43  * 
44  * 16    9/01/99 5:28p Jefff
45  * hi res art shows up now
46  * 
47  * 15    9/01/99 4:20p Jefff
48  * mo' pictures
49  * 
50  * 14    9/01/99 12:19p Jefff
51  * text splitting for long lines
52  * 
53  * 13    7/19/99 2:13p Dave
54  * Added some new strings for Heiko.
55  * 
56  * 12    2/03/99 6:06p Dave
57  * Groundwork for FS2 PXO usertracker support.  Gametracker support next.
58  * 
59  * 11    2/03/99 11:44a Dave
60  * Fixed d3d transparent textures.
61  * 
62  * 10    2/01/99 5:55p Dave
63  * Removed the idea of explicit bitmaps for buttons. Fixed text
64  * highlighting for disabled gadgets.
65  * 
66  * 9     1/30/99 9:01p Dave
67  * Coord fixes.
68  * 
69  * 8     1/30/99 5:08p Dave
70  * More new hi-res stuff.Support for nice D3D textures.
71  * 
72  * 7     1/29/99 12:47a Dave
73  * Put in sounds for beam weapon. A bunch of interface screens (tech
74  * database stuff).
75  * 
76  * 6     1/28/99 1:46a Dave
77  * Updated coords and bitmaps.
78  * 
79  * 5     1/14/99 5:15p Neilk
80  * changed credits, command debrief interfaces to high resolution support
81  * 
82  * 4     11/20/98 4:08p Dave
83  * Fixed flak effect in multiplayer.
84  * 
85  * 3     10/13/98 9:28a Dave
86  * Started neatening up freespace.h. Many variables renamed and
87  * reorganized. Added AlphaColors.[h,cpp]
88  * 
89  * 2     10/07/98 10:53a Dave
90  * Initial checkin.
91  * 
92  * 1     10/07/98 10:49a Dave
93  * 
94  * 18    6/19/98 3:51p Lawrance
95  * deal with foreign chars in the credits
96  * 
97  * 17    6/01/98 11:43a John
98  * JAS & MK:  Classified all strings for localization.
99  * 
100  * 16    5/24/98 9:01p Lawrance
101  * Add commit sounds when accept is pressed
102  * 
103  * 15    5/20/98 1:04p Hoffoss
104  * Made credits screen use new artwork and removed rating field usage from
105  * Fred (a goal struct member).
106  * 
107  * 14    5/12/98 4:17p Hoffoss
108  * Make ctrl-arrows (up/down) switch between tech room screens.
109  * 
110  * 13    5/12/98 11:21a Hoffoss
111  * Disabled cutscene screen and simulator room.
112  * 
113  * 12    5/11/98 8:04p Hoffoss
114  * Fixed minor bugs.
115  * 
116  * 11    4/22/98 3:35p John
117  * String externalization marking
118  * 
119  * 10    4/22/98 10:46a Hoffoss
120  * Added images to credits screen.
121  * 
122  * 9     4/21/98 7:07p Hoffoss
123  * Fixed problem where when switching screens flashes old tab hilight once
124  * before switching to new state.
125  * 
126  * 8     4/17/98 3:28p Hoffoss
127  * Added new credits screen code.
128  * 
129  * 7     3/05/98 11:15p Hoffoss
130  * Changed non-game key checking to use game_check_key() instead of
131  * game_poll().
132  * 
133  * 6     2/22/98 12:19p John
134  * Externalized some strings
135  * 
136  * 5     1/05/98 2:30p John
137  * made credits.tbl display
138  * 
139  * 4     9/19/97 5:14p Lawrance
140  * use new naming convention for spooled music
141  * 
142  * 3     8/31/97 6:38p Lawrance
143  * pass in frametime to do_frame loop
144  * 
145  * 2     4/22/97 11:06a Lawrance
146  * credits music playing, credits screen is a separate state
147  *
148  * $NoKeywords: $
149  */
150
151 #include <stdlib.h>
152
153 #include "gamesequence.h"
154 #include "font.h"
155 #include "key.h"
156 #include "bmpman.h"
157 #include "2d.h"
158 #include "timer.h"
159 #include "gamesnd.h"
160 #include "audiostr.h"
161 #include "eventmusic.h" /* for Master_event_music_volume */
162 #include "cfile.h"
163 #include "ui.h"
164 #include "missionscreencommon.h"
165 #include "player.h"
166 #include "freespace.h"
167 #include "alphacolors.h"
168 #include "localize.h"
169
170 #define CREDITS_MUSIC_DELAY     2000
171 #define CREDITS_SCROLL_RATE     15.0f
172 #define CREDITS_ARTWORK_DISPLAY_TIME    9.0f
173 #define CREDITS_ARTWORK_FADE_TIME               1.0f
174
175 #define NUM_BUTTONS                             5
176 #define NUM_IMAGES                              46
177
178 #define TECH_DATABASE_BUTTON    0
179 #define SIMULATOR_BUTTON                1
180 #define CUTSCENES_BUTTON                2
181 #define CREDITS_BUTTON                  3
182 #define EXIT_BUTTON                             4
183
184 // inidicies for coordinates
185 #define CREDITS_X_COORD 0
186 #define CREDITS_Y_COORD 1
187 #define CREDITS_W_COORD 2
188 #define CREDITS_H_COORD 3
189
190 static char* Credits_bitmap_fname[GR_NUM_RESOLUTIONS] = {
191         "Credits",                      // GR_640
192         "2_Credits"
193 };
194
195 static char* Credits_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
196         "Credits-M",                    // GR_640
197         "2_Credits-M"
198 };
199
200 int Credits_image_coords[GR_NUM_RESOLUTIONS][4] = {
201         {
202 #ifdef MAKE_FS1
203                 225, 15, 400, 292
204 #else
205                 219, 15, 394, 286                       // GR_640
206 #endif
207         },
208         {
209                 351, 25, 629, 455                       // GR_1024
210         }
211 };
212
213 // x, y, w, h
214 int Credits_text_coords[GR_NUM_RESOLUTIONS][4] = {
215         {
216 #ifdef MAKE_FS1
217                 46, 321, 450, 134
218 #else
219                 26, 316, 482, 157                       // GR_640
220 #endif
221         },
222         {
223                 144, 507, 568, 249                      // GR_640
224         }
225 };
226
227 struct credits_screen_buttons {
228         char *filename;
229         int x, y, xt, yt;
230         int hotspot;
231         UI_BUTTON button;  // because we have a class inside this struct, we need the constructor below..
232
233         credits_screen_buttons(char *name, int x1, int y1, int xt1, int yt1, int h) : filename(name), x(x1), y(y1), xt(xt1), yt(yt1), hotspot(h) {}
234 };
235
236 static int Background_bitmap;
237 #ifdef MAKE_FS1
238 static int CreditsWin01 = -1;
239 static int CreditsWin02 = -1;
240 static int CreditsWin03 = -1;
241 static int CreditsWin04 = -1;
242 #endif
243
244 static UI_WINDOW Ui_window;
245
246 static credits_screen_buttons Buttons[NUM_BUTTONS][GR_NUM_RESOLUTIONS] = {
247 //XSTR:OFF
248 #ifdef MAKE_FS1
249     {
250                         credits_screen_buttons("TDB_00", 0, 0, -1, -1, 0),              // GR_640
251                         credits_screen_buttons("2_TDB_00", 12, 5, 59, 12, 0)            // GR_1024
252     },
253     {
254                         credits_screen_buttons("TDB_01", 0, 19, -1, -1, 1),             // GR_640
255                         credits_screen_buttons("2_TDB_01", 12, 31, 59, 37, 1)           // GR_1024
256     },
257     {
258                         credits_screen_buttons("TDB_02", 0, 35, -1, -1, 2),             // GR_640
259                         credits_screen_buttons("2_TDB_02", 12, 56, 59, 62, 2)           // GR_1024
260     },
261     {
262                         credits_screen_buttons("TDB_03", 0, 56, -1, -1, 3),             // GR_640
263                         credits_screen_buttons("2_TDB_03", 12, 81, 59, 88, 3)           // GR_1024
264     },
265     {
266                         credits_screen_buttons("CRB_04", 561, 411, -1, -1, 4),  // GR_640
267                         credits_screen_buttons("2_CRB_04", 914, 681, 953, 68, 4)        // GR_1024
268     }
269 #else
270         {
271                         credits_screen_buttons("TDB_00", 7, 3, 37, 7, 0),                       // GR_640
272                         credits_screen_buttons("2_TDB_00", 12, 5, 59, 12, 0)                    // GR_1024
273         },
274         {
275                         credits_screen_buttons("TDB_01", 7, 18, 37, 23, 1),             // GR_640
276                         credits_screen_buttons("2_TDB_01", 12, 31, 59, 37, 1)           // GR_1024
277         },
278         {
279                         credits_screen_buttons("TDB_02", 7, 34, 37, 38, 2),             // GR_640
280                         credits_screen_buttons("2_TDB_02", 12, 56, 59, 62, 2)           // GR_1024
281         },
282         {
283                         credits_screen_buttons("TDB_03", 7, 49, 37, 54, 3),             // GR_640
284                         credits_screen_buttons("2_TDB_03", 12, 81, 59, 88, 3)           // GR_1024
285         },
286         {
287                         credits_screen_buttons("CRB_04", 571, 425, 588, 413, 4),        // GR_640
288                         credits_screen_buttons("2_CRB_04", 914, 681, 953, 668, 4)       // GR_1024
289         }
290 #endif
291 //XSTR:ON
292 };
293
294 static int      Credits_music_handle = -1;
295 static int      Credits_music_begin_timestamp;
296
297 static int      Credits_frametime;              // frametime of credits_do_frame() loop in ms
298 static int      Credits_last_time;              // timestamp used to calc frametime (in ms)
299 static float Credits_counter;
300 static int Credits_artwork_index;
301 static int Credits_bmps[NUM_IMAGES];
302
303 char *Credit_text = NULL;
304 int Credit_text_malloced = 0;                   // TRUE if credit_text was malloced
305
306 // Positions for credits...
307 float Credit_start_pos, Credit_stop_pos, Credit_position = 0.0f;
308
309 void credits_stop_music()
310 {
311         if ( Credits_music_handle != -1 ) {
312                 audiostream_close_file(Credits_music_handle);
313                 Credits_music_handle = -1;
314         }
315 }
316
317 void credits_load_music(char* fname)
318 {
319         if ( Credits_music_handle != -1 ){
320                 return;
321         }
322
323         if ( fname ){
324                 Credits_music_handle = audiostream_open( fname, ASF_EVENTMUSIC );
325         }
326 }
327
328 void credits_start_music()
329 {
330         if (Credits_music_handle != -1) {
331                 if ( !audiostream_is_playing(Credits_music_handle) ){
332                         audiostream_play(Credits_music_handle, Master_event_music_volume);
333                 }
334         } else {
335                 nprintf(("Warning", "Cannot play credits music\n"));
336         }
337 }
338
339 int credits_screen_button_pressed(int n)
340 {
341         switch (n) {
342         case TECH_DATABASE_BUTTON:
343                 gamesnd_play_iface(SND_SWITCH_SCREENS);
344                 gameseq_post_event(GS_EVENT_TECH_MENU);
345                 return 1;
346
347         case SIMULATOR_BUTTON:
348                 gamesnd_play_iface(SND_SWITCH_SCREENS);
349                 gameseq_post_event(GS_EVENT_SIMULATOR_ROOM);
350                 return 1;
351
352         case CUTSCENES_BUTTON:
353                 gamesnd_play_iface(SND_SWITCH_SCREENS);
354                 gameseq_post_event(GS_EVENT_GOTO_VIEW_CUTSCENES_SCREEN);
355                 return 1;
356
357         case EXIT_BUTTON:
358                 gamesnd_play_iface(SND_COMMIT_PRESSED);
359                 gameseq_post_event(GS_EVENT_MAIN_MENU);
360                 game_flush();
361                 break;
362         }
363
364         return 0;
365 }
366
367 void credits_init()
368 {
369         int i, w, h;
370         credits_screen_buttons *b;
371         char line[512] = "";    
372         char *linep1, *linep2;  
373
374         int credits_spooled_music_index = event_music_get_spooled_music_index("Cinema");        
375         if(credits_spooled_music_index != -1){
376                 char *credits_wavfile_name = Spooled_music[credits_spooled_music_index].filename;               
377                 if(credits_wavfile_name != NULL){
378                         credits_load_music(credits_wavfile_name);
379                 }
380         }
381
382         // Use this id to trigger the start of music playing on the briefing screen
383         Credits_music_begin_timestamp = timestamp(CREDITS_MUSIC_DELAY);
384
385         Credits_frametime = 0;
386         Credits_last_time = timer_get_milliseconds();
387
388         Credit_text = NULL;
389         Credit_text_malloced = 0;
390
391         // allocate enough space for credits text
392         CFILE *fp = cfopen( NOX("credits.tbl"), "rb" );
393         if(fp != NULL){
394                 int size;
395                 size = cfilelength(fp);
396                 Credit_text = (char *) malloc(size + 200);
397                 cfclose(fp);
398
399                 // open localization and parse
400                 lcl_ext_open();
401                 read_file_text("credits.tbl");
402                 reset_parse();
403
404                 // keep reading everything in
405                 strcpy(Credit_text,"");         
406 #ifndef MAKE_FS1
407                 while(!check_for_string_raw("#end")){
408 #else
409                 char *ugh = Mp;
410                 char ch;
411                 int line_count = 0;
412
413                 // get the line count, probably a crappy way to do it but it's the best way i've
414                 // found to step through the credits without crashing problems since there's no
415                 // definite end line in FS1
416                 while (*ugh && *ugh != EOF_CHAR) {
417                         ch = *ugh;
418
419                         if (ch == '\n'){
420                                 line_count++;
421                         }
422                         ugh++;
423                 }
424
425                 while(line_count > 0){
426                         line_count--;
427 #endif
428                         stuff_string_line(line, 511);
429                         linep1 = line;
430
431                         do {
432                                 linep2 = split_str_once(linep1, Credits_text_coords[gr_screen.res][2]);
433                                 strcat(Credit_text, linep1);
434                                 strcat(Credit_text, "\n");                      
435                                 linep1 = linep2;
436                         } while (linep2 != NULL);
437                 }               
438
439                 // close localization
440                 lcl_ext_close();        
441         } else {
442                 Credit_text = NOX("No credits available.\n");
443         }       
444
445         int ch;
446         for ( i = 0; Credit_text[i]; i++ ) {
447                         ch = Credit_text[i];
448                         switch (ch) {
449                         case -4:
450                                 ch = 129;
451                                 break;
452
453                         case -28:
454                                 ch = 132;
455                                 break;
456
457                         case -10:
458                                 ch = 148;
459                                 break;
460
461                         case -23:
462                                 ch = 130;
463                                 break;
464
465                         case -30:
466                                 ch = 131;
467                                 break;
468
469                         case -25:
470                                 ch = 135;
471                                 break;
472
473                         case -21:
474                                 ch = 137;
475                                 break;
476
477                         case -24:
478                                 ch = 138;
479                                 break;
480
481                         case -17:
482                                 ch = 139;
483                                 break;
484
485                         case -18:
486                                 ch = 140;
487                                 break;
488
489                         case -60:
490                                 ch = 142;
491                                 break;
492
493                         case -55:
494                                 ch = 144;
495                                 break;
496
497                         case -12:
498                                 ch = 147;
499                                 break;
500
501                         case -14:
502                                 ch = 149;
503                                 break;
504
505                         case -5:
506                                 ch = 150;
507                                 break;
508
509                         case -7:
510                                 ch = 151;
511                                 break;
512
513                         case -42:
514                                 ch = 153;
515                                 break;
516
517                         case -36:
518                                 ch = 154;
519                                 break;
520
521                         case -31:
522                                 ch = 160;
523                                 break;
524
525                         case -19:
526                                 ch = 161;
527                                 break;
528
529                         case -13:
530                                 ch = 162;
531                                 break;
532
533                         case -6:
534                                 ch = 163;
535                                 break;
536
537                         case -32:
538                                 ch = 133;
539                                 break;
540
541                         case -22:
542                                 ch = 136;
543                                 break;
544
545                         case -20:
546                                 ch = 141;
547                                 break;
548                         }
549                         Credit_text[i] = (char)ch;
550         }
551
552         gr_get_string_size(&w, &h, Credit_text);
553
554         Credit_start_pos = i2fl(Credits_text_coords[gr_screen.res][CREDITS_H_COORD]);
555         Credit_stop_pos = -i2fl(h);
556         Credit_position = Credit_start_pos;
557
558         Ui_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
559         Ui_window.set_mask_bmap(Credits_bitmap_mask_fname[gr_screen.res]);
560         common_set_interface_palette("InterfacePalette");  // set the interface palette
561
562         for (i=0; i<NUM_BUTTONS; i++) {
563                 b = &Buttons[i][gr_screen.res];
564
565                 b->button.create(&Ui_window, "", b->x, b->y, 60, 30, (i < 2), 1);
566                 // set up callback for when a mouse first goes over a button
567                 b->button.set_highlight_action(common_play_highlight_sound);
568                 b->button.set_bmaps(b->filename);
569                 b->button.link_hotspot(b->hotspot);
570         }
571
572 #ifndef MAKE_FS1
573         // add some text
574         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);
575         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);
576         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);
577         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);
578         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);
579 #endif
580
581         if (Player->flags & PLAYER_FLAGS_IS_MULTI) {
582                 Buttons[SIMULATOR_BUTTON][gr_screen.res].button.disable();
583                 Buttons[CUTSCENES_BUTTON][gr_screen.res].button.disable();
584         }
585
586         Buttons[EXIT_BUTTON][gr_screen.res].button.set_hotkey(KEY_CTRLED | KEY_ENTER);
587
588         Background_bitmap = bm_load(Credits_bitmap_fname[gr_screen.res]);
589         Credits_artwork_index = rand() % NUM_IMAGES;
590         for (i=0; i<NUM_IMAGES; i++){
591                 Credits_bmps[i] = -1;
592         }
593
594 #ifdef MAKE_FS1
595         CreditsWin01 = bm_load(NOX("CreditsWin01"));
596         CreditsWin02 = bm_load(NOX("CreditsWin02"));
597         CreditsWin03 = bm_load(NOX("CreditsWin03"));
598         CreditsWin04 = bm_load(NOX("CreditsWin04"));
599 #endif
600 }
601
602 void credits_close()
603 {       
604         int i;
605
606 #ifdef MAKE_FS1
607         if (CreditsWin01 != -1){
608                 bm_unload(CreditsWin01);
609                 CreditsWin01 = -1;
610         }
611         if (CreditsWin02 != -1){
612                 bm_unload(CreditsWin02);
613                 CreditsWin02 = -1;
614         }
615         if (CreditsWin03 != -1){
616                 bm_unload(CreditsWin03);
617                 CreditsWin03 = -1;
618         }
619         if (CreditsWin04 != -1){
620                 bm_unload(CreditsWin04);
621                 CreditsWin04 = -1;
622         }
623 #endif
624
625         for (i=0; i<NUM_IMAGES; i++){
626                 if (Credits_bmps[i] >= 0){
627                         bm_unload(Credits_bmps[i]);
628                         Credits_bmps[i] = -1;
629                 }
630         }       
631
632         credits_stop_music();
633
634         if (Credit_text) {
635                 if (Credit_text_malloced){
636                         free(Credit_text);
637                 }
638
639                 Credit_text = NULL;
640         }
641
642         if (Background_bitmap){
643                 bm_unload(Background_bitmap);
644         }
645
646         Ui_window.destroy();
647         common_free_interface_palette();                // restore game palette
648 }
649
650 void credits_do_frame(float frametime)
651 {
652         int i, k, next, percent, bm1, bm2;
653         int bx1, by1, bw1, bh1;
654         int bx2, by2, bw2, bh2;
655
656         // Use this id to trigger the start of music playing on the credits screen
657         if ( timestamp_elapsed(Credits_music_begin_timestamp) ) {
658                 Credits_music_begin_timestamp = 0;
659                 credits_start_music();
660         }
661
662         k = Ui_window.process();
663         switch (k) {
664         case KEY_ESC:
665                 gameseq_post_event(GS_EVENT_MAIN_MENU);
666                 key_flush();
667                 break;
668
669         case KEY_CTRLED | KEY_UP:
670         case KEY_SHIFTED | KEY_TAB:
671                 if ( !(Player->flags & PLAYER_FLAGS_IS_MULTI) ) {
672                         credits_screen_button_pressed(CUTSCENES_BUTTON);
673                         break;
674                 }
675                 // else, react like tab key.
676
677         case KEY_CTRLED | KEY_DOWN:
678         case KEY_TAB:
679                 credits_screen_button_pressed(TECH_DATABASE_BUTTON);
680                 break;
681
682         default:
683                 break;
684         } // end switch
685
686         for (i=0; i<NUM_BUTTONS; i++){
687                 if (Buttons[i][gr_screen.res].button.pressed()){
688                         if (credits_screen_button_pressed(i)){
689                                 return;
690                         }
691                 }
692         }
693
694         gr_reset_clip();        
695         GR_MAYBE_CLEAR_RES(Background_bitmap);
696         if (Background_bitmap >= 0) {
697                 gr_set_bitmap(Background_bitmap);
698                 gr_bitmap(0, 0);
699         } 
700
701         percent = (int) (100.0f - (CREDITS_ARTWORK_DISPLAY_TIME - Credits_counter) * 100.0f / CREDITS_ARTWORK_FADE_TIME);
702         if (percent < 0){
703                 percent = 0;
704         }
705
706         next = Credits_artwork_index + 1;
707         if (next >= NUM_IMAGES){
708                 next = 0;
709         }
710
711         if (Credits_bmps[Credits_artwork_index] < 0) {
712                 char buf[40];
713
714                 if (gr_screen.res == GR_1024) {
715                         sprintf(buf, NOX("2_CrIm%0.2d"), Credits_artwork_index);
716                 } else {
717                         sprintf(buf, NOX("CrIm%0.2d"), Credits_artwork_index);
718                 }
719                 Credits_bmps[Credits_artwork_index] = bm_load(buf);
720         }
721
722         if (Credits_bmps[next] < 0) {
723                 char buf[40];
724
725                 if (gr_screen.res == GR_1024) {
726                         sprintf(buf, NOX("2_CrIm%0.2d"), Credits_artwork_index);
727                 } else {
728                         sprintf(buf, NOX("CrIm%0.2d"), next);
729                 }
730                 Credits_bmps[next] = bm_load(buf);
731         }
732
733         bm1 = Credits_bmps[Credits_artwork_index];
734         bm2 = Credits_bmps[next];
735
736         if((bm1 != -1) && (bm2 != -1)){
737                 Assert(percent >= 0 && percent <= 100);
738
739                 // get width and height
740                 bm_get_info(bm1, &bw1, &bh1, NULL, NULL, NULL); 
741                 bm_get_info(bm2, &bw2, &bh2, NULL, NULL, NULL); 
742         
743                 // determine where to draw the coords
744                 bx1 = Credits_image_coords[gr_screen.res][CREDITS_X_COORD] + ((Credits_image_coords[gr_screen.res][CREDITS_W_COORD] - bw1)/2);
745                 by1 = Credits_image_coords[gr_screen.res][CREDITS_Y_COORD] + ((Credits_image_coords[gr_screen.res][CREDITS_H_COORD] - bh1)/2);
746                 bx2 = Credits_image_coords[gr_screen.res][CREDITS_X_COORD] + ((Credits_image_coords[gr_screen.res][CREDITS_W_COORD] - bw2)/2);
747                 by2 = Credits_image_coords[gr_screen.res][CREDITS_Y_COORD] + ((Credits_image_coords[gr_screen.res][CREDITS_H_COORD] - bh2)/2);
748
749                 gr_cross_fade(bm1, bm2, bx1, by1, bx2, by2, (float)percent / 100.0f);
750         }
751
752 #ifdef MAKE_FS1
753         if (CreditsWin01 != -1) {
754                 gr_set_bitmap(CreditsWin01);
755                 gr_bitmap(233, 5);
756         }
757
758         if (CreditsWin02 != -1) {
759                 gr_set_bitmap(CreditsWin02);
760                 gr_bitmap(616, 8);
761         }
762
763         if (CreditsWin03 != -1) {
764                 gr_set_bitmap(CreditsWin03);
765                 gr_bitmap(233, 299);
766         }
767
768         if (CreditsWin04 != -1) {
769                 gr_set_bitmap(CreditsWin04);
770                 gr_bitmap(215, 8);
771         }
772 #endif
773
774         Ui_window.draw();
775
776         for (i=TECH_DATABASE_BUTTON; i<=CREDITS_BUTTON; i++){
777                 if (Buttons[i][gr_screen.res].button.button_down()){
778                         break;
779                 }
780         }
781
782         if (i > CREDITS_BUTTON){
783                 Buttons[CREDITS_BUTTON][gr_screen.res].button.draw_forced(2);
784         }
785
786         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]);
787         gr_set_font(FONT1);
788         gr_set_color_fast(&Color_normal);
789
790         int sy;
791         if ( Credit_position > 0 ) {
792                 sy = fl2i(Credit_position+0.5f);
793         } else {
794                 sy = fl2i(Credit_position-0.5f);
795         }
796
797         // HACK - I don't want to change the string code, so we'll just use a special version here
798         if(gr_screen.mode == GR_GLIDE){
799 #ifndef PLAT_UNIX
800                 extern void gr_glide_string_hack(int sx, int sy, char *s);
801                 gr_glide_string_hack(0x8000, sy, Credit_text);
802 #endif
803         } else {
804                 gr_string(0x8000, sy, Credit_text);
805         }
806
807         int temp_time;
808         temp_time = timer_get_milliseconds();
809
810         Credits_frametime = temp_time - Credits_last_time;
811         Credits_last_time = temp_time;
812         timestamp_inc(Credits_frametime / 1000.0f);
813
814         float fl_frametime = i2fl(Credits_frametime) / 1000.f;
815         if (keyd_pressed[KEY_LSHIFT]) {
816                 Credit_position -= fl_frametime * CREDITS_SCROLL_RATE * 4.0f;
817         } else {
818                 Credit_position -= fl_frametime * CREDITS_SCROLL_RATE;
819         }
820
821         if (Credit_position < Credit_stop_pos){
822                 Credit_position = Credit_start_pos;
823         }
824
825         Credits_counter += fl_frametime;
826         while (Credits_counter >= CREDITS_ARTWORK_DISPLAY_TIME) {
827                 Credits_counter -= CREDITS_ARTWORK_DISPLAY_TIME;
828                 Credits_artwork_index = next;
829         }
830
831         gr_flip();
832 }
833