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