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