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