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