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