]> icculus.org git repositories - taylor/freespace2.git/blob - src/hud/hudmessage.cpp
More stuff compiles
[taylor/freespace2.git] / src / hud / hudmessage.cpp
1 /*
2  * $Logfile: /Freespace2/code/Hud/HUDmessage.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  *
7  * C module that controls and manages the message window on the HUD
8  *
9  * $Log$
10  * Revision 1.2  2002/05/03 13:34:33  theoddone33
11  * More stuff compiles
12  *
13  * Revision 1.1.1.1  2002/05/03 03:28:09  root
14  * Initial import.
15  *
16  * 
17  * 23    9/08/99 5:38p Jefff
18  * 
19  * 22    9/08/99 2:38p Jefff
20  * sound pausing going to menu from game
21  * 
22  * 21    9/07/99 9:42p Jefff
23  * 
24  * 20    9/02/99 11:47a Jefff
25  * yet another hud message length adjustment.  yeesh.
26  * 
27  * 19    8/25/99 10:08a Jefff
28  * fixed another messge cut-off bug
29  * 
30  * 18    8/23/99 11:12a Jefff
31  * fixed 1024 message cut-off bug
32  * 
33  * 17    8/20/99 2:26p Jefff
34  * hud message text wrapping problem in hires fixed
35  * 
36  * 16    8/03/99 7:27p Jefff
37  * hud messages go completely across screnn in high res now
38  * 
39  * 15    8/03/99 6:21p Jefff
40  * fixed stupid bug with objectives screen key
41  * 
42  * 14    8/01/99 12:39p Dave
43  * Added HUD contrast control key (for nebula).
44  * 
45  * 13    7/29/99 2:58p Jefff
46  * Ingame objective screen icon key now uses normal objective icons and
47  * text is drawn in code.
48  * 
49  * 12    6/16/99 5:26p Dave
50  * Fixed some bitmap and coordinate problems on the mission scrollback
51  * screen.
52  * 
53  * 11    6/10/99 3:43p Dave
54  * Do a better job of syncing text colors to HUD gauges.
55  * 
56  * 10    2/02/99 10:13a Neilk
57  * fixed more coords
58  * 
59  * 9     2/01/99 5:55p Dave
60  * Removed the idea of explicit bitmaps for buttons. Fixed text
61  * highlighting for disabled gadgets.
62  * 
63  * 8     1/30/99 5:08p Dave
64  * More new hi-res stuff.Support for nice D3D textures.
65  * 
66  * 7     1/30/99 3:09p Neilk
67  * More mission log coord fixes
68  * 
69  * 6     1/30/99 2:59p Neilk
70  * Fixed more mission log coords
71  * 
72  * 5     1/29/99 7:57p Neilk
73  * Added support for multiresolutions
74  * 
75  * 4     1/06/99 2:24p Dave
76  * Stubs and release build fixes.
77  * 
78  * 3     10/13/98 9:28a Dave
79  * Started neatening up freespace.h. Many variables renamed and
80  * reorganized. Added AlphaColors.[h,cpp]
81  * 
82  * 2     10/07/98 10:53a Dave
83  * Initial checkin.
84  * 
85  * 1     10/07/98 10:49a Dave
86  * 
87  * 89    6/09/98 5:17p Lawrance
88  * French/German localization
89  * 
90  * 88    6/09/98 10:31a Hoffoss
91  * Created index numbers for all xstr() references.  Any new xstr() stuff
92  * added from here on out should be added to the end if the list.  The
93  * current list count can be found in FreeSpace.cpp (search for
94  * XSTR_SIZE).
95  * 
96  * 87    5/19/98 8:36p Andsager
97  * Fix bug where last line of message log would not show if more than one
98  * screen of text.
99  * 
100  * 86    4/27/98 8:49p Allender
101  * make terran command display in white (with correct code anyway).
102  * 
103  * 85    4/25/98 11:49p Lawrance
104  * init pos to zero
105  * 
106  * 84    4/25/98 9:10p Hoffoss
107  * Fixed bug with scrollback origin.
108  * 
109  * 83    4/25/98 5:22p Hoffoss
110  * Fixed some problems with scrolling and positioning, and added code to
111  * support pageup/pagedown.
112  * 
113  * 82    4/14/98 5:06p Dave
114  * Don't load or send invalid pilot pics. Fixed chatbox graphic errors.
115  * Made chatbox display team icons in a team vs. team game. Fixed up pause
116  * and endgame sequencing issues.
117  * 
118  * 81    4/14/98 2:44p Hoffoss
119  * Made arrow keys do what tab does.
120  * 
121  * 80    4/08/98 4:10p John
122  * Removed all remaining traces of the evil gr_init_font_ex.
123  * 
124  * 79    4/07/98 11:33a Hoffoss
125  * Fixed bug where scroll offset was wrong when first entering the F4
126  * screen.
127  * 
128  * 78    4/05/98 3:30p Dave
129  * Print netplayer messages in brighter green on the hud, with
130  * accompanying sound. Echo netplayer messages on sending machine. Fixed
131  * standalone sequencing bug where host never get the "enter mission"
132  * button.
133  * 
134  * 77    3/27/98 11:57a Dave
135  * Put in expression checking for text messages.
136  * 
137  * 76    3/17/98 4:01p Hoffoss
138  * Added HUD_SOURCE_TERRAN_CMD and changed code to utilize it when a
139  * message is being sent from Terran Command.
140  * 
141  * 75    3/16/98 5:55p Lawrance
142  * Increase width of HUD message line, don't draw lines while comm menu is
143  * up
144  * 
145  * 74    3/12/98 4:03p Hoffoss
146  * Changed formatting used in hug scrollbacl log.
147  * 
148  * 73    3/09/98 4:47p Hoffoss
149  * Changed F4 screen to start in objectives mode rather than HUD messages
150  * mode.
151  * 
152  * 72    3/09/98 2:50p Hoffoss
153  * Changed to use different palette file, and fixed bug with text
154  * overrunning the right edge of screen.
155  * 
156  * 71    3/02/98 5:42p John
157  * Removed WinAVI stuff from Freespace.  Made all HUD gauges wriggle from
158  * afterburner.  Made gr_set_clip work good with negative x &y.  Made
159  * model_caching be on by default.  Made each cached model have it's own
160  * bitmap id.  Made asteroids not rotate when model_caching is on.  
161  * 
162  * 70    2/27/98 4:55p Hoffoss
163  * Fixed some alignment problems.
164  * 
165  * 69    2/27/98 4:37p Hoffoss
166  * Combined Objectives screen into Mission Log screen.
167  * 
168  * 68    2/22/98 4:17p John
169  * More string externalization classification... 190 left to go!
170  * 
171  * 67    2/22/98 12:19p John
172  * Externalized some strings
173  * 
174  * 66    2/06/98 2:58p Hoffoss
175  * Fixed bug where mission log scrolls twice when using the arrow keys.
176  * 
177  * 65    1/29/98 10:26a Hoffoss
178  * Made changes so arrow buttons repeat scrolling when held down.
179  * 
180  * 64    1/20/98 4:39p Hoffoss
181  * Added mission time display to scrollback log screen.
182  * 
183  * 63    1/18/98 5:09p Lawrance
184  * Added support for TEAM_TRAITOR
185  * 
186  * 62    1/12/98 11:16p Lawrance
187  * Wonderful HUD config.
188  * 
189  * 61    1/08/98 1:33p Hoffoss
190  * Made scroll offset reset to bottom of list instead of top.
191  * 
192  * 60    1/05/98 2:59p Hoffoss
193  * Fixed bug with messages drawing outside of limits.
194  * 
195  * 59    1/02/98 9:10p Lawrance
196  * Big changes to how colors get set on the HUD.
197  * 
198  * 58    1/02/98 10:23a Hoffoss
199  * Fixed incorrect scrolling directions in message scrollback log.
200  * 
201  * 57    12/11/97 10:17p Dave
202  * Put in some checks to make sure HUD_printfs aren't
203  * done in certain multiplayer situations.
204  * 
205  * 56    12/05/97 2:16p Hoffoss
206  * Made hidden hud messages actually not show up in scrollback.
207  * 
208  * 55    12/03/97 6:07p Dave
209  * Added assert that hud scrollback initialized before adding messages to
210  * it.
211  * 
212  * 54    12/03/97 4:16p Hoffoss
213  * Changed sound stuff used in interface screens for interface purposes.
214  * 
215  * 53    12/03/97 11:35a Hoffoss
216  * Made changes to HUD messages send throughout the game.
217  * 
218  * 52    12/02/97 5:57p Hoffoss
219  * Changed Hud messaging code to align text to right after sending ship's
220  * name.
221  * 
222  * 51    12/01/97 4:30p Hoffoss
223  * Changed code to list hud messages in scrollback from top down.
224  * 
225  * 50    11/25/97 10:01a Jasen
226  * Remoced excess buttons from MessageLog screen.
227  * 
228  * 49    11/24/97 10:03p Jasen
229  * Dang... had to make an entirely new revision of the last button.  :)
230  * 
231  * 48    11/24/97 9:44p Jasen
232  * Changed button name and coords to new exit button.
233  * 
234  * 47    11/20/97 12:02p Lawrance
235  * change Error to nprintf at warning level
236  * 
237  * 46    11/17/97 6:37p Lawrance
238  * new gauges: extended target view, new lock triangles, support ship view
239  * 
240  * 45    11/14/97 2:46p Lawrance
241  * decrease width of HUD message line to 435, so it doesn overlap with
242  * message menu
243  * 
244  * 44    11/13/97 10:16p Hoffoss
245  * Added icons to mission log scrollback.
246  * 
247  * 43    11/13/97 4:05p Hoffoss
248  * Added hiding code for mission log entries.
249  * 
250  * 42    11/12/97 6:00p Hoffoss
251  * Added training messages to hud scrollback log.
252  * 
253  * 41    11/11/97 11:16a Hoffoss
254  * Changed hud scrollback to color entire lines.
255  * 
256  * 40    11/06/97 5:42p Hoffoss
257  * Added support for fixed size timstamp rendering.
258  * 
259  * 39    11/05/97 7:11p Hoffoss
260  * Made changed to the hud message system.  Hud messages can now have
261  * sources so they can be color coded.
262  * 
263  * 38    11/04/97 4:56p Jasen
264  * Updated coordinates for buttons
265  * 
266  * 37    11/03/97 10:12p Hoffoss
267  * Finished up work on the hud message/mission log scrollback screen.
268  * 
269  * 36    11/03/97 5:38p Dave
270  * Cleaned up more multiplayer sequencing. Added OBJ_OBSERVER module/type.
271  * Restructured HUD_config structs/flags.
272  * 
273  * 35    10/25/97 4:02p Lawrance
274  * took out unused hud_message struct members
275  * 
276  * 34    10/02/97 9:53p Hoffoss
277  * Added event evaluation analysis debug screen so we can determine the
278  * state of events and their sexp trees to track down logic problems and
279  * such.
280  * 
281  * 33    9/17/97 5:12p John
282  * Restructured collision routines.  Probably broke a lot of stuff.
283  * 
284  * 32    9/08/97 12:01p Lawrance
285  * when re-using HUD scrollback entries, ensure memory gets free'ed
286  * properly
287  * 
288  * 31    8/31/97 6:38p Lawrance
289  * pass in frametime to do_frame loop
290  * 
291  * 30    8/22/97 10:03a Lawrance
292  * fix exception that occurred when hud scrollback was selected from the
293  * main menu
294  * 
295  * 29    6/23/97 12:03p Lawrance
296  * move split_str() to Parselo
297  * 
298  * 28    6/17/97 12:25p Lawrance
299  * HUD message lines are split into multiple lines when they exceed
300  * display width
301  * 
302  * 27    6/12/97 10:23a John
303  * added new colors to freespace.  made most menus display the background
304  * bitmap rather than dull black screen.
305  * 
306  * 26    6/11/97 1:12p John
307  * Started fixing all the text colors in the game.
308  * 
309  * 25    4/22/97 3:14p Lawrance
310  * only free HUD scrollback messages if they exist
311  * 
312  * 24    4/15/97 1:26p Lawrance
313  * using a static array of nodes to store hud scrollback messages, storage
314  * for text is dynamic
315  * 
316  * 23    4/14/97 9:55a Mike
317  * Fixed HUD message system.
318  * Better game sequencing.
319  * 
320  * 22    1/28/97 5:33p Lawrance
321  * saving number of msg window lines in save game and player file
322  * 
323  * 21    1/28/97 4:59p Lawrance
324  * allowing number of lines on hud message bar to be configured
325  * 
326  * 20    1/24/97 9:47a Lawrance
327  * made number of message lines in HUD message area confiurable
328  * 
329  * 19    1/22/97 10:56a Lawrance
330  * added check for NULL after malloc()
331  * 
332  * 18    1/07/97 5:36p Lawrance
333  * Enabled save/restore for  old/present/pending hud messages
334  * 
335  * 17    12/10/96 12:28p Lawrance
336  * adding new offscreen target indicator
337  * 
338  * 16    12/08/96 1:54a Lawrance
339  * integrating hud configuration
340  * 
341  * 15    11/29/96 6:12p Lawrance
342  * took out duplicate include of timer.h
343  * 
344  * 14    11/29/96 11:17a Lawrance
345  * added comments, put check in for zero-length HUD messages in the
346  * scrollback
347  * 
348  * 13    11/28/96 6:27p Lawrance
349  * added some additional comments
350  * 
351  * 12    11/27/96 3:20p Lawrance
352  * added scroll-back message code
353  * 
354  * 11    11/22/96 1:00p Lawrance
355  * fixed bug when a key held down and created tons of messages
356  * 
357  * 10    11/22/96 12:35p Lawrance
358  * 
359  * 9     11/20/96 11:51a Lawrance
360  * trying out losing the HUD message shaded area
361  * 
362  * 8     11/19/96 4:46p Lawrance
363  * fixed problem when too many messages were to be displayed at once
364  * 
365  * 7     11/19/96 3:55p Lawrance
366  * modifed HUD message details (scroll speed, fade time etc)
367  * 
368  * 6     11/19/96 10:16a Lawrance
369  * changing to new use of color scheme
370  * 
371  * 5     11/17/96 5:28p Lawrance
372  * using HUD color globals instead of hard-coded numbers
373  * 
374  * 4     11/15/96 11:46a Lawrance
375  * tweaked size of HUD message bar to prevent clipping of g's etc on the
376  * bottom line
377  * 
378  * 3     11/15/96 11:39a Lawrance
379  * got HUD messages scrolling
380  * 
381  * 2     11/15/96 12:11a Lawrance
382  * HUD message bar working
383  *
384  * $NoKeywords: $
385  *
386 */
387
388 #include <stdlib.h>
389 #include <stdarg.h>
390
391 #include "hud.h"
392 #include "hudmessage.h"
393 #include "hudtarget.h"
394 #include "freespace.h"
395 #include "gamesequence.h"
396 #include "2d.h"
397 #include "key.h"
398 #include "timer.h"
399 #include "math.h"
400 #include "mouse.h"
401 #include "winmidi.h"
402 #include "player.h"
403 #include "linklist.h"
404 #include "missionlog.h"
405 #include "ui.h"
406 #include "missionscreencommon.h"
407 #include "bmpman.h"
408 #include "font.h"
409 #include "gamesnd.h"
410 #include "multi.h"
411 #include "missiongoals.h"
412 #include "alphacolors.h"
413 #include "beam.h"
414 #include "audiostr.h"
415
416 /* replaced with those static ints that follow
417 #define LIST_X          46
418 #define LIST_X2 108  // second column x start position
419 #define LIST_Y          60
420 #define LIST_W          558  // total width including both columns
421 #define LIST_W2 (LIST_W + LIST_X - LIST_X2)  // width of second column
422 #define LIST_H          297
423 #define LIST_H_O        275  // applies only to objectives mode
424 */
425
426 // 1st column, width includes both columns
427 static int Hud_mission_log_list_coords[GR_NUM_RESOLUTIONS][4] = {
428         {
429                 46,60,558,269           // GR_640
430         },
431         {
432                 74,96,558,297           // GR_1024
433         }
434 };
435
436 // 2nd column, width is just of second column
437 static int Hud_mission_log_list2_coords[GR_NUM_RESOLUTIONS][4] = {
438         {
439                 108, 60, 496, 297               // GR_640
440         },
441         {
442                 136, 96, 496, 436               // GR_1024
443         }
444 };
445
446 static int Hud_mission_log_list_objective_x_coord[GR_NUM_RESOLUTIONS] = {
447         275,    // GR_640
448         440     // GR_1024
449 };
450
451 static int Hud_mission_log_time_coords[GR_NUM_RESOLUTIONS][2] = {
452         {
453                 41, 372 // GR_640
454         },
455         {
456                 66, 595 // GR_1024
457         }
458 };
459
460 static int Hud_mission_log_time2_coords[GR_NUM_RESOLUTIONS][2] = {
461         {
462                 103, 372        // GR_640
463         },
464         {
465                 128, 595        // GR_1024
466         }
467 };
468
469
470 #define SCROLLBACK_MODE_MSGS_LOG                0
471 #define SCROLLBACK_MODE_EVENT_LOG       1
472 #define SCROLLBACK_MODE_OBJECTIVES      2
473
474 #define NUM_BUTTONS                     6
475
476 #define SCROLL_UP_BUTTON        0
477 #define SCROLL_DOWN_BUTTON      1
478 #define SHOW_MSGS_BUTTON        2
479 #define SHOW_EVENTS_BUTTON      3
480 #define SHOW_OBJS_BUTTON        4
481 #define ACCEPT_BUTTON           5
482
483 #define HUD_MESSAGE_TOTAL_LIFE  14000   // total time a HUD message is alive (in milliseconds)
484
485 #define HUD_MSG_LENGTH_MAX              2048
486 //#define HUD_MSG_MAX_PIXEL_W   439     // maximum number of pixels wide message display area is
487 //#define HUD_MSG_MAX_PIXEL_W   619     // maximum number of pixels wide message display area is
488
489 static int Hud_mission_log_status_coords[GR_NUM_RESOLUTIONS][2] = {
490         {
491                 170, 339                // GR_640
492         },
493         {
494                 361, 542                // GR_1024
495         }
496 };
497
498 struct scrollback_buttons {
499         char *filename;
500         int x, y;
501         int xt, yt;
502         int hotspot;
503         UI_BUTTON button;  // because we have a class inside this struct, we need the constructor below..
504
505         scrollback_buttons(char *name, int x1, int y1, int x2, int y2, int h) : filename(name), x(x1), y(y1), xt(x2), yt(y2), hotspot(h) {}
506 };
507
508 int Scroll_time_id;
509
510 int MSG_WINDOW_X_START = 5;
511 int MSG_WINDOW_Y_START = 5;
512 int MSG_WINDOW_WIDTH;           // initialed in hud_init_msg_window()
513 int MSG_WINDOW_HEIGHT;
514 int MSG_WINDOW_FONT_HEIGHT;
515 int ACTIVE_BUFFER_LINES = 4;                            // number of HUD messages that can be shown at one time + 1
516 int OLD_ACTIVE_BUFFER_LINES;
517
518 int Hud_list_start;                     // points to the next msg to be printed in the queue
519 int Hud_list_end;                               // points to the last msg in the queue
520
521 int Active_index=0;
522 int Scroll_needed=0;
523 int Scroll_in_progress=0;
524
525 HUD_message_data HUD_pending[SCROLL_BUFFER_LINES];
526 Hud_display_info HUD_active_msgs_list[MAX_ACTIVE_BUFFER_LINES];
527
528 int HUD_msg_inited = FALSE;
529
530 // There is a maximum number of lines that will be stored in the message scrollback.  Oldest
531 // messages are deleted to make way for newest messages.
532 #define MAX_MSG_SCROLLBACK_LINES        100
533 line_node Msg_scrollback_lines[MAX_MSG_SCROLLBACK_LINES];
534
535 line_node Msg_scrollback_free_list;
536 line_node Msg_scrollback_used_list;
537
538 #define MAX_HUD_FT      1
539
540 typedef struct HUD_ft {
541         int     end_time;                                               //      Timestamp at which this message will go away.
542         char    text[MAX_HUD_LINE_LEN];         //      Text to display.
543         int     color;                                                  //      0rgb color, 8 bit fields.
544 } HUD_ft;
545
546 HUD_ft HUD_fixed_text[MAX_HUD_FT];
547
548 static int Num_obj_lines;
549 static int Scroll_offset;
550 static int Scroll_max;
551 static int Scrollback_mode = SCROLLBACK_MODE_OBJECTIVES;
552 static int Selected_line;
553 // static int Status_bitmap;
554 static int Background_bitmap;
555 static UI_WINDOW Ui_window;
556
557 static char* Hud_mission_log_fname[GR_NUM_RESOLUTIONS] = {
558         "MissionLog",           // GR_640
559         "2_MissionLog"          // GR_1024
560 };
561
562 static char* Hud_mission_log_status_fname[GR_NUM_RESOLUTIONS] = {
563         "MLStatus",             // GR_640
564         "MLStatus"              // GR_1024
565 };
566
567 static char* Hud_mission_log_mask_fname[GR_NUM_RESOLUTIONS] = {
568         "MissionLog-m",         // GR_640
569         "2_MissionLog-m"                // GR_1024
570 };
571
572 static scrollback_buttons Buttons[GR_NUM_RESOLUTIONS][NUM_BUTTONS] = {
573         {       // GR_640
574         //XSTR:OFF
575                 scrollback_buttons("LB_00",     1,              67,     -1,     -1,     0),
576                 scrollback_buttons("LB_01",     1,              307,    -1,     -1,     1),
577                 scrollback_buttons("LB_02",     111,    376,    108,    413,    2),
578                 scrollback_buttons("LB_03",     209,    376,    205,    413,    3),
579                 scrollback_buttons("LB_04",     12,     376,    7,              413,    4),
580                 scrollback_buttons("CB_05a",    571,    425,    564,    413,    5)
581         //XSTR:ON
582         },
583         {       // GR_1024
584         //XSTR:OFF
585                 scrollback_buttons("2_LB_00",   1,              108,    -1,     -1,     0),
586                 scrollback_buttons("2_LB_01",   1,              492,    -1,     -1,     1),
587                 scrollback_buttons("2_LB_02",   177,    602,    173,    661,    2),
588                 scrollback_buttons("2_LB_03",   335,    602,    335,    661,    3),
589                 scrollback_buttons("2_LB_04",   20,     602,    11,     661,    4),
590                 scrollback_buttons("2_CB_05a",914,      681,    946,    661,    5)
591         //XSTR:ON
592         }
593 }
594 ;
595
596 // ----------------------------------------------------------------------
597 // HUD_init_fixed_text()
598 //
599 void HUD_init_fixed_text()
600 {
601         int     i;
602
603         for (i=0; i<MAX_HUD_FT; i++)
604                 HUD_fixed_text[i].end_time = timestamp(0);
605 }
606
607 // ----------------------------------------------------------------------
608 // hud_init_msg_window()
609 //
610 // Called from HUD_init(), which is called from game_level_init()
611 //
612 void hud_init_msg_window()
613 {
614         int i, h;
615
616         MSG_WINDOW_WIDTH = gr_screen.clip_width - 20;
617
618         Hud_list_start = 0;
619         Hud_list_end = 0;
620
621         for (i=0; i<SCROLL_BUFFER_LINES; i++)
622                 HUD_pending[i].text[0] = HUD_pending[i].text[MAX_HUD_LINE_LEN - 1] = 0;
623         
624         for ( i=0; i < MAX_ACTIVE_BUFFER_LINES; i++ ) {
625                 HUD_active_msgs_list[i].total_life = 1;
626         }
627
628         Scroll_time_id = 1;
629
630         // determine the height of the msg window, which depends on the font height     
631         gr_set_font(FONT1);
632         h = gr_get_font_height();
633
634         //ACTIVE_BUFFER_LINES = Players[Player_num].HUD_config.num_msg_window_lines;
635 //      ACTIVE_BUFFER_LINES = HUD_config.num_msg_window_lines;
636         ACTIVE_BUFFER_LINES = 4;
637
638         MSG_WINDOW_FONT_HEIGHT = h;
639         MSG_WINDOW_HEIGHT = MSG_WINDOW_FONT_HEIGHT * (ACTIVE_BUFFER_LINES-1);
640
641         // starting a mission, free the scroll-back buffers, but only if we've been
642         // through this function once already
643         if ( HUD_msg_inited == TRUE ) {
644                 hud_free_scrollback_list();
645         }
646
647         list_init( &Msg_scrollback_free_list );
648         list_init( &Msg_scrollback_used_list );
649
650         // first slot is reserved for dummy node
651         for (i=1; i < MAX_MSG_SCROLLBACK_LINES; i++)    {
652                 Msg_scrollback_lines[i].text = NULL;
653                 list_append(&Msg_scrollback_free_list, &Msg_scrollback_lines[i]);
654         }
655
656         Active_index=0;
657         Scroll_needed=0;
658         Scroll_in_progress=0;
659
660         OLD_ACTIVE_BUFFER_LINES = ACTIVE_BUFFER_LINES;
661
662         HUD_init_fixed_text();
663         HUD_msg_inited = TRUE;
664 }
665
666 // ---------------------------------------------------------------------------------------
667 // hud_show_msg_window() will display the active HUD messages on the HUD.  It will scroll
668 // the messages up when a new message arrives.  
669 //
670 void hud_show_msg_window()
671 {
672         int i, index;
673
674         hud_set_default_color();
675         gr_set_font(FONT1);
676
677         HUD_set_clip(MSG_WINDOW_X_START,MSG_WINDOW_Y_START, MSG_WINDOW_WIDTH, MSG_WINDOW_HEIGHT+2);
678
679         if ( OLD_ACTIVE_BUFFER_LINES != ACTIVE_BUFFER_LINES ) {
680                 // the size of the message window has changed, the best thing to do is to put all
681                 // the blank out the current hud messages.  There is no need to add them to the 
682                 // scrollback buffer, since they are already there!
683         
684                 for ( i=0; i < ACTIVE_BUFFER_LINES; i++ ) {
685                         if ( !timestamp_elapsed(HUD_active_msgs_list[i].total_life) ) {
686                                 HUD_active_msgs_list[i].total_life = 1;
687                                 Active_index=0;
688                         }
689                 }
690         }
691         
692         OLD_ACTIVE_BUFFER_LINES = ACTIVE_BUFFER_LINES;
693
694         // check if there is a message to display on the HUD, and if there is room to display it
695         if ( Hud_list_start != Hud_list_end && !Scroll_needed) {
696
697                 Hud_list_start++;
698
699                 // if the pointer exceeds the array size, wrap around to element 1.  element 0 is not used.             
700                 if (Hud_list_start >= SCROLL_BUFFER_LINES)
701                         Hud_list_start = 1;
702
703                 HUD_active_msgs_list[Active_index].msg = HUD_pending[Hud_list_start];
704                 HUD_active_msgs_list[Active_index].total_life = timestamp(HUD_MESSAGE_TOTAL_LIFE);
705
706                 for (i=Active_index+1; i < Active_index+ACTIVE_BUFFER_LINES; i++) {
707                         index = i % ACTIVE_BUFFER_LINES;
708
709                         // determine if there are any existing messages, if so need to scroll them up
710
711                         if ( !timestamp_elapsed(HUD_active_msgs_list[index].total_life) ) {
712                                 HUD_active_msgs_list[index].target_y -=  MSG_WINDOW_FONT_HEIGHT;
713                                 Scroll_needed=1;
714                         }
715
716                 }
717
718                 if (Scroll_needed) {
719                         HUD_active_msgs_list[Active_index].y = (ACTIVE_BUFFER_LINES-1)*MSG_WINDOW_FONT_HEIGHT;
720                         HUD_active_msgs_list[Active_index].target_y = HUD_active_msgs_list[Active_index].y - MSG_WINDOW_FONT_HEIGHT;
721                 }
722                 else {
723                         HUD_active_msgs_list[Active_index].y = (ACTIVE_BUFFER_LINES-2)*MSG_WINDOW_FONT_HEIGHT;
724                         HUD_active_msgs_list[Active_index].target_y = HUD_active_msgs_list[Active_index].y;
725                 }
726
727                 Active_index++;
728                 if (Active_index >= ACTIVE_BUFFER_LINES) Active_index = 0;
729
730                 if (Hud_list_end == Hud_list_start) {   // just printed the last msg
731                         Hud_list_start = Hud_list_end = 0;
732                 }
733         }
734
735         Scroll_in_progress=0;
736         Scroll_needed = 0;
737
738         for ( i=0; i < ACTIVE_BUFFER_LINES; i++ ) {
739
740                 if ( !timestamp_elapsed(HUD_active_msgs_list[i].total_life) ) {
741
742                         if (HUD_active_msgs_list[i].y > HUD_active_msgs_list[i].target_y) {
743                                 Scroll_needed=1;
744                                 if (timestamp_elapsed(Scroll_time_id) ){
745                                         HUD_active_msgs_list[i].y -= SCROLL_STEP_SIZE;
746                                         if (HUD_active_msgs_list[i].y < HUD_active_msgs_list[i].target_y)
747                                                 HUD_active_msgs_list[i].y = HUD_active_msgs_list[i].target_y;
748
749                                         Scroll_in_progress=1;
750                                 }
751
752                         }
753
754                         if ( hud_gauge_active(HUD_MESSAGE_LINES) ) {
755                                 if ( !(Player->flags & PLAYER_FLAGS_MSG_MODE) ) {
756                                         // set the appropriate color                                    
757                                         if(HUD_active_msgs_list[i].msg.source){
758                                                 hud_set_gauge_color(HUD_MESSAGE_LINES, HUD_C_BRIGHT);
759                                         } else {
760                                                 hud_set_gauge_color(HUD_MESSAGE_LINES);
761                                         }
762
763                                         // print the message out
764                                         gr_printf(MSG_WINDOW_X_START + HUD_active_msgs_list[i].msg.x - 2, HUD_active_msgs_list[i].y, "%s", HUD_active_msgs_list[i].msg.text);
765                                 }
766                         }
767                 }
768
769         } // end for
770
771         if (Scroll_in_progress)
772                 Scroll_time_id = timestamp(SCROLL_TIME);
773
774         HUD_reset_clip();
775 }
776
777 void hud_show_fixed_text()
778 {
779         HUD_ft  *hp;
780
781         hp = &HUD_fixed_text[0];
782
783         if (!timestamp_elapsed(hp->end_time)) {
784                 //gr_set_color((hp->color >> 16) & 0xff, (hp->color >> 8) & 0xff, hp->color & 0xff);
785                 gr_printf(0x8000, MSG_WINDOW_Y_START + MSG_WINDOW_HEIGHT + 8, hp->text);
786         }
787 }
788
789 //      Similar to HUD printf, but shows only one message at a time, at a fixed location.
790 void HUD_fixed_printf(float duration, char * format, ...)
791 {
792         va_list args;
793         char            tmp[HUD_MSG_LENGTH_MAX];
794         int             msg_length;
795
796         // make sure we only print these messages if we're in the correct state
797         if((Game_mode & GM_MULTIPLAYER) && (Netgame.game_state != NETGAME_STATE_IN_MISSION)){
798                 nprintf(("Network","HUD_fixed_printf bailing because not in multiplayer game play state\n"));
799                 return;
800         }
801
802         va_start(args, format);
803         vsprintf(tmp, format, args);
804         va_end(args);
805
806         msg_length = strlen(tmp);
807         Assert(msg_length < HUD_MSG_LENGTH_MAX);        //      If greater than this, probably crashed anyway.
808
809         if ( !msg_length ) {
810                 nprintf(("Warning", "HUD_fixed_printf ==> attempt to print a 0 length string in msg window\n"));
811                 return;
812
813         } else if (msg_length > MAX_HUD_LINE_LEN - 1){
814                 nprintf(("Warning", "HUD_fixed_printf ==> Following string truncated to %d chars: %s\n",MAX_HUD_LINE_LEN,tmp));
815         }
816
817         if (duration == 0.0f){
818                 HUD_fixed_text[0].end_time = timestamp(-1);
819         } else {
820                 HUD_fixed_text[0].end_time = timestamp((int) (1000.0f * duration));
821         }
822
823         strncpy(HUD_fixed_text[0].text, tmp, MAX_HUD_LINE_LEN - 1);
824         HUD_fixed_text[0].color = 0xff0000;
825 }
826
827 //      Clear all pending text.
828 void HUD_fixed_printf_reset()
829 {
830         HUD_init_fixed_text();
831 }
832
833
834 // --------------------------------------------------------------------------------------
835 // HUD_printf_line() 
836 //
837 //      Print a single line of text to the HUD.  We know that the text will fit on the screen,
838 // since that was taken care of in HUD_printf();
839 //
840 void HUD_printf_line(char *text, int source, int time = 0, int x = 0)
841 {
842         Assert(text != NULL);
843
844         // if the pointer exceeds the array size, wrap around to element 1.  element 0 is not used.             
845         Hud_list_end++;
846         if (Hud_list_end >= SCROLL_BUFFER_LINES)
847                 Hud_list_end = 1;
848
849         if (Hud_list_end == Hud_list_start) {
850                 nprintf(("Warning", "HUD ==> Exceeded msg scroll buffer, discarding message %s\n", text));
851                 Hud_list_end--;
852                 if (Hud_list_end == 0)
853                         Hud_list_end = SCROLL_BUFFER_LINES - 1;
854                 return;
855         }
856
857         if ( strlen(text) > MAX_HUD_LINE_LEN - 1 ){
858                 nprintf(("Warning", "HUD_printf_line() ==> Following string truncated to %d chars: %s\n", MAX_HUD_LINE_LEN, text));
859         }
860
861         strncpy(HUD_pending[Hud_list_end].text, text, MAX_HUD_LINE_LEN - 1);
862         HUD_pending[Hud_list_end].text[MAX_HUD_LINE_LEN - 1] = 0;
863         HUD_pending[Hud_list_end].source = source;
864         HUD_pending[Hud_list_end].time = time;
865         HUD_pending[Hud_list_end].x = x;
866 }
867
868 // converts a TEAM_* define to a HUD_SOURCE_* define
869 int HUD_get_team_source(int team)
870 {
871         switch (team) {
872                 case TEAM_FRIENDLY:
873                         return HUD_SOURCE_FRIENDLY;
874
875                 case TEAM_HOSTILE:
876                         return HUD_SOURCE_HOSTILE;
877
878                 case TEAM_NEUTRAL:
879                         return HUD_SOURCE_NEUTRAL;
880
881                 case TEAM_UNKNOWN:
882                         return HUD_SOURCE_UNKNOWN;
883
884                 case TEAM_TRAITOR:
885                         return HUD_SOURCE_TRAITOR;
886         }
887
888         nprintf(("warning", "Unknown TEAM_* define used! (%d)", team));
889         return 0;
890 }
891
892 void HUD_printf(char *format, ...)
893 {
894         va_list args;
895         char tmp[HUD_MSG_LENGTH_MAX];
896         int len;
897
898         // make sure we only print these messages if we're in the correct state
899         if((Game_mode & GM_MULTIPLAYER) && (Net_player->state != NETPLAYER_STATE_IN_MISSION)){
900                 nprintf(("Network","HUD_printf bailing because not in multiplayer game play state\n"));
901                 return;
902         }
903
904         va_start(args, format);
905         vsprintf(tmp, format, args);
906         va_end(args);
907
908         len = strlen(tmp);
909         Assert(len < HUD_MSG_LENGTH_MAX);       //      If greater than this, probably crashed anyway.
910         hud_sourced_print(HUD_SOURCE_COMPUTER, tmp);
911 }
912
913 void HUD_ship_sent_printf(int sh, char *format, ...)
914 {
915         va_list args;
916         char tmp[HUD_MSG_LENGTH_MAX];
917         int len;
918
919         sprintf(tmp, NOX("%s: "), Ships[sh].ship_name);
920         len = strlen(tmp);
921         Assert(len < HUD_MSG_LENGTH_MAX);
922
923         va_start(args, format);
924         vsprintf(tmp + len, format, args);
925         va_end(args);
926
927         len = strlen(tmp);
928         Assert(len < HUD_MSG_LENGTH_MAX);       //      If greater than this, probably crashed anyway.
929         hud_sourced_print(HUD_get_team_source(Ships[sh].team), tmp);
930 }
931
932 // --------------------------------------------------------------------------------------
933 // HUD_sourced_printf() 
934 //
935 // HUD_sourced_printf() has the same parameters as printf(), but displays the text as a scrolling
936 // message on the HUD.  Text is split into multiple lines if width exceeds msg display area
937 // width.  'source' is used to indicate who send the message, and is used to color code text.
938 //
939 void HUD_sourced_printf(int source, char *format, ...)
940 {
941         va_list args;
942         char tmp[HUD_MSG_LENGTH_MAX];
943
944         // make sure we only print these messages if we're in the correct state
945         if((Game_mode & GM_MULTIPLAYER) && (Net_player->state != NETPLAYER_STATE_IN_MISSION)){
946                 nprintf(("Network","HUD_sourced_printf bailing because not in multiplayer game play state\n"));
947                 return;
948         }
949         
950         va_start(args, format);
951         vsprintf(tmp, format, args);
952         va_end(args);
953         Assert(strlen(tmp) < HUD_MSG_LENGTH_MAX);       //      If greater than this, probably crashed anyway.
954         hud_sourced_print(source, tmp);
955 }
956
957 void hud_sourced_print(int source, char *msg)
958 {
959         char *ptr, *str;
960         //char *src_str, *msg_str;
961         int sw, t, x, offset = 0;
962         //int fudge = (gr_screen.res == GR_640) ? 15 : 50;              // prevents string from running off screen
963
964         if ( !strlen(msg) ) {
965                 nprintf(("Warning", "HUD ==> attempt to print a 0 length string in msg window\n"));
966                 return;
967         }
968
969         // add message to the scrollback log first
970         hud_add_msg_to_scrollback(msg, source, timestamp());
971
972         ptr = strstr(msg, NOX(": ")) + 2;
973         if (ptr) {
974                 gr_get_string_size(&sw, NULL, msg, ptr - msg);                  // get width of the speaker field
975                 //if (sw < MSG_WINDOW_WIDTH - 20)
976                 offset = sw;
977         }
978
979         x = 0;
980         t = timestamp();
981         str = msg;
982         while ((ptr = split_str_once(str, MSG_WINDOW_WIDTH - x - 7)) != NULL) {         // the 7 is a fudge hack
983                 HUD_printf_line(str, source, t, x);
984                 str = ptr;
985                 x = offset;
986                 t = 0;
987         }
988
989         HUD_printf_line(str, source, t, x);
990 }
991
992 int hud_query_scrollback_size()
993 {
994         int count = 0, y_add = 0;
995         int font_height = gr_get_font_height();
996         line_node *ptr;
997
998         if (EMPTY(&Msg_scrollback_used_list) || !HUD_msg_inited)
999                 return 0;
1000
1001         ptr = GET_FIRST(&Msg_scrollback_used_list);
1002         while (ptr != END_OF_LIST(&Msg_scrollback_used_list)) {
1003                 if (ptr->source != HUD_SOURCE_HIDDEN) {
1004                         y_add = ptr->y;
1005                         count += font_height + ptr->y;
1006                 }
1007
1008                 ptr = GET_NEXT(ptr);
1009         }
1010
1011         count -= y_add;
1012         return count;
1013 }
1014
1015 // add text directly to the hud scrollback log, without displaying on the hud
1016 void HUD_add_to_scrollback(char *text, int source)
1017 {
1018         if (!strlen(text)) {
1019                 nprintf(("Warning", "HUD ==> attempt to print a 0 length string in msg window\n"));
1020                 return;
1021         }
1022
1023         hud_add_msg_to_scrollback(text, source, timestamp());
1024 }
1025
1026 // hud_add_msg_to_scrollback() adds the new_msg to the scroll-back message list.  If there
1027 // are no more free slots, the first slot is released to make room for the new message.
1028 //
1029 void hud_add_line_to_scrollback(char *text, int source, int t, int x, int y, int underline_width)
1030 {
1031         line_node *new_line;
1032
1033         Assert(HUD_msg_inited);
1034         if (!text || !strlen(text))
1035                 return;
1036
1037         if ( EMPTY(&Msg_scrollback_free_list) ) {
1038                 new_line = GET_FIRST(&Msg_scrollback_used_list);
1039                 list_remove(&Msg_scrollback_used_list, new_line);
1040                 free(new_line->text);
1041
1042         } else {
1043                 new_line = GET_FIRST(&Msg_scrollback_free_list);
1044                 list_remove(&Msg_scrollback_free_list, new_line);
1045         }
1046
1047         new_line->x = x;
1048         new_line->y = y;
1049         new_line->underline_width = underline_width;
1050         new_line->time = t;
1051         new_line->source = source;
1052         new_line->text = (char *) malloc( strlen(text) + 1 );
1053         strcpy(new_line->text, text);
1054         list_append(&Msg_scrollback_used_list, new_line);
1055 }
1056
1057 void hud_add_msg_to_scrollback(char *text, int source, int t)
1058 {
1059         char buf[HUD_MSG_LENGTH_MAX], *ptr, *str;
1060         int msg_len, w, max_width, x, offset = 0;
1061
1062         max_width = Hud_mission_log_list2_coords[gr_screen.res][2];
1063         msg_len = strlen(text);
1064         if (msg_len == 0)
1065                 return;
1066
1067         w = 0;
1068         Assert(msg_len < HUD_MSG_LENGTH_MAX);
1069         strcpy(buf, text);
1070         ptr = strstr(buf, NOX(": "));
1071         if (ptr) {
1072                 gr_get_string_size(&w, NULL, buf, ptr - buf);
1073         }
1074
1075 //      if (ptr) {
1076 //              gr_get_string_size(&w, NULL, buf, ptr - buf + 2);
1077 //              if (w < max_width - 20)
1078 //                      offset = w;
1079 //      }
1080
1081         x = 0;
1082         str = buf;
1083         while ((ptr = split_str_once(str, max_width - x)) != NULL) {
1084                 hud_add_line_to_scrollback(str, source, t, x, 1, w);
1085                 str = ptr;
1086                 x = offset;
1087                 t = w = 0;
1088         }
1089
1090         hud_add_line_to_scrollback(str, source, t, x, 3, w);
1091 }
1092
1093 // hud_free_scrollback_list() will free the memory that was allocated to store the messages
1094 // for the scroll-back list
1095 //
1096 void hud_free_scrollback_list()
1097 {
1098         line_node *A;
1099
1100         // check if the list has been inited yet.  If not, return with doing nothing.
1101         if ( Msg_scrollback_used_list.next == NULL || Msg_scrollback_used_list.prev == NULL )
1102                 return;
1103
1104         A = GET_FIRST(&Msg_scrollback_used_list);
1105         while( A !=END_OF_LIST(&Msg_scrollback_used_list) )     {
1106                 if ( A->text != NULL ) {
1107                         free(A->text);
1108                         A->text = NULL;
1109                 }
1110
1111                 A = GET_NEXT(A);
1112         }
1113 }
1114
1115 // how many lines to skip
1116 int hud_get_scroll_max_pos()
1117 {
1118         int max = 0, font_height = gr_get_font_height();
1119
1120         if (Scrollback_mode == SCROLLBACK_MODE_MSGS_LOG) {
1121                 int count = 0;
1122                 line_node *ptr;
1123                 // number of pixels in excess of what can be displayed
1124                 int excess = Scroll_max - Hud_mission_log_list_coords[gr_screen.res][3];
1125
1126                 if (EMPTY(&Msg_scrollback_used_list) || !HUD_msg_inited) {
1127                         max = 0;
1128
1129                 } else {
1130                         ptr = GET_FIRST(&Msg_scrollback_used_list);
1131                         while (ptr != END_OF_LIST(&Msg_scrollback_used_list)) {
1132                                 if (ptr->source != HUD_SOURCE_HIDDEN) {
1133
1134                                         if (excess > 0) {
1135                                                 excess -= font_height;
1136                                                 count++;
1137                                         }
1138
1139                                         if (excess <= 0) {
1140                                                 max = count;
1141                                                 break;
1142                                         }
1143
1144                                         // spacing between lines
1145                                         excess -= ptr->y;
1146
1147                                 }
1148
1149                                 ptr = GET_NEXT(ptr);
1150                         }
1151                 }
1152
1153         } else {
1154                 max = (Scroll_max - Hud_mission_log_list_coords[gr_screen.res][3]) / font_height;
1155         }
1156
1157         if (max < 0)
1158                 max = 0;
1159
1160         return max;
1161 }
1162
1163 void hud_scroll_reset()
1164 {
1165         if (Scrollback_mode == SCROLLBACK_MODE_OBJECTIVES) {
1166                 Scroll_offset = 0;
1167
1168         } else {
1169                 Scroll_offset = hud_get_scroll_max_pos();
1170         }
1171 }
1172
1173 void hud_scroll_list(int dir)
1174 {
1175         if (dir) {
1176                 if (Scroll_offset) {
1177                         Scroll_offset--;
1178                         gamesnd_play_iface(SND_SCROLL);
1179
1180                 } else
1181                         gamesnd_play_iface(SND_GENERAL_FAIL);
1182
1183         } else {
1184                 if (Scroll_offset < hud_get_scroll_max_pos()) {
1185                         Scroll_offset++;
1186                         gamesnd_play_iface(SND_SCROLL);
1187
1188                 } else
1189                         gamesnd_play_iface(SND_GENERAL_FAIL);
1190         }
1191 }
1192
1193 void hud_goto_pos(int delta)
1194 {
1195         int pos=0, font_height = gr_get_font_height();
1196
1197         if (Scrollback_mode == SCROLLBACK_MODE_MSGS_LOG) {
1198                 int count = 0, y = 0;
1199                 line_node *ptr;
1200
1201                 if (EMPTY(&Msg_scrollback_used_list) || !HUD_msg_inited)
1202                         return;
1203
1204                 ptr = GET_FIRST(&Msg_scrollback_used_list);
1205                 while (ptr != END_OF_LIST(&Msg_scrollback_used_list)) {
1206                         if (ptr->source != HUD_SOURCE_HIDDEN) {
1207                                 if (count == Scroll_offset) {
1208                                         pos = y;
1209                                         break;
1210                                 }
1211
1212                                 y += font_height + ptr->y;
1213                                 count++;
1214                         }
1215
1216                         ptr = GET_NEXT(ptr);
1217                 }
1218
1219                 Scroll_offset = count = y = 0;
1220                 ptr = GET_FIRST(&Msg_scrollback_used_list);
1221                 while (ptr != END_OF_LIST(&Msg_scrollback_used_list)) {
1222                         if (ptr->source != HUD_SOURCE_HIDDEN) {
1223                                 if (y <= pos + delta)
1224                                         Scroll_offset = count;
1225
1226                                 y += font_height + ptr->y;
1227                                 count++;
1228                         }
1229
1230                         ptr = GET_NEXT(ptr);
1231                 }
1232
1233         } else {
1234                 pos = Scroll_offset * font_height;
1235                 pos += delta;
1236                 Scroll_offset = pos / font_height;
1237         }
1238 }
1239
1240 void hud_page_scroll_list(int dir)
1241 {
1242         int max = hud_get_scroll_max_pos();
1243
1244         if (dir) {
1245                 if (Scroll_offset) {
1246                         hud_goto_pos(-Hud_mission_log_list_coords[gr_screen.res][3]);
1247                         if (Scroll_offset < 0)
1248                                 Scroll_offset = 0;
1249
1250                         gamesnd_play_iface(SND_SCROLL);
1251
1252                 } else
1253                         gamesnd_play_iface(SND_GENERAL_FAIL);
1254
1255         } else {
1256                 if (Scroll_offset < max) {
1257                         hud_goto_pos(Hud_mission_log_list_coords[gr_screen.res][3]);
1258                         if (Scroll_offset > max)
1259                                 Scroll_offset = max;
1260
1261                         gamesnd_play_iface(SND_SCROLL);
1262
1263                 } else
1264                         gamesnd_play_iface(SND_GENERAL_FAIL);
1265         }
1266 }
1267
1268 void hud_scrollback_button_pressed(int n)
1269 {
1270         switch (n) {
1271                 case SCROLL_UP_BUTTON:
1272                         hud_scroll_list(1);
1273                         break;
1274
1275                 case SCROLL_DOWN_BUTTON:
1276                         hud_scroll_list(0);
1277                         break;
1278
1279                 case SHOW_MSGS_BUTTON:
1280                         Scrollback_mode = SCROLLBACK_MODE_MSGS_LOG;
1281                         Scroll_max = hud_query_scrollback_size();
1282                         hud_scroll_reset();
1283                         break;
1284
1285                 case SHOW_EVENTS_BUTTON:
1286                         Scrollback_mode = SCROLLBACK_MODE_EVENT_LOG;
1287                         Scroll_max = Num_log_lines * gr_get_font_height();
1288                         hud_scroll_reset();
1289                         break;
1290
1291                 case SHOW_OBJS_BUTTON:
1292                         Scrollback_mode = SCROLLBACK_MODE_OBJECTIVES;
1293                         Scroll_max = Num_obj_lines * gr_get_font_height();
1294                         Scroll_offset = 0;
1295                         break;
1296
1297                 case ACCEPT_BUTTON:
1298                         hud_scrollback_exit();                  
1299                         break;
1300         }
1301 }
1302
1303 void hud_scrollback_init()
1304 {
1305         int i;
1306         scrollback_buttons *b;
1307
1308         // pause all game sounds
1309         beam_pause_sounds();
1310         audiostream_pause_all();
1311
1312         common_set_interface_palette("BriefingPalette");  // set the interface palette
1313         Ui_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
1314         Ui_window.set_mask_bmap(Hud_mission_log_mask_fname[gr_screen.res]);
1315
1316         for (i=0; i<NUM_BUTTONS; i++) {
1317                 b = &Buttons[gr_screen.res][i];
1318
1319                 b->button.create(&Ui_window, "", b->x, b->y, 60, 30, (i < 2), 1);
1320                 // set up callback for when a mouse first goes over a button
1321                 b->button.set_highlight_action(common_play_highlight_sound);
1322                 b->button.set_bmaps(b->filename);
1323                 b->button.link_hotspot(b->hotspot);
1324         }
1325
1326         // add all strings      
1327         Ui_window.add_XSTR("Continue", 1069, Buttons[gr_screen.res][ACCEPT_BUTTON].xt,  Buttons[gr_screen.res][ACCEPT_BUTTON].yt, &Buttons[gr_screen.res][ACCEPT_BUTTON].button, UI_XSTR_COLOR_PINK);
1328         Ui_window.add_XSTR("Events", 1070, Buttons[gr_screen.res][SHOW_EVENTS_BUTTON].xt,  Buttons[gr_screen.res][SHOW_EVENTS_BUTTON].yt, &Buttons[gr_screen.res][SHOW_EVENTS_BUTTON].button, UI_XSTR_COLOR_GREEN);
1329         Ui_window.add_XSTR("Objectives", 1071, Buttons[gr_screen.res][SHOW_OBJS_BUTTON].xt,  Buttons[gr_screen.res][SHOW_OBJS_BUTTON].yt, &Buttons[gr_screen.res][SHOW_OBJS_BUTTON].button, UI_XSTR_COLOR_GREEN);
1330         Ui_window.add_XSTR("Messages", 1072, Buttons[gr_screen.res][SHOW_MSGS_BUTTON].xt,  Buttons[gr_screen.res][SHOW_MSGS_BUTTON].yt, &Buttons[gr_screen.res][SHOW_MSGS_BUTTON].button, UI_XSTR_COLOR_GREEN);
1331
1332         // set up hotkeys for buttons so we draw the correct animation frame when a key is pressed
1333         Buttons[gr_screen.res][SCROLL_UP_BUTTON].button.set_hotkey(KEY_UP);
1334         Buttons[gr_screen.res][SCROLL_DOWN_BUTTON].button.set_hotkey(KEY_DOWN);
1335
1336         Background_bitmap = bm_load(Hud_mission_log_fname[gr_screen.res]);
1337         // Status_bitmap = bm_load(Hud_mission_log_status_fname[gr_screen.res]);
1338
1339         message_log_init_scrollback(Hud_mission_log_list_coords[gr_screen.res][2]);
1340         if (Scrollback_mode == SCROLLBACK_MODE_EVENT_LOG)
1341                 Scroll_max = Num_log_lines * gr_get_font_height();
1342         else if (Scrollback_mode == SCROLLBACK_MODE_OBJECTIVES)
1343                 Scroll_max = Num_obj_lines * gr_get_font_height();
1344         else
1345                 Scroll_max = hud_query_scrollback_size();
1346
1347         Num_obj_lines = ML_objectives_init(Hud_mission_log_list_coords[gr_screen.res][0], Hud_mission_log_list_coords[gr_screen.res][1], Hud_mission_log_list_coords[gr_screen.res][2], Hud_mission_log_list_objective_x_coord[gr_screen.res]);
1348         hud_scroll_reset();
1349 }
1350
1351 void hud_scrollback_close()
1352 {
1353         ML_objectives_close();
1354         message_log_shutdown_scrollback();
1355         if (Background_bitmap >= 0)
1356                 bm_unload(Background_bitmap);
1357         //if (Status_bitmap >= 0)
1358         //      bm_unload(Status_bitmap);
1359
1360         Ui_window.destroy();
1361         common_free_interface_palette();                // restore game palette
1362         game_flush();
1363
1364         // unpause all game sounds
1365         beam_unpause_sounds();
1366         audiostream_unpause_all();
1367
1368 }
1369
1370 void hud_scrollback_do_frame(float frametime)
1371 {
1372         int i, k, x, y;
1373         int font_height = gr_get_font_height();
1374
1375         k = Ui_window.process();
1376         switch (k) {
1377                 case KEY_RIGHT:
1378                 case KEY_TAB:
1379                         if (Scrollback_mode == SCROLLBACK_MODE_OBJECTIVES) {
1380                                 Scrollback_mode = SCROLLBACK_MODE_MSGS_LOG;
1381                                 Scroll_max = hud_query_scrollback_size();
1382                                 hud_scroll_reset();
1383
1384                         } else if (Scrollback_mode == SCROLLBACK_MODE_MSGS_LOG) {
1385                                 Scrollback_mode = SCROLLBACK_MODE_EVENT_LOG;
1386                                 Scroll_max = Num_log_lines * gr_get_font_height();
1387                                 hud_scroll_reset();
1388
1389                         } else {
1390                                 Scrollback_mode = SCROLLBACK_MODE_OBJECTIVES;
1391                                 Scroll_max = Num_obj_lines * gr_get_font_height();
1392                                 Scroll_offset = 0;
1393                         }
1394
1395                         break;
1396
1397                 case KEY_LEFT:
1398                 case KEY_SHIFTED | KEY_TAB:
1399                         if (Scrollback_mode == SCROLLBACK_MODE_OBJECTIVES) {
1400                                 Scrollback_mode = SCROLLBACK_MODE_EVENT_LOG;
1401                                 Scroll_max = Num_log_lines * gr_get_font_height();
1402                                 hud_scroll_reset();
1403
1404                         } else if (Scrollback_mode == SCROLLBACK_MODE_MSGS_LOG) {
1405                                 Scrollback_mode = SCROLLBACK_MODE_OBJECTIVES;
1406                                 Scroll_max = Num_obj_lines * gr_get_font_height();
1407                                 Scroll_offset = 0;
1408
1409                         } else {
1410                                 Scrollback_mode = SCROLLBACK_MODE_MSGS_LOG;
1411                                 Scroll_max = hud_query_scrollback_size();
1412                                 hud_scroll_reset();
1413                         }
1414
1415                         break;
1416
1417                 case KEY_PAGEUP:
1418                         hud_page_scroll_list(1);
1419                         break;
1420
1421                 case KEY_PAGEDOWN:
1422                         hud_page_scroll_list(0);
1423                         break;
1424
1425                 case KEY_ENTER:
1426                 case KEY_CTRLED | KEY_ENTER:
1427                 case KEY_ESC:                   
1428                         hud_scrollback_exit();
1429                         break;
1430
1431                 case KEY_F1:  // show help overlay
1432                         break;
1433
1434                 case KEY_F2:  // goto options screen
1435                         gameseq_post_event(GS_EVENT_OPTIONS_MENU);
1436                         break;
1437         }       // end switch
1438
1439         for (i=0; i<NUM_BUTTONS; i++){
1440                 if (Buttons[gr_screen.res][i].button.pressed()){
1441                         hud_scrollback_button_pressed(i);               
1442                 }
1443         }
1444
1445         GR_MAYBE_CLEAR_RES(Background_bitmap);
1446         if (Background_bitmap >= 0) {
1447                 gr_set_bitmap(Background_bitmap);
1448                 gr_bitmap(0, 0);
1449         }
1450
1451         /*
1452         if ((Scrollback_mode == SCROLLBACK_MODE_OBJECTIVES) && (Status_bitmap >= 0)) {
1453                 gr_set_bitmap(Status_bitmap);
1454                 gr_bitmap(Hud_mission_log_status_coords[gr_screen.res][0], Hud_mission_log_status_coords[gr_screen.res][1]);
1455         }
1456         */
1457
1458         // draw the objectives key at the bottom of the ingame objectives screen
1459         if (Scrollback_mode == SCROLLBACK_MODE_OBJECTIVES) {
1460                 ML_render_objectives_key();
1461         }
1462
1463         Ui_window.draw();
1464
1465         if (Scrollback_mode == SCROLLBACK_MODE_EVENT_LOG) {
1466                 Buttons[gr_screen.res][SHOW_EVENTS_BUTTON].button.draw_forced(2);
1467                 mission_log_scrollback(Scroll_offset, Hud_mission_log_list_coords[gr_screen.res][0], Hud_mission_log_list_coords[gr_screen.res][1], Hud_mission_log_list_coords[gr_screen.res][2], Hud_mission_log_list_coords[gr_screen.res][3]);
1468
1469         } else if (Scrollback_mode == SCROLLBACK_MODE_OBJECTIVES) {
1470                 Buttons[gr_screen.res][SHOW_OBJS_BUTTON].button.draw_forced(2);
1471                 ML_objectives_do_frame(Scroll_offset);
1472
1473         } else {
1474                 line_node *node_ptr;
1475
1476                 Buttons[gr_screen.res][SHOW_MSGS_BUTTON].button.draw_forced(2);
1477 //              y = ((LIST_H / font_height) - 1) * font_height;
1478                 y = 0;
1479                 if ( !EMPTY(&Msg_scrollback_used_list) && HUD_msg_inited ) {
1480                         node_ptr = GET_FIRST(&Msg_scrollback_used_list);
1481                         i = 0;
1482                         while ( node_ptr != END_OF_LIST(&Msg_scrollback_used_list) ) {
1483                                 if ((node_ptr->source == HUD_SOURCE_HIDDEN) || (i++ < Scroll_offset)) {
1484                                         node_ptr = GET_NEXT(node_ptr);
1485
1486                                 } else {
1487                                         switch (node_ptr->source) {
1488                                                 case HUD_SOURCE_FRIENDLY:
1489                                                         SET_COLOR_FRIENDLY;
1490                                                         break;
1491
1492                                                 case HUD_SOURCE_HOSTILE:
1493                                                         SET_COLOR_HOSTILE;
1494                                                         break;
1495
1496                                                 case HUD_SOURCE_NEUTRAL:
1497                                                         SET_COLOR_NEUTRAL;
1498                                                         break;
1499
1500                                                 case HUD_SOURCE_UNKNOWN:
1501                                                         SET_COLOR_UNKNOWN;
1502                                                         break;
1503
1504                                                 case HUD_SOURCE_TRAINING:
1505                                                         gr_set_color_fast(&Color_bright_blue);
1506                                                         break;
1507
1508                                                 case HUD_SOURCE_TERRAN_CMD:
1509                                                         gr_set_color_fast(&Color_bright_white);
1510                                                         break;
1511
1512                                                 case HUD_SOURCE_IMPORTANT:
1513                                                 case HUD_SOURCE_FAILED:
1514                                                 case HUD_SOURCE_SATISFIED:
1515                                                         gr_set_color_fast(&Color_bright_white);
1516                                                         break;
1517
1518                                                 default:
1519                                                         gr_set_color_fast(&Color_text_normal);
1520                                                         break;
1521                                         }
1522
1523                                         if (node_ptr->time)
1524                                                 gr_print_timestamp(Hud_mission_log_list_coords[gr_screen.res][0], Hud_mission_log_list_coords[gr_screen.res][1] + y, node_ptr->time);
1525
1526                                         x = Hud_mission_log_list2_coords[gr_screen.res][0] + node_ptr->x;
1527                                         gr_printf(x, Hud_mission_log_list_coords[gr_screen.res][1] + y, "%s", node_ptr->text);
1528                                         if (node_ptr->underline_width)
1529                                                 gr_line(x, Hud_mission_log_list_coords[gr_screen.res][1] + y + font_height - 1, x + node_ptr->underline_width, Hud_mission_log_list_coords[gr_screen.res][1] + y + font_height - 1);
1530
1531                                         if ((node_ptr->source == HUD_SOURCE_FAILED) || (node_ptr->source == HUD_SOURCE_SATISFIED)) {
1532                                                 // draw goal icon
1533                                                 if (node_ptr->source == HUD_SOURCE_FAILED)
1534                                                         gr_set_color_fast(&Color_bright_red);
1535                                                 else
1536                                                         gr_set_color_fast(&Color_bright_green);
1537
1538                                                 i = Hud_mission_log_list_coords[gr_screen.res][1] + y + font_height / 2 - 1;
1539                                                 gr_circle(Hud_mission_log_list2_coords[gr_screen.res][0] - 6, i, 5);
1540
1541                                                 gr_set_color_fast(&Color_bright);
1542                                                 gr_line(Hud_mission_log_list2_coords[gr_screen.res][0] - 10, i, Hud_mission_log_list2_coords[gr_screen.res][0] - 8, i);
1543                                                 gr_line(Hud_mission_log_list2_coords[gr_screen.res][0] - 6, i - 4, Hud_mission_log_list2_coords[gr_screen.res][0] - 6, i - 2);
1544                                                 gr_line(Hud_mission_log_list2_coords[gr_screen.res][0] - 4, i, Hud_mission_log_list2_coords[gr_screen.res][0] - 2, i);
1545                                                 gr_line(Hud_mission_log_list2_coords[gr_screen.res][0] - 6, i + 2, Hud_mission_log_list2_coords[gr_screen.res][0] - 6, i + 4);
1546                                         }
1547
1548                                         y += font_height + node_ptr->y;
1549                                         node_ptr = GET_NEXT(node_ptr);
1550                                         if (y + font_height > Hud_mission_log_list_coords[gr_screen.res][3])
1551                                                 break;
1552                                 }
1553                         }
1554                 }
1555         }
1556
1557         gr_set_color_fast(&Color_text_heading);
1558         gr_print_timestamp(Hud_mission_log_time_coords[gr_screen.res][0], Hud_mission_log_time_coords[gr_screen.res][1] - font_height, (int) (f2fl(Missiontime) * 1000));
1559         gr_string(Hud_mission_log_time2_coords[gr_screen.res][0], Hud_mission_log_time_coords[gr_screen.res][1] - font_height, XSTR( "Current time", 289));
1560         gr_flip();
1561 }
1562
1563 void hud_scrollback_exit()
1564 {
1565         gameseq_post_event(GS_EVENT_PREVIOUS_STATE);
1566 }
1567