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