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