]> icculus.org git repositories - taylor/freespace2.git/blob - src/hud/hud.cpp
automatic language selection support
[taylor/freespace2.git] / src / hud / hud.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/HUD.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  *
15  * C module that contains all the HUD functions at a high level
16  *
17  * $Log$
18  * Revision 1.4  2003/05/18 03:55:30  taylor
19  * automatic language selection support
20  *
21  * Revision 1.3  2002/06/17 06:33:09  relnev
22  * ryan's struct patch for gcc 2.95
23  *
24  * Revision 1.2  2002/06/09 04:41:21  relnev
25  * added copyright header
26  *
27  * Revision 1.1.1.1  2002/05/03 03:28:09  root
28  * Initial import.
29  *
30  * 
31  * 58    10/28/99 2:04a Jefff
32  * some german specific coords.  
33  * 
34  * 57    10/25/99 5:43p Jefff
35  * added (and subsequently commented) some scoring debug code.  checked in
36  * to work on later
37  * 
38  * 56    9/09/99 3:55a Andsager
39  * Reset Hud_support_objnum to -1 when guage stops displaying
40  * 
41  * 55    9/01/99 11:16a Andsager
42  * Fix bug where support ship guage would not show up if second support
43  * ship called in whlile 1st one dying.
44  * 
45  * 54    8/23/99 1:49p Dave
46  * Fixed damage popup (hopefully)
47  * 
48  * 53    8/23/99 11:34a Dave
49  * Fixed shield intensity rendering problems.
50  * 
51  * 52    8/19/99 6:16p Jefff
52  * 
53  * 51    8/17/99 7:15p Jefff
54  * auto-target & auto-speed text drawn in code
55  * 
56  * 50    8/16/99 4:04p Dave
57  * Big honking checkin.
58  * 
59  * 49    8/09/99 3:47p Dave
60  * Fixed incorrect nebula regeneration. Default HUD to low-contrast in
61  * non-nebula missions.
62  * 
63  * 48    8/09/99 3:14p Dave
64  * Make "launch" warning gauge draw in code.
65  * 
66  * 47    8/05/99 2:05a Dave
67  * Fixes. Optimized detail level stuff.
68  * 
69  * 46    8/04/99 2:56p Jefff
70  * fixed black box behind pilot head in hi-res
71  * 
72  * 45    8/04/99 9:54a Andsager
73  * Auto target turrets on big ships.
74  * 
75  * 44    8/01/99 12:39p Dave
76  * Added HUD contrast control key (for nebula).
77  * 
78  * 43    7/31/99 4:15p Dave
79  * Fixed supernova particle velocities. Handle OBJ_NONE in target
80  * monitoring view. Properly use objectives notify gauge colors.
81  * 
82  * 42    7/31/99 1:16p Dave
83  * Use larger font for 1024 HUD flash text box. Make beam weapons aware of
84  * weapon subsystem damage on firing ship.
85  * 
86  * 41    7/26/99 10:41a Jefff
87  * added call to hud_maybe_show_damage() in hud_render_2d().  not sure how
88  * this got out in the 1st place.
89  * 
90  * 40    7/24/99 1:54p Dave
91  * Hud text flash gauge. Reworked dead popup to use 4 buttons in red-alert
92  * missions.
93  * 
94  * 39    7/22/99 4:00p Dave
95  * Fixed beam weapon muzzle glow rendering. Externalized hud shield info.
96  * 
97  * 38    7/21/99 8:10p Dave
98  * First run of supernova effect.
99  * 
100  * 37    7/21/99 3:19p Jefff
101  * adjusted subspace and red alert popup text coords
102  * 
103  * 36    7/19/99 2:13p Dave
104  * Added some new strings for Heiko.
105  * 
106  * 35    7/19/99 11:48a Jefff
107  * Countermeasure success sound added
108  * 
109  * 34    7/16/99 12:22p Jefff
110  * Added sound FX to objective popups
111  * 
112  * 33    7/15/99 7:16p Jefff
113  * Red Alert box is now red
114  * 
115  * 32    7/09/99 12:00a Andsager
116  * Added target box with distance for remote detonate weapons
117  * 
118  * 31    6/28/99 4:33p Jasenw
119  * Fixed coords for hi res engine wash gauge
120  * 
121  * 30    6/11/99 11:13a Dave
122  * last minute changes before press tour build.
123  * 
124  * 29    6/10/99 3:43p Dave
125  * Do a better job of syncing text colors to HUD gauges.
126  * 
127  * 28    6/08/99 1:14a Dave
128  * Multi colored hud test.
129  * 
130  * 27    6/07/99 4:20p Andsager
131  * Add HUD color for tagged object.  Apply to target and radar.
132  * 
133  * 26    5/28/99 5:36p Andsager
134  * stupid comment
135  * 
136  * 25    5/28/99 10:00a Andsager
137  * Make player hud target affected by Nebula range
138  * 
139  * 24    5/22/99 5:35p Dave
140  * Debrief and chatbox screens. Fixed small hi-res HUD bug.
141  * 
142  * 23    5/21/99 5:36p Andsager
143  * Put in high res engine wash gauge and approx coords
144  * 
145  * 22    5/21/99 1:44p Andsager
146  * Add engine wash gauge
147  * 
148  * 21    4/20/99 6:39p Dave
149  * Almost done with artillery targeting. Added support for downloading
150  * images on the PXO screen.
151  * 
152  * 20    2/25/99 4:19p Dave
153  * Added multiplayer_beta defines. Added cd_check define. Fixed a few
154  * release build warnings. Added more data to the squad war request and
155  * response packets.
156  * 
157  * 19    2/24/99 4:02p Dave
158  * Fixed weapon locking and homing problems for multiplayer dogfight mode.
159  * 
160  * 18    2/17/99 2:10p Dave
161  * First full run of squad war. All freespace and tracker side stuff
162  * works.
163  * 
164  * 17    2/03/99 8:37a Jasen
165  * Fixed dock in coords
166  * 
167  * 16    2/01/99 9:24a Jasen
168  * Fixed subspace and objectives displays for hi res.
169  * 
170  * 15    1/25/99 5:03a Dave
171  * First run of stealth, AWACS and TAG missile support. New mission type
172  * :)
173  * 
174  * 14    1/21/99 9:28p Dave
175  * Fixed damage gauge coords.
176  * 
177  * 13    1/07/99 9:05a Jasen
178  * coords, coords, coords
179  * 
180  * 12    1/06/99 3:24p Dave
181  * Fixed stupid code.
182  * 
183  * 11    1/06/99 3:14p Jasen
184  * more new coords
185  * 
186  * 10    1/06/99 2:33p Jasen
187  * updated coords
188  * 
189  * 9     1/06/99 1:27p Dave
190  * Removed duplicate global var.
191  * 
192  * 8     1/06/99 1:26p Dave
193  * Put in seperate X coords for "dock in" and the associated time value
194  * for the support ship gauge.
195  * 
196  * 7     12/28/98 3:17p Dave
197  * Support for multiple hud bitmap filenames for hi-res mode.
198  * 
199  * 6     12/21/98 5:02p Dave
200  * Modified all hud elements to be multi-resolution friendly.
201  * 
202  * 5     12/18/98 1:13a Dave
203  * Rough 1024x768 support for Direct3D. Proper detection and usage through
204  * the launcher.
205  * 
206  * 4     11/05/98 4:18p Dave
207  * First run nebula support. Beefed up localization a bit. Removed all
208  * conditional compiles for foreign versions. Modified mission file
209  * format.
210  * 
211  * 3     10/13/98 9:28a Dave
212  * Started neatening up freespace.h. Many variables renamed and
213  * reorganized. Added AlphaColors.[h,cpp]
214  * 
215  * 2     10/07/98 10:53a Dave
216  * Initial checkin.
217  * 
218  * 1     10/07/98 10:49a Dave
219  * 
220  * 223   8/28/98 3:28p Dave
221  * EMP effect done. AI effects may need some tweaking as required.
222  * 
223  * 222   8/25/98 1:48p Dave
224  * First rev of EMP effect. Player side stuff basically done. Next comes
225  * AI code.
226  * 
227  * 221   8/09/98 4:45p Lawrance
228  * center various HUD text - fixes problems in the German version
229  * 
230  * 220   6/18/98 10:10a Allender
231  * fixed compiler warnings
232  * 
233  * 219   6/17/98 11:03a Lawrance
234  * position subspace notify correctly for german version
235  * 
236  * 218   6/13/98 10:48p Lawrance
237  * Changed code to utilize proper fixed-space 1 character.
238  * 
239  * 217   6/13/98 6:01p Hoffoss
240  * Externalized all new (or forgot to be added) strings to all the code.
241  * 
242  * 216   6/12/98 2:49p Dave
243  * Patch 1.02 changes.
244  * 
245  * 215   6/09/98 10:31a Hoffoss
246  * Created index numbers for all xstr() references.  Any new xstr() stuff
247  * added from here on out should be added to the end if the list.  The
248  * current list count can be found in FreeSpace.cpp (search for
249  * XSTR_SIZE).
250  * 
251  * 214   6/01/98 11:43a John
252  * JAS & MK:  Classified all strings for localization.
253  * 
254  * 213   5/23/98 4:14p John
255  * Added code to preload textures to video card for AGP.   Added in code
256  * to page in some bitmaps that weren't getting paged in at level start.
257  * 
258  * 212   5/17/98 3:32p Lawrance
259  * Allow red alert orders to get downloaded when in an out-of-cockpit view
260  * 
261  * 211   5/15/98 8:36p Lawrance
262  * Add 'target ship that last sent transmission' target key
263  * 
264  * 210   5/10/98 5:28p Lawrance
265  * Ensure hud messages and talking heads show up when viewing from another
266  * ship
267  * 
268  * 209   5/10/98 12:11a Lawrance
269  * Fix a couple of problems with 2D gauges showing up in external views
270  * 
271  * 208   5/09/98 4:52p Lawrance
272  * Implement padlock view (up/rear/left/right)
273  * 
274  * 207   5/09/98 12:20a Lawrance
275  * Show hud messages in all views
276  * 
277  * 206   5/08/98 5:32p Lawrance
278  * Allow cargo scanning even if target gauge is disabled
279  * 
280  * 205   5/08/98 10:13a Lawrance
281  * Don't allow targeting of ships that have SF_EXPLODED flag set
282  * 
283  * 204   5/07/98 1:01a Chad
284  * Yet another hud gauage which shouldn't be rendered as a multiplayer
285  * observer.
286  * 
287  * 203   5/04/98 12:08p Ed
288  * from allender:  move hud_target_change_check() after code which does
289  * possible auto target change.  Fixed multiplayer problem where locking
290  * subsys does not match ship currently targeted
291  * 
292  * 202   5/04/98 6:12p Lawrance
293  * Write generic function hud_end_string_at_first_hash_symbol(), to use in
294  * various spots on the HUD
295  * 
296  * 201   4/30/98 3:32p Lawrance
297  * Cull dead/departed ships from escort ship in hud_update_frame()
298  * 
299  * 200   4/23/98 10:24p Mike
300  * Int3(), then recover gracefully from some error in which ship to be
301  * repaired is killed. 
302  * 
303  * $NoKeywords: $
304  *
305 */
306
307 #include "pstypes.h"
308 #include "freespace.h"
309 #include "systemvars.h"
310 #include "hud.h"
311 #include "hudtarget.h"
312 #include "hudreticle.h"
313 #include "hudmessage.h"
314 #include "sound.h"
315 #include "player.h"
316 #include "multi.h"
317 #include "multiutil.h"
318 #include "gamesnd.h"
319 #include "hudsquadmsg.h"
320 #include "timer.h"
321 #include "eventmusic.h"
322 #include "hudlock.h"
323 #include "hudets.h"
324 #include "2d.h"
325 #include "3d.h"
326 #include "ai.h"
327 #include "aigoals.h"
328 #include "hudescort.h"
329 #include "hudshield.h"
330 #include "linklist.h"
331 #include "hudtargetbox.h"
332 #include "missionmessage.h"
333 #include "missiontraining.h"
334 #include "bmpman.h"
335 #include "radar.h"
336 #include "hudobserver.h"
337 #include "hudtargetbox.h"
338 #include "hudconfig.h"
339 #include "missiongoals.h"
340 #include "asteroid.h"
341 #include "starfield.h"
342 #include "hudwingmanstatus.h"
343 #include "multi_voice.h"
344 #include "multi_pmsg.h"
345 #include "redalert.h"
346 #include "emp.h"
347 #include "alphacolors.h"
348 #include "localize.h"
349 #include "supernova.h"
350 #include "font.h"
351
352 // new values for HUD alpha
353 #define HUD_NEW_ALPHA_DIM                               80      
354 #define HUD_NEW_ALPHA_NORMAL                    120
355 #define HUD_NEW_ALPHA_BRIGHT                    220
356
357 // high contrast
358 #define HUD_NEW_ALPHA_DIM_HI                    130
359 #define HUD_NEW_ALPHA_NORMAL_HI         190
360 #define HUD_NEW_ALPHA_BRIGHT_HI         255
361
362 // globals that will control the color of the HUD gauges
363 int HUD_color_red = 0;
364 int HUD_color_green = 255;
365 int HUD_color_blue = 0;
366 int HUD_color_alpha = HUD_COLOR_ALPHA_DEFAULT;          // 1 -> HUD_COLOR_ALPHA_USER_MAX
367
368 int HUD_contrast = 0;                                                                           // high or lo contrast (for nebula, etc)
369
370 color HUD_color_defaults[HUD_NUM_COLOR_LEVELS];         // array of colors with different alpha blending
371 color HUD_color_debug;                                                                          // grey debug text shown on HUD
372
373 static int Player_engine_snd_loop = -1;
374
375 // animations for damages gauges
376 hud_anim Target_static;
377 hud_anim        Radar_static;
378
379 // HUD render frame offsets
380 float HUD_offset_x = 0.0f;
381 float HUD_offset_y = 0.0f;
382
383 // Global: integrity of player's target
384 float Pl_target_integrity;
385
386 static int Hud_last_can_target; // whether Player is able to target in the last frame
387 static int Hud_can_target_timer;        // timestamp to allow target gauge to draw static once targeting functions are not allowed
388
389 // centered text message gauges (collision, emp, etc)
390 char Hud_text_flash[512] = "";
391 int Hud_text_flash_coords[GR_NUM_RESOLUTIONS][2] = {
392         { // GR_640
393                 -1, 172
394         },
395         { // GR_1024
396                 -1, 275
397         }
398 };
399 void hud_init_text_flash_gauge();
400 void hud_start_text_flash(char *txt, int t);
401 void hud_maybe_show_text_flash_icon();
402
403
404 // multiplayer messaging text
405 int Multi_msg_coords[GR_NUM_RESOLUTIONS][2] = {
406         { // GR_640
407                 5, 150
408         },
409         { // GR_1024
410                 8, 240
411         }
412 };
413
414 // multiplayer voice stuff
415 int Voice_coords[GR_NUM_RESOLUTIONS][2] = {
416         { // GR_640
417                 5, 165
418         },
419         { // GR_1024
420                 8, 255
421         }
422 };
423
424 // redalert downloading new orders text
425 int Red_text_coords[GR_NUM_RESOLUTIONS][2] = {
426         { // GR_640
427                 -1, 116
428         },
429         { // GR_1024
430                 -1, 186
431         }
432 };
433 int Red_text_val_coords[GR_NUM_RESOLUTIONS][2] = {
434         { // GR_640
435                 -1, 124
436         },
437         { // GR_1024
438                 -1, 194
439         }
440 };
441
442 // subspace popup
443 int Subspace_text_coords[GR_NUM_RESOLUTIONS][2] = {
444         { // GR_640
445                 -1, 116
446         },
447         { // GR_1024
448                 -1, 186
449         }
450 };
451 int Subspace_text_val_coords[GR_NUM_RESOLUTIONS][2] = {
452         { // GR_640
453                 100, 124
454         },
455         { // GR_1024
456                 140, 194
457         }
458 };
459
460 // message text coords
461 int Head_message_coords[GR_NUM_RESOLUTIONS][2] = {
462         { // GR_640
463                 7, 37
464         },
465         { // GR_1024
466                 11, 57
467         }
468 };
469
470 // ping text coords
471 int Ping_coords[GR_NUM_RESOLUTIONS][2] = {
472         { // GR_640
473                 560, 3
474         },
475         { // GR_1024
476                 896, 5
477         }
478 };
479
480 // supernova coords
481 int Supernova_coords[GR_NUM_RESOLUTIONS][2] = {
482         { // GR_640
483                 100, 100
484         },
485         { // GR_1024
486                 170, 170
487         }
488 };
489         
490 // used to draw the netlag icon on the HUD
491 hud_frames Netlag_icon;
492 int Netlag_icon_loaded=0;
493 int Netlag_coords[GR_NUM_RESOLUTIONS][2] = {
494         { // GR_640
495                 386, 331
496         },
497         { // GR_1024
498                 627, 529
499         }
500 };
501 char Netlag_fname[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN] = {
502         "netlag1",
503         "netlag1"
504 };
505
506 // used to draw the kills gauge
507 hud_frames Kills_gauge;
508 int Kills_gauge_loaded = 0;
509 int Kills_gauge_coords[GR_NUM_RESOLUTIONS][2] = {
510         { // GR_640
511                 497, 361
512         },
513         { // GR_1024
514                 880, 624
515         }
516 };
517 int Kills_text_coords[GR_NUM_RESOLUTIONS][2] = {
518         { // GR_640
519                 503, 365
520         },
521         { // GR_1024
522                 886, 628
523         }
524 };
525
526 // for German version
527 int Kills_text_val_coords_gr[GR_NUM_RESOLUTIONS][2] = {
528         { // GR_640
529                 615, 365
530         },
531         { // GR_1024
532                 984, 628
533         }
534 };
535
536 int Kills_text_val_coords[GR_NUM_RESOLUTIONS][2] = {
537         { // GR_640
538                 571, 365
539         },
540         { // GR_1024
541                 954, 628
542         }
543 };
544
545 char Kills_fname[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN] = {
546         "kills1",
547         "kills1"
548 };
549
550 // used to draw border around a talking head
551 static hud_frames Head_frame_gauge;
552 static int Head_frame_gauge_loaded = 0;
553 int Head_frame_coords[GR_NUM_RESOLUTIONS][2] = {
554         { // GR_640
555                 5, 35
556         },
557         { // GR_1024
558                 5, 56
559         }
560 };
561 char Head_fname[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN] = {
562         "head1",
563         "head1"
564 };
565
566 // mission time frame
567 static hud_frames Mission_time_gauge;
568 static int Mission_time_gauge_loaded = 0;
569 int Mission_time_coords[GR_NUM_RESOLUTIONS][2] = {
570         { // GR_640
571                 587, 448
572         },
573         { // GR_1024
574                 969, 716
575         }
576 };
577 int Mission_time_text_coords[GR_NUM_RESOLUTIONS][2] = {
578         { // GR_640
579                 591, 452
580         },
581         { // GR_1024
582                 973, 720
583         }
584 };
585 int Mission_time_text_val_coords[GR_NUM_RESOLUTIONS][2] = {
586         { // GR_640
587                 613, 460
588         },
589         { // GR_640
590                 995, 728
591         }
592 };
593 char Mission_time_fname[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN] = {
594         "time1",
595         "time1"
596 };
597
598 // used to draw the hud support view
599 static hud_frames Support_view_gauge;
600 static int Support_view_gauge_loaded = 0;
601 static int Hud_support_view_active;
602 static int Hud_support_view_abort;              // active when we need to display abort message
603 static int Hud_support_view_fade;               // timer
604 static int Hud_support_obj_sig, Hud_support_objnum, Hud_support_target_sig;
605 int Support_view_coords[GR_NUM_RESOLUTIONS][2] = {
606         { // GR_640
607                 265, 334
608         },
609         { // GR_1024
610                 459, 534
611         }
612 };
613 int Support_text_coords[GR_NUM_RESOLUTIONS][2] = {
614         { // GR_640
615                 267, 335
616         },
617         { // GR_1024
618                 462, 536
619         }
620 };
621 int Support_text_val_coords[GR_NUM_RESOLUTIONS][2] = {
622         { // GR_640
623                 -1, 348
624         },
625         { // GR_1024
626                 -1, 546
627         }
628 };
629 int Support_text_dock_coords[GR_NUM_RESOLUTIONS][2] = {                 // "dock in" x coord
630         { // GR_640
631                 270, -1
632         },
633         { // GR_1024
634                 465, -1
635         }
636 };
637 int Support_text_dock_val_coords[GR_NUM_RESOLUTIONS][2] = {             // time value for "dock in" x coord
638         { // GR_640
639                 328, -1
640         },
641         { // GR_1024
642                 524, -1
643         }
644 };
645 char Support_fname[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN] = {
646         "support1",
647         "support1"
648 };
649
650 // damage gauge stuff
651 #define NUM_DAMAGE_GAUGES       3
652 static hud_frames Damage_gauges[NUM_DAMAGE_GAUGES];
653 static int Damage_gauges_loaded = 0;
654 char *Damage_gauge_fnames[GR_NUM_RESOLUTIONS][NUM_DAMAGE_GAUGES] = 
655 {
656         //XSTR:OFF
657         { // GR_640
658                 "damage1",
659                 "damage2",
660                 "damage3",
661         },
662         { // GR_1024
663                 "damage1",
664                 "damage2",
665                 "damage3",
666         }
667 //XSTR:ON
668 };
669 int Damage_gauge_line_h[GR_NUM_RESOLUTIONS] = { 
670         9, 
671         9
672 };
673 int Damage_gauge_coords[GR_NUM_RESOLUTIONS][2][2] = {
674         { // GR_640
675                 {245, 38},
676                 {245, 63}
677         },
678         { // GR_1024
679                 {440, 61},
680                 {440, 86}
681         }
682         // These #'s seem to work, although I really don't know why. Frankly, it frightens me,
683         // because it means the 640 coords _shouldn't_. This may be due to D3D strangeness, so
684         // we'll have to investigate when we get hi-res Glide in.
685 };
686 int Damage_text_coords[GR_NUM_RESOLUTIONS][2] = {
687         { // GR_640
688                 248, 40
689         },
690         { // GR_1024
691                 443, 63
692         }
693 };
694 int Hull_integ_coords[GR_NUM_RESOLUTIONS][2] = {
695         { // GR_640
696                 249, 53
697         },
698         { // GR_1024
699                 444, 76
700         }
701 };
702 int Hull_integ_val_coords[GR_NUM_RESOLUTIONS][2] = {
703         { // GR_640
704                 387, 53
705         },
706         { // GR_1024
707                 582, 76
708         },
709 };
710 int Damage_subsys_text_coords[GR_NUM_RESOLUTIONS][2] = {
711         { // GR_640
712                 249, 65
713         },
714         { // GR_1024
715                 444, 88
716         }
717 };
718
719
720 // flashing gauges
721 #define HUD_GAUGE_FLASH_DURATION                5000
722 #define HUD_GAUGE_FLASH_INTERVAL                200
723 int HUD_gauge_flash_duration[NUM_HUD_GAUGES];
724 int HUD_gauge_flash_next[NUM_HUD_GAUGES];
725 int HUD_gauge_bright;
726
727 // Objective display
728 typedef struct objective_display_info
729 {
730         int display_timer;
731         int goal_type;
732         int goal_status;
733         int goal_ntotal;
734         int goal_nresolved;
735
736 } objective_display_info;
737
738 static objective_display_info Objective_display;
739
740 static int                      Objective_display_gauge_inited=0;
741 static hud_frames       Objective_display_gauge;
742 int Objective_display_coords[GR_NUM_RESOLUTIONS][2] = {
743         { // GR_640
744                 245, 114
745         },
746         { // GR_1024
747                 436, 184
748         }
749 };
750 int Objective_text_coords[GR_NUM_RESOLUTIONS][2] = {
751         { // GR_640
752                 -1, 116
753         },
754         { // GR_1024
755                 -1, 186
756         }
757 };
758 int Objective_text_val_coords[GR_NUM_RESOLUTIONS][2] = {
759         { // GR_640
760                 -1, 125
761         },
762         { // GR_1024
763                 -1, 195
764         }
765 };
766 char Objective_fname[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN] = {
767         "objective1",
768         "objective1"
769 };
770
771 // Subspace notify display
772 static int Subspace_notify_active;
773 static int Objective_notify_active;
774 static int HUD_abort_subspace_timer = 1;
775
776 // used to track how player subsystems are getting damaged
777 typedef struct hud_subsys_info
778 {
779         float   last_str;
780         int     flash_duration_timestamp;
781 } hud_subsys_info;
782
783 static hud_subsys_info  Pl_hud_subsys_info[SUBSYSTEM_MAX];
784 static int                                      Pl_hud_next_flash_timestamp;
785 static int                                      Pl_hud_is_bright;
786
787 #define SUBSYS_DAMAGE_FLASH_DURATION    1800
788 #define SUBSYS_DAMAGE_FLASH_INTERVAL    100
789
790 // timers used for popup gauges
791 int HUD_popup_timers[NUM_HUD_GAUGES];
792
793 // forward declarations
794 void update_throttle_sound();
795 void hud_show_damage_popup();
796 void hud_damage_popup_init();
797 void hud_support_view_init();
798 void hud_gauge_flash_init();
799 void hud_objective_message_init();
800 void hud_maybe_display_objective_message();
801 void hud_stop_subspace_notify();
802 void hud_start_subspace_notify();
803 void hud_stop_objective_notify();
804 void hud_start_objective_notify();
805 int hud_subspace_notify_active();
806 int hud_objective_notify_active();
807 void hud_subspace_notify_abort();
808 void hud_maybe_display_subspace_notify();
809 void hud_init_netlag_icon();
810 void hud_maybe_show_netlag_icon();
811 void hud_maybe_display_red_alert();
812 void hud_init_kills_gauge();
813 void hud_show_kills_gauge();
814 int hud_maybe_render_emp_icon();
815 void hud_init_emp_icon();
816
817 //      Saturate a value in minv..maxv.
818 void saturate(int *i, int minv, int maxv)
819 {
820         if (*i < minv)
821                 *i = minv;
822         else if (*i > maxv)
823                 *i = maxv;
824 }
825
826 // init the colors used for the different shades of the HUD
827 void HUD_init_hud_color_array()
828 {
829         int i;
830
831         for ( i = 0; i < HUD_NUM_COLOR_LEVELS; i++ ) {
832                 gr_init_alphacolor( &HUD_color_defaults[i], HUD_color_red, HUD_color_green, HUD_color_blue, (i+1)*16 );
833         }
834 }
835
836 // HUD_init will call all the various HUD gauge init functions.  This function is called at the
837 // start of each mission (level)
838 void HUD_init_colors()
839 {
840         saturate(&HUD_color_red, 0, 255);
841         saturate(&HUD_color_green, 0, 255);
842         saturate(&HUD_color_blue, 0, 255);
843         saturate(&HUD_color_alpha, 0, HUD_COLOR_ALPHA_USER_MAX);
844
845         gr_init_alphacolor( &HUD_color_debug, 128, 255, 128, HUD_color_alpha*16 );
846         HUD_init_hud_color_array();
847
848         hud_init_targeting_colors();
849         hud_gauge_flash_init();
850 }
851
852 // The following global data is used to determine if we should change the engine sound.
853 // We only check if the throttle has changed every THROTTLE_SOUND_CHECK_INTERVAL ms, and
854 // then we make sure that the throttle has actually changed.  If it has changed, we start
855 // a new sound and/or adjust the volume.  This occurs in update_throttle_sound()
856 //
857 static float last_percent_throttle;
858 #define THROTTLE_SOUND_CHECK_INTERVAL   50      // in ms
859 static int throttle_sound_check_id;
860
861 // used for the display of damaged subsystems
862 typedef struct hud_subsys_damage
863 {
864         int     str;
865         int     type;
866         char    *name;
867 } hud_subsys_damage;
868
869 #define DAMAGE_FLASH_TIME 150
870 static int Damage_flash_bright;
871 static int Damage_flash_timer;
872
873 // initialize the timers used for popup gauges
874 void hud_init_popup_timers()
875 {
876         int i;
877         for (i=0; i<NUM_HUD_GAUGES; i++) {
878                 HUD_popup_timers[i] = timestamp(0);
879         }
880 }
881
882 // Load in the bitmap for the talking head gauge if required
883 void hud_init_talking_head_gauge()
884 {
885         // ensure the talking head border is loaded
886         if ( !Head_frame_gauge_loaded ) {
887                 Head_frame_gauge.first_frame = bm_load_animation(Head_fname[gr_screen.res], &Head_frame_gauge.num_frames);
888                 if ( Head_frame_gauge.first_frame == -1 ) {
889                         Warning(LOCATION, "Could not load in ani: Head_fname[gr_screen.res]\n");
890                 }
891                 Head_frame_gauge_loaded = 1;
892         }
893 }
894
895 // Load in the bitmap for the mission time gauge if required
896 void hud_init_mission_time_gauge()
897 {
898         // ensure the talking head border is loaded
899         if ( !Mission_time_gauge_loaded ) {
900                 Mission_time_gauge.first_frame = bm_load_animation(Mission_time_fname[gr_screen.res], &Mission_time_gauge.num_frames);
901                 if ( Mission_time_gauge.first_frame == -1 ) {
902                         Warning(LOCATION, "Could not load in ani: Mission_time_fname[gr_screen.res]\n");
903                 }
904                 Mission_time_gauge_loaded = 1;
905         }
906 }
907
908 // ----------------------------------------------------------------------
909 // HUD_init()
910 //
911 // Called each level to initalize HUD systems
912 //
913 void HUD_init()
914 {
915         HUD_init_colors();
916         hud_init_msg_window();
917         hud_init_targeting();
918         hud_init_reticle();
919         hud_shield_level_init();
920         hud_init_ets();
921         hud_targetbox_init();
922         hud_escort_init();
923         hud_damage_popup_init();
924         hud_support_view_init();
925         hud_init_squadmsg();            // initialize the vars needed for squadmate messaging
926         hud_init_popup_timers();
927         hud_objective_message_init();
928         hud_init_wingman_status_gauge();
929         hud_anim_init(&Target_static, Target_window_coords[gr_screen.res][0], Target_window_coords[gr_screen.res][1], NOX("TargetStatic"));
930         hud_targetbox_static_init();
931         hud_init_text_flash_gauge();
932         hud_init_netlag_icon(); 
933         hud_init_talking_head_gauge();
934         hud_init_mission_time_gauge();
935         hud_init_kills_gauge();
936         hud_stop_subspace_notify();
937         hud_stop_objective_notify();
938         hud_target_last_transmit_level_init();
939
940         throttle_sound_check_id = timestamp(THROTTLE_SOUND_CHECK_INTERVAL);
941         HUD_abort_subspace_timer = 1;
942         Hud_last_can_target = 1;
943         Hud_can_target_timer = 1;
944         last_percent_throttle = 0.0f;
945
946         // default to high contrast in the nebula
947         HUD_contrast = 0;
948         if(The_mission.flags & MISSION_FLAG_FULLNEB){
949                 HUD_contrast = 1;
950         } 
951 }
952
953 // return !0 if HUD is disabled (ie no gauges are shown/usable), otherwise return 0
954 int hud_disabled()
955 {
956         // if ( Ship_info[Player_ship->ship_info_index].species != SPECIES_TERRAN ) {
957                 //return 1;
958         //}
959
960         return 0;
961 }
962
963 // Determine if we should popup the weapons gauge on the HUD.
964 void hud_maybe_popup_weapons_gauge()
965 {
966         if ( hud_gauge_is_popup(HUD_WEAPONS_GAUGE) ) {
967                 ship_weapon *swp = &Player_ship->weapons;
968                 int                     i;
969
970                 for ( i = 0; i < swp->num_secondary_banks; i++ ) {
971                         if ( swp->secondary_bank_ammo[i] > 0 ) {
972                                 int ms_till_fire = timestamp_until(swp->next_secondary_fire_stamp[i]);
973                                 if ( ms_till_fire >= 1000 ) {
974                                         hud_gauge_popup_start(HUD_WEAPONS_GAUGE, 2500);
975                                 }
976                         }
977                 }
978         }
979 }
980
981 // hud_update_frame() will update hud systems
982 //
983 // This function updates those parts of the hud that are not dependant on the
984 // rendering of the hud.
985 void hud_update_frame()
986 {
987         object  *targetp;
988         int             can_target;
989
990         update_throttle_sound();
991         hud_check_reticle_list();
992         hud_wingman_status_update();
993
994         // Check hotkey selections to see if any ships need to be removed
995         hud_prune_hotkeys();
996
997         // Remove dead/departed ships from the escort list
998         hud_escort_cull_list();
999
1000         hud_update_reticle( Player );
1001         hud_shield_hit_update();
1002         hud_maybe_popup_weapons_gauge();        
1003
1004         // if emp is active we have to allow targeting by the "random emp" system
1005         // we will intercept player targeting requests in hud_sensors_ok() when checking key commands
1006         // DB - 8/24/98
1007         can_target = hud_sensors_ok(Player_ship, 0);
1008         if(emp_active_local()){
1009                 can_target = 1;
1010         }
1011         if ( !can_target && Hud_last_can_target ) {
1012                 Hud_can_target_timer = timestamp(1200);         
1013         }
1014         Hud_last_can_target = can_target;
1015
1016         if ( timestamp_elapsed(Hud_can_target_timer) ) {
1017                 if ( (Player_ai->target_objnum != -1) && !can_target ){
1018                         Player_ai->target_objnum = -1;
1019                 }
1020         }
1021
1022         // if there is no target, check if auto-targeting is enabled, and select new target
1023         int retarget = 0;
1024         int retarget_turret = 0;
1025
1026         if (Player_ai->target_objnum == -1){
1027                 retarget = 1;
1028         } else if (Objects[Player_ai->target_objnum].type == OBJ_SHIP) {
1029                 if (Ships[Objects[Player_ai->target_objnum].instance].flags & SF_DYING){
1030                         if (timestamp_elapsed(Ships[Objects[Player_ai->target_objnum].instance].final_death_time)) {
1031                                 retarget = 1;
1032                         }
1033                 }
1034         }
1035
1036         // check if big ship and currently selected subsys is turret and turret is dead
1037         // only do this is not retargeting
1038         if ((!retarget) && (Player_ai->target_objnum != -1)) {
1039                 if (Objects[Player_ai->target_objnum].type == OBJ_SHIP) {
1040                         if ( !(Ships[Objects[Player_ai->target_objnum].instance].flags & SF_DYING) ) {
1041                                 if ( Ship_info[Ships[Objects[Player_ai->target_objnum].instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP) ) {
1042                                         ship_subsys *ss = Player_ai->targeted_subsys;
1043                                         if (ss != NULL) {
1044                                                 if ((ss->system_info->type == SUBSYSTEM_TURRET) && (ss->current_hits == 0)) {
1045                                                         retarget_turret = 1;
1046                                                 }
1047                                         }
1048                                 }
1049                         }
1050                 }
1051         }
1052
1053         if ( retarget && can_target ) {
1054                 Player_ai->current_target_is_locked = 0;
1055                 if ( Players[Player_num].flags & PLAYER_FLAGS_AUTO_TARGETING ) {
1056                         Player_ai->target_objnum = -1;
1057                         hud_target_auto_target_next();
1058                 }
1059         }
1060
1061         if (retarget_turret && can_target) {
1062                 Assert(!retarget);
1063                 // get closest weighted live turret
1064                 // hud_target_closest(OBJ_INDEX(Player_obj), FALSE, FALSE);
1065                 void hud_update_closest_turret();
1066                 hud_update_closest_turret();
1067         }
1068
1069         hud_target_change_check();
1070
1071         if (Player_ai->target_objnum == -1) {
1072                 if ( Target_static_looping != -1 ) {
1073                         snd_stop(Target_static_looping);
1074                 }
1075                 return;
1076         }
1077
1078         targetp = &Objects[Player_ai->target_objnum];
1079
1080
1081         int stop_targetting_this_thing = 0;
1082
1083         // check to see if the target is still alive
1084         if ( targetp->flags&OF_SHOULD_BE_DEAD ) {
1085                 stop_targetting_this_thing = 1;
1086         }
1087
1088         Player->target_is_dying = FALSE;
1089         ship    *target_shipp = NULL;
1090         
1091         if ( targetp->type == OBJ_SHIP ) {
1092                 Assert ( targetp->instance >=0 && targetp->instance < MAX_SHIPS );
1093                 target_shipp = &Ships[targetp->instance];
1094                 Player->target_is_dying = target_shipp->flags & SF_DYING;
1095
1096                 // If it is warping out (or exploded), turn off targeting
1097                 if ( target_shipp->flags & (SF_DEPART_WARP|SF_EXPLODED) ) {
1098                         stop_targetting_this_thing = 1;
1099                 }
1100         }
1101
1102         // Check if can still be seen in Nebula
1103         if ( hud_target_invalid_awacs(targetp) ) {
1104                 stop_targetting_this_thing = 1;
1105         }
1106
1107         // If this was found to be something we shouldn't
1108         // target anymore, just remove it
1109         if ( stop_targetting_this_thing )       {
1110                 Player_ai->target_objnum = -1;
1111                 Player_ai->targeted_subsys = NULL;
1112                 hud_stop_looped_locking_sounds();
1113         }
1114         
1115         if (Player->target_is_dying) {
1116                 hud_stop_looped_locking_sounds();
1117                 if ( Players[Player_num].flags & PLAYER_FLAGS_AUTO_TARGETING ) {
1118                         hud_target_auto_target_next();
1119                 }
1120         }
1121
1122         // Switch to battle track when a targeted ship is hostile and within BATTLE_START_MIN_TARGET_DIST
1123         if (targetp->type == OBJ_SHIP && Event_Music_battle_started == 0 ) {
1124                 Assert( target_shipp != NULL );
1125
1126                 if (opposing_team_mask(Player_ship->team)) {
1127                         float   dist_to_target;
1128
1129                         dist_to_target = vm_vec_dist_quick(&targetp->pos, &Player_obj->pos);
1130                         if (dist_to_target < BATTLE_START_MIN_TARGET_DIST) {
1131
1132                                 // If the target has an AI class of none, it is a Cargo, NavBuoy or other non-aggressive
1133                                 // ship, so don't start the battle music        
1134                                 if (stricmp(Ai_class_names[Ai_info[target_shipp->ai_index].ai_class], NOX("none")))
1135                                         event_music_battle_start();
1136                         }
1137                 }
1138         }
1139
1140         // Since we need to reference the player's target integrity in several places this upcoming 
1141         // frame, only calculate once here
1142         if ( target_shipp ) {
1143                 float initial_hull;
1144                 initial_hull = Ship_info[target_shipp->ship_info_index].initial_hull_strength;
1145                 if (  initial_hull <= 0 ) {
1146                         Int3(); // illegal initial hull strength
1147                         Pl_target_integrity = 0.0f;
1148                 } else {
1149                         Pl_target_integrity = targetp->hull_strength / initial_hull;
1150                         if (Pl_target_integrity < 0)
1151                                 Pl_target_integrity = 0.0f;
1152                 }
1153         }
1154
1155         hud_update_cargo_scan_sound();
1156
1157 }
1158
1159 void HUD_render_forward_icon(object *objp)
1160 {
1161         vertex  v0;
1162         vector  p0;
1163
1164         vm_vec_scale_add(&p0, &objp->pos, &objp->orient.v.fvec, 100.0f);
1165         g3_rotate_vertex(&v0, &p0);
1166
1167         gr_set_color(255, 0, 0);
1168         if ((!(v0.flags & PF_OVERFLOW)) && (v0.codes == 0)) // make sure point projected
1169                 g3_draw_sphere(&v0, 1.25f);
1170         else if (v0.codes != 0) { // target center is not on screen
1171                 // draw the offscreen indicator at the edge of the screen where the target is closest to
1172                 hud_draw_offscreen_indicator(&v0, &p0);
1173         }
1174 }
1175
1176 // Draw white brackets around asteroids which has the AF_DRAW_BRACKETS flag set
1177 void hud_show_asteroid_brackets()
1178 {
1179         if ( hud_sensors_ok(Player_ship, 0) ) {
1180                 asteroid_show_brackets();
1181         }
1182 }
1183
1184 // Draw radar gauge on the HUD
1185 void hud_show_radar()
1186 {
1187         if ( hud_disabled() ) {
1188                 return;
1189         }
1190
1191         if (!(Viewer_mode & (VM_EXTERNAL | VM_SLEWED | VM_CHASE | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY ))) {
1192                 if ( Game_detail_flags & DETAIL_FLAG_HUD )      {
1193                         if ( hud_gauge_active(HUD_RADAR) ) {
1194                                 HUD_reset_clip();
1195                                 radar_frame_render(flFrametime);
1196                         }
1197                 }
1198         }
1199 }
1200
1201 // Render model of target in the target view box
1202 void hud_show_target_model()
1203 {
1204         if ( hud_disabled() ) {
1205                 return;
1206         }
1207
1208         // display the miniature model of the target in the target box and shade
1209         if ( Game_detail_flags & DETAIL_FLAG_HUD )      {
1210                 if (!(Viewer_mode & (VM_EXTERNAL | VM_SLEWED | VM_CHASE | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY)))
1211                         hud_render_target_model();
1212         }
1213 }
1214
1215 void hud_show_common_3d_gauges(float frametime, int in_cockpit)
1216 {
1217         // draw boxes around current selection set, if any
1218         hud_show_selection_set();
1219
1220         // draw the targeting data around any message sender
1221         hud_show_message_sender();
1222
1223         // draw brackets around asteroids is necessary
1224         hud_show_asteroid_brackets();
1225
1226         // draw targetting data around the current target
1227         hud_show_targeting_gauges(frametime, in_cockpit);
1228
1229         // draw brackets and distance to remote detonate missile
1230         hud_show_remote_detonate_missile();
1231 }
1232
1233 // Render gauges that need to be between a g3_start_frame() and a g3_end_frame()
1234 void HUD_render_3d(float frametime)
1235 {
1236         Player->subsys_in_view = -1;
1237
1238         if ( hud_disabled() ) {
1239                 return;
1240         }
1241
1242         if (!(Viewer_mode & (VM_EXTERNAL | VM_SLEWED | VM_CHASE | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY))) {
1243
1244                 hud_show_common_3d_gauges(frametime, 1);
1245
1246                 //      Show all homing missiles locked onto the player.
1247                 //      Currently not supporting a way to toggle this as I'm not sure we'll stick wtih this gauge. -- MK, 3/17/97.
1248                 if ( hud_gauge_active(HUD_MISSILE_WARNING_ARROW) ) {
1249                         hud_show_homing_missiles();
1250                 }
1251
1252         } else if ( Viewer_mode & (VM_CHASE | VM_EXTERNAL | VM_WARP_CHASE | VM_PADLOCK_ANY ) ) {
1253                 // If the player is warping out, don't draw the targeting gauges
1254                 Assert(Player != NULL);
1255                 if ( Player->control_mode != PCM_NORMAL ) {
1256                         return;
1257                 }
1258
1259                 hud_show_common_3d_gauges(frametime, 0);
1260         }
1261
1262         if (Viewer_mode & VM_SLEWED) {
1263                 HUD_render_forward_icon(Player_obj);
1264         }
1265 }
1266
1267
1268 // call from HUD_render_2d() when in gameplay, and call when in navmap
1269 void hud_show_messages()
1270 {
1271         // draw the message window
1272         hud_show_msg_window();
1273         hud_show_fixed_text();
1274 }
1275
1276 // decide if we want to blit damage status to the screen
1277 void hud_maybe_show_damage()
1278 {
1279         if ( !hud_gauge_active(HUD_DAMAGE_GAUGE) ) {
1280                 return;
1281         }
1282
1283         // display the current weapon info for the player ship, with ammo/energy counts
1284         if ( hud_gauge_active(HUD_DAMAGE_GAUGE) ) {
1285                 int show_gauge_flag;
1286
1287                 if ( (Ship_info[Player_ship->ship_info_index].initial_hull_strength - Player_obj->hull_strength) > 1.0f ) {
1288                         show_gauge_flag = 1;
1289                 } else {
1290                         show_gauge_flag = 0;
1291                 }
1292
1293                 // is gauge configured as a popup?
1294                 if ( hud_gauge_is_popup(HUD_DAMAGE_GAUGE) ) {
1295                         if ( !hud_gauge_popup_active(HUD_DAMAGE_GAUGE) ) {
1296                                 show_gauge_flag=0;
1297                         }
1298                 }
1299                         
1300                 if ( show_gauge_flag ) {
1301                         hud_show_damage_popup();
1302                 }
1303         }
1304 }
1305
1306 // The damage toggle button was pressed, so change state
1307 void hud_damage_popup_toggle()
1308 {
1309         snd_play(&Snds[SND_SQUADMSGING_ON]);
1310         
1311         // If gague is disabled (off), make it on all the time
1312         if ( !hud_gauge_active(HUD_DAMAGE_GAUGE) ) {
1313                 hud_config_set_gauge_flags(HUD_DAMAGE_GAUGE,1,0);               
1314                 return;
1315         }
1316
1317         // if gauge is popup, turn it off if it is current up, otherwise force it to be up
1318         if ( hud_gauge_is_popup(HUD_DAMAGE_GAUGE) ) {
1319                 if ( Player_obj->hull_strength == Ship_info[Player_ship->ship_info_index].initial_hull_strength ) {
1320                         hud_config_set_gauge_flags(HUD_DAMAGE_GAUGE,1,0);               
1321                 } else {
1322                         hud_config_set_gauge_flags(HUD_DAMAGE_GAUGE,0,0);               
1323                 }
1324                 return;
1325         }
1326
1327         // gauge is on, without any popup... so force it to be off
1328         hud_config_set_gauge_flags(HUD_DAMAGE_GAUGE,0,0);               
1329 }
1330
1331
1332 // Display the current mission time in MM:SS format
1333 void hud_show_mission_time()
1334 {
1335         float mission_time, time_comp;
1336         int minutes=0;
1337         int seconds=0;
1338         
1339         mission_time = f2fl(Missiontime);  // convert to seconds
1340
1341         minutes=(int)(mission_time/60);
1342         seconds=(int)mission_time%60;
1343
1344         hud_set_gauge_color(HUD_MISSION_TIME);
1345
1346         // blit background frame
1347         if ( Mission_time_gauge.first_frame >= 0 ) {
1348                 GR_AABITMAP(Mission_time_gauge.first_frame, Mission_time_coords[gr_screen.res][0], Mission_time_coords[gr_screen.res][1]);                              
1349         }
1350
1351         // print out mission time in MM:SS format
1352         gr_printf(Mission_time_text_coords[gr_screen.res][0], Mission_time_text_coords[gr_screen.res][1], NOX("%02d:%02d"), minutes, seconds);
1353
1354         // display time compression as xN
1355         time_comp = f2fl(Game_time_compression);
1356         if ( time_comp < 1 ) {
1357                 gr_printf(Mission_time_text_val_coords[gr_screen.res][0], Mission_time_text_val_coords[gr_screen.res][1], XSTR( "x%.1f", 215), time_comp);
1358         } else {
1359                 gr_printf(Mission_time_text_val_coords[gr_screen.res][0], Mission_time_text_val_coords[gr_screen.res][1], XSTR( "x%.0f", 216), time_comp);
1360         }
1361 }
1362
1363 // If a head animation is playing, then blit a border around it
1364 void hud_maybe_blit_head_border()
1365 {
1366         if ( Head_frame_gauge.first_frame == -1 ){
1367                 return;
1368         }
1369
1370         if ( message_anim_is_playing() ) {
1371                 // draw frame
1372                 // hud_set_default_color();
1373                 hud_set_gauge_color(HUD_TALKING_HEAD);
1374
1375                 GR_AABITMAP(Head_frame_gauge.first_frame, Head_frame_coords[gr_screen.res][0], Head_frame_coords[gr_screen.res][1]);            
1376
1377                 // draw title
1378                 gr_string(Head_message_coords[gr_screen.res][0], Head_message_coords[gr_screen.res][1], XSTR("message", 217));
1379         }
1380 }
1381
1382 // Black out area behind head animation
1383 void hud_maybe_clear_head_area()
1384 {
1385         if ( Head_frame_gauge.first_frame == -1 ) {
1386                 return;
1387         }
1388
1389         if ( message_anim_is_playing() ) {
1390                 // clear
1391                 if (gr_screen.res == GR_640) {
1392                         HUD_set_clip(7, 45, 160, 120);          // these coords are set in MissionMessage.cpp
1393                 } else {
1394                         HUD_set_clip(7, 66, 160, 120);
1395                 }
1396                 gr_clear();
1397                 HUD_reset_clip();
1398         }
1399 }
1400
1401 void hud_maybe_display_supernova()
1402 {
1403         float time_left;
1404
1405         // if there's a supernova coming
1406         time_left = supernova_time_left();
1407         if(time_left < 0.0f){
1408                 return;
1409         }
1410
1411         gr_set_color_fast(&Color_bright_red);
1412         gr_printf(Supernova_coords[gr_screen.res][0], Supernova_coords[gr_screen.res][1], "Supernova Warning : %.2f s", time_left);
1413 }
1414
1415 // render multiplayer ping time to the server if appropriate
1416 void hud_render_multi_ping()
1417 {
1418         // if we shouldn't be displaying a ping time, return here
1419         if(!multi_show_ingame_ping()){
1420                 return;
1421         }
1422         
1423         // if we're in multiplayer mode, display our ping time to the server
1424         if((Game_mode & GM_MULTIPLAYER) && (Net_player != NULL) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
1425                 char ping_str[50];
1426                 memset(ping_str,0,50);
1427
1428                 // if our ping is positive, display it
1429                 if((Netgame.server != NULL) && (Netgame.server->s_info.ping.ping_avg > 0)){
1430                         // get the string
1431                         if(Netgame.server->s_info.ping.ping_avg >= 1000){
1432                                 sprintf(ping_str,XSTR("> 1 sec",628));
1433                         } else {
1434                                 sprintf(ping_str,XSTR("%d ms",629),Netgame.server->s_info.ping.ping_avg);
1435                         }
1436
1437                         // blit the string out
1438                         hud_set_default_color();
1439                         gr_string(Ping_coords[gr_screen.res][0], Ping_coords[gr_screen.res][1], ping_str);
1440                 }
1441         }
1442 }
1443
1444 // render all the 2D gauges on the HUD
1445 void HUD_render_2d(float frametime)
1446 {
1447         int show_gauge_flag;
1448
1449         HUD_reset_clip();
1450
1451 /*
1452         // show some scoring debug stuff
1453         {
1454                 extern char Scoring_debug_text[];
1455                 gr_string( 10, 40, Scoring_debug_text );
1456         }
1457 */
1458         if ( hud_disabled() ) {
1459                 return;
1460         }
1461
1462         if (!(Viewer_mode & (VM_EXTERNAL | VM_SLEWED | VM_CHASE | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY ))) {           
1463                 // display Energy Transfer System gauges
1464                 if ( hud_gauge_active(HUD_ETS_GAUGE) ) {
1465                         show_gauge_flag=1;
1466                         // is gauge configured as a popup?
1467                         if ( hud_gauge_is_popup(HUD_ETS_GAUGE) ) {
1468                                 if ( !hud_gauge_popup_active(HUD_ETS_GAUGE) ) {
1469                                         show_gauge_flag=0;
1470                                 }
1471                         }
1472                         
1473                         if ( show_gauge_flag ) {
1474                                 hud_show_ets();
1475                         }
1476                 }
1477
1478                 // display afterburner fuel gauge
1479                 if ( hud_gauge_active(HUD_AFTERBURNER_ENERGY) ) {
1480                         hud_set_gauge_color(HUD_AFTERBURNER_ENERGY);
1481                         hud_show_afterburner_gauge();
1482                 }               
1483
1484                 // text flash gauge
1485                 hud_maybe_show_text_flash_icon();
1486
1487                 // maybe show the netlag icon
1488                 if(Game_mode & GM_MULTIPLAYER){
1489                         hud_maybe_show_netlag_icon();
1490
1491                         if(Net_player->flags & NETINFO_FLAG_OBSERVER){
1492                                 hud_render_observer();                                  
1493                         }
1494                 }
1495
1496                 // draw the reticle
1497                 hud_show_reticle();
1498
1499                 /*
1500                 // why is this here twice?
1501                 // display Energy Transfer System gauges
1502                 if ( hud_gauge_active(HUD_ETS_GAUGE) ) {
1503                         show_gauge_flag=1;
1504                         // is gauge configured as a popup?
1505                         if ( hud_gauge_is_popup(HUD_ETS_GAUGE) ) {
1506                                 if ( !hud_gauge_popup_active(HUD_ETS_GAUGE) ) {
1507                                         show_gauge_flag=0;
1508                                 }
1509                         }
1510                         
1511                         if ( show_gauge_flag ) {
1512                                 hud_show_ets();
1513                         }
1514                 }
1515                 */
1516
1517                 // display info on the ships in the escort list
1518                 if ( hud_gauge_active(HUD_ESCORT_VIEW) ) {
1519                         show_gauge_flag=1;
1520                         // is gauge configured as a popup?
1521                         if ( hud_gauge_is_popup(HUD_ESCORT_VIEW) ) {
1522                                 if ( !hud_gauge_popup_active(HUD_ESCORT_VIEW) ) {
1523                                         show_gauge_flag=0;
1524                                 }
1525                         }
1526                         
1527                         if ( show_gauge_flag ) {
1528                                 hud_set_gauge_color(HUD_ESCORT_VIEW);
1529                                 hud_display_escort();
1530                         }
1531                 }
1532
1533                 // display the current weapon info for the player ship, with ammo/energy counts
1534                 if ( hud_gauge_active(HUD_WEAPONS_GAUGE) ) {
1535                         show_gauge_flag=1;
1536                         // is gauge configured as a popup?
1537                         if ( hud_gauge_is_popup(HUD_WEAPONS_GAUGE) ) {
1538                                 if ( !hud_gauge_popup_active(HUD_WEAPONS_GAUGE) ) {
1539                                         show_gauge_flag=0;
1540                                 }
1541                         }
1542                         
1543                         if ( show_gauge_flag ) {
1544                                 hud_show_weapons();
1545                         }
1546                 }
1547
1548                 // display player countermeasures count
1549                 if ( hud_gauge_active(HUD_CMEASURE_GAUGE) ) {
1550                         show_gauge_flag=1;
1551                         // is gauge configured as a popup?
1552                         if ( hud_gauge_is_popup(HUD_CMEASURE_GAUGE) ) {
1553                                 if ( !hud_gauge_popup_active(HUD_CMEASURE_GAUGE) ) {
1554                                         show_gauge_flag=0;
1555                                 }
1556                         }
1557                         
1558                         if ( show_gauge_flag ) {
1559                                 hud_show_cmeasure_gague();
1560                         }
1561                 }
1562
1563                 if ( hud_gauge_active(HUD_WEAPONS_ENERGY) ) {
1564                         hud_show_weapon_energy_gauge();
1565                 }
1566
1567                 // show the auto-target icons
1568                 hud_show_auto_icons();                          
1569
1570                 // draw a border around a talking head if it is playing
1571                 hud_maybe_blit_head_border();
1572
1573                 // draw the status of support ship servicing the player
1574                 hud_support_view_blit();
1575
1576                 // draw the damage status
1577                 hud_maybe_show_damage();
1578
1579                 // show mission time 
1580                 if ( hud_gauge_active(HUD_MISSION_TIME) ) {
1581                         hud_show_mission_time();
1582                 }
1583
1584                 // show subspace notify gauge
1585                 hud_maybe_display_subspace_notify();
1586
1587                 // show objective status gauge
1588                 if ( hud_gauge_active(HUD_OBJECTIVES_NOTIFY_GAUGE) ) {
1589                         hud_maybe_display_objective_message();
1590                 }
1591
1592                 if ( hud_gauge_active(HUD_WINGMEN_STATUS) ) {
1593                         hud_wingman_status_render();
1594                 }
1595
1596                 if ( hud_gauge_active(HUD_KILLS_GAUGE) ) {
1597                         show_gauge_flag=1;
1598                         // is gauge configured as a popup?
1599                         if ( hud_gauge_is_popup(HUD_KILLS_GAUGE) ) {
1600                                 if ( !hud_gauge_popup_active(HUD_KILLS_GAUGE) ) {
1601                                         show_gauge_flag=0;
1602                                 }
1603                         }
1604                         
1605                         if ( show_gauge_flag ) {
1606                                 hud_show_kills_gauge();
1607                         }
1608                 }
1609
1610                 // show the player shields
1611                 if ( hud_gauge_active(HUD_PLAYER_SHIELD_ICON) ) {
1612                         hud_shield_show(Player_obj);
1613                 }
1614
1615                 // show the directives popup and/or training popup
1616                 message_training_display();
1617
1618                 // if this is a multiplayer game, blit any icons/bitmaps indicating voice recording or playback
1619                 if(Game_mode & GM_MULTIPLAYER){
1620                         hud_show_voice_status();
1621                 }
1622         }
1623
1624         hud_show_messages();
1625
1626         // maybe render any necessary multiplayer text messaging strings being entered
1627         hud_maybe_render_multi_text();
1628
1629         // show red alert notify gauge when moving to red alert
1630         hud_maybe_display_red_alert();  
1631
1632         // display supernova warning
1633         hud_maybe_display_supernova();
1634
1635         // check to see if we are in messaging mode.  If so, send the key to the code
1636         // to deal with the message.  hud_sqaudmsg_do_frame will return 0 if the key
1637         // wasn't used in messaging mode, otherwise 1.  In the event the key was used,
1638         // return immediately out of this function.
1639         if ( Players->flags & PLAYER_FLAGS_MSG_MODE ) {
1640                 if ( hud_squadmsg_do_frame() ){
1641                         return;
1642                 }
1643         }
1644
1645         hud_render_multi_ping();        
1646 }
1647
1648
1649 // hud_stop_looped_engine_sounds()
1650 //
1651 // This function will set the loop id's for the engine noises to -1, this will force any
1652 // looping engine sounds to stop.  This should only be called when the game decides to
1653 // stop all looping sounds
1654 //
1655
1656 void hud_stop_looped_engine_sounds()
1657 {
1658         if ( Player_engine_snd_loop > -1 )      {
1659                 snd_stop(Player_engine_snd_loop);
1660                 //snd_chg_loop_status(Player_engine_snd_loop, 0);
1661                 Player_engine_snd_loop = -1;
1662         }
1663 }
1664
1665 #define ZERO_PERCENT                    0.01f
1666 #define ENGINE_MAX_VOL          1.0f
1667 #define ENGINE_MAX_PITCH        44100
1668
1669 void update_throttle_sound()
1670 {
1671         // determine what engine sound to play
1672         float percent_throttle;
1673 //      int     throttle_pitch;
1674
1675         // if we're a multiplayer observer, stop any engine sounds from playing and return
1676         if((Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_OBSERVER)){
1677                 // stop engine sound if it is playing
1678                 if(Player_engine_snd_loop != -1){
1679                         snd_stop(Player_engine_snd_loop);
1680                         Player_engine_snd_loop = -1;
1681                 }
1682
1683                 // return
1684                 return;
1685         }
1686
1687         if ( timestamp_elapsed(throttle_sound_check_id) ) {
1688
1689                 throttle_sound_check_id = timestamp(THROTTLE_SOUND_CHECK_INTERVAL);
1690         
1691                 if ( Ships[Player_obj->instance].current_max_speed == 0 ) {
1692                         percent_throttle = Player_obj->phys_info.fspeed / Ship_info[Ships[Player_obj->instance].ship_info_index].max_speed;
1693                 } else {
1694                         percent_throttle = Player_obj->phys_info.fspeed / Ships[Player_obj->instance].current_max_speed;
1695                 }
1696
1697                 // If the throttle has changed, modify the sound
1698                 if ( percent_throttle != last_percent_throttle || Player_engine_snd_loop == -1 ) {
1699
1700                         if ( percent_throttle < ZERO_PERCENT ) {
1701                                 if ( Player_engine_snd_loop > -1 )      {
1702                                         snd_chg_loop_status(Player_engine_snd_loop, 0);
1703                                         Player_engine_snd_loop = -1;
1704                                 }
1705                         }
1706                         else {
1707                                 if ( Player_engine_snd_loop == -1 ){
1708                                         Player_engine_snd_loop = snd_play_looping( &Snds[SND_ENGINE], 0.0f , -1, -1, percent_throttle * ENGINE_MAX_VOL );
1709                                 } else {
1710                                         // The sound may have been trashed at the low-level if sound channel overflow.
1711                                         // TODO: implement system where certain sounds cannot be interrupted (priority?)
1712                                         if ( snd_is_playing(Player_engine_snd_loop) ) {
1713                                                 snd_set_volume(Player_engine_snd_loop, percent_throttle * ENGINE_MAX_VOL);
1714                                         }
1715                                         else {
1716                                                 Player_engine_snd_loop = -1;
1717                                         }
1718                                 }
1719                         }
1720
1721 //                      throttle_pitch = snd_get_pitch(Player_engine_snd_loop);
1722 //                      if ( percent_throttle > 0.5 ) {
1723 //                              snd_set_pitch(Player_engine_snd_loop, fl2i(22050 + (percent_throttle-0.5f)*1000));
1724 //                      }
1725
1726                 }       // end if (percent_throttle != last_percent_throttle)
1727
1728                 last_percent_throttle = percent_throttle;
1729
1730         }       // end if ( timestamp_elapsed(throttle_sound_check_id) )
1731 }
1732
1733 // called at the beginning of each level.  Loads frame data in once, and initializes any damage
1734 // gauge specific data
1735 void hud_damage_popup_init()
1736 {
1737         int i;
1738
1739         if ( !Damage_gauges_loaded ) {
1740                 for ( i = 0; i < NUM_DAMAGE_GAUGES; i++ ) {
1741                         Damage_gauges[i].first_frame = bm_load_animation(Damage_gauge_fnames[gr_screen.res][i], &Damage_gauges[i].num_frames);
1742                         if ( Damage_gauges[i].first_frame == -1 ) {
1743                                 Warning(LOCATION, "Could not load in the ani: %s\n", Damage_gauge_fnames[gr_screen.res][i]);
1744                                 return;
1745                         }
1746                 }
1747                 Damage_gauges_loaded = 1;
1748         }
1749
1750         Damage_flash_bright = 0;
1751         Damage_flash_timer =    1;
1752
1753         for ( i = 0; i < SUBSYSTEM_MAX; i++ ) {
1754                 Pl_hud_subsys_info[i].last_str = 1000.0f;
1755                 Pl_hud_subsys_info[i].flash_duration_timestamp = 1;
1756                 Pl_hud_next_flash_timestamp = 1;
1757                 Pl_hud_is_bright = 0;
1758         }
1759 }
1760
1761 // ---------------------------------------------------------
1762 // show player damage status via a popup window
1763
1764 void hud_show_damage_popup()
1765 {
1766         model_subsystem *psub;
1767         ship_subsys                     *pss;
1768         ship_info                       *sip;
1769         int                                     sx, sy, bx, by, w, h, screen_integrity, num, best_str, best_index;
1770         float                                   strength, shield, integrity;
1771         char                                    buf[128];
1772         hud_subsys_damage       hud_subsys_list[SUBSYSTEM_MAX]; 
1773
1774         if ( Damage_gauges[0].first_frame == -1 ) {
1775                 return;
1776         }
1777
1778         if ( (The_mission.game_type & MISSION_TYPE_TRAINING) && Training_msg_visible ){
1779                 return;
1780         }
1781                 
1782         sip = &Ship_info[Player_ship->ship_info_index];
1783         hud_get_target_strength(Player_obj, &shield, &integrity);
1784         screen_integrity = fl2i(integrity*100);
1785
1786         if ( hud_gauge_is_popup(HUD_DAMAGE_GAUGE) ) {
1787                 if ( screen_integrity >= 100 ) {
1788                         return;
1789                 }
1790         }
1791
1792         if ( timestamp_elapsed(Damage_flash_timer) ) {
1793                 Damage_flash_timer = timestamp(DAMAGE_FLASH_TIME);
1794                 Damage_flash_bright ^= 1;
1795         }
1796
1797         hud_set_gauge_color(HUD_DAMAGE_GAUGE);
1798
1799         // draw the top of the damage pop-up
1800         GR_AABITMAP(Damage_gauges[0].first_frame, Damage_gauge_coords[gr_screen.res][0][0], Damage_gauge_coords[gr_screen.res][0][1]);  
1801         gr_string(Damage_text_coords[gr_screen.res][0], Damage_text_coords[gr_screen.res][1], XSTR( "damage", 218));
1802
1803         // show hull integrity
1804         if ( screen_integrity < 100 ) {         
1805                 if ( screen_integrity == 0 ) {
1806                         screen_integrity = 1;
1807                 }
1808                 sprintf(buf, XSTR( "%d%%", 219), screen_integrity);
1809                 hud_num_make_mono(buf);
1810                 gr_get_string_size(&w, &h, buf);
1811                 if ( screen_integrity < 30 ) {
1812                         gr_set_color_fast(&Color_red);
1813                 }
1814                 gr_string(Hull_integ_coords[gr_screen.res][0], Hull_integ_coords[gr_screen.res][1], XSTR( "Hull Integrity", 220));
1815                 gr_string(Hull_integ_val_coords[gr_screen.res][0] - w, Hull_integ_val_coords[gr_screen.res][1], buf);
1816         } 
1817
1818         // show damaged subsystems
1819         sx = Damage_subsys_text_coords[gr_screen.res][0];
1820         sy = Damage_subsys_text_coords[gr_screen.res][1];
1821         bx = Damage_gauge_coords[gr_screen.res][1][0];
1822         by = Damage_gauge_coords[gr_screen.res][1][1];
1823
1824         num = 0;
1825         for ( pss = GET_FIRST(&Player_ship->subsys_list); pss !=END_OF_LIST(&Player_ship->subsys_list); pss = GET_NEXT(pss) ) {
1826                 psub = pss->system_info;
1827                 strength = ship_get_subsystem_strength(Player_ship, psub->type);
1828                 if ( strength < 1 ) {
1829                         screen_integrity = fl2i(strength*100);
1830                         if ( screen_integrity == 0 ) {
1831                                 if ( strength > 0 ) {
1832                                         screen_integrity = 1;
1833                                 }
1834                         }
1835                         hud_subsys_list[num].name = psub->name;
1836                         hud_subsys_list[num].str  = screen_integrity;
1837                         hud_subsys_list[num].type = psub->type;
1838                         num++;
1839
1840                         if ( strength < Pl_hud_subsys_info[psub->type].last_str ) {
1841                                 Pl_hud_subsys_info[psub->type].flash_duration_timestamp = timestamp(SUBSYS_DAMAGE_FLASH_DURATION);
1842                         }
1843                         Pl_hud_subsys_info[psub->type].last_str = strength;
1844                 }
1845         }
1846
1847         int type;
1848         for ( int i = 0; i < num; i++ ) {
1849                 best_str = 1000;
1850                 best_index = -1;
1851                 for ( int j = 0; j < num-i; j++ ) {
1852                         if ( hud_subsys_list[j].str < best_str ) {
1853                                 best_str = hud_subsys_list[j].str;
1854                                 best_index = j;
1855                         }
1856                 }
1857
1858                 Assert(best_index >= 0);
1859                 Assert(best_str >= 0);
1860
1861                 // display strongest subsystem left in list
1862                 // draw the bitmap
1863                 // hud_set_default_color();
1864                 hud_set_gauge_color(HUD_DAMAGE_GAUGE);
1865
1866                 GR_AABITMAP(Damage_gauges[1].first_frame, bx, by);
1867                 by += Damage_gauge_line_h[gr_screen.res];
1868
1869                 type = hud_subsys_list[best_index].type;
1870                 if ( !timestamp_elapsed( Pl_hud_subsys_info[type].flash_duration_timestamp ) ) {
1871                         if ( timestamp_elapsed( Pl_hud_next_flash_timestamp ) ) {
1872                                 Pl_hud_is_bright ^= 1;
1873                                 Pl_hud_next_flash_timestamp = timestamp(SUBSYS_DAMAGE_FLASH_INTERVAL);
1874                         }
1875                         
1876                         if ( Pl_hud_is_bright ) {
1877                                 int alpha_color;
1878                                 alpha_color = min(HUD_COLOR_ALPHA_MAX,HUD_color_alpha+HUD_BRIGHT_DELTA);
1879                                 // gr_set_color_fast(&HUD_color_defaults[alpha_color]);
1880
1881                                 hud_set_gauge_color(HUD_DAMAGE_GAUGE, alpha_color);
1882                         } else {                                
1883                                 hud_set_gauge_color(HUD_DAMAGE_GAUGE);
1884                         }
1885                 }
1886
1887                 // draw the text
1888                 if ( best_str < 30 ) {
1889                         if ( best_str <= 0 ) {
1890                                 if ( Damage_flash_bright ) {
1891                                         gr_set_color_fast(&Color_bright_red);
1892                                 } else {
1893                                         gr_set_color_fast(&Color_red);
1894                                 }
1895
1896                         } else {
1897                                 gr_set_color_fast(&Color_red);
1898                         }
1899                 } else {
1900                         hud_set_gauge_color(HUD_DAMAGE_GAUGE);
1901                 }               
1902
1903                 gr_string(sx, sy, hud_targetbox_truncate_subsys_name(hud_subsys_list[best_index].name));
1904                 sprintf(buf, XSTR( "%d%%", 219), best_str);
1905                 hud_num_make_mono(buf);
1906                 gr_get_string_size(&w, &h, buf);
1907                 gr_string(Hull_integ_val_coords[gr_screen.res][0] - w, sy, buf);
1908                 sy += Damage_gauge_line_h[gr_screen.res];
1909
1910                 // remove it from hud_subsys_list
1911                 if ( best_index < (num-i-1) ) {
1912                         hud_subsys_list[best_index] = hud_subsys_list[num-i-1];
1913                 }
1914         }
1915
1916         // draw the bottom of the gauge
1917         // hud_set_default_color();
1918         hud_set_gauge_color(HUD_DAMAGE_GAUGE);
1919
1920         GR_AABITMAP(Damage_gauges[2].first_frame, bx, by);              
1921 }
1922
1923 // init the members of the hud_anim struct to default values
1924 void hud_anim_init(hud_anim *ha, int sx, int sy, char *filename)
1925 {
1926         ha->first_frame = -1;
1927         ha->num_frames          = 0;
1928         ha->total_time          = 0.0f;
1929         ha->time_elapsed        = 0.0f;
1930         ha->sx                          = sx;
1931         ha->sy                          = sy;
1932         strcpy(ha->name, filename);
1933 }
1934
1935 // call to unload the targetbox static animation
1936 void hud_anim_release(hud_anim *ha)
1937 {
1938         int i;
1939         for ( i = 0; i < ha->num_frames; i++ ) {
1940                 bm_unload(ha->first_frame + i);
1941         }
1942 }
1943
1944 // load a hud_anim
1945 // return 0 is successful, otherwise return -1
1946 int hud_anim_load(hud_anim *ha)
1947 {
1948         int             fps;
1949
1950         ha->first_frame = bm_load_animation(ha->name, &ha->num_frames, &fps);
1951         if ( ha->first_frame == -1 ) {
1952                 Int3(); // couldn't load animation file in
1953                 return -1;
1954         }
1955         Assert(fps != 0);
1956         ha->total_time = i2fl(ha->num_frames)/fps;
1957         return 0;
1958 }
1959
1960 // render out a frame of the targetbox static animation, based on how much time has
1961 // elapsed
1962 // input:       ha                              =>      pointer to hud anim info
1963 //                              frametime       =>      seconds elapsed since last frame
1964 //                              draw_alpha      =>      draw bitmap as alpha-bitmap (default 0)
1965 //                              loop                    =>      anim should loop (default 1)
1966 //                              hold_last       =>      should last frame be held (default 0)
1967 //                              reverse         =>      play animation in reverse (default 0)
1968 int hud_anim_render(hud_anim *ha, float frametime, int draw_alpha, int loop, int hold_last, int reverse)
1969 {
1970         int framenum;
1971
1972         if ( ha->num_frames <= 0 ) {
1973                 if ( hud_anim_load(ha) == -1 )
1974                         return 0;
1975         }
1976
1977         ha->time_elapsed += frametime;
1978         if ( ha->time_elapsed > ha->total_time ) {
1979                 if ( loop ) {
1980                         ha->time_elapsed = 0.0f;
1981                 } else {
1982                         if ( !hold_last ) {
1983                                 return 0;
1984                         }
1985                 }
1986         }
1987
1988         // draw the correct frame of animation
1989         framenum = fl2i( (ha->time_elapsed * ha->num_frames) / ha->total_time );
1990         if (reverse) {
1991                 framenum = (ha->num_frames-1) - framenum;
1992         }
1993
1994         if ( framenum < 0 )
1995                 framenum = 0;
1996         if ( framenum >= ha->num_frames )
1997                 framenum = ha->num_frames-1;
1998
1999         // Blit the bitmap for this frame
2000         if(emp_should_blit_gauge()){
2001                 gr_set_bitmap(ha->first_frame + framenum);
2002                 if ( draw_alpha ){
2003                         gr_aabitmap(ha->sx, ha->sy);
2004                 } else {
2005                         gr_bitmap(ha->sx, ha->sy);
2006                 }
2007         }
2008
2009         return 1;
2010 }
2011
2012 // convert a number string to use mono-spaced 1 character
2013 void hud_num_make_mono(char *num_str)
2014 {
2015         int len, i, sc;
2016         len = strlen(num_str);
2017
2018         sc = Lcl_special_chars;
2019         for ( i = 0; i < len; i++ ) {
2020                 if ( num_str[i] == '1' ) {
2021                         num_str[i] = (char)(sc + 1);
2022                 }
2023         }
2024 }
2025
2026 // flashing text gauge
2027 void hud_init_text_flash_gauge()
2028 {       
2029 }
2030
2031 void hud_start_text_flash(char *txt, int t)
2032 {
2033         // bogus
2034         if(txt == NULL){
2035                 strcpy(Hud_text_flash, "");
2036                 return;
2037         }
2038
2039         // HACK. don't override EMP if its still going    :)
2040         if(!strcmp(Hud_text_flash, NOX("Emp")) && !hud_targetbox_flash_expired(TBOX_FLASH_CMEASURE)){
2041                 return;
2042         }
2043
2044         strncpy(Hud_text_flash, txt, 500);
2045         hud_targetbox_start_flash(TBOX_FLASH_CMEASURE, t);      
2046 }
2047
2048 void hud_maybe_show_text_flash_icon()
2049 {               
2050         int bright;
2051
2052         if ( hud_targetbox_flash_expired(TBOX_FLASH_CMEASURE) ) {
2053                 return;
2054         }
2055
2056         hud_targetbox_maybe_flash(TBOX_FLASH_CMEASURE);         
2057
2058         // bright?
2059         bright = hud_targetbox_is_bright(TBOX_FLASH_CMEASURE);
2060
2061         // draw
2062         hud_show_text_flash_icon(Hud_text_flash, Hud_text_flash_coords[gr_screen.res][1], bright);
2063 }
2064
2065 void hud_show_text_flash_icon(char *txt, int y, int bright)
2066 {
2067         int w, h;
2068
2069         // different font size in hi-res
2070         if(gr_screen.res != GR_640){
2071                 gr_set_font(FONT3);
2072         }
2073
2074         // set color
2075         if(bright){
2076                 hud_set_gauge_color(HUD_TEXT_FLASH, HUD_C_DIM);
2077         } else {
2078                 gr_set_color_fast(&Color_black);
2079         }
2080
2081         // string size
2082         gr_get_string_size(&w, &h, txt);
2083
2084         // draw the box 
2085         gr_rect( (int)((((float)gr_screen.max_w / 2.0f) - ((float)w / 2.0f)) - 1.0f), (int)((float)y - 1.0f), w + 2, h + 1);
2086
2087         // string
2088         hud_set_gauge_color(HUD_TEXT_FLASH, HUD_C_BRIGHT);      
2089         gr_string(0x8000, y, txt);
2090
2091         // go back to normal font
2092         gr_set_font(FONT1);
2093 }
2094
2095 // maybe display the kills gauge on the HUD
2096 void hud_show_kills_gauge()
2097 {
2098         if ( Kills_gauge.first_frame < 0 ) {
2099                 return;
2100         }
2101
2102         // hud_set_default_color();
2103         hud_set_gauge_color(HUD_KILLS_GAUGE);
2104
2105         // draw background
2106         GR_AABITMAP(Kills_gauge.first_frame, Kills_gauge_coords[gr_screen.res][0], Kills_gauge_coords[gr_screen.res][1]);       
2107
2108         gr_string(Kills_text_coords[gr_screen.res][0], Kills_text_coords[gr_screen.res][1], XSTR( "kills:", 223));
2109
2110         // display how many kills the player has so far
2111         char    num_kills_string[32];
2112         int     w,h;
2113
2114         if ( !Player ) {
2115                 Int3();
2116                 return;
2117         }
2118
2119         sprintf(num_kills_string, "%d", Player->stats.m_kill_count_ok);
2120
2121         gr_get_string_size(&w, &h, num_kills_string);
2122         if (Lcl_gr) {
2123                 gr_string(Kills_text_val_coords_gr[gr_screen.res][0]-w, Kills_text_val_coords_gr[gr_screen.res][1], num_kills_string);
2124         } else {
2125                 gr_string(Kills_text_val_coords[gr_screen.res][0]-w, Kills_text_val_coords[gr_screen.res][1], num_kills_string);
2126         }
2127 }
2128
2129 // maybe show the netlag icon on the hud
2130 void hud_maybe_show_netlag_icon()
2131 {
2132         int lag_status;
2133
2134         if ( Netlag_icon.first_frame == -1 ) {
2135                 Int3();
2136                 return;
2137         }
2138
2139         lag_status = multi_query_lag_status();  
2140
2141         switch(lag_status) {
2142         case 0:
2143                 // draw the net lag icon flashing
2144                 hud_targetbox_start_flash(TBOX_FLASH_NETLAG);
2145                 if(hud_targetbox_maybe_flash(TBOX_FLASH_NETLAG)){
2146                         hud_set_gauge_color(HUD_LAG_GAUGE, HUD_C_BRIGHT);
2147                 } else {
2148                         hud_set_gauge_color(HUD_LAG_GAUGE);
2149                 }
2150                 gr_set_bitmap(Netlag_icon.first_frame);
2151                 break;
2152         case 1:
2153                 // draw the disconnected icon flashing fast
2154                 if(hud_targetbox_maybe_flash(TBOX_FLASH_NETLAG,1)){
2155                         hud_set_gauge_color(HUD_LAG_GAUGE, HUD_C_BRIGHT);
2156                 } else {
2157                         hud_set_gauge_color(HUD_LAG_GAUGE);
2158                 }
2159                 gr_set_bitmap(Netlag_icon.first_frame+1);
2160                 break;
2161         default:
2162                 // nothing to draw
2163                 return;
2164         }
2165         
2166         if(emp_should_blit_gauge()){
2167                 gr_aabitmap(Netlag_coords[gr_screen.res][0], Netlag_coords[gr_screen.res][1]);
2168         }
2169 }
2170
2171 // load in kills gauge if required
2172 void hud_init_kills_gauge()
2173 {
2174         if ( !Kills_gauge_loaded ) {
2175                 Kills_gauge.first_frame = bm_load_animation(Kills_fname[gr_screen.res], &Kills_gauge.num_frames);
2176                 if ( Kills_gauge.first_frame == -1 ) {
2177                         Warning(LOCATION, "Could not load in the kills ani: Kills_fname[gr_screen.res]\n");
2178                         return;
2179                 }
2180                 Kills_gauge_loaded = 1;
2181         }
2182 }
2183
2184 // load in netlag icon if required
2185 void hud_init_netlag_icon()
2186 {
2187         if ( !Netlag_icon_loaded ) {
2188                 Netlag_icon.first_frame = bm_load_animation(Netlag_fname[gr_screen.res], &Netlag_icon.num_frames);
2189                 if ( Netlag_icon.first_frame == -1 ) {
2190                         Warning(LOCATION, "Could not load in the netlag ani: Netlag_fname[gr_screen.res]\n");
2191                         return;
2192                 }
2193                 Netlag_icon_loaded = 1;
2194         }
2195 }
2196
2197 // called at mission start to init data, and load support view bitmap if required
2198 void hud_support_view_init()
2199 {
2200         Hud_support_view_fade = 1;
2201         Hud_support_obj_sig = -1;
2202         Hud_support_target_sig = -1;
2203         Hud_support_objnum = -1;
2204         Hud_support_view_active = 0;
2205         Hud_support_view_abort = 0;
2206
2207         // ensure the talking head border is loaded
2208         if ( !Support_view_gauge_loaded ) {
2209                 Support_view_gauge.first_frame = bm_load_animation(Support_fname[gr_screen.res], &Support_view_gauge.num_frames);
2210                 if ( Support_view_gauge.first_frame == -1 ) {
2211                         Warning(LOCATION, "Could not load in ani: Support_fname[gr_screen.res]\n");
2212                 }
2213                 Support_view_gauge_loaded = 1;
2214         }
2215 }
2216
2217 // start displaying the support view pop-up.  This will remain up until hud_support_view_stop is called.
2218 // input:       objnum  =>              object number for the support ship
2219 void hud_support_view_start()
2220 {
2221         Hud_support_view_active = 1;
2222         Hud_support_view_fade = 1;
2223 }
2224
2225 // stop displaying the support view pop-up
2226 void hud_support_view_stop(int stop_now)
2227 {
2228         if ( stop_now ) {
2229                 Hud_support_view_active = 0;
2230                 Hud_support_view_fade = 1;
2231                 Hud_support_view_abort = 0;
2232         } else {
2233                 Hud_support_view_fade = timestamp(2000);
2234         }
2235
2236         Hud_support_obj_sig = -1;
2237         Hud_support_target_sig = -1;
2238         Hud_support_objnum = -1;
2239 }
2240
2241 void hud_support_view_abort()
2242 {
2243         hud_support_view_stop(0);
2244         Hud_support_view_abort = 1;
2245 }
2246
2247 // return the number of seconds until repair ship will dock with player, return -1 if error
2248 // 
2249 // mwa made this function more general purpose
2250 //
2251 // NOTE: This function is pretty stupid now.  It just assumes the player is sitting still, and
2252 //                 the support ship is moving directly to the player.
2253 int hud_support_get_dock_time( int objnum )
2254 {
2255         ai_info *aip;
2256         object  *support_objp, *other_objp;
2257         float           dist, rel_speed, support_speed;
2258         vector  rel_vel;
2259
2260         support_objp = &Objects[objnum];
2261         aip = &Ai_info[Ships[support_objp->instance].ai_index];
2262
2263         // if the ship is docked, return 0
2264         if ( aip->ai_flags & AIF_DOCKED )
2265                 return 0;
2266
2267         // get the dockee object pointer
2268         if (aip->goal_objnum == -1) {
2269                 Int3(); //      Shouldn't happen, but let's recover gracefully.
2270                 return 0;
2271         }
2272
2273         other_objp = &Objects[aip->goal_objnum];
2274
2275         vm_vec_sub(&rel_vel, &support_objp->phys_info.vel, &other_objp->phys_info.vel);
2276         rel_speed = vm_vec_mag_quick(&rel_vel);
2277
2278         dist = vm_vec_dist_quick(&other_objp->pos, &support_objp->pos);
2279
2280         support_speed = support_objp->phys_info.speed;
2281
2282         if ( rel_speed <= support_speed/2.0f) { //      This means the player is moving away fast from the support ship.
2283                 return (int) (dist/support_speed);
2284         } else {
2285                 float   d1;
2286                 float   d = dist;
2287                 float   time = 0.0f;
2288                 
2289                 if (rel_speed < 20.0f)
2290                         rel_speed = 20.0f;
2291
2292                 //      When faraway, use max speed, not current speed.  Might not have sped up yet.
2293                 if (d > 100.0f) {
2294                         time += (d - 100.0f)/support_objp->phys_info.max_vel.xyz.z;
2295                 }
2296
2297                 //      For mid-range, use current speed.
2298                 if (d > 60.0f) {
2299                         d1 = min(d, 100.0f);
2300
2301                         time += (d1 - 60.0f)/rel_speed;
2302                 }
2303
2304                 //      For nearby, ship will have to slow down a bit for docking maneuver.
2305                 if (d > 30.0f) {
2306                         d1 = min(d, 60.0f);
2307
2308                         time += (d1 - 30.0f)/5.0f;
2309                 }
2310
2311                 //      For very nearby, ship moves quite slowly.
2312                 d1 = min(d, 30.0f);
2313                 time += d1/7.5f;
2314
2315                 return fl2i(time);
2316         }
2317 }
2318
2319 // Locate the closest support ship which is trying to dock with player, return -1 if there is no support
2320 // ship currently trying to dock with the player
2321 // MA:  4/22/98 -- pass in objp to find support ship trying to dock with objp
2322 int hud_support_find_closest( int objnum )
2323 {
2324         ship_obj                *sop;
2325         ai_info         *aip;
2326         object          *objp;
2327         int i;
2328
2329         objp = &Objects[objnum];
2330
2331         sop = GET_FIRST(&Ship_obj_list);
2332         while(sop != END_OF_LIST(&Ship_obj_list)){
2333                 if ( Ship_info[Ships[Objects[sop->objnum].instance].ship_info_index].flags & SIF_SUPPORT ) {
2334                         int pship_index, sindex;
2335
2336                         // make sure support ship is not dying
2337                         if ( !(Ships[Objects[sop->objnum].instance].flags & (SF_DYING|SF_EXPLODED)) ) {
2338
2339                                 Assert( objp->type == OBJ_SHIP );
2340                                 aip = &Ai_info[Ships[Objects[sop->objnum].instance].ai_index];
2341                                 pship_index = objp->instance;
2342
2343                                 // we must check all goals for this support ship -- not just the first one
2344                                 for ( i = 0; i < MAX_AI_GOALS; i++ ) {
2345
2346                                         // we can use == in the next statement (and should) since a ship will only ever be
2347                                         // following one order at a time.
2348                                         if ( aip->goals[i].ai_mode == AI_GOAL_REARM_REPAIR ) {
2349                                                 Assert( aip->goals[i].ship_name );
2350                                                 sindex = ship_name_lookup( aip->goals[i].ship_name );
2351                                                 if ( sindex == pship_index )
2352                                                         return sop->objnum;
2353                                         }
2354                                 }
2355                         }
2356                 }
2357                 sop = GET_NEXT(sop);
2358         }
2359
2360         return -1;
2361 }
2362
2363 // dipaly the hud_support view popup
2364 void hud_support_view_blit()
2365 {
2366         int     show_time;
2367         char    outstr[64];
2368         
2369         if ( !Hud_support_view_active ) {
2370                 return;
2371         }
2372
2373         // don't render this gauge for multiplayer observers
2374         if((Game_mode & GM_MULTIPLAYER) && ((Net_player->flags & NETINFO_FLAG_OBSERVER) || (Player_obj->type == OBJ_OBSERVER))){
2375                 return;
2376         }
2377
2378         // If we haven't determined yet who the rearm ship is, try to!
2379         if (Hud_support_objnum == -1) {
2380                 Hud_support_objnum = hud_support_find_closest( OBJ_INDEX(Player_obj) );
2381                 if ( Hud_support_objnum >= 0 ) {
2382                         Hud_support_obj_sig = Objects[Hud_support_objnum].signature;
2383                         Hud_support_target_sig = Player_obj->signature;
2384                 }
2385         } else {
2386                 // check to see if support ship is still alive
2387                 if ( (Objects[Hud_support_objnum].signature != Hud_support_obj_sig) || (Hud_support_target_sig != Player_obj->signature) ) {
2388                         hud_support_view_stop(1);
2389                         return;
2390                 }
2391         }
2392
2393         // set hud color
2394         hud_set_gauge_color(HUD_SUPPORT_GAUGE);
2395
2396         GR_AABITMAP(Support_view_gauge.first_frame, Support_view_coords[gr_screen.res][0], Support_view_coords[gr_screen.res][1]);      
2397
2398         gr_string(Support_text_coords[gr_screen.res][0], Support_text_coords[gr_screen.res][1], XSTR( "support", 224));
2399
2400         if ( Hud_support_view_fade > 1 ) {
2401                 if ( !timestamp_elapsed(Hud_support_view_fade) ) {
2402                         if ( Hud_support_view_abort){
2403                                 gr_string(0x8000, Support_text_val_coords[gr_screen.res][1], XSTR( "aborted", 225));
2404                         } else {
2405                                 gr_string(0x8000, Support_text_val_coords[gr_screen.res][1], XSTR( "complete", 1407));
2406                         }
2407                         return;
2408                 } else {
2409                         Hud_support_view_abort = 0;
2410                         Hud_support_view_active = 0;
2411                         Hud_support_view_fade = 1;
2412                         Hud_support_objnum = -1;
2413                         return;
2414                 }
2415         }
2416
2417         show_time = 0;
2418         if ( Player_ai->ai_flags & AIF_BEING_REPAIRED ) {
2419                 Assert(Ship_info[Player_ship->ship_info_index].initial_hull_strength > 0);
2420                 if (  (ship_get_subsystem_strength(Player_ship, SUBSYSTEM_ENGINE) < 1.0 ) ||
2421                                 (ship_get_subsystem_strength(Player_ship, SUBSYSTEM_SENSORS) < 1.0 ) ||
2422                                 (ship_get_subsystem_strength(Player_ship, SUBSYSTEM_WEAPONS) < 1.0 ) ||
2423                                 (ship_get_subsystem_strength(Player_ship, SUBSYSTEM_COMMUNICATION) < 1.0 ) ) {
2424                         sprintf(outstr, XSTR( "repairing", 227));
2425                 } else {
2426                         sprintf(outstr, XSTR( "rearming", 228));
2427                 }
2428                 gr_string(0x8000, Support_text_val_coords[gr_screen.res][1], outstr);
2429         } else if (Player_ai->ai_flags & AIF_REPAIR_OBSTRUCTED) {
2430                 sprintf(outstr, XSTR( "obstructed", 229));
2431                 gr_string(0x8000, Support_text_val_coords[gr_screen.res][1], outstr);
2432         } else {
2433                 if ( Hud_support_objnum == -1 ) {
2434                         sprintf(outstr, XSTR( "warping in", 230));
2435                         gr_string(0x8000, Support_text_val_coords[gr_screen.res][1], outstr);
2436                 } else {
2437                         ai_info *aip;
2438
2439                         // display "busy" when support ship isn't actually enroute to me
2440                         aip = &Ai_info[Ships[Objects[Hud_support_objnum].instance].ai_index];
2441                         if ( aip->goal_objnum != OBJ_INDEX(Player_obj) ) {
2442                                 sprintf(outstr, XSTR( "busy", 231));
2443                                 show_time = 0;
2444
2445                         } else {
2446                                 sprintf(outstr, XSTR( "dock in:", 232));
2447                                 show_time = 1;
2448                         }               
2449
2450                         if (!show_time) {
2451                                 gr_string(Support_text_dock_coords[gr_screen.res][0], Support_text_val_coords[gr_screen.res][1], outstr);
2452                         } else {                        
2453                                 gr_string(Support_text_dock_coords[gr_screen.res][0], Support_text_val_coords[gr_screen.res][1], outstr);
2454                         }
2455                 }
2456         }
2457
2458         if ( show_time ) {
2459                 int seconds, minutes;
2460
2461                 Assert( Hud_support_objnum != -1 );
2462
2463                 // ensure support ship is still alive
2464                 if ( (Objects[Hud_support_objnum].signature != Hud_support_obj_sig) || (Hud_support_target_sig != Player_obj->signature) ) {
2465                         hud_support_view_stop(1);
2466                         seconds = 0;
2467                 } else {
2468                         seconds = hud_support_get_dock_time( Hud_support_objnum );
2469                 }
2470
2471                 if ( seconds >= 0 ) {
2472                         minutes = seconds/60;
2473                         seconds = seconds%60;
2474                         if ( minutes > 99 ) {
2475                                 minutes = 99;
2476                                 seconds = 99;
2477                         }
2478                 } else {
2479                         minutes = 99;
2480                         seconds = 99;
2481                 }
2482                 gr_printf(Support_text_dock_val_coords[gr_screen.res][0], Support_text_val_coords[gr_screen.res][1], NOX("%02d:%02d"), minutes, seconds);
2483         }
2484 }
2485
2486 // Set the current color to the default HUD color (with default alpha)
2487 void hud_set_default_color()
2488 {
2489         Assert(HUD_color_alpha >= 0 && HUD_color_alpha < HUD_NUM_COLOR_LEVELS);
2490         gr_set_color_fast(&HUD_color_defaults[HUD_color_alpha]);
2491 }
2492
2493 // Set the current color to a bright HUD color (ie high alpha)
2494 void hud_set_bright_color()
2495 {
2496         int alpha_color;
2497         alpha_color = min(HUD_COLOR_ALPHA_MAX,HUD_color_alpha+HUD_BRIGHT_DELTA);
2498         gr_set_color_fast(&HUD_color_defaults[alpha_color]);
2499 }
2500
2501 // Set the current color to a dim HUD color (ie low alpha)
2502 void hud_set_dim_color()
2503 {
2504         if ( HUD_color_alpha > 2 ) {
2505                 gr_set_color_fast(&HUD_color_defaults[2]);
2506         }
2507 }
2508
2509 // hud_set_iff_color() will set the color to the IFF color based on the team
2510 //
2511 // input:       team                    =>              team to base color on
2512 //                              is_bright       =>              default parameter (value 0) which uses bright version of IFF color
2513 void hud_set_iff_color(object *objp, int is_bright)
2514 {
2515         // AL 12-26-97: it seems IFF color needs to be set relative to the player team.  If
2516         //                                              the team in question is the same as the player, then it should be 
2517         //                                              drawn friendly.  If the team is different than the players, then draw the
2518         //                                              appropriate IFF.          
2519         int team;
2520         team = obj_team(objp);
2521
2522         if ( ship_is_tagged(objp) ) {
2523                 gr_set_color_fast(&IFF_colors[IFF_COLOR_TAGGED][is_bright]);
2524         } else if ( (team == Player_ship->team) && (Player_ship->team != TEAM_TRAITOR) ) {
2525                 gr_set_color_fast(&IFF_colors[IFF_COLOR_FRIENDLY][is_bright]);
2526         } else {
2527                 switch (team) {
2528                 case TEAM_NEUTRAL:
2529                         gr_set_color_fast(&IFF_colors[IFF_COLOR_NEUTRAL][is_bright]);
2530                         break;
2531                 case TEAM_UNKNOWN:
2532                         gr_set_color_fast(&IFF_colors[IFF_COLOR_UNKNOWN][is_bright]);
2533                         break;
2534                 case TEAM_HOSTILE:
2535                 case TEAM_FRIENDLY:
2536                 case TEAM_TRAITOR:
2537                         gr_set_color_fast(&IFF_colors[IFF_COLOR_HOSTILE][is_bright]);
2538                         break;
2539                 default:
2540                         Int3();
2541                         gr_set_color_fast(&IFF_colors[IFF_COLOR_UNKNOWN][is_bright]);
2542                         break;
2543                 }
2544         }
2545 }
2546
2547 // Determine if ship team should be ignored, based on
2548 // team filter
2549 // input:       team_filter     =>      team mask used to select friendly or hostile ships
2550 //                              ship_team       =>      team of the ship in question
2551 // exit:                1                               =>      ship_team matches filter from player perspective
2552 //                              0                               =>      ship_team does match team filter
2553 int hud_team_matches_filter(int team_filter, int ship_team)
2554 {
2555         return team_filter & ship_team;
2556 }
2557
2558
2559 // reset gauge flashing data
2560 void hud_gauge_flash_init()
2561 {
2562         int i;
2563         for ( i=0; i<NUM_HUD_GAUGES; i++ ) {
2564                 HUD_gauge_flash_duration[i]=timestamp(0);
2565                 HUD_gauge_flash_next[i]=timestamp(0);
2566         }
2567         HUD_gauge_bright=0;
2568 }
2569
2570 #define NUM_VM_OTHER_SHIP_GAUGES 5
2571 static int Vm_other_ship_gauges[NUM_VM_OTHER_SHIP_GAUGES] = 
2572 {
2573         HUD_CENTER_RETICLE,
2574         HUD_TARGET_MONITOR,
2575         HUD_TARGET_MONITOR_EXTRA_DATA,
2576         HUD_MESSAGE_LINES,
2577         HUD_TALKING_HEAD
2578 };
2579
2580 // determine if the specified HUD gauge should be displayed
2581 int hud_gauge_active(int gauge_index)
2582 {
2583         Assert(gauge_index >=0 && gauge_index < NUM_HUD_GAUGES);
2584
2585         // AL: Special code: Only show two gauges when not viewing from own ship
2586         if ( Viewer_mode & VM_OTHER_SHIP ) {
2587                 for ( int i = 0; i < NUM_VM_OTHER_SHIP_GAUGES; i++ ) {
2588                         if ( gauge_index == Vm_other_ship_gauges[i] ) {
2589                                 return 1;
2590                         }
2591                 }
2592                 return 0;
2593         }
2594
2595         return hud_config_show_flag_is_set(gauge_index);
2596 }
2597
2598 // determine if gauge is in pop-up mode or not
2599 int hud_gauge_is_popup(int gauge_index)
2600 {
2601         Assert(gauge_index >=0 && gauge_index < NUM_HUD_GAUGES);
2602         return hud_config_popup_flag_is_set(gauge_index);
2603 }
2604
2605 // determine if a popup gauge should be drawn
2606 int hud_gauge_popup_active(int gauge_index)
2607 {
2608         Assert(gauge_index >=0 && gauge_index < NUM_HUD_GAUGES);
2609         if ( !hud_gauge_is_popup(gauge_index) ) {
2610                 return 0;
2611         }
2612
2613         if ( !timestamp_elapsed(HUD_popup_timers[gauge_index]) ) {
2614                 return 1;
2615         } else {
2616                 return 0;
2617         }
2618 }
2619
2620 // start a gauge to popup
2621 void hud_gauge_popup_start(int gauge_index, int time) 
2622 {
2623         Assert(gauge_index >=0 && gauge_index < NUM_HUD_GAUGES);
2624         if ( !hud_gauge_is_popup(gauge_index) ) {
2625                 return;
2626         }
2627
2628         HUD_popup_timers[gauge_index] = timestamp(time);
2629
2630 }
2631
2632 // call HUD function to flash gauge
2633 void hud_gauge_start_flash(int gauge_index)
2634 {
2635         Assert(gauge_index >=0 && gauge_index < NUM_HUD_GAUGES);
2636         HUD_gauge_flash_duration[gauge_index] = timestamp(HUD_GAUGE_FLASH_DURATION);
2637         HUD_gauge_flash_next[gauge_index] = 1;
2638 }
2639
2640 // Set the HUD color for the gauge, based on whether it is flashing or not
2641 void hud_set_gauge_color(int gauge_index, int bright_index)
2642 {
2643         color use_color;
2644         int flash_status = hud_gauge_maybe_flash(gauge_index);
2645         use_color = HUD_config.clr[gauge_index];
2646         int alpha;
2647
2648         // if we're drawing it as bright
2649         if(bright_index != HUD_C_NONE){
2650                 switch(bright_index){
2651                 case HUD_C_DIM:
2652                         alpha = HUD_contrast ? HUD_NEW_ALPHA_DIM_HI : HUD_NEW_ALPHA_DIM;
2653                         gr_init_alphacolor(&use_color, use_color.red, use_color.green, use_color.blue, alpha);
2654                         break;
2655
2656                 case HUD_C_NORMAL:
2657                         alpha = HUD_contrast ? HUD_NEW_ALPHA_NORMAL_HI : HUD_NEW_ALPHA_NORMAL;
2658                         gr_init_alphacolor(&use_color, use_color.red, use_color.green, use_color.blue, alpha);
2659                         break;
2660
2661                 case HUD_C_BRIGHT:
2662                         alpha = HUD_contrast ? HUD_NEW_ALPHA_BRIGHT_HI : HUD_NEW_ALPHA_BRIGHT;
2663                         gr_init_alphacolor(&use_color, use_color.red, use_color.green, use_color.blue, alpha);
2664                         break;
2665
2666                 // intensity
2667                 default: 
2668                         Assert((bright_index >= 0) && (bright_index < HUD_NUM_COLOR_LEVELS));
2669                         if(bright_index < 0){
2670                                 bright_index = 0;
2671                         }
2672                         if(bright_index >= HUD_NUM_COLOR_LEVELS){
2673                                 bright_index = HUD_NUM_COLOR_LEVELS - 1;
2674                         }
2675
2676                         // alpha = 255 - (255 / (bright_index + 1));
2677                         // alpha = (int)((float)alpha * 1.5f);
2678                         int level = 255 / (HUD_NUM_COLOR_LEVELS);
2679                         alpha = level * bright_index;
2680                         if(alpha > 255){
2681                                 alpha = 255;
2682                         }
2683                         if(alpha < 0){
2684                                 alpha = 0;
2685                         }
2686                         gr_init_alphacolor(&use_color, use_color.red, use_color.green, use_color.blue, alpha);
2687                         break;
2688                 }
2689         } else {
2690                 switch(flash_status) {
2691                 case 0:
2692                         alpha = HUD_contrast ? HUD_NEW_ALPHA_DIM_HI : HUD_NEW_ALPHA_DIM;
2693                         gr_init_alphacolor(&use_color, use_color.red, use_color.green, use_color.blue, alpha);
2694                         break;
2695                 case 1:                 
2696                         alpha = HUD_contrast ? HUD_NEW_ALPHA_BRIGHT_HI : HUD_NEW_ALPHA_BRIGHT;
2697                         gr_init_alphacolor(&use_color, use_color.red, use_color.green, use_color.blue, alpha);
2698                         break;
2699                 default:                        
2700                         alpha = HUD_contrast ? HUD_NEW_ALPHA_NORMAL_HI : HUD_NEW_ALPHA_NORMAL;  
2701                         gr_init_alphacolor(&use_color, use_color.red, use_color.green, use_color.blue, alpha);
2702                         break;
2703                 }
2704         }
2705
2706         gr_set_color_fast(&use_color);  
2707 }
2708
2709 // set the color for a gauge that may be flashing
2710 // exit:        -1      =>      gauge is not flashing
2711 //                      0       =>      gauge is flashing, draw dim
2712 //                      1       =>      gauge is flashing, draw bright
2713 int hud_gauge_maybe_flash(int gauge_index)
2714 {
2715         Assert(gauge_index >=0 && gauge_index < NUM_HUD_GAUGES);
2716         int flash_status=-1;
2717         if ( !timestamp_elapsed(HUD_gauge_flash_duration[gauge_index]) ) {
2718                 if ( timestamp_elapsed(HUD_gauge_flash_next[gauge_index]) ) {
2719                         HUD_gauge_flash_next[gauge_index] = timestamp(HUD_GAUGE_FLASH_INTERVAL);
2720                         HUD_gauge_bright ^= (1<<gauge_index);   // toggle between default and bright frames
2721                 }
2722
2723                 if ( HUD_gauge_bright & (1<<gauge_index) ) {
2724                         flash_status=1;
2725                 } else {
2726                         flash_status=0;
2727                 }
2728         }
2729         return flash_status;
2730 }
2731
2732 // Init the objective message display data
2733 void hud_objective_message_init()
2734 {
2735         // ensure the talking head border is loaded
2736         if ( !Objective_display_gauge_inited ) {
2737                 Objective_display_gauge.first_frame = bm_load_animation(Objective_fname[gr_screen.res], &Objective_display_gauge.num_frames);
2738                 if ( Objective_display_gauge.first_frame == -1 ) {
2739                         Warning(LOCATION, "Could not load in ani: Objective_fname[gr_screen.res]\n");
2740                 }
2741                 Objective_display_gauge_inited = 1;
2742         }
2743
2744         Objective_display.display_timer=timestamp(0);
2745 }
2746
2747 // Display objective status on the HUD
2748 // input:       type                    =>      type of goal, one of:   PRIMARY_GOAL
2749 //                                                                                                                                      SECONDARY_GOAL
2750 //                                                                                                                                      BONUS_GOAL
2751 //
2752 //                              status          => status of goal, one of:      GOAL_FAILED
2753 //                                                                                                                                      GOAL_COMPLETE
2754 //                                                                                                                                      GOAL_INCOMPLETE
2755 //
2756 void hud_add_objective_messsage(int type, int status)
2757 {
2758         Objective_display.display_timer=timestamp(7000);
2759         Objective_display.goal_type=type;
2760         Objective_display.goal_status=status;
2761
2762         // if this is a multiplayer tvt game
2763         if((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_TEAM) && (Net_player != NULL)){
2764                 mission_goal_fetch_num_resolved(type, &Objective_display.goal_nresolved, &Objective_display.goal_ntotal, Net_player->p_info.team);
2765         } else {
2766                 mission_goal_fetch_num_resolved(type, &Objective_display.goal_nresolved, &Objective_display.goal_ntotal);
2767         }
2768
2769         // TODO: play a sound?
2770 }
2771
2772 // maybe display the 'subspace drive engaged' message
2773 void hud_maybe_display_subspace_notify()
2774 {
2775         int warp_aborted = 0;
2776         // maybe make gauge active
2777         if ( (Player->control_mode == PCM_WARPOUT_STAGE1) || (Player->control_mode == PCM_WARPOUT_STAGE2) || (Player->control_mode == PCM_WARPOUT_STAGE3) ) {
2778                 if (!hud_subspace_notify_active()) {
2779                         // keep sound from being played 1e06 times
2780                         hud_start_subspace_notify();
2781                 }
2782         } else {
2783                 if ( !timestamp_elapsed(HUD_abort_subspace_timer) ) {
2784                         warp_aborted = 1;
2785                 } else {
2786                         hud_stop_subspace_notify();
2787                 }
2788         }
2789
2790         if ( !hud_subspace_notify_active() ) {
2791                 return;
2792         }
2793
2794         if ( Objective_display_gauge.first_frame < 0 ) {
2795                 return;
2796         }
2797
2798         // blit the background  
2799         hud_set_gauge_color(HUD_OBJECTIVES_NOTIFY_GAUGE);
2800         GR_AABITMAP(Objective_display_gauge.first_frame, Objective_display_coords[gr_screen.res][0], Objective_display_coords[gr_screen.res][1]);       
2801
2802         hud_targetbox_start_flash(TBOX_FLASH_OBJECTIVE);
2803         if(hud_targetbox_maybe_flash(TBOX_FLASH_OBJECTIVE)){
2804                 hud_set_gauge_color(HUD_OBJECTIVES_NOTIFY_GAUGE, HUD_C_BRIGHT);
2805         } else {
2806                 hud_set_gauge_color(HUD_OBJECTIVES_NOTIFY_GAUGE);
2807         }
2808
2809
2810         gr_string(0x8000, Subspace_text_coords[gr_screen.res][1],XSTR( "subspace drive", 233));
2811         if ( warp_aborted ) {
2812                 gr_string(0x8000, Subspace_text_val_coords[gr_screen.res][1],XSTR( "aborted", 225));
2813         } else {
2814                 gr_string(0x8000, Subspace_text_val_coords[gr_screen.res][1],XSTR( "engaged", 234));
2815         }
2816 }
2817
2818 // maybe display the 'Downloading new orders' message
2819 void hud_maybe_display_red_alert()
2820 {
2821         if ( !red_alert_check_status() ) {
2822                 return;
2823         }
2824
2825         if ( Objective_display_gauge.first_frame < 0 ) {
2826                 return;
2827         }
2828
2829         if ( hud_subspace_notify_active() ) {
2830                 return;
2831         }
2832
2833         if ( hud_objective_notify_active() ) {
2834                 return;
2835         }
2836
2837         // blit the background
2838         gr_set_color_fast(&Color_red);          // color box red, cuz its an emergency for cryin out loud
2839
2840         GR_AABITMAP(Objective_display_gauge.first_frame, Objective_display_coords[gr_screen.res][0], Objective_display_coords[gr_screen.res][1]);       
2841
2842         hud_targetbox_start_flash(TBOX_FLASH_OBJECTIVE);
2843         if(hud_targetbox_maybe_flash(TBOX_FLASH_OBJECTIVE)) {
2844                 gr_set_color_fast(&Color_red);
2845         } else {
2846                 gr_set_color_fast(&Color_bright_red);
2847         }
2848
2849         gr_string(0x8000, Red_text_coords[gr_screen.res][1], XSTR( "downloading new", 235));
2850         gr_string(0x8000, Red_text_val_coords[gr_screen.res][1], XSTR( "orders...", 236));
2851
2852         // TODO: play a sound?
2853 }
2854
2855 // Maybe show an objective status update on the HUD
2856 void hud_maybe_display_objective_message()
2857 {
2858         char buf[128];
2859
2860         if ( timestamp_elapsed(Objective_display.display_timer) ) {
2861                 hud_stop_objective_notify();
2862                 return;
2863         }
2864
2865         if ( Objective_display_gauge.first_frame < 0 ) {
2866                 return;
2867         }
2868
2869         if ( hud_subspace_notify_active() ) {
2870                 return;
2871         }
2872
2873         if (!hud_objective_notify_active()) {
2874                 hud_start_objective_notify();
2875         }
2876         
2877         // blit the background
2878         hud_set_gauge_color(HUD_OBJECTIVES_NOTIFY_GAUGE);
2879         GR_AABITMAP(Objective_display_gauge.first_frame, Objective_display_coords[gr_screen.res][0], Objective_display_coords[gr_screen.res][1]);       
2880
2881         hud_targetbox_start_flash(TBOX_FLASH_OBJECTIVE);
2882         if(hud_targetbox_maybe_flash(TBOX_FLASH_OBJECTIVE)){
2883                 hud_set_gauge_color(HUD_OBJECTIVES_NOTIFY_GAUGE, HUD_C_BRIGHT);
2884         } else {
2885                 hud_set_gauge_color(HUD_OBJECTIVES_NOTIFY_GAUGE);
2886         }
2887
2888         // draw the correct goal type
2889         switch(Objective_display.goal_type) {
2890         case PRIMARY_GOAL:
2891                 gr_string(0x8000, Objective_text_coords[gr_screen.res][1],XSTR( "primary objective", 237));
2892                 break;
2893         case SECONDARY_GOAL:
2894                 gr_string(0x8000, Objective_text_coords[gr_screen.res][1],XSTR( "secondary objective", 238));
2895                 break;
2896         case BONUS_GOAL:
2897                 gr_string(0x8000, Objective_text_coords[gr_screen.res][1],XSTR( "bonus objective", 239));
2898                 break;
2899         }
2900
2901         // show the status
2902         switch(Objective_display.goal_type) {
2903         case PRIMARY_GOAL:
2904         case SECONDARY_GOAL:
2905                 switch(Objective_display.goal_status) {
2906                 case GOAL_FAILED:
2907                         sprintf(buf, XSTR( "failed (%d/%d)", 240), Objective_display.goal_nresolved, Objective_display.goal_ntotal);
2908                         gr_string(0x8000, Objective_text_val_coords[gr_screen.res][1], buf);
2909                         break;
2910                 default:
2911                         sprintf(buf, XSTR( "complete (%d/%d)", 241), Objective_display.goal_nresolved, Objective_display.goal_ntotal);
2912                         gr_string(0x8000, Objective_text_val_coords[gr_screen.res][1], buf);
2913                         break;
2914                 }               
2915                 break;
2916         case BONUS_GOAL:
2917                 switch(Objective_display.goal_status) {
2918                 case GOAL_FAILED:
2919                         gr_string(0x8000, Objective_text_val_coords[gr_screen.res][1], XSTR( "failed", 242));
2920                         break;
2921                 default:
2922                         gr_string(0x8000, Objective_text_val_coords[gr_screen.res][1], XSTR( "complete", 226));
2923                         break;
2924                 }               
2925                 break;
2926         }
2927 }
2928
2929 // return wing slot (0->3) based on name of ship.  Assumes ship is from Alpha,Beta, or 
2930 // Gamma wings
2931 int hud_wing_slot_from_name(char *name)
2932 {
2933         int     rval;
2934         char    num[2];
2935
2936         num[0]=name[strlen(name)-1];
2937         num[1]=0;
2938
2939         rval = num[0] - '1';
2940         Assert(rval >= 0 && rval < 4);
2941         return rval;
2942 }
2943
2944 // return index in starting wings (0->11) for specified ship.
2945 int hud_wing_index_from_ship(int shipnum)
2946 {
2947         int     i;
2948         ship    *shipp;
2949
2950         shipp = &Ships[shipnum];
2951
2952         int wing_num=0, wing_slot=0;
2953
2954         for (i=0; i<3; i++) {
2955                 if ( Starting_wings[i] < 0 ) {
2956                         continue;
2957                 }
2958
2959                 if (shipp->wingnum == Starting_wings[i]) {
2960                         wing_num=i;
2961                         break;
2962                 }
2963         }
2964
2965         if ( i==3 ) {
2966                 Int3();
2967         }
2968
2969         wing_slot = hud_wing_slot_from_name(shipp->ship_name);
2970         return (i*4+wing_slot);
2971 }
2972
2973 void hud_show_voice_status()
2974 {
2975         char play_callsign[CALLSIGN_LEN+5];
2976         
2977         // if we are currently playing a rtvoice sound stream from another player back
2978         memset(play_callsign,0,CALLSIGN_LEN+5);
2979         switch(multi_voice_status()){
2980         // the player has been denied the voice token
2981         case MULTI_VOICE_STATUS_DENIED:
2982                 // show a red indicator or something
2983                 gr_string(Voice_coords[gr_screen.res][0], Voice_coords[gr_screen.res][1], XSTR( "[voice denied]", 243));
2984                 break;
2985
2986         // the player is currently recording
2987         case MULTI_VOICE_STATUS_RECORDING:
2988                 gr_string(Voice_coords[gr_screen.res][0], Voice_coords[gr_screen.res][1], XSTR( "[recording voice]", 244));
2989                 break;
2990                 
2991         // the player is current playing back voice from someone
2992         case MULTI_VOICE_STATUS_PLAYING:
2993                 gr_string(Voice_coords[gr_screen.res][0], Voice_coords[gr_screen.res][1], XSTR( "[playing voice]", 245));
2994                 break;
2995
2996         // nothing voice related is happening on my machine
2997         case MULTI_VOICE_STATUS_IDLE:
2998                 // probably shouldn't be displaying anything
2999                 break;
3000         }       
3001 }
3002
3003 void hud_subspace_notify_abort()
3004 {
3005         HUD_abort_subspace_timer = timestamp(1500);
3006 }
3007
3008 void hud_stop_subspace_notify()
3009 {
3010         Subspace_notify_active=0;
3011 }
3012
3013 void hud_start_subspace_notify()
3014 {
3015
3016         Subspace_notify_active=1;
3017 }
3018
3019 int hud_subspace_notify_active()
3020 {
3021         return Subspace_notify_active;
3022 }
3023
3024 void hud_stop_objective_notify()
3025 {
3026         Objective_notify_active = 0;
3027 }
3028
3029 void hud_start_objective_notify()
3030 {
3031         snd_play(&(Snds[SND_DIRECTIVE_COMPLETE]));
3032         Objective_notify_active = 1;
3033 }
3034
3035 int hud_objective_notify_active()
3036 {
3037         return Objective_notify_active;
3038 }
3039
3040 // render multiplayer text message currently being entered if any
3041 void hud_maybe_render_multi_text()
3042 {
3043         char txt[MULTI_MSG_MAX_TEXT_LEN+20];
3044
3045         // clear the text
3046         memset(txt,0,MULTI_MSG_MAX_TEXT_LEN+1);
3047
3048         // if there is valid multiplayer message text to be displayed
3049         if(multi_msg_message_text(txt)){
3050                 gr_set_color_fast(&Color_normal);
3051                 gr_string(Multi_msg_coords[gr_screen.res][0], Multi_msg_coords[gr_screen.res][1], txt);
3052         }
3053 }
3054
3055 // cut any text off after (and including) '#' char
3056 void hud_end_string_at_first_hash_symbol(char *src)
3057 {
3058         char *pointer_to_last_char;
3059
3060         pointer_to_last_char = strstr(src, NOX("#"));
3061
3062         if ( pointer_to_last_char ) {
3063                 *pointer_to_last_char = 0;
3064         }
3065 }
3066
3067 // set the offset values for this render frame
3068 void HUD_set_offsets(object *viewer_obj, int wiggedy_wack)
3069 {
3070         if ( (viewer_obj == Player_obj) && wiggedy_wack ){              
3071                 vector tmp;
3072                 vertex pt;
3073                 ubyte flags;            
3074
3075                 HUD_offset_x = 0.0f;
3076                 HUD_offset_y = 0.0f;
3077
3078                 vm_vec_scale_add( &tmp, &Viewer_obj->pos, &Viewer_obj->orient.v.fvec, 100.0f );
3079                 
3080                 flags = g3_rotate_vertex(&pt,&tmp);
3081
3082                 if (flags == 0) {
3083
3084                         g3_project_vertex(&pt);
3085
3086                         if (!(pt.flags & PF_OVERFLOW))  {
3087                                 HUD_offset_x -= 0.45f * (i2fl(gr_screen.clip_width)*0.5f - pt.sx);
3088                                 HUD_offset_y -= 0.45f * (i2fl(gr_screen.clip_height)*0.5f - pt.sy);
3089                         }
3090                 }
3091
3092                 if ( HUD_offset_x > 100.0f )    {
3093                         HUD_offset_x = 100.0f;
3094                 } else if ( HUD_offset_x < -100.0f )    {
3095                         HUD_offset_x += 100.0f;
3096                 }
3097
3098                 if ( HUD_offset_y > 100.0f )    {
3099                         HUD_offset_y = 100.0f;
3100                 } else if ( HUD_offset_y < -100.0f )    {
3101                         HUD_offset_y += 100.0f;
3102                 }
3103
3104         } else {
3105                 HUD_offset_x = 0.0f;
3106                 HUD_offset_y = 0.0f;
3107         }
3108 }
3109
3110 // Basically like gr_reset_clip only it accounts for hud jittering
3111 void HUD_reset_clip()
3112 {
3113         int hx = fl2i(HUD_offset_x);
3114         int hy = fl2i(HUD_offset_y);
3115
3116         gr_set_clip(hx, hy, gr_screen.max_w, gr_screen.max_h );
3117 }
3118
3119 // Basically like gr_set_clip only it accounts for hud jittering
3120 void HUD_set_clip(int x, int y, int w, int h)
3121 {
3122         int hx = fl2i(HUD_offset_x);
3123         int hy = fl2i(HUD_offset_y);
3124
3125         gr_set_clip(hx+x, hy+y, w, h );
3126 }
3127
3128 void hud_toggle_contrast()
3129 {
3130         HUD_contrast = !HUD_contrast;
3131 }
3132
3133 void hud_set_contrast(int high)
3134 {
3135         HUD_contrast = high;
3136 }
3137
3138 // Paging functions for the rest of the hud code
3139 extern void hudwingmanstatus_page_in();
3140 extern void hudescort_page_in();
3141 extern void hudets_page_in();
3142 extern void hudlock_page_in();
3143 extern void hudreticle_page_in();
3144 extern void hudshield_page_in();
3145 extern void hudsquadmsg_page_in();
3146 extern void hudtarget_page_in();
3147 extern void hudtargetbox_page_in();
3148
3149 // Page in all hud bitmaps
3150 void hud_page_in()
3151 {
3152         int i;
3153
3154         bm_page_in_aabitmap( Kills_gauge.first_frame, Kills_gauge.num_frames );
3155         bm_page_in_aabitmap( Head_frame_gauge.first_frame, Head_frame_gauge.num_frames );
3156         bm_page_in_aabitmap( Mission_time_gauge.first_frame, Mission_time_gauge.num_frames );
3157         for ( i = 0; i < NUM_DAMAGE_GAUGES; i++ ) {
3158                 bm_page_in_aabitmap( Damage_gauges[i].first_frame, Damage_gauges[i].num_frames);
3159         }
3160
3161         bm_page_in_aabitmap( Netlag_icon.first_frame, Netlag_icon.num_frames);                  
3162         bm_page_in_aabitmap( Support_view_gauge.first_frame, Support_view_gauge.num_frames);
3163         bm_page_in_aabitmap( Objective_display_gauge.first_frame, Objective_display_gauge.num_frames);          
3164
3165         // Paging functions for the rest of the hud code
3166         hudwingmanstatus_page_in();
3167         hudescort_page_in();
3168         hudets_page_in();
3169         hudlock_page_in();
3170         hudreticle_page_in();
3171         hudshield_page_in();
3172         hudsquadmsg_page_in();
3173         hudtarget_page_in();
3174         hudtargetbox_page_in();
3175 }
3176