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