2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
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
10 * $Logfile: /Freespace2/code/Hud/HUD.cpp $
15 * C module that contains all the HUD functions at a high level
18 * Revision 1.9 2005/10/01 22:04:58 taylor
19 * fix FS1 (de)briefing voices, the directory names are different in FS1
20 * hard code the table values so that the fs1.vp file isn't needed
21 * hard code a mission fix for sm2-08a since a have no idea how to fix it otherwise
22 * generally cleanup some FS1 code
23 * fix volume sliders in the options screen that never went all the way up
25 * Revision 1.8 2004/09/20 01:31:44 theoddone33
28 * Revision 1.7 2003/08/03 16:10:29 taylor
29 * cleanup; compile warning fixes
31 * Revision 1.6 2003/06/03 04:00:40 taylor
32 * Polish language support (Janusz Dziemidowicz)
34 * Revision 1.5 2003/05/25 02:30:42 taylor
37 * Revision 1.4 2003/05/18 03:55:30 taylor
38 * automatic language selection support
40 * Revision 1.3 2002/06/17 06:33:09 relnev
41 * ryan's struct patch for gcc 2.95
43 * Revision 1.2 2002/06/09 04:41:21 relnev
44 * added copyright header
46 * Revision 1.1.1.1 2002/05/03 03:28:09 root
50 * 58 10/28/99 2:04a Jefff
51 * some german specific coords.
53 * 57 10/25/99 5:43p Jefff
54 * added (and subsequently commented) some scoring debug code. checked in
57 * 56 9/09/99 3:55a Andsager
58 * Reset Hud_support_objnum to -1 when guage stops displaying
60 * 55 9/01/99 11:16a Andsager
61 * Fix bug where support ship guage would not show up if second support
62 * ship called in whlile 1st one dying.
64 * 54 8/23/99 1:49p Dave
65 * Fixed damage popup (hopefully)
67 * 53 8/23/99 11:34a Dave
68 * Fixed shield intensity rendering problems.
70 * 52 8/19/99 6:16p Jefff
72 * 51 8/17/99 7:15p Jefff
73 * auto-target & auto-speed text drawn in code
75 * 50 8/16/99 4:04p Dave
76 * Big honking checkin.
78 * 49 8/09/99 3:47p Dave
79 * Fixed incorrect nebula regeneration. Default HUD to low-contrast in
80 * non-nebula missions.
82 * 48 8/09/99 3:14p Dave
83 * Make "launch" warning gauge draw in code.
85 * 47 8/05/99 2:05a Dave
86 * Fixes. Optimized detail level stuff.
88 * 46 8/04/99 2:56p Jefff
89 * fixed black box behind pilot head in hi-res
91 * 45 8/04/99 9:54a Andsager
92 * Auto target turrets on big ships.
94 * 44 8/01/99 12:39p Dave
95 * Added HUD contrast control key (for nebula).
97 * 43 7/31/99 4:15p Dave
98 * Fixed supernova particle velocities. Handle OBJ_NONE in target
99 * monitoring view. Properly use objectives notify gauge colors.
101 * 42 7/31/99 1:16p Dave
102 * Use larger font for 1024 HUD flash text box. Make beam weapons aware of
103 * weapon subsystem damage on firing ship.
105 * 41 7/26/99 10:41a Jefff
106 * added call to hud_maybe_show_damage() in hud_render_2d(). not sure how
107 * this got out in the 1st place.
109 * 40 7/24/99 1:54p Dave
110 * Hud text flash gauge. Reworked dead popup to use 4 buttons in red-alert
113 * 39 7/22/99 4:00p Dave
114 * Fixed beam weapon muzzle glow rendering. Externalized hud shield info.
116 * 38 7/21/99 8:10p Dave
117 * First run of supernova effect.
119 * 37 7/21/99 3:19p Jefff
120 * adjusted subspace and red alert popup text coords
122 * 36 7/19/99 2:13p Dave
123 * Added some new strings for Heiko.
125 * 35 7/19/99 11:48a Jefff
126 * Countermeasure success sound added
128 * 34 7/16/99 12:22p Jefff
129 * Added sound FX to objective popups
131 * 33 7/15/99 7:16p Jefff
132 * Red Alert box is now red
134 * 32 7/09/99 12:00a Andsager
135 * Added target box with distance for remote detonate weapons
137 * 31 6/28/99 4:33p Jasenw
138 * Fixed coords for hi res engine wash gauge
140 * 30 6/11/99 11:13a Dave
141 * last minute changes before press tour build.
143 * 29 6/10/99 3:43p Dave
144 * Do a better job of syncing text colors to HUD gauges.
146 * 28 6/08/99 1:14a Dave
147 * Multi colored hud test.
149 * 27 6/07/99 4:20p Andsager
150 * Add HUD color for tagged object. Apply to target and radar.
152 * 26 5/28/99 5:36p Andsager
155 * 25 5/28/99 10:00a Andsager
156 * Make player hud target affected by Nebula range
158 * 24 5/22/99 5:35p Dave
159 * Debrief and chatbox screens. Fixed small hi-res HUD bug.
161 * 23 5/21/99 5:36p Andsager
162 * Put in high res engine wash gauge and approx coords
164 * 22 5/21/99 1:44p Andsager
165 * Add engine wash gauge
167 * 21 4/20/99 6:39p Dave
168 * Almost done with artillery targeting. Added support for downloading
169 * images on the PXO screen.
171 * 20 2/25/99 4:19p Dave
172 * Added multiplayer_beta defines. Added cd_check define. Fixed a few
173 * release build warnings. Added more data to the squad war request and
176 * 19 2/24/99 4:02p Dave
177 * Fixed weapon locking and homing problems for multiplayer dogfight mode.
179 * 18 2/17/99 2:10p Dave
180 * First full run of squad war. All freespace and tracker side stuff
183 * 17 2/03/99 8:37a Jasen
184 * Fixed dock in coords
186 * 16 2/01/99 9:24a Jasen
187 * Fixed subspace and objectives displays for hi res.
189 * 15 1/25/99 5:03a Dave
190 * First run of stealth, AWACS and TAG missile support. New mission type
193 * 14 1/21/99 9:28p Dave
194 * Fixed damage gauge coords.
196 * 13 1/07/99 9:05a Jasen
197 * coords, coords, coords
199 * 12 1/06/99 3:24p Dave
202 * 11 1/06/99 3:14p Jasen
205 * 10 1/06/99 2:33p Jasen
208 * 9 1/06/99 1:27p Dave
209 * Removed duplicate global var.
211 * 8 1/06/99 1:26p Dave
212 * Put in seperate X coords for "dock in" and the associated time value
213 * for the support ship gauge.
215 * 7 12/28/98 3:17p Dave
216 * Support for multiple hud bitmap filenames for hi-res mode.
218 * 6 12/21/98 5:02p Dave
219 * Modified all hud elements to be multi-resolution friendly.
221 * 5 12/18/98 1:13a Dave
222 * Rough 1024x768 support for Direct3D. Proper detection and usage through
225 * 4 11/05/98 4:18p Dave
226 * First run nebula support. Beefed up localization a bit. Removed all
227 * conditional compiles for foreign versions. Modified mission file
230 * 3 10/13/98 9:28a Dave
231 * Started neatening up freespace.h. Many variables renamed and
232 * reorganized. Added AlphaColors.[h,cpp]
234 * 2 10/07/98 10:53a Dave
237 * 1 10/07/98 10:49a Dave
239 * 223 8/28/98 3:28p Dave
240 * EMP effect done. AI effects may need some tweaking as required.
242 * 222 8/25/98 1:48p Dave
243 * First rev of EMP effect. Player side stuff basically done. Next comes
246 * 221 8/09/98 4:45p Lawrance
247 * center various HUD text - fixes problems in the German version
249 * 220 6/18/98 10:10a Allender
250 * fixed compiler warnings
252 * 219 6/17/98 11:03a Lawrance
253 * position subspace notify correctly for german version
255 * 218 6/13/98 10:48p Lawrance
256 * Changed code to utilize proper fixed-space 1 character.
258 * 217 6/13/98 6:01p Hoffoss
259 * Externalized all new (or forgot to be added) strings to all the code.
261 * 216 6/12/98 2:49p Dave
262 * Patch 1.02 changes.
264 * 215 6/09/98 10:31a Hoffoss
265 * Created index numbers for all xstr() references. Any new xstr() stuff
266 * added from here on out should be added to the end if the list. The
267 * current list count can be found in FreeSpace.cpp (search for
270 * 214 6/01/98 11:43a John
271 * JAS & MK: Classified all strings for localization.
273 * 213 5/23/98 4:14p John
274 * Added code to preload textures to video card for AGP. Added in code
275 * to page in some bitmaps that weren't getting paged in at level start.
277 * 212 5/17/98 3:32p Lawrance
278 * Allow red alert orders to get downloaded when in an out-of-cockpit view
280 * 211 5/15/98 8:36p Lawrance
281 * Add 'target ship that last sent transmission' target key
283 * 210 5/10/98 5:28p Lawrance
284 * Ensure hud messages and talking heads show up when viewing from another
287 * 209 5/10/98 12:11a Lawrance
288 * Fix a couple of problems with 2D gauges showing up in external views
290 * 208 5/09/98 4:52p Lawrance
291 * Implement padlock view (up/rear/left/right)
293 * 207 5/09/98 12:20a Lawrance
294 * Show hud messages in all views
296 * 206 5/08/98 5:32p Lawrance
297 * Allow cargo scanning even if target gauge is disabled
299 * 205 5/08/98 10:13a Lawrance
300 * Don't allow targeting of ships that have SF_EXPLODED flag set
302 * 204 5/07/98 1:01a Chad
303 * Yet another hud gauage which shouldn't be rendered as a multiplayer
306 * 203 5/04/98 12:08p Ed
307 * from allender: move hud_target_change_check() after code which does
308 * possible auto target change. Fixed multiplayer problem where locking
309 * subsys does not match ship currently targeted
311 * 202 5/04/98 6:12p Lawrance
312 * Write generic function hud_end_string_at_first_hash_symbol(), to use in
313 * various spots on the HUD
315 * 201 4/30/98 3:32p Lawrance
316 * Cull dead/departed ships from escort ship in hud_update_frame()
318 * 200 4/23/98 10:24p Mike
319 * Int3(), then recover gracefully from some error in which ship to be
320 * repaired is killed.
327 #include "freespace.h"
328 #include "systemvars.h"
330 #include "hudtarget.h"
331 #include "hudreticle.h"
332 #include "hudmessage.h"
336 #include "multiutil.h"
338 #include "hudsquadmsg.h"
340 #include "eventmusic.h"
347 #include "hudescort.h"
348 #include "hudshield.h"
349 #include "linklist.h"
350 #include "hudtargetbox.h"
351 #include "missionmessage.h"
352 #include "missiontraining.h"
355 #include "hudobserver.h"
356 #include "hudtargetbox.h"
357 #include "hudconfig.h"
358 #include "missiongoals.h"
359 #include "asteroid.h"
360 #include "starfield.h"
361 #include "hudwingmanstatus.h"
362 #include "multi_voice.h"
363 #include "multi_pmsg.h"
364 #include "redalert.h"
366 #include "alphacolors.h"
367 #include "localize.h"
368 #include "supernova.h"
371 // new values for HUD alpha
372 #define HUD_NEW_ALPHA_DIM 80
373 #define HUD_NEW_ALPHA_NORMAL 120
374 #define HUD_NEW_ALPHA_BRIGHT 220
377 #define HUD_NEW_ALPHA_DIM_HI 130
378 #define HUD_NEW_ALPHA_NORMAL_HI 190
379 #define HUD_NEW_ALPHA_BRIGHT_HI 255
381 // globals that will control the color of the HUD gauges
382 int HUD_color_red = 0;
383 int HUD_color_green = 255;
384 int HUD_color_blue = 0;
385 int HUD_color_alpha = HUD_COLOR_ALPHA_DEFAULT; // 1 -> HUD_COLOR_ALPHA_USER_MAX
387 int HUD_contrast = 0; // high or lo contrast (for nebula, etc)
389 color HUD_color_defaults[HUD_NUM_COLOR_LEVELS]; // array of colors with different alpha blending
390 color HUD_color_debug; // grey debug text shown on HUD
392 static int Player_engine_snd_loop = -1;
394 // animations for damages gauges
395 hud_anim Target_static;
396 hud_anim Radar_static;
398 // HUD render frame offsets
399 float HUD_offset_x = 0.0f;
400 float HUD_offset_y = 0.0f;
402 // Global: integrity of player's target
403 float Pl_target_integrity;
405 static int Hud_last_can_target; // whether Player is able to target in the last frame
406 static int Hud_can_target_timer; // timestamp to allow target gauge to draw static once targeting functions are not allowed
408 // centered text message gauges (collision, emp, etc)
409 char Hud_text_flash[512] = "";
410 int Hud_text_flash_coords[GR_NUM_RESOLUTIONS][2] = {
418 void hud_init_text_flash_gauge();
419 void hud_start_text_flash(const char *txt, int t);
420 void hud_maybe_show_text_flash_icon();
423 // multiplayer messaging text
424 int Multi_msg_coords[GR_NUM_RESOLUTIONS][2] = {
433 // multiplayer voice stuff
434 int Voice_coords[GR_NUM_RESOLUTIONS][2] = {
443 // redalert downloading new orders text
444 int Red_text_coords[GR_NUM_RESOLUTIONS][2] = {
452 int Red_text_val_coords[GR_NUM_RESOLUTIONS][2] = {
462 int Subspace_text_coords[GR_NUM_RESOLUTIONS][2] = {
470 int Subspace_text_val_coords[GR_NUM_RESOLUTIONS][2] = {
479 // message text coords
480 int Head_message_coords[GR_NUM_RESOLUTIONS][2] = {
490 int Ping_coords[GR_NUM_RESOLUTIONS][2] = {
500 int Supernova_coords[GR_NUM_RESOLUTIONS][2] = {
509 // used to draw the netlag icon on the HUD
510 hud_frames Netlag_icon;
511 int Netlag_icon_loaded=0;
512 int Netlag_coords[GR_NUM_RESOLUTIONS][2] = {
520 const char *Netlag_fname[GR_NUM_RESOLUTIONS] = {
525 // used to draw the kills gauge
526 hud_frames Kills_gauge;
527 int Kills_gauge_loaded = 0;
528 int Kills_gauge_coords[GR_NUM_RESOLUTIONS][2] = {
536 int Kills_text_coords[GR_NUM_RESOLUTIONS][2] = {
545 // for German version
546 int Kills_text_val_coords_gr[GR_NUM_RESOLUTIONS][2] = {
555 int Kills_text_val_coords[GR_NUM_RESOLUTIONS][2] = {
564 const char *Kills_fname[GR_NUM_RESOLUTIONS] = {
569 // used to draw border around a talking head
570 static hud_frames Head_frame_gauge;
571 static int Head_frame_gauge_loaded = 0;
572 int Head_frame_coords[GR_NUM_RESOLUTIONS][2] = {
580 const char *Head_fname[GR_NUM_RESOLUTIONS] = {
585 // mission time frame
586 static hud_frames Mission_time_gauge;
587 static int Mission_time_gauge_loaded = 0;
588 int Mission_time_coords[GR_NUM_RESOLUTIONS][2] = {
596 int Mission_time_text_coords[GR_NUM_RESOLUTIONS][2] = {
604 int Mission_time_text_val_coords[GR_NUM_RESOLUTIONS][2] = {
612 const char *Mission_time_fname[GR_NUM_RESOLUTIONS] = {
617 // used to draw the hud support view
618 static hud_frames Support_view_gauge;
619 static int Support_view_gauge_loaded = 0;
620 static int Hud_support_view_active;
621 static int Hud_support_view_abort; // active when we need to display abort message
622 static int Hud_support_view_fade; // timer
623 static int Hud_support_obj_sig, Hud_support_objnum, Hud_support_target_sig;
624 int Support_view_coords[GR_NUM_RESOLUTIONS][2] = {
632 int Support_text_coords[GR_NUM_RESOLUTIONS][2] = {
640 int Support_text_val_coords[GR_NUM_RESOLUTIONS][2] = {
648 int Support_text_dock_coords[GR_NUM_RESOLUTIONS][2] = { // "dock in" x coord
656 int Support_text_dock_val_coords[GR_NUM_RESOLUTIONS][2] = { // time value for "dock in" x coord
664 const char *Support_fname[GR_NUM_RESOLUTIONS] = {
669 // damage gauge stuff
670 #define NUM_DAMAGE_GAUGES 3
671 static hud_frames Damage_gauges[NUM_DAMAGE_GAUGES];
672 static int Damage_gauges_loaded = 0;
673 const char *Damage_gauge_fnames[GR_NUM_RESOLUTIONS][NUM_DAMAGE_GAUGES] =
688 int Damage_gauge_line_h[GR_NUM_RESOLUTIONS] = {
692 int Damage_gauge_coords[GR_NUM_RESOLUTIONS][2][2] = {
701 // These #'s seem to work, although I really don't know why. Frankly, it frightens me,
702 // because it means the 640 coords _shouldn't_. This may be due to D3D strangeness, so
703 // we'll have to investigate when we get hi-res Glide in.
705 int Damage_text_coords[GR_NUM_RESOLUTIONS][2] = {
713 int Hull_integ_coords[GR_NUM_RESOLUTIONS][2] = {
721 int Hull_integ_val_coords[GR_NUM_RESOLUTIONS][2] = {
729 int Damage_subsys_text_coords[GR_NUM_RESOLUTIONS][2] = {
740 #define HUD_GAUGE_FLASH_DURATION 5000
741 #define HUD_GAUGE_FLASH_INTERVAL 200
742 int HUD_gauge_flash_duration[NUM_HUD_GAUGES];
743 int HUD_gauge_flash_next[NUM_HUD_GAUGES];
744 int HUD_gauge_bright;
747 typedef struct objective_display_info
755 } objective_display_info;
757 static objective_display_info Objective_display;
759 static int Objective_display_gauge_inited=0;
760 static hud_frames Objective_display_gauge;
761 int Objective_display_coords[GR_NUM_RESOLUTIONS][2] = {
769 int Objective_text_coords[GR_NUM_RESOLUTIONS][2] = {
777 int Objective_text_val_coords[GR_NUM_RESOLUTIONS][2] = {
785 char Objective_fname[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN] = {
790 // Subspace notify display
791 static int Subspace_notify_active;
792 static int Objective_notify_active;
793 static int HUD_abort_subspace_timer = 1;
795 // used to track how player subsystems are getting damaged
796 typedef struct hud_subsys_info
799 int flash_duration_timestamp;
802 static hud_subsys_info Pl_hud_subsys_info[SUBSYSTEM_MAX];
803 static int Pl_hud_next_flash_timestamp;
804 static int Pl_hud_is_bright;
806 #define SUBSYS_DAMAGE_FLASH_DURATION 1800
807 #define SUBSYS_DAMAGE_FLASH_INTERVAL 100
809 // timers used for popup gauges
810 int HUD_popup_timers[NUM_HUD_GAUGES];
812 // forward declarations
813 void update_throttle_sound();
814 void hud_show_damage_popup();
815 void hud_damage_popup_init();
816 void hud_support_view_init();
817 void hud_gauge_flash_init();
818 void hud_objective_message_init();
819 void hud_maybe_display_objective_message();
820 void hud_stop_subspace_notify();
821 void hud_start_subspace_notify();
822 void hud_stop_objective_notify();
823 void hud_start_objective_notify();
824 int hud_subspace_notify_active();
825 int hud_objective_notify_active();
826 void hud_subspace_notify_abort();
827 void hud_maybe_display_subspace_notify();
828 void hud_init_netlag_icon();
829 void hud_maybe_show_netlag_icon();
830 void hud_maybe_display_red_alert();
831 void hud_init_kills_gauge();
832 void hud_show_kills_gauge();
833 int hud_maybe_render_emp_icon();
834 void hud_init_emp_icon();
836 // Saturate a value in minv..maxv.
837 void saturate(int *i, int minv, int maxv)
845 // init the colors used for the different shades of the HUD
846 void HUD_init_hud_color_array()
850 for ( i = 0; i < HUD_NUM_COLOR_LEVELS; i++ ) {
851 gr_init_alphacolor( &HUD_color_defaults[i], HUD_color_red, HUD_color_green, HUD_color_blue, (i+1)*16, AC_TYPE_HUD );
855 // HUD_init will call all the various HUD gauge init functions. This function is called at the
856 // start of each mission (level)
857 void HUD_init_colors()
859 saturate(&HUD_color_red, 0, 255);
860 saturate(&HUD_color_green, 0, 255);
861 saturate(&HUD_color_blue, 0, 255);
862 saturate(&HUD_color_alpha, 0, HUD_COLOR_ALPHA_USER_MAX);
864 gr_init_alphacolor( &HUD_color_debug, 128, 255, 128, HUD_color_alpha*16, AC_TYPE_HUD );
865 HUD_init_hud_color_array();
867 hud_init_targeting_colors();
868 hud_gauge_flash_init();
871 // The following global data is used to determine if we should change the engine sound.
872 // We only check if the throttle has changed every THROTTLE_SOUND_CHECK_INTERVAL ms, and
873 // then we make sure that the throttle has actually changed. If it has changed, we start
874 // a new sound and/or adjust the volume. This occurs in update_throttle_sound()
876 static float last_percent_throttle;
877 #define THROTTLE_SOUND_CHECK_INTERVAL 50 // in ms
878 static int throttle_sound_check_id;
880 // used for the display of damaged subsystems
881 typedef struct hud_subsys_damage
888 #define DAMAGE_FLASH_TIME 150
889 static int Damage_flash_bright;
890 static int Damage_flash_timer;
892 // initialize the timers used for popup gauges
893 void hud_init_popup_timers()
896 for (i=0; i<NUM_HUD_GAUGES; i++) {
897 HUD_popup_timers[i] = timestamp(0);
901 // Load in the bitmap for the talking head gauge if required
902 void hud_init_talking_head_gauge()
904 // ensure the talking head border is loaded
905 if ( !Head_frame_gauge_loaded ) {
906 Head_frame_gauge.first_frame = bm_load_animation(Head_fname[gr_screen.res], &Head_frame_gauge.num_frames);
907 if ( Head_frame_gauge.first_frame == -1 ) {
908 Warning(LOCATION, "Could not load in ani: Head_fname[gr_screen.res]\n");
910 Head_frame_gauge_loaded = 1;
914 // Load in the bitmap for the mission time gauge if required
915 void hud_init_mission_time_gauge()
917 // ensure the talking head border is loaded
918 if ( !Mission_time_gauge_loaded ) {
919 Mission_time_gauge.first_frame = bm_load_animation(Mission_time_fname[gr_screen.res], &Mission_time_gauge.num_frames);
920 if ( Mission_time_gauge.first_frame == -1 ) {
921 Warning(LOCATION, "Could not load in ani: Mission_time_fname[gr_screen.res]\n");
923 Mission_time_gauge_loaded = 1;
927 // ----------------------------------------------------------------------
930 // Called each level to initalize HUD systems
935 hud_init_msg_window();
936 hud_init_targeting();
938 hud_shield_level_init();
940 hud_targetbox_init();
942 hud_damage_popup_init();
943 hud_support_view_init();
944 hud_init_squadmsg(); // initialize the vars needed for squadmate messaging
945 hud_init_popup_timers();
946 hud_objective_message_init();
947 hud_init_wingman_status_gauge();
948 hud_anim_init(&Target_static, Target_window_coords[gr_screen.res][0], Target_window_coords[gr_screen.res][1], NOX("TargetStatic"));
949 hud_targetbox_static_init();
950 hud_init_text_flash_gauge();
951 hud_init_netlag_icon();
952 hud_init_talking_head_gauge();
953 hud_init_mission_time_gauge();
954 hud_init_kills_gauge();
955 hud_stop_subspace_notify();
956 hud_stop_objective_notify();
957 hud_target_last_transmit_level_init();
959 throttle_sound_check_id = timestamp(THROTTLE_SOUND_CHECK_INTERVAL);
960 HUD_abort_subspace_timer = 1;
961 Hud_last_can_target = 1;
962 Hud_can_target_timer = 1;
963 last_percent_throttle = 0.0f;
965 // default to high contrast in the nebula
967 if(The_mission.flags & MISSION_FLAG_FULLNEB){
972 // return !0 if HUD is disabled (ie no gauges are shown/usable), otherwise return 0
975 // if ( Ship_info[Player_ship->ship_info_index].species != SPECIES_TERRAN ) {
982 // Determine if we should popup the weapons gauge on the HUD.
983 void hud_maybe_popup_weapons_gauge()
985 if ( hud_gauge_is_popup(HUD_WEAPONS_GAUGE) ) {
986 ship_weapon *swp = &Player_ship->weapons;
989 for ( i = 0; i < swp->num_secondary_banks; i++ ) {
990 if ( swp->secondary_bank_ammo[i] > 0 ) {
991 int ms_till_fire = timestamp_until(swp->next_secondary_fire_stamp[i]);
992 if ( ms_till_fire >= 1000 ) {
993 hud_gauge_popup_start(HUD_WEAPONS_GAUGE, 2500);
1000 // hud_update_frame() will update hud systems
1002 // This function updates those parts of the hud that are not dependant on the
1003 // rendering of the hud.
1004 void hud_update_frame()
1009 update_throttle_sound();
1010 hud_check_reticle_list();
1011 hud_wingman_status_update();
1013 // Check hotkey selections to see if any ships need to be removed
1014 hud_prune_hotkeys();
1016 // Remove dead/departed ships from the escort list
1017 hud_escort_cull_list();
1019 hud_update_reticle( Player );
1020 hud_shield_hit_update();
1021 hud_maybe_popup_weapons_gauge();
1023 // if emp is active we have to allow targeting by the "random emp" system
1024 // we will intercept player targeting requests in hud_sensors_ok() when checking key commands
1026 can_target = hud_sensors_ok(Player_ship, 0);
1027 if(emp_active_local()){
1030 if ( !can_target && Hud_last_can_target ) {
1031 Hud_can_target_timer = timestamp(1200);
1033 Hud_last_can_target = can_target;
1035 if ( timestamp_elapsed(Hud_can_target_timer) ) {
1036 if ( (Player_ai->target_objnum != -1) && !can_target ){
1037 Player_ai->target_objnum = -1;
1041 // if there is no target, check if auto-targeting is enabled, and select new target
1043 int retarget_turret = 0;
1045 if (Player_ai->target_objnum == -1){
1047 } else if (Objects[Player_ai->target_objnum].type == OBJ_SHIP) {
1048 if (Ships[Objects[Player_ai->target_objnum].instance].flags & SF_DYING){
1049 if (timestamp_elapsed(Ships[Objects[Player_ai->target_objnum].instance].final_death_time)) {
1055 // check if big ship and currently selected subsys is turret and turret is dead
1056 // only do this is not retargeting
1057 if ((!retarget) && (Player_ai->target_objnum != -1)) {
1058 if (Objects[Player_ai->target_objnum].type == OBJ_SHIP) {
1059 if ( !(Ships[Objects[Player_ai->target_objnum].instance].flags & SF_DYING) ) {
1060 if ( Ship_info[Ships[Objects[Player_ai->target_objnum].instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP) ) {
1061 ship_subsys *ss = Player_ai->targeted_subsys;
1063 if ((ss->system_info->type == SUBSYSTEM_TURRET) && (ss->current_hits == 0)) {
1064 retarget_turret = 1;
1072 if ( retarget && can_target ) {
1073 Player_ai->current_target_is_locked = 0;
1074 if ( Players[Player_num].flags & PLAYER_FLAGS_AUTO_TARGETING ) {
1075 Player_ai->target_objnum = -1;
1076 hud_target_auto_target_next();
1080 if (retarget_turret && can_target) {
1081 SDL_assert(!retarget);
1082 // get closest weighted live turret
1083 // hud_target_closest(OBJ_INDEX(Player_obj), FALSE, FALSE);
1084 void hud_update_closest_turret();
1085 hud_update_closest_turret();
1088 hud_target_change_check();
1090 if (Player_ai->target_objnum == -1) {
1091 if ( Target_static_looping != -1 ) {
1092 snd_stop(Target_static_looping);
1097 targetp = &Objects[Player_ai->target_objnum];
1100 int stop_targetting_this_thing = 0;
1102 // check to see if the target is still alive
1103 if ( targetp->flags&OF_SHOULD_BE_DEAD ) {
1104 stop_targetting_this_thing = 1;
1107 Player->target_is_dying = FALSE;
1108 ship *target_shipp = NULL;
1110 if ( targetp->type == OBJ_SHIP ) {
1111 SDL_assert ( targetp->instance >=0 && targetp->instance < MAX_SHIPS );
1112 target_shipp = &Ships[targetp->instance];
1113 Player->target_is_dying = target_shipp->flags & SF_DYING;
1115 // If it is warping out (or exploded), turn off targeting
1116 if ( target_shipp->flags & (SF_DEPART_WARP|SF_EXPLODED) ) {
1117 stop_targetting_this_thing = 1;
1121 // Check if can still be seen in Nebula
1122 if ( hud_target_invalid_awacs(targetp) ) {
1123 stop_targetting_this_thing = 1;
1126 // If this was found to be something we shouldn't
1127 // target anymore, just remove it
1128 if ( stop_targetting_this_thing ) {
1129 Player_ai->target_objnum = -1;
1130 Player_ai->targeted_subsys = NULL;
1131 hud_stop_looped_locking_sounds();
1134 if (Player->target_is_dying) {
1135 hud_stop_looped_locking_sounds();
1136 if ( Players[Player_num].flags & PLAYER_FLAGS_AUTO_TARGETING ) {
1137 hud_target_auto_target_next();
1141 // Switch to battle track when a targeted ship is hostile and within BATTLE_START_MIN_TARGET_DIST
1142 if (targetp->type == OBJ_SHIP && Event_Music_battle_started == 0 ) {
1143 SDL_assert( target_shipp != NULL );
1145 if (opposing_team_mask(Player_ship->team)) {
1146 float dist_to_target;
1148 dist_to_target = vm_vec_dist_quick(&targetp->pos, &Player_obj->pos);
1149 if (dist_to_target < BATTLE_START_MIN_TARGET_DIST) {
1151 // If the target has an AI class of none, it is a Cargo, NavBuoy or other non-aggressive
1152 // ship, so don't start the battle music
1153 if (SDL_strcasecmp(Ai_class_names[Ai_info[target_shipp->ai_index].ai_class], NOX("none")))
1154 event_music_battle_start();
1159 // Since we need to reference the player's target integrity in several places this upcoming
1160 // frame, only calculate once here
1161 if ( target_shipp ) {
1163 initial_hull = Ship_info[target_shipp->ship_info_index].initial_hull_strength;
1164 if ( initial_hull <= 0 ) {
1165 Int3(); // illegal initial hull strength
1166 Pl_target_integrity = 0.0f;
1168 Pl_target_integrity = targetp->hull_strength / initial_hull;
1169 if (Pl_target_integrity < 0)
1170 Pl_target_integrity = 0.0f;
1174 hud_update_cargo_scan_sound();
1178 void HUD_render_forward_icon(object *objp)
1183 vm_vec_scale_add(&p0, &objp->pos, &objp->orient.v.fvec, 100.0f);
1184 g3_rotate_vertex(&v0, &p0);
1186 gr_set_color(255, 0, 0);
1187 if ((!(v0.flags & PF_OVERFLOW)) && (v0.codes == 0)) // make sure point projected
1188 g3_draw_sphere(&v0, 1.25f);
1189 else if (v0.codes != 0) { // target center is not on screen
1190 // draw the offscreen indicator at the edge of the screen where the target is closest to
1191 hud_draw_offscreen_indicator(&v0, &p0);
1195 // Draw white brackets around asteroids which has the AF_DRAW_BRACKETS flag set
1196 void hud_show_asteroid_brackets()
1198 if ( hud_sensors_ok(Player_ship, 0) ) {
1199 asteroid_show_brackets();
1203 // Draw radar gauge on the HUD
1204 void hud_show_radar()
1206 if ( hud_disabled() ) {
1210 if (!(Viewer_mode & (VM_EXTERNAL | VM_SLEWED | VM_CHASE | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY ))) {
1211 if ( Game_detail_flags & DETAIL_FLAG_HUD ) {
1212 if ( hud_gauge_active(HUD_RADAR) ) {
1214 radar_frame_render(flFrametime);
1220 // Render model of target in the target view box
1221 void hud_show_target_model()
1223 if ( hud_disabled() ) {
1227 // display the miniature model of the target in the target box and shade
1228 if ( Game_detail_flags & DETAIL_FLAG_HUD ) {
1229 if (!(Viewer_mode & (VM_EXTERNAL | VM_SLEWED | VM_CHASE | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY)))
1230 hud_render_target_model();
1234 void hud_show_common_3d_gauges(float frametime, int in_cockpit)
1236 // draw boxes around current selection set, if any
1237 hud_show_selection_set();
1239 // draw the targeting data around any message sender
1240 hud_show_message_sender();
1242 // draw brackets around asteroids is necessary
1243 hud_show_asteroid_brackets();
1245 // draw targetting data around the current target
1246 hud_show_targeting_gauges(frametime, in_cockpit);
1248 // draw brackets and distance to remote detonate missile
1249 hud_show_remote_detonate_missile();
1252 // Render gauges that need to be between a g3_start_frame() and a g3_end_frame()
1253 void HUD_render_3d(float frametime)
1255 Player->subsys_in_view = -1;
1257 if ( hud_disabled() ) {
1261 if (!(Viewer_mode & (VM_EXTERNAL | VM_SLEWED | VM_CHASE | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY))) {
1263 hud_show_common_3d_gauges(frametime, 1);
1265 // Show all homing missiles locked onto the player.
1266 // Currently not supporting a way to toggle this as I'm not sure we'll stick wtih this gauge. -- MK, 3/17/97.
1267 if ( hud_gauge_active(HUD_MISSILE_WARNING_ARROW) ) {
1268 hud_show_homing_missiles();
1271 } else if ( Viewer_mode & (VM_CHASE | VM_EXTERNAL | VM_WARP_CHASE | VM_PADLOCK_ANY ) ) {
1272 // If the player is warping out, don't draw the targeting gauges
1273 SDL_assert(Player != NULL);
1274 if ( Player->control_mode != PCM_NORMAL ) {
1278 hud_show_common_3d_gauges(frametime, 0);
1281 if (Viewer_mode & VM_SLEWED) {
1282 HUD_render_forward_icon(Player_obj);
1287 // call from HUD_render_2d() when in gameplay, and call when in navmap
1288 void hud_show_messages()
1290 // draw the message window
1291 hud_show_msg_window();
1292 hud_show_fixed_text();
1295 // decide if we want to blit damage status to the screen
1296 void hud_maybe_show_damage()
1298 if ( !hud_gauge_active(HUD_DAMAGE_GAUGE) ) {
1302 // display the current weapon info for the player ship, with ammo/energy counts
1303 if ( hud_gauge_active(HUD_DAMAGE_GAUGE) ) {
1304 int show_gauge_flag;
1306 if ( (Ship_info[Player_ship->ship_info_index].initial_hull_strength - Player_obj->hull_strength) > 1.0f ) {
1307 show_gauge_flag = 1;
1309 show_gauge_flag = 0;
1312 // is gauge configured as a popup?
1313 if ( hud_gauge_is_popup(HUD_DAMAGE_GAUGE) ) {
1314 if ( !hud_gauge_popup_active(HUD_DAMAGE_GAUGE) ) {
1319 if ( show_gauge_flag ) {
1320 hud_show_damage_popup();
1325 // The damage toggle button was pressed, so change state
1326 void hud_damage_popup_toggle()
1328 snd_play(&Snds[SND_SQUADMSGING_ON]);
1330 // If gague is disabled (off), make it on all the time
1331 if ( !hud_gauge_active(HUD_DAMAGE_GAUGE) ) {
1332 hud_config_set_gauge_flags(HUD_DAMAGE_GAUGE,1,0);
1336 // if gauge is popup, turn it off if it is current up, otherwise force it to be up
1337 if ( hud_gauge_is_popup(HUD_DAMAGE_GAUGE) ) {
1338 if ( Player_obj->hull_strength == Ship_info[Player_ship->ship_info_index].initial_hull_strength ) {
1339 hud_config_set_gauge_flags(HUD_DAMAGE_GAUGE,1,0);
1341 hud_config_set_gauge_flags(HUD_DAMAGE_GAUGE,0,0);
1346 // gauge is on, without any popup... so force it to be off
1347 hud_config_set_gauge_flags(HUD_DAMAGE_GAUGE,0,0);
1351 // Display the current mission time in MM:SS format
1352 void hud_show_mission_time()
1354 float mission_time, time_comp;
1358 mission_time = f2fl(Missiontime); // convert to seconds
1360 minutes=(int)(mission_time/60);
1361 seconds=(int)mission_time%60;
1363 hud_set_gauge_color(HUD_MISSION_TIME);
1365 // blit background frame
1366 if ( Mission_time_gauge.first_frame >= 0 ) {
1367 GR_AABITMAP(Mission_time_gauge.first_frame, Mission_time_coords[gr_screen.res][0], Mission_time_coords[gr_screen.res][1]);
1370 // print out mission time in MM:SS format
1371 gr_printf(Mission_time_text_coords[gr_screen.res][0], Mission_time_text_coords[gr_screen.res][1], NOX("%02d:%02d"), minutes, seconds);
1373 // display time compression as xN
1374 time_comp = f2fl(Game_time_compression);
1375 if ( time_comp < 1 ) {
1376 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);
1378 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);
1382 // If a head animation is playing, then blit a border around it
1383 void hud_maybe_blit_head_border()
1385 if ( Head_frame_gauge.first_frame == -1 ){
1389 if ( message_anim_is_playing() ) {
1391 // hud_set_default_color();
1392 hud_set_gauge_color(HUD_TALKING_HEAD);
1394 GR_AABITMAP(Head_frame_gauge.first_frame, Head_frame_coords[gr_screen.res][0], Head_frame_coords[gr_screen.res][1]);
1397 gr_string(Head_message_coords[gr_screen.res][0], Head_message_coords[gr_screen.res][1], XSTR("message", 217));
1401 // Black out area behind head animation
1402 void hud_maybe_clear_head_area()
1404 if ( Head_frame_gauge.first_frame == -1 ) {
1408 if ( message_anim_is_playing() ) {
1410 if (gr_screen.res == GR_640) {
1411 HUD_set_clip(7, 45, 160, 120); // these coords are set in MissionMessage.cpp
1413 HUD_set_clip(7, 66, 160, 120);
1420 void hud_maybe_display_supernova()
1424 // if there's a supernova coming
1425 time_left = supernova_time_left();
1426 if(time_left < 0.0f){
1430 gr_set_color_fast(&Color_bright_red);
1432 gr_printf(Supernova_coords[gr_screen.res][0], Supernova_coords[gr_screen.res][1], "Wybuch supernowej : %.2f s", time_left);
1434 gr_printf(Supernova_coords[gr_screen.res][0], Supernova_coords[gr_screen.res][1], "Supernova Warning : %.2f s", time_left);
1438 // render multiplayer ping time to the server if appropriate
1439 void hud_render_multi_ping()
1441 // if we shouldn't be displaying a ping time, return here
1442 if(!multi_show_ingame_ping()){
1446 // if we're in multiplayer mode, display our ping time to the server
1447 if((Game_mode & GM_MULTIPLAYER) && (Net_player != NULL) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
1449 memset(ping_str,0,50);
1451 // if our ping is positive, display it
1452 if((Netgame.server != NULL) && (Netgame.server->s_info.ping.ping_avg > 0)){
1454 if(Netgame.server->s_info.ping.ping_avg >= 1000){
1455 SDL_strlcpy(ping_str, XSTR("> 1 sec",628), SDL_arraysize(ping_str));
1457 SDL_snprintf(ping_str, SDL_arraysize(ping_str), XSTR("%d ms", 629), Netgame.server->s_info.ping.ping_avg);
1460 // blit the string out
1461 hud_set_default_color();
1462 gr_string(Ping_coords[gr_screen.res][0], Ping_coords[gr_screen.res][1], ping_str);
1467 // render all the 2D gauges on the HUD
1468 void HUD_render_2d(float frametime)
1470 int show_gauge_flag;
1475 // show some scoring debug stuff
1477 extern char Scoring_debug_text[];
1478 gr_string( 10, 40, Scoring_debug_text );
1481 if ( hud_disabled() ) {
1485 if (!(Viewer_mode & (VM_EXTERNAL | VM_SLEWED | VM_CHASE | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY ))) {
1486 // display Energy Transfer System gauges
1487 if ( hud_gauge_active(HUD_ETS_GAUGE) ) {
1489 // is gauge configured as a popup?
1490 if ( hud_gauge_is_popup(HUD_ETS_GAUGE) ) {
1491 if ( !hud_gauge_popup_active(HUD_ETS_GAUGE) ) {
1496 if ( show_gauge_flag ) {
1501 // display afterburner fuel gauge
1502 if ( hud_gauge_active(HUD_AFTERBURNER_ENERGY) ) {
1503 hud_set_gauge_color(HUD_AFTERBURNER_ENERGY);
1504 hud_show_afterburner_gauge();
1508 hud_maybe_show_text_flash_icon();
1510 // maybe show the netlag icon
1511 if(Game_mode & GM_MULTIPLAYER){
1512 hud_maybe_show_netlag_icon();
1514 if(Net_player->flags & NETINFO_FLAG_OBSERVER){
1515 hud_render_observer();
1523 // why is this here twice?
1524 // display Energy Transfer System gauges
1525 if ( hud_gauge_active(HUD_ETS_GAUGE) ) {
1527 // is gauge configured as a popup?
1528 if ( hud_gauge_is_popup(HUD_ETS_GAUGE) ) {
1529 if ( !hud_gauge_popup_active(HUD_ETS_GAUGE) ) {
1534 if ( show_gauge_flag ) {
1540 // display info on the ships in the escort list
1541 if ( hud_gauge_active(HUD_ESCORT_VIEW) ) {
1543 // is gauge configured as a popup?
1544 if ( hud_gauge_is_popup(HUD_ESCORT_VIEW) ) {
1545 if ( !hud_gauge_popup_active(HUD_ESCORT_VIEW) ) {
1550 if ( show_gauge_flag ) {
1551 hud_set_gauge_color(HUD_ESCORT_VIEW);
1552 hud_display_escort();
1556 // display the current weapon info for the player ship, with ammo/energy counts
1557 if ( hud_gauge_active(HUD_WEAPONS_GAUGE) ) {
1559 // is gauge configured as a popup?
1560 if ( hud_gauge_is_popup(HUD_WEAPONS_GAUGE) ) {
1561 if ( !hud_gauge_popup_active(HUD_WEAPONS_GAUGE) ) {
1566 if ( show_gauge_flag ) {
1571 // display player countermeasures count
1572 if ( hud_gauge_active(HUD_CMEASURE_GAUGE) ) {
1574 // is gauge configured as a popup?
1575 if ( hud_gauge_is_popup(HUD_CMEASURE_GAUGE) ) {
1576 if ( !hud_gauge_popup_active(HUD_CMEASURE_GAUGE) ) {
1581 if ( show_gauge_flag ) {
1582 hud_show_cmeasure_gague();
1586 if ( hud_gauge_active(HUD_WEAPONS_ENERGY) ) {
1587 hud_show_weapon_energy_gauge();
1590 // show the auto-target icons
1591 hud_show_auto_icons();
1593 // draw a border around a talking head if it is playing
1594 hud_maybe_blit_head_border();
1596 // draw the status of support ship servicing the player
1597 hud_support_view_blit();
1599 // draw the damage status
1600 hud_maybe_show_damage();
1602 // show mission time
1603 if ( hud_gauge_active(HUD_MISSION_TIME) ) {
1604 hud_show_mission_time();
1607 // show subspace notify gauge
1608 hud_maybe_display_subspace_notify();
1610 // show objective status gauge
1611 if ( hud_gauge_active(HUD_OBJECTIVES_NOTIFY_GAUGE) ) {
1612 hud_maybe_display_objective_message();
1615 if ( hud_gauge_active(HUD_WINGMEN_STATUS) ) {
1616 hud_wingman_status_render();
1619 if ( hud_gauge_active(HUD_KILLS_GAUGE) ) {
1621 // is gauge configured as a popup?
1622 if ( hud_gauge_is_popup(HUD_KILLS_GAUGE) ) {
1623 if ( !hud_gauge_popup_active(HUD_KILLS_GAUGE) ) {
1628 if ( show_gauge_flag ) {
1629 hud_show_kills_gauge();
1633 // show the player shields
1634 if ( hud_gauge_active(HUD_PLAYER_SHIELD_ICON) ) {
1635 hud_shield_show(Player_obj);
1638 // show the directives popup and/or training popup
1639 message_training_display();
1641 // if this is a multiplayer game, blit any icons/bitmaps indicating voice recording or playback
1642 if(Game_mode & GM_MULTIPLAYER){
1643 hud_show_voice_status();
1647 hud_show_messages();
1649 // maybe render any necessary multiplayer text messaging strings being entered
1650 hud_maybe_render_multi_text();
1652 // show red alert notify gauge when moving to red alert
1653 hud_maybe_display_red_alert();
1655 // display supernova warning
1656 hud_maybe_display_supernova();
1658 // check to see if we are in messaging mode. If so, send the key to the code
1659 // to deal with the message. hud_sqaudmsg_do_frame will return 0 if the key
1660 // wasn't used in messaging mode, otherwise 1. In the event the key was used,
1661 // return immediately out of this function.
1662 if ( Players->flags & PLAYER_FLAGS_MSG_MODE ) {
1663 if ( hud_squadmsg_do_frame() ){
1668 hud_render_multi_ping();
1672 // hud_stop_looped_engine_sounds()
1674 // This function will set the loop id's for the engine noises to -1, this will force any
1675 // looping engine sounds to stop. This should only be called when the game decides to
1676 // stop all looping sounds
1679 void hud_stop_looped_engine_sounds()
1681 if ( Player_engine_snd_loop > -1 ) {
1682 snd_stop(Player_engine_snd_loop);
1683 //snd_chg_loop_status(Player_engine_snd_loop, 0);
1684 Player_engine_snd_loop = -1;
1688 #define ZERO_PERCENT 0.01f
1689 #define ENGINE_MAX_VOL 1.0f
1690 #define ENGINE_MAX_PITCH 44100
1692 void update_throttle_sound()
1694 // determine what engine sound to play
1695 float percent_throttle;
1696 // float throttle_pitch;
1698 // if we're a multiplayer observer, stop any engine sounds from playing and return
1699 if((Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_OBSERVER)){
1700 // stop engine sound if it is playing
1701 if(Player_engine_snd_loop != -1){
1702 snd_stop(Player_engine_snd_loop);
1703 Player_engine_snd_loop = -1;
1710 if ( timestamp_elapsed(throttle_sound_check_id) ) {
1712 throttle_sound_check_id = timestamp(THROTTLE_SOUND_CHECK_INTERVAL);
1714 if ( Ships[Player_obj->instance].current_max_speed == 0 ) {
1715 percent_throttle = Player_obj->phys_info.fspeed / Ship_info[Ships[Player_obj->instance].ship_info_index].max_speed;
1717 percent_throttle = Player_obj->phys_info.fspeed / Ships[Player_obj->instance].current_max_speed;
1720 // If the throttle has changed, modify the sound
1721 if ( percent_throttle != last_percent_throttle || Player_engine_snd_loop == -1 ) {
1723 if ( percent_throttle < ZERO_PERCENT ) {
1724 if ( Player_engine_snd_loop > -1 ) {
1725 snd_chg_loop_status(Player_engine_snd_loop, 0);
1726 Player_engine_snd_loop = -1;
1730 if ( Player_engine_snd_loop == -1 ){
1731 Player_engine_snd_loop = snd_play_looping( &Snds[SND_ENGINE], 0.0f, percent_throttle * ENGINE_MAX_VOL );
1733 // The sound may have been trashed at the low-level if sound channel overflow.
1734 // TODO: implement system where certain sounds cannot be interrupted (priority?)
1735 if ( snd_is_playing(Player_engine_snd_loop) ) {
1736 snd_set_volume(Player_engine_snd_loop, percent_throttle * ENGINE_MAX_VOL);
1739 Player_engine_snd_loop = -1;
1744 // throttle_pitch = snd_get_pitch(Player_engine_snd_loop);
1745 // if ( percent_throttle > 0.5f ) {
1746 // snd_set_pitch(Player_engine_snd_loop, 1.0f + (percent_throttle-0.5f)*1.0f));
1749 } // end if (percent_throttle != last_percent_throttle)
1751 last_percent_throttle = percent_throttle;
1753 } // end if ( timestamp_elapsed(throttle_sound_check_id) )
1756 // called at the beginning of each level. Loads frame data in once, and initializes any damage
1757 // gauge specific data
1758 void hud_damage_popup_init()
1762 if ( !Damage_gauges_loaded ) {
1763 for ( i = 0; i < NUM_DAMAGE_GAUGES; i++ ) {
1764 Damage_gauges[i].first_frame = bm_load_animation(Damage_gauge_fnames[gr_screen.res][i], &Damage_gauges[i].num_frames);
1765 if ( Damage_gauges[i].first_frame == -1 ) {
1766 Warning(LOCATION, "Could not load in the ani: %s\n", Damage_gauge_fnames[gr_screen.res][i]);
1770 Damage_gauges_loaded = 1;
1773 Damage_flash_bright = 0;
1774 Damage_flash_timer = 1;
1776 for ( i = 0; i < SUBSYSTEM_MAX; i++ ) {
1777 Pl_hud_subsys_info[i].last_str = 1000.0f;
1778 Pl_hud_subsys_info[i].flash_duration_timestamp = 1;
1779 Pl_hud_next_flash_timestamp = 1;
1780 Pl_hud_is_bright = 0;
1784 // ---------------------------------------------------------
1785 // show player damage status via a popup window
1787 void hud_show_damage_popup()
1789 model_subsystem *psub;
1791 int sx, sy, bx, by, w, h, screen_integrity, num, best_str, best_index;
1792 float strength, shield, integrity;
1794 hud_subsys_damage hud_subsys_list[SUBSYSTEM_MAX];
1796 if ( Damage_gauges[0].first_frame == -1 ) {
1800 if ( (The_mission.game_type & MISSION_TYPE_TRAINING) && Training_msg_visible ){
1804 hud_get_target_strength(Player_obj, &shield, &integrity);
1805 screen_integrity = fl2i(integrity*100);
1807 if ( hud_gauge_is_popup(HUD_DAMAGE_GAUGE) ) {
1808 if ( screen_integrity >= 100 ) {
1813 if ( timestamp_elapsed(Damage_flash_timer) ) {
1814 Damage_flash_timer = timestamp(DAMAGE_FLASH_TIME);
1815 Damage_flash_bright ^= 1;
1818 hud_set_gauge_color(HUD_DAMAGE_GAUGE);
1820 // draw the top of the damage pop-up
1821 GR_AABITMAP(Damage_gauges[0].first_frame, Damage_gauge_coords[gr_screen.res][0][0], Damage_gauge_coords[gr_screen.res][0][1]);
1822 gr_string(Damage_text_coords[gr_screen.res][0], Damage_text_coords[gr_screen.res][1], XSTR( "damage", 218));
1824 // show hull integrity
1825 if ( screen_integrity < 100 ) {
1826 if ( screen_integrity == 0 ) {
1827 screen_integrity = 1;
1829 SDL_snprintf(buf, SDL_arraysize(buf), XSTR( "%d%%", 219), screen_integrity);
1830 hud_num_make_mono(buf);
1831 gr_get_string_size(&w, &h, buf);
1832 if ( screen_integrity < 30 ) {
1833 gr_set_color_fast(&Color_red);
1835 gr_string(Hull_integ_coords[gr_screen.res][0], Hull_integ_coords[gr_screen.res][1], XSTR( "Hull Integrity", 220));
1836 gr_string(Hull_integ_val_coords[gr_screen.res][0] - w, Hull_integ_val_coords[gr_screen.res][1], buf);
1839 // show damaged subsystems
1840 sx = Damage_subsys_text_coords[gr_screen.res][0];
1841 sy = Damage_subsys_text_coords[gr_screen.res][1];
1842 bx = Damage_gauge_coords[gr_screen.res][1][0];
1843 by = Damage_gauge_coords[gr_screen.res][1][1];
1846 for ( pss = GET_FIRST(&Player_ship->subsys_list); pss !=END_OF_LIST(&Player_ship->subsys_list); pss = GET_NEXT(pss) ) {
1847 psub = pss->system_info;
1848 strength = ship_get_subsystem_strength(Player_ship, psub->type);
1849 if ( strength < 1 ) {
1850 screen_integrity = fl2i(strength*100);
1851 if ( screen_integrity == 0 ) {
1852 if ( strength > 0 ) {
1853 screen_integrity = 1;
1856 hud_subsys_list[num].name = psub->name;
1857 hud_subsys_list[num].str = screen_integrity;
1858 hud_subsys_list[num].type = psub->type;
1861 if ( strength < Pl_hud_subsys_info[psub->type].last_str ) {
1862 Pl_hud_subsys_info[psub->type].flash_duration_timestamp = timestamp(SUBSYS_DAMAGE_FLASH_DURATION);
1864 Pl_hud_subsys_info[psub->type].last_str = strength;
1869 for ( int i = 0; i < num; i++ ) {
1872 for ( int j = 0; j < num-i; j++ ) {
1873 if ( hud_subsys_list[j].str < best_str ) {
1874 best_str = hud_subsys_list[j].str;
1879 SDL_assert(best_index >= 0);
1880 SDL_assert(best_str >= 0);
1882 // display strongest subsystem left in list
1884 // hud_set_default_color();
1885 hud_set_gauge_color(HUD_DAMAGE_GAUGE);
1887 GR_AABITMAP(Damage_gauges[1].first_frame, bx, by);
1888 by += Damage_gauge_line_h[gr_screen.res];
1890 type = hud_subsys_list[best_index].type;
1891 if ( !timestamp_elapsed( Pl_hud_subsys_info[type].flash_duration_timestamp ) ) {
1892 if ( timestamp_elapsed( Pl_hud_next_flash_timestamp ) ) {
1893 Pl_hud_is_bright ^= 1;
1894 Pl_hud_next_flash_timestamp = timestamp(SUBSYS_DAMAGE_FLASH_INTERVAL);
1897 if ( Pl_hud_is_bright ) {
1899 alpha_color = min(HUD_COLOR_ALPHA_MAX,HUD_color_alpha+HUD_BRIGHT_DELTA);
1900 // gr_set_color_fast(&HUD_color_defaults[alpha_color]);
1902 hud_set_gauge_color(HUD_DAMAGE_GAUGE, alpha_color);
1904 hud_set_gauge_color(HUD_DAMAGE_GAUGE);
1909 if ( best_str < 30 ) {
1910 if ( best_str <= 0 ) {
1911 if ( Damage_flash_bright ) {
1912 gr_set_color_fast(&Color_bright_red);
1914 gr_set_color_fast(&Color_red);
1918 gr_set_color_fast(&Color_red);
1921 hud_set_gauge_color(HUD_DAMAGE_GAUGE);
1924 gr_string(sx, sy, hud_targetbox_truncate_subsys_name(hud_subsys_list[best_index].name, MAX_NAME_LEN));
1925 SDL_snprintf(buf, SDL_arraysize(buf), XSTR( "%d%%", 219), best_str);
1926 hud_num_make_mono(buf);
1927 gr_get_string_size(&w, &h, buf);
1928 gr_string(Hull_integ_val_coords[gr_screen.res][0] - w, sy, buf);
1929 sy += Damage_gauge_line_h[gr_screen.res];
1931 // remove it from hud_subsys_list
1932 if ( best_index < (num-i-1) ) {
1933 hud_subsys_list[best_index] = hud_subsys_list[num-i-1];
1937 // draw the bottom of the gauge
1938 // hud_set_default_color();
1939 hud_set_gauge_color(HUD_DAMAGE_GAUGE);
1941 GR_AABITMAP(Damage_gauges[2].first_frame, bx, by);
1944 // init the members of the hud_anim struct to default values
1945 void hud_anim_init(hud_anim *ha, int sx, int sy, const char *filename)
1947 ha->first_frame = -1;
1949 ha->total_time = 0.0f;
1950 ha->time_elapsed = 0.0f;
1953 SDL_strlcpy(ha->name, filename, SDL_arraysize(ha->name));
1956 // call to unload the targetbox static animation
1957 void hud_anim_release(hud_anim *ha)
1961 if (ha->first_frame < 0)
1964 for ( i = 0; i < ha->num_frames; i++ ) {
1965 bm_unload(ha->first_frame + i);
1970 // return 0 is successful, otherwise return -1
1971 int hud_anim_load(hud_anim *ha)
1975 ha->first_frame = bm_load_animation(ha->name, &ha->num_frames, &fps);
1976 if ( ha->first_frame == -1 ) {
1977 Int3(); // couldn't load animation file in
1980 SDL_assert(fps != 0);
1981 ha->total_time = i2fl(ha->num_frames)/fps;
1985 // render out a frame of the targetbox static animation, based on how much time has
1987 // input: ha => pointer to hud anim info
1988 // frametime => seconds elapsed since last frame
1989 // draw_alpha => draw bitmap as alpha-bitmap (default 0)
1990 // loop => anim should loop (default 1)
1991 // hold_last => should last frame be held (default 0)
1992 // reverse => play animation in reverse (default 0)
1993 int hud_anim_render(hud_anim *ha, float frametime, int draw_alpha, int loop, int hold_last, int reverse)
1997 if ( ha->num_frames <= 0 ) {
1998 if ( hud_anim_load(ha) == -1 )
2002 ha->time_elapsed += frametime;
2003 if ( ha->time_elapsed > ha->total_time ) {
2005 ha->time_elapsed = 0.0f;
2013 // draw the correct frame of animation
2014 framenum = fl2i( (ha->time_elapsed * ha->num_frames) / ha->total_time );
2016 framenum = (ha->num_frames-1) - framenum;
2021 if ( framenum >= ha->num_frames )
2022 framenum = ha->num_frames-1;
2024 // Blit the bitmap for this frame
2025 if(emp_should_blit_gauge()){
2026 gr_set_bitmap(ha->first_frame + framenum, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
2028 gr_aabitmap(ha->sx, ha->sy);
2030 gr_bitmap(ha->sx, ha->sy);
2037 // convert a number string to use mono-spaced 1 character
2038 void hud_num_make_mono(char *num_str)
2041 len = strlen(num_str);
2043 sc = Lcl_special_chars;
2044 for ( i = 0; i < len; i++ ) {
2045 if ( num_str[i] == '1' ) {
2046 num_str[i] = (char)(sc + 1);
2051 // flashing text gauge
2052 void hud_init_text_flash_gauge()
2056 void hud_start_text_flash(const char *txt, int t)
2060 SDL_strlcpy(Hud_text_flash, "", SDL_arraysize(Hud_text_flash));
2064 // HACK. don't override EMP if its still going :)
2065 if(!strcmp(Hud_text_flash, NOX("Emp")) && !hud_targetbox_flash_expired(TBOX_FLASH_CMEASURE)){
2069 SDL_strlcpy(Hud_text_flash, txt, SDL_arraysize(Hud_text_flash));
2070 hud_targetbox_start_flash(TBOX_FLASH_CMEASURE, t);
2073 void hud_maybe_show_text_flash_icon()
2077 if ( hud_targetbox_flash_expired(TBOX_FLASH_CMEASURE) ) {
2081 hud_targetbox_maybe_flash(TBOX_FLASH_CMEASURE);
2084 bright = hud_targetbox_is_bright(TBOX_FLASH_CMEASURE);
2087 hud_show_text_flash_icon(Hud_text_flash, Hud_text_flash_coords[gr_screen.res][1], bright);
2090 void hud_show_text_flash_icon(const char *txt, int y, int bright)
2095 // different font size in hi-res
2096 if(gr_screen.res != GR_640){
2103 hud_set_gauge_color(HUD_TEXT_FLASH, HUD_C_DIM);
2105 gr_set_color_fast(&Color_black);
2109 gr_get_string_size(&w, &h, txt);
2112 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);
2115 hud_set_gauge_color(HUD_TEXT_FLASH, HUD_C_BRIGHT);
2116 gr_string(0x8000, y, txt);
2118 // go back to normal font
2122 // maybe display the kills gauge on the HUD
2123 void hud_show_kills_gauge()
2125 if ( Kills_gauge.first_frame < 0 ) {
2129 // hud_set_default_color();
2130 hud_set_gauge_color(HUD_KILLS_GAUGE);
2133 GR_AABITMAP(Kills_gauge.first_frame, Kills_gauge_coords[gr_screen.res][0], Kills_gauge_coords[gr_screen.res][1]);
2135 gr_string(Kills_text_coords[gr_screen.res][0], Kills_text_coords[gr_screen.res][1], XSTR( "kills:", 223));
2137 // display how many kills the player has so far
2138 char num_kills_string[32];
2146 SDL_snprintf(num_kills_string, SDL_arraysize(num_kills_string), "%d", Player->stats.m_kill_count_ok);
2148 gr_get_string_size(&w, &h, num_kills_string);
2150 gr_string(Kills_text_val_coords_gr[gr_screen.res][0]-w, Kills_text_val_coords_gr[gr_screen.res][1], num_kills_string);
2152 gr_string(Kills_text_val_coords[gr_screen.res][0]-w, Kills_text_val_coords[gr_screen.res][1], num_kills_string);
2156 // maybe show the netlag icon on the hud
2157 void hud_maybe_show_netlag_icon()
2161 if ( Netlag_icon.first_frame == -1 ) {
2166 lag_status = multi_query_lag_status();
2168 switch(lag_status) {
2170 // draw the net lag icon flashing
2171 hud_targetbox_start_flash(TBOX_FLASH_NETLAG);
2172 if(hud_targetbox_maybe_flash(TBOX_FLASH_NETLAG)){
2173 hud_set_gauge_color(HUD_LAG_GAUGE, HUD_C_BRIGHT);
2175 hud_set_gauge_color(HUD_LAG_GAUGE);
2177 gr_set_bitmap(Netlag_icon.first_frame, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
2180 // draw the disconnected icon flashing fast
2181 if(hud_targetbox_maybe_flash(TBOX_FLASH_NETLAG,1)){
2182 hud_set_gauge_color(HUD_LAG_GAUGE, HUD_C_BRIGHT);
2184 hud_set_gauge_color(HUD_LAG_GAUGE);
2186 gr_set_bitmap(Netlag_icon.first_frame+1, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
2193 if(emp_should_blit_gauge()){
2194 gr_aabitmap(Netlag_coords[gr_screen.res][0], Netlag_coords[gr_screen.res][1]);
2198 // load in kills gauge if required
2199 void hud_init_kills_gauge()
2201 if ( !Kills_gauge_loaded ) {
2202 Kills_gauge.first_frame = bm_load_animation(Kills_fname[gr_screen.res], &Kills_gauge.num_frames);
2203 if ( Kills_gauge.first_frame == -1 ) {
2204 Warning(LOCATION, "Could not load in the kills ani: Kills_fname[gr_screen.res]\n");
2207 Kills_gauge_loaded = 1;
2211 // load in netlag icon if required
2212 void hud_init_netlag_icon()
2214 if ( !Netlag_icon_loaded ) {
2215 Netlag_icon.first_frame = bm_load_animation(Netlag_fname[gr_screen.res], &Netlag_icon.num_frames);
2216 if ( Netlag_icon.first_frame == -1 ) {
2217 Warning(LOCATION, "Could not load in the netlag ani: Netlag_fname[gr_screen.res]\n");
2220 Netlag_icon_loaded = 1;
2224 // called at mission start to init data, and load support view bitmap if required
2225 void hud_support_view_init()
2227 Hud_support_view_fade = 1;
2228 Hud_support_obj_sig = -1;
2229 Hud_support_target_sig = -1;
2230 Hud_support_objnum = -1;
2231 Hud_support_view_active = 0;
2232 Hud_support_view_abort = 0;
2234 // ensure the talking head border is loaded
2235 if ( !Support_view_gauge_loaded ) {
2236 Support_view_gauge.first_frame = bm_load_animation(Support_fname[gr_screen.res], &Support_view_gauge.num_frames);
2237 if ( Support_view_gauge.first_frame == -1 ) {
2238 Warning(LOCATION, "Could not load in ani: Support_fname[gr_screen.res]\n");
2240 Support_view_gauge_loaded = 1;
2244 // start displaying the support view pop-up. This will remain up until hud_support_view_stop is called.
2245 // input: objnum => object number for the support ship
2246 void hud_support_view_start()
2248 Hud_support_view_active = 1;
2249 Hud_support_view_fade = 1;
2252 // stop displaying the support view pop-up
2253 void hud_support_view_stop(int stop_now)
2256 Hud_support_view_active = 0;
2257 Hud_support_view_fade = 1;
2258 Hud_support_view_abort = 0;
2260 Hud_support_view_fade = timestamp(2000);
2263 Hud_support_obj_sig = -1;
2264 Hud_support_target_sig = -1;
2265 Hud_support_objnum = -1;
2268 void hud_support_view_abort()
2270 hud_support_view_stop(0);
2271 Hud_support_view_abort = 1;
2274 // return the number of seconds until repair ship will dock with player, return -1 if error
2276 // mwa made this function more general purpose
2278 // NOTE: This function is pretty stupid now. It just assumes the player is sitting still, and
2279 // the support ship is moving directly to the player.
2280 int hud_support_get_dock_time( int objnum )
2283 object *support_objp, *other_objp;
2284 float dist, rel_speed, support_speed;
2287 support_objp = &Objects[objnum];
2288 aip = &Ai_info[Ships[support_objp->instance].ai_index];
2290 // if the ship is docked, return 0
2291 if ( aip->ai_flags & AIF_DOCKED )
2294 // get the dockee object pointer
2295 if (aip->goal_objnum == -1) {
2296 Int3(); // Shouldn't happen, but let's recover gracefully.
2300 other_objp = &Objects[aip->goal_objnum];
2302 vm_vec_sub(&rel_vel, &support_objp->phys_info.vel, &other_objp->phys_info.vel);
2303 rel_speed = vm_vec_mag_quick(&rel_vel);
2305 dist = vm_vec_dist_quick(&other_objp->pos, &support_objp->pos);
2307 support_speed = support_objp->phys_info.speed;
2309 if ( rel_speed <= support_speed/2.0f) { // This means the player is moving away fast from the support ship.
2310 return (int) (dist/support_speed);
2316 if (rel_speed < 20.0f)
2319 // When faraway, use max speed, not current speed. Might not have sped up yet.
2321 time += (d - 100.0f)/support_objp->phys_info.max_vel.xyz.z;
2324 // For mid-range, use current speed.
2326 d1 = min(d, 100.0f);
2328 time += (d1 - 60.0f)/rel_speed;
2331 // For nearby, ship will have to slow down a bit for docking maneuver.
2335 time += (d1 - 30.0f)/5.0f;
2338 // For very nearby, ship moves quite slowly.
2346 // Locate the closest support ship which is trying to dock with player, return -1 if there is no support
2347 // ship currently trying to dock with the player
2348 // MA: 4/22/98 -- pass in objp to find support ship trying to dock with objp
2349 int hud_support_find_closest( int objnum )
2356 objp = &Objects[objnum];
2358 sop = GET_FIRST(&Ship_obj_list);
2359 while(sop != END_OF_LIST(&Ship_obj_list)){
2360 if ( Ship_info[Ships[Objects[sop->objnum].instance].ship_info_index].flags & SIF_SUPPORT ) {
2361 int pship_index, sindex;
2363 // make sure support ship is not dying
2364 if ( !(Ships[Objects[sop->objnum].instance].flags & (SF_DYING|SF_EXPLODED)) ) {
2366 SDL_assert( objp->type == OBJ_SHIP );
2367 aip = &Ai_info[Ships[Objects[sop->objnum].instance].ai_index];
2368 pship_index = objp->instance;
2370 // we must check all goals for this support ship -- not just the first one
2371 for ( i = 0; i < MAX_AI_GOALS; i++ ) {
2373 // we can use == in the next statement (and should) since a ship will only ever be
2374 // following one order at a time.
2375 if ( aip->goals[i].ai_mode == AI_GOAL_REARM_REPAIR ) {
2376 SDL_assert( aip->goals[i].ship_name );
2377 sindex = ship_name_lookup( aip->goals[i].ship_name );
2378 if ( sindex == pship_index )
2384 sop = GET_NEXT(sop);
2390 // dipaly the hud_support view popup
2391 void hud_support_view_blit()
2396 if ( !Hud_support_view_active ) {
2400 // don't render this gauge for multiplayer observers
2401 if((Game_mode & GM_MULTIPLAYER) && ((Net_player->flags & NETINFO_FLAG_OBSERVER) || (Player_obj->type == OBJ_OBSERVER))){
2405 // If we haven't determined yet who the rearm ship is, try to!
2406 if (Hud_support_objnum == -1) {
2407 Hud_support_objnum = hud_support_find_closest( OBJ_INDEX(Player_obj) );
2408 if ( Hud_support_objnum >= 0 ) {
2409 Hud_support_obj_sig = Objects[Hud_support_objnum].signature;
2410 Hud_support_target_sig = Player_obj->signature;
2413 // check to see if support ship is still alive
2414 if ( (Objects[Hud_support_objnum].signature != Hud_support_obj_sig) || (Hud_support_target_sig != Player_obj->signature) ) {
2415 hud_support_view_stop(1);
2421 hud_set_gauge_color(HUD_SUPPORT_GAUGE);
2423 GR_AABITMAP(Support_view_gauge.first_frame, Support_view_coords[gr_screen.res][0], Support_view_coords[gr_screen.res][1]);
2425 gr_string(Support_text_coords[gr_screen.res][0], Support_text_coords[gr_screen.res][1], XSTR( "support", 224));
2427 if ( Hud_support_view_fade > 1 ) {
2428 if ( !timestamp_elapsed(Hud_support_view_fade) ) {
2429 if ( Hud_support_view_abort){
2430 gr_string(0x8000, Support_text_val_coords[gr_screen.res][1], XSTR( "aborted", 225));
2432 gr_string(0x8000, Support_text_val_coords[gr_screen.res][1], XSTR( "complete", 1407));
2436 Hud_support_view_abort = 0;
2437 Hud_support_view_active = 0;
2438 Hud_support_view_fade = 1;
2439 Hud_support_objnum = -1;
2445 if ( Player_ai->ai_flags & AIF_BEING_REPAIRED ) {
2446 SDL_assert(Ship_info[Player_ship->ship_info_index].initial_hull_strength > 0);
2447 if ( (ship_get_subsystem_strength(Player_ship, SUBSYSTEM_ENGINE) < 1.0 ) ||
2448 (ship_get_subsystem_strength(Player_ship, SUBSYSTEM_SENSORS) < 1.0 ) ||
2449 (ship_get_subsystem_strength(Player_ship, SUBSYSTEM_WEAPONS) < 1.0 ) ||
2450 (ship_get_subsystem_strength(Player_ship, SUBSYSTEM_COMMUNICATION) < 1.0 ) ) {
2451 SDL_strlcpy(outstr, XSTR( "repairing", 227), SDL_arraysize(outstr));
2453 SDL_strlcpy(outstr, XSTR( "rearming", 228), SDL_arraysize(outstr));
2455 gr_string(0x8000, Support_text_val_coords[gr_screen.res][1], outstr);
2456 } else if (Player_ai->ai_flags & AIF_REPAIR_OBSTRUCTED) {
2457 SDL_strlcpy(outstr, XSTR( "obstructed", 229), SDL_arraysize(outstr));
2458 gr_string(0x8000, Support_text_val_coords[gr_screen.res][1], outstr);
2460 if ( Hud_support_objnum == -1 ) {
2461 SDL_strlcpy(outstr, XSTR( "warping in", 230), SDL_arraysize(outstr));
2462 gr_string(0x8000, Support_text_val_coords[gr_screen.res][1], outstr);
2466 // display "busy" when support ship isn't actually enroute to me
2467 aip = &Ai_info[Ships[Objects[Hud_support_objnum].instance].ai_index];
2468 if ( aip->goal_objnum != OBJ_INDEX(Player_obj) ) {
2469 SDL_strlcpy(outstr, XSTR( "busy", 231), SDL_arraysize(outstr));
2473 SDL_strlcpy(outstr, XSTR( "dock in:", 232), SDL_arraysize(outstr));
2478 gr_string(Support_text_dock_coords[gr_screen.res][0], Support_text_val_coords[gr_screen.res][1], outstr);
2480 gr_string(Support_text_dock_coords[gr_screen.res][0], Support_text_val_coords[gr_screen.res][1], outstr);
2486 int seconds, minutes;
2488 SDL_assert( Hud_support_objnum != -1 );
2490 // ensure support ship is still alive
2491 if ( (Objects[Hud_support_objnum].signature != Hud_support_obj_sig) || (Hud_support_target_sig != Player_obj->signature) ) {
2492 hud_support_view_stop(1);
2495 seconds = hud_support_get_dock_time( Hud_support_objnum );
2498 if ( seconds >= 0 ) {
2499 minutes = seconds/60;
2500 seconds = seconds%60;
2501 if ( minutes > 99 ) {
2509 gr_printf(Support_text_dock_val_coords[gr_screen.res][0], Support_text_val_coords[gr_screen.res][1], NOX("%02d:%02d"), minutes, seconds);
2513 // Set the current color to the default HUD color (with default alpha)
2514 void hud_set_default_color()
2516 SDL_assert(HUD_color_alpha >= 0 && HUD_color_alpha < HUD_NUM_COLOR_LEVELS);
2517 gr_set_color_fast(&HUD_color_defaults[HUD_color_alpha]);
2520 // Set the current color to a bright HUD color (ie high alpha)
2521 void hud_set_bright_color()
2524 alpha_color = min(HUD_COLOR_ALPHA_MAX,HUD_color_alpha+HUD_BRIGHT_DELTA);
2525 gr_set_color_fast(&HUD_color_defaults[alpha_color]);
2528 // Set the current color to a dim HUD color (ie low alpha)
2529 void hud_set_dim_color()
2531 if ( HUD_color_alpha > 2 ) {
2532 gr_set_color_fast(&HUD_color_defaults[2]);
2536 // hud_set_iff_color() will set the color to the IFF color based on the team
2538 // input: team => team to base color on
2539 // is_bright => default parameter (value 0) which uses bright version of IFF color
2540 void hud_set_iff_color(object *objp, int is_bright)
2542 // AL 12-26-97: it seems IFF color needs to be set relative to the player team. If
2543 // the team in question is the same as the player, then it should be
2544 // drawn friendly. If the team is different than the players, then draw the
2547 team = obj_team(objp);
2549 if ( ship_is_tagged(objp) ) {
2550 gr_set_color_fast(&IFF_colors[IFF_COLOR_TAGGED][is_bright]);
2551 } else if ( (team == Player_ship->team) && (Player_ship->team != TEAM_TRAITOR) ) {
2552 gr_set_color_fast(&IFF_colors[IFF_COLOR_FRIENDLY][is_bright]);
2556 gr_set_color_fast(&IFF_colors[IFF_COLOR_NEUTRAL][is_bright]);
2559 gr_set_color_fast(&IFF_colors[IFF_COLOR_UNKNOWN][is_bright]);
2564 gr_set_color_fast(&IFF_colors[IFF_COLOR_HOSTILE][is_bright]);
2568 gr_set_color_fast(&IFF_colors[IFF_COLOR_UNKNOWN][is_bright]);
2574 // Determine if ship team should be ignored, based on
2576 // input: team_filter => team mask used to select friendly or hostile ships
2577 // ship_team => team of the ship in question
2578 // exit: 1 => ship_team matches filter from player perspective
2579 // 0 => ship_team does match team filter
2580 int hud_team_matches_filter(int team_filter, int ship_team)
2582 return team_filter & ship_team;
2586 // reset gauge flashing data
2587 void hud_gauge_flash_init()
2590 for ( i=0; i<NUM_HUD_GAUGES; i++ ) {
2591 HUD_gauge_flash_duration[i]=timestamp(0);
2592 HUD_gauge_flash_next[i]=timestamp(0);
2597 #define NUM_VM_OTHER_SHIP_GAUGES 5
2598 static int Vm_other_ship_gauges[NUM_VM_OTHER_SHIP_GAUGES] =
2602 HUD_TARGET_MONITOR_EXTRA_DATA,
2607 // determine if the specified HUD gauge should be displayed
2608 int hud_gauge_active(int gauge_index)
2610 SDL_assert(gauge_index >=0 && gauge_index < NUM_HUD_GAUGES);
2612 // AL: Special code: Only show two gauges when not viewing from own ship
2613 if ( Viewer_mode & VM_OTHER_SHIP ) {
2614 for ( int i = 0; i < NUM_VM_OTHER_SHIP_GAUGES; i++ ) {
2615 if ( gauge_index == Vm_other_ship_gauges[i] ) {
2622 return hud_config_show_flag_is_set(gauge_index);
2625 // determine if gauge is in pop-up mode or not
2626 int hud_gauge_is_popup(int gauge_index)
2628 SDL_assert(gauge_index >=0 && gauge_index < NUM_HUD_GAUGES);
2629 return hud_config_popup_flag_is_set(gauge_index);
2632 // determine if a popup gauge should be drawn
2633 int hud_gauge_popup_active(int gauge_index)
2635 SDL_assert(gauge_index >=0 && gauge_index < NUM_HUD_GAUGES);
2636 if ( !hud_gauge_is_popup(gauge_index) ) {
2640 if ( !timestamp_elapsed(HUD_popup_timers[gauge_index]) ) {
2647 // start a gauge to popup
2648 void hud_gauge_popup_start(int gauge_index, int time)
2650 SDL_assert(gauge_index >=0 && gauge_index < NUM_HUD_GAUGES);
2651 if ( !hud_gauge_is_popup(gauge_index) ) {
2655 HUD_popup_timers[gauge_index] = timestamp(time);
2659 // call HUD function to flash gauge
2660 void hud_gauge_start_flash(int gauge_index)
2662 SDL_assert(gauge_index >=0 && gauge_index < NUM_HUD_GAUGES);
2663 HUD_gauge_flash_duration[gauge_index] = timestamp(HUD_GAUGE_FLASH_DURATION);
2664 HUD_gauge_flash_next[gauge_index] = 1;
2667 // Set the HUD color for the gauge, based on whether it is flashing or not
2668 void hud_set_gauge_color(int gauge_index, int bright_index)
2671 int flash_status = hud_gauge_maybe_flash(gauge_index);
2672 use_color = HUD_config.clr[gauge_index];
2675 // if we're drawing it as bright
2676 if(bright_index != HUD_C_NONE){
2677 switch(bright_index){
2679 alpha = HUD_contrast ? HUD_NEW_ALPHA_DIM_HI : HUD_NEW_ALPHA_DIM;
2680 gr_init_alphacolor(&use_color, use_color.red, use_color.green, use_color.blue, alpha, AC_TYPE_HUD);
2684 alpha = HUD_contrast ? HUD_NEW_ALPHA_NORMAL_HI : HUD_NEW_ALPHA_NORMAL;
2685 gr_init_alphacolor(&use_color, use_color.red, use_color.green, use_color.blue, alpha, AC_TYPE_HUD);
2689 alpha = HUD_contrast ? HUD_NEW_ALPHA_BRIGHT_HI : HUD_NEW_ALPHA_BRIGHT;
2690 gr_init_alphacolor(&use_color, use_color.red, use_color.green, use_color.blue, alpha, AC_TYPE_HUD);
2695 SDL_assert((bright_index >= 0) && (bright_index < HUD_NUM_COLOR_LEVELS));
2696 if(bright_index < 0){
2699 if(bright_index >= HUD_NUM_COLOR_LEVELS){
2700 bright_index = HUD_NUM_COLOR_LEVELS - 1;
2703 // alpha = 255 - (255 / (bright_index + 1));
2704 // alpha = (int)((float)alpha * 1.5f);
2705 int level = 255 / (HUD_NUM_COLOR_LEVELS);
2706 alpha = level * bright_index;
2713 gr_init_alphacolor(&use_color, use_color.red, use_color.green, use_color.blue, alpha, AC_TYPE_HUD);
2717 switch(flash_status) {
2719 alpha = HUD_contrast ? HUD_NEW_ALPHA_DIM_HI : HUD_NEW_ALPHA_DIM;
2720 gr_init_alphacolor(&use_color, use_color.red, use_color.green, use_color.blue, alpha, AC_TYPE_HUD);
2723 alpha = HUD_contrast ? HUD_NEW_ALPHA_BRIGHT_HI : HUD_NEW_ALPHA_BRIGHT;
2724 gr_init_alphacolor(&use_color, use_color.red, use_color.green, use_color.blue, alpha, AC_TYPE_HUD);
2727 alpha = HUD_contrast ? HUD_NEW_ALPHA_NORMAL_HI : HUD_NEW_ALPHA_NORMAL;
2728 gr_init_alphacolor(&use_color, use_color.red, use_color.green, use_color.blue, alpha, AC_TYPE_HUD);
2733 gr_set_color_fast(&use_color);
2736 // set the color for a gauge that may be flashing
2737 // exit: -1 => gauge is not flashing
2738 // 0 => gauge is flashing, draw dim
2739 // 1 => gauge is flashing, draw bright
2740 int hud_gauge_maybe_flash(int gauge_index)
2742 SDL_assert(gauge_index >=0 && gauge_index < NUM_HUD_GAUGES);
2743 int flash_status=-1;
2744 if ( !timestamp_elapsed(HUD_gauge_flash_duration[gauge_index]) ) {
2745 if ( timestamp_elapsed(HUD_gauge_flash_next[gauge_index]) ) {
2746 HUD_gauge_flash_next[gauge_index] = timestamp(HUD_GAUGE_FLASH_INTERVAL);
2747 HUD_gauge_bright ^= (1<<gauge_index); // toggle between default and bright frames
2750 if ( HUD_gauge_bright & (1<<gauge_index) ) {
2756 return flash_status;
2759 // Init the objective message display data
2760 void hud_objective_message_init()
2762 // ensure the talking head border is loaded
2763 if ( !Objective_display_gauge_inited ) {
2764 Objective_display_gauge.first_frame = bm_load_animation(Objective_fname[gr_screen.res], &Objective_display_gauge.num_frames);
2765 if ( Objective_display_gauge.first_frame == -1 ) {
2766 Warning(LOCATION, "Could not load in ani: Objective_fname[gr_screen.res]\n");
2768 Objective_display_gauge_inited = 1;
2771 Objective_display.display_timer=timestamp(0);
2774 // Display objective status on the HUD
2775 // input: type => type of goal, one of: PRIMARY_GOAL
2779 // status => status of goal, one of: GOAL_FAILED
2783 void hud_add_objective_messsage(int type, int status)
2785 Objective_display.display_timer=timestamp(7000);
2786 Objective_display.goal_type=type;
2787 Objective_display.goal_status=status;
2789 // if this is a multiplayer tvt game
2790 if((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_TEAM) && (Net_player != NULL)){
2791 mission_goal_fetch_num_resolved(type, &Objective_display.goal_nresolved, &Objective_display.goal_ntotal, Net_player->p_info.team);
2793 mission_goal_fetch_num_resolved(type, &Objective_display.goal_nresolved, &Objective_display.goal_ntotal);
2796 // TODO: play a sound?
2799 // maybe display the 'subspace drive engaged' message
2800 void hud_maybe_display_subspace_notify()
2802 int warp_aborted = 0;
2803 // maybe make gauge active
2804 if ( (Player->control_mode == PCM_WARPOUT_STAGE1) || (Player->control_mode == PCM_WARPOUT_STAGE2) || (Player->control_mode == PCM_WARPOUT_STAGE3) ) {
2805 if (!hud_subspace_notify_active()) {
2806 // keep sound from being played 1e06 times
2807 hud_start_subspace_notify();
2810 if ( !timestamp_elapsed(HUD_abort_subspace_timer) ) {
2813 hud_stop_subspace_notify();
2817 if ( !hud_subspace_notify_active() ) {
2821 if ( Objective_display_gauge.first_frame < 0 ) {
2825 // blit the background
2826 hud_set_gauge_color(HUD_OBJECTIVES_NOTIFY_GAUGE);
2827 GR_AABITMAP(Objective_display_gauge.first_frame, Objective_display_coords[gr_screen.res][0], Objective_display_coords[gr_screen.res][1]);
2829 hud_targetbox_start_flash(TBOX_FLASH_OBJECTIVE);
2830 if(hud_targetbox_maybe_flash(TBOX_FLASH_OBJECTIVE)){
2831 hud_set_gauge_color(HUD_OBJECTIVES_NOTIFY_GAUGE, HUD_C_BRIGHT);
2833 hud_set_gauge_color(HUD_OBJECTIVES_NOTIFY_GAUGE);
2837 gr_string(0x8000, Subspace_text_coords[gr_screen.res][1],XSTR( "subspace drive", 233));
2838 if ( warp_aborted ) {
2839 gr_string(0x8000, Subspace_text_val_coords[gr_screen.res][1],XSTR( "aborted", 225));
2841 gr_string(0x8000, Subspace_text_val_coords[gr_screen.res][1],XSTR( "engaged", 234));
2845 // maybe display the 'Downloading new orders' message
2846 void hud_maybe_display_red_alert()
2848 if ( !red_alert_check_status() ) {
2852 if ( Objective_display_gauge.first_frame < 0 ) {
2856 if ( hud_subspace_notify_active() ) {
2860 if ( hud_objective_notify_active() ) {
2864 // blit the background
2865 gr_set_color_fast(&Color_red); // color box red, cuz its an emergency for cryin out loud
2867 GR_AABITMAP(Objective_display_gauge.first_frame, Objective_display_coords[gr_screen.res][0], Objective_display_coords[gr_screen.res][1]);
2869 hud_targetbox_start_flash(TBOX_FLASH_OBJECTIVE);
2870 if(hud_targetbox_maybe_flash(TBOX_FLASH_OBJECTIVE)) {
2871 gr_set_color_fast(&Color_red);
2873 gr_set_color_fast(&Color_bright_red);
2876 gr_string(0x8000, Red_text_coords[gr_screen.res][1], XSTR( "downloading new", 235));
2877 gr_string(0x8000, Red_text_val_coords[gr_screen.res][1], XSTR( "orders...", 236));
2879 // TODO: play a sound?
2882 // Maybe show an objective status update on the HUD
2883 void hud_maybe_display_objective_message()
2887 if ( timestamp_elapsed(Objective_display.display_timer) ) {
2888 hud_stop_objective_notify();
2892 if ( Objective_display_gauge.first_frame < 0 ) {
2896 if ( hud_subspace_notify_active() ) {
2900 if (!hud_objective_notify_active()) {
2901 hud_start_objective_notify();
2904 // blit the background
2905 hud_set_gauge_color(HUD_OBJECTIVES_NOTIFY_GAUGE);
2906 GR_AABITMAP(Objective_display_gauge.first_frame, Objective_display_coords[gr_screen.res][0], Objective_display_coords[gr_screen.res][1]);
2908 hud_targetbox_start_flash(TBOX_FLASH_OBJECTIVE);
2909 if(hud_targetbox_maybe_flash(TBOX_FLASH_OBJECTIVE)){
2910 hud_set_gauge_color(HUD_OBJECTIVES_NOTIFY_GAUGE, HUD_C_BRIGHT);
2912 hud_set_gauge_color(HUD_OBJECTIVES_NOTIFY_GAUGE);
2915 // draw the correct goal type
2916 switch(Objective_display.goal_type) {
2918 gr_string(0x8000, Objective_text_coords[gr_screen.res][1],XSTR( "primary objective", 237));
2920 case SECONDARY_GOAL:
2921 gr_string(0x8000, Objective_text_coords[gr_screen.res][1],XSTR( "secondary objective", 238));
2924 gr_string(0x8000, Objective_text_coords[gr_screen.res][1],XSTR( "bonus objective", 239));
2929 switch(Objective_display.goal_type) {
2931 case SECONDARY_GOAL:
2932 switch(Objective_display.goal_status) {
2934 SDL_snprintf(buf, SDL_arraysize(buf), XSTR( "failed (%d/%d)", 240), Objective_display.goal_nresolved, Objective_display.goal_ntotal);
2935 gr_string(0x8000, Objective_text_val_coords[gr_screen.res][1], buf);
2938 SDL_snprintf(buf, SDL_arraysize(buf), XSTR( "complete (%d/%d)", 241), Objective_display.goal_nresolved, Objective_display.goal_ntotal);
2939 gr_string(0x8000, Objective_text_val_coords[gr_screen.res][1], buf);
2944 switch(Objective_display.goal_status) {
2946 gr_string(0x8000, Objective_text_val_coords[gr_screen.res][1], XSTR( "failed", 242));
2949 gr_string(0x8000, Objective_text_val_coords[gr_screen.res][1], XSTR( "complete", 226));
2956 // return wing slot (0->3) based on name of ship. Assumes ship is from Alpha,Beta, or
2958 int hud_wing_slot_from_name(const char *name)
2963 num[0]=name[strlen(name)-1];
2966 rval = num[0] - '1';
2967 SDL_assert(rval >= 0 && rval < 4);
2971 // return index in starting wings (0->11) for specified ship.
2972 int hud_wing_index_from_ship(int shipnum)
2977 shipp = &Ships[shipnum];
2981 for (i=0; i<3; i++) {
2982 if ( Starting_wings[i] < 0 ) {
2986 if (shipp->wingnum == Starting_wings[i]) {
2995 wing_slot = hud_wing_slot_from_name(shipp->ship_name);
2996 return (i*4+wing_slot);
2999 void hud_show_voice_status()
3001 char play_callsign[CALLSIGN_LEN+5];
3003 // if we are currently playing a rtvoice sound stream from another player back
3004 memset(play_callsign,0,CALLSIGN_LEN+5);
3005 switch(multi_voice_status()){
3006 // the player has been denied the voice token
3007 case MULTI_VOICE_STATUS_DENIED:
3008 // show a red indicator or something
3009 gr_string(Voice_coords[gr_screen.res][0], Voice_coords[gr_screen.res][1], XSTR( "[voice denied]", 243));
3012 // the player is currently recording
3013 case MULTI_VOICE_STATUS_RECORDING:
3014 gr_string(Voice_coords[gr_screen.res][0], Voice_coords[gr_screen.res][1], XSTR( "[recording voice]", 244));
3017 // the player is current playing back voice from someone
3018 case MULTI_VOICE_STATUS_PLAYING:
3019 gr_string(Voice_coords[gr_screen.res][0], Voice_coords[gr_screen.res][1], XSTR( "[playing voice]", 245));
3022 // nothing voice related is happening on my machine
3023 case MULTI_VOICE_STATUS_IDLE:
3024 // probably shouldn't be displaying anything
3029 void hud_subspace_notify_abort()
3031 HUD_abort_subspace_timer = timestamp(1500);
3034 void hud_stop_subspace_notify()
3036 Subspace_notify_active=0;
3039 void hud_start_subspace_notify()
3042 Subspace_notify_active=1;
3045 int hud_subspace_notify_active()
3047 return Subspace_notify_active;
3050 void hud_stop_objective_notify()
3052 Objective_notify_active = 0;
3055 void hud_start_objective_notify()
3057 snd_play(&(Snds[SND_DIRECTIVE_COMPLETE]));
3058 Objective_notify_active = 1;
3061 int hud_objective_notify_active()
3063 return Objective_notify_active;
3066 // render multiplayer text message currently being entered if any
3067 void hud_maybe_render_multi_text()
3069 char txt[MULTI_MSG_MAX_TEXT_LEN+20];
3072 memset(txt,0,MULTI_MSG_MAX_TEXT_LEN+1);
3074 // if there is valid multiplayer message text to be displayed
3075 if(multi_msg_message_text(txt, SDL_arraysize(txt))){
3076 gr_set_color_fast(&Color_normal);
3077 gr_string(Multi_msg_coords[gr_screen.res][0], Multi_msg_coords[gr_screen.res][1], txt);
3081 // cut any text off after (and including) '#' char
3082 void hud_end_string_at_first_hash_symbol(char *src)
3084 char *pointer_to_last_char;
3086 pointer_to_last_char = strstr(src, NOX("#"));
3088 if ( pointer_to_last_char ) {
3089 *pointer_to_last_char = 0;
3093 // set the offset values for this render frame
3094 void HUD_set_offsets(object *viewer_obj, int wiggedy_wack)
3096 if ( (viewer_obj == Player_obj) && wiggedy_wack ){
3101 HUD_offset_x = 0.0f;
3102 HUD_offset_y = 0.0f;
3104 vm_vec_scale_add( &tmp, &Viewer_obj->pos, &Viewer_obj->orient.v.fvec, 100.0f );
3106 flags = g3_rotate_vertex(&pt,&tmp);
3110 g3_project_vertex(&pt);
3112 if (!(pt.flags & PF_OVERFLOW)) {
3113 HUD_offset_x -= 0.45f * (i2fl(gr_screen.clip_width)*0.5f - pt.sx);
3114 HUD_offset_y -= 0.45f * (i2fl(gr_screen.clip_height)*0.5f - pt.sy);
3118 if ( HUD_offset_x > 100.0f ) {
3119 HUD_offset_x = 100.0f;
3120 } else if ( HUD_offset_x < -100.0f ) {
3121 HUD_offset_x += 100.0f;
3124 if ( HUD_offset_y > 100.0f ) {
3125 HUD_offset_y = 100.0f;
3126 } else if ( HUD_offset_y < -100.0f ) {
3127 HUD_offset_y += 100.0f;
3131 HUD_offset_x = 0.0f;
3132 HUD_offset_y = 0.0f;
3136 // Basically like gr_reset_clip only it accounts for hud jittering
3137 void HUD_reset_clip()
3139 int hx = fl2i(HUD_offset_x);
3140 int hy = fl2i(HUD_offset_y);
3142 gr_set_clip(hx, hy, gr_screen.max_w, gr_screen.max_h );
3145 // Basically like gr_set_clip only it accounts for hud jittering
3146 void HUD_set_clip(int x, int y, int w, int h)
3148 int hx = fl2i(HUD_offset_x);
3149 int hy = fl2i(HUD_offset_y);
3151 gr_set_clip(hx+x, hy+y, w, h );
3154 void hud_toggle_contrast()
3156 HUD_contrast = !HUD_contrast;
3159 void hud_set_contrast(int high)
3161 HUD_contrast = high;
3164 // Paging functions for the rest of the hud code
3165 extern void hudwingmanstatus_page_in();
3166 extern void hudescort_page_in();
3167 extern void hudets_page_in();
3168 extern void hudlock_page_in();
3169 extern void hudreticle_page_in();
3170 extern void hudshield_page_in();
3171 extern void hudsquadmsg_page_in();
3172 extern void hudtarget_page_in();
3173 extern void hudtargetbox_page_in();
3175 // Page in all hud bitmaps
3180 bm_page_in_aabitmap( Kills_gauge.first_frame, Kills_gauge.num_frames );
3181 bm_page_in_aabitmap( Head_frame_gauge.first_frame, Head_frame_gauge.num_frames );
3182 bm_page_in_aabitmap( Mission_time_gauge.first_frame, Mission_time_gauge.num_frames );
3183 for ( i = 0; i < NUM_DAMAGE_GAUGES; i++ ) {
3184 bm_page_in_aabitmap( Damage_gauges[i].first_frame, Damage_gauges[i].num_frames);
3187 bm_page_in_aabitmap( Netlag_icon.first_frame, Netlag_icon.num_frames);
3188 bm_page_in_aabitmap( Support_view_gauge.first_frame, Support_view_gauge.num_frames);
3189 bm_page_in_aabitmap( Objective_display_gauge.first_frame, Objective_display_gauge.num_frames);
3191 // Paging functions for the rest of the hud code
3192 hudwingmanstatus_page_in();
3193 hudescort_page_in();
3196 hudreticle_page_in();
3197 hudshield_page_in();
3198 hudsquadmsg_page_in();
3199 hudtarget_page_in();
3200 hudtargetbox_page_in();