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