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