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 the
6  * source.
7  *
8 */
9 
10 
11 #include "ai/aigoals.h"
12 #include "asteroid/asteroid.h"
13 #include "bmpman/bmpman.h"
14 #include "cmdline/cmdline.h"
15 #include "freespace.h"
16 #include "gamesnd/eventmusic.h"
17 #include "gamesnd/gamesnd.h"
18 #include "globalincs/alphacolors.h"
19 #include "globalincs/linklist.h"
20 #include "hud/hud.h"
21 #include "hud/hudconfig.h"
22 #include "hud/hudescort.h"
23 #include "hud/hudets.h"
24 #include "hud/hudlock.h"
25 #include "hud/hudmessage.h"
26 #include "hud/hudnavigation.h"    //kazan
27 #include "hud/hudobserver.h"
28 #include "hud/hudreticle.h"
29 #include "hud/hudscripting.h"
30 #include "hud/hudshield.h"
31 #include "hud/hudsquadmsg.h"
32 #include "hud/hudtarget.h"
33 #include "hud/hudtargetbox.h"
34 #include "hud/hudwingmanstatus.h"
35 #include "iff_defs/iff_defs.h"
36 #include "io/timer.h"
37 #include "localization/localize.h"
38 #include "mission/missiongoals.h"
39 #include "mission/missionmessage.h"
40 #include "mission/missionparse.h"
41 #include "mission/missiontraining.h"
42 #include "missionui/redalert.h"
43 #include "model/model.h"
44 #include "network/multi_pmsg.h"
45 #include "network/multi_voice.h"
46 #include "network/multiutil.h"
47 #include "tracing/tracing.h"
48 #include "object/object.h"
49 #include "object/objectdock.h"
50 #include "playerman/player.h"
51 #include "radar/radar.h"
52 #include "radar/radarsetup.h"
53 #include "render/3d.h"
54 #include "scripting/scripting.h"
55 #include "ship/ship.h"
56 #include "starfield/supernova.h"
57 #include "weapon/emp.h"
58 #include "weapon/weapon.h"
59 
60 SCP_vector<std::unique_ptr<HudGauge>> default_hud_gauges;
61 
62 // new values for HUD alpha
63 #define HUD_NEW_ALPHA_DIM				80
64 #define HUD_NEW_ALPHA_NORMAL			120
65 #define HUD_NEW_ALPHA_BRIGHT			220
66 
67 // high contrast
68 #define HUD_NEW_ALPHA_DIM_HI			130
69 #define HUD_NEW_ALPHA_NORMAL_HI			190
70 #define HUD_NEW_ALPHA_BRIGHT_HI			255
71 
72 // Externals not related to the HUD code itself
73 extern float View_zoom;
74 
75 // globals that will control the color of the HUD gauges
76 int HUD_color_red = 0;
77 int HUD_color_green = 255;
78 int HUD_color_blue = 0;
79 int HUD_color_alpha = HUD_COLOR_ALPHA_DEFAULT;		// 1 -> HUD_COLOR_ALPHA_USER_MAX
80 
81 int HUD_draw     = 1;
82 int HUD_contrast = 0;										// high or lo contrast (for nebula, etc)
83 
84 // Goober5000
85 int HUD_disable_except_messages = 0;
86 
87 color HUD_color_defaults[HUD_NUM_COLOR_LEVELS];		// array of colors with different alpha blending
88 color HUD_color_debug;										// grey debug text shown on HUD
89 
90 static sound_handle Player_engine_snd_loop = sound_handle::invalid();
91 
92 // HUD render frame offsets
93 float HUD_offset_x = 0.0f;
94 float HUD_offset_y = 0.0f;
95 
96 // the offset of the player's view vector and the ship forward vector in pixels (Swifty)
97 int HUD_nose_x;
98 int HUD_nose_y;
99 // Global: integrity of player's target
100 float Pl_target_integrity;
101 
102 int Hud_max_targeting_range;
103 
104 static int Hud_last_can_target;	// whether Player is able to target in the last frame
105 static int Hud_can_target_timer;	// timestamp to allow target gauge to draw static once targeting functions are not allowed
106 
107 // centered text message gauges (collision, emp, etc)
108 char Hud_text_flash[512] = "";
109 int Hud_text_flash_timer = 0;
110 int Hud_text_flash_interval = 0;
111 
112 void hud_init_text_flash_gauge();
113 void hud_start_text_flash(char *txt, int t, int interval);
114 
115 // multiplayer messaging text
116 int Multi_msg_coords[GR_NUM_RESOLUTIONS][2] = {
117 	{ // GR_640
118 		5, 150
119 	},
120 	{ // GR_1024
121 		8, 240
122 	}
123 };
124 
125 // multiplayer voice stuff
126 int Voice_coords[GR_NUM_RESOLUTIONS][2] = {
127 	{ // GR_640
128 		5, 165
129 	},
130 	{ // GR_1024
131 		8, 255
132 	}
133 };
134 
135 // ping text coords
136 int Ping_coords[GR_NUM_RESOLUTIONS][2] = {
137 	{ // GR_640
138 		560, 3
139 	},
140 	{ // GR_1024
141 		896, 5
142 	}
143 };
144 
145 // supernova coords
146 int Supernova_coords[GR_NUM_RESOLUTIONS][2] = {
147 	{ // GR_640
148 		100, 100
149 	},
150 	{ // GR_1024
151 		170, 170
152 	}
153 };
154 
155 // used to draw the hud support view
156 static int Hud_support_view_active;
157 static int Hud_support_view_abort;		// active when we need to display abort message
158 static int Hud_support_view_fade;		// timer
159 static int Hud_support_obj_sig, Hud_support_objnum, Hud_support_target_sig;
160 
161 // flashing gauges
162 #define HUD_GAUGE_FLASH_DURATION		5000
163 #define HUD_GAUGE_FLASH_INTERVAL		200
164 int HUD_gauge_flash_duration[NUM_HUD_GAUGES];
165 int HUD_gauge_flash_next[NUM_HUD_GAUGES];
166 int HUD_gauge_bright;
167 
168 static objective_display_info Objective_display;
169 
170 // Subspace notify display
171 static int Subspace_notify_active;
172 static int Objective_notify_active;
173 static int HUD_abort_subspace_timer = 1;
174 
175 static hud_subsys_info	Pl_hud_subsys_info[SUBSYSTEM_MAX];
176 static int					Pl_hud_next_flash_timestamp;
177 static int					Pl_hud_is_bright;
178 
179 #define SUBSYS_DAMAGE_FLASH_DURATION	1800
180 #define SUBSYS_DAMAGE_FLASH_INTERVAL	100
181 
182 float Player_rearm_eta = 0;
183 
184 // forward declarations
185 void update_throttle_sound();
186 void hud_damage_popup_init();
187 void hud_support_view_init();
188 void hud_gauge_flash_init();
189 void hud_objective_message_init();
190 void hud_stop_subspace_notify();
191 void hud_start_subspace_notify();
192 void hud_stop_objective_notify();
193 void hud_start_objective_notify();
194 int hud_subspace_notify_active();
195 int hud_objective_notify_active();
196 void hud_subspace_notify_abort();
197 void hud_maybe_display_subspace_notify();
198 int hud_maybe_render_emp_icon();
199 void hud_init_emp_icon();
200 
201 //	Saturate a value in minv..maxv.
saturate(int * i,int minv,int maxv)202 void saturate(int *i, int minv, int maxv)
203 {
204 	if (*i < minv)
205 		*i = minv;
206 	else if (*i > maxv)
207 		*i = maxv;
208 }
209 
210 // init the colors used for the different shades of the HUD
HUD_init_hud_color_array()211 void HUD_init_hud_color_array()
212 {
213 	int i;
214 
215 	for ( i = 0; i < HUD_NUM_COLOR_LEVELS; i++ ) {
216 		gr_init_alphacolor( &HUD_color_defaults[i], HUD_color_red, HUD_color_green, HUD_color_blue, (i+1)*16 );
217 	}
218 }
219 
220 // HUD_init will call all the various HUD gauge init functions.  This function is called at the
221 // start of each mission (level)
HUD_init_colors()222 void HUD_init_colors()
223 {
224 	saturate(&HUD_color_red, 0, 255);
225 	saturate(&HUD_color_green, 0, 255);
226 	saturate(&HUD_color_blue, 0, 255);
227 	saturate(&HUD_color_alpha, 0, HUD_COLOR_ALPHA_USER_MAX);
228 
229 	gr_init_alphacolor( &HUD_color_debug, 128, 255, 128, HUD_color_alpha*16 );
230 	HUD_init_hud_color_array();
231 
232 	hud_init_targeting_colors();
233 	hud_gauge_flash_init();
234 }
235 
236 // The following global data is used to determine if we should change the engine sound.
237 // We only check if the throttle has changed every THROTTLE_SOUND_CHECK_INTERVAL ms, and
238 // then we make sure that the throttle has actually changed.  If it has changed, we start
239 // a new sound and/or adjust the volume.  This occurs in update_throttle_sound()
240 //
241 static float last_percent_throttle;
242 #define THROTTLE_SOUND_CHECK_INTERVAL	50	// in ms
243 static int throttle_sound_check_id;
244 
245 #define DAMAGE_FLASH_TIME 150
246 static int Damage_flash_bright;
247 static int Damage_flash_timer;
248 
HudGauge()249 HudGauge::HudGauge():
250 base_w(0), base_h(0), gauge_config(-1), font_num(font::FONT1), lock_color(false), sexp_lock_color(false), reticle_follow(false),
251 active(false), off_by_default(false), sexp_override(false), pop_up(false), disabled_views(0), only_render_in_chase_view(false), custom_gauge(false),
252 texture_target(-1), canvas_w(-1), canvas_h(-1), target_w(-1), target_h(-1)
253 {
254 	position[0] = 0;
255 	position[1] = 0;
256 
257 	gr_init_alphacolor(&gauge_color, 255, 255, 255, (HUD_color_alpha+1)*16);
258 	flash_duration = timestamp(1);
259 	flash_next = timestamp(1);
260 	flash_status = false;
261 
262 	popup_timer = timestamp(1);
263 
264 	texture_target_fname[0] = '\0';
265 
266 	custom_name[0] = '\0';
267 	custom_text = "";
268 	custom_frame.first_frame = -1;
269 	custom_frame.num_frames = 0;
270 	custom_frame_offset = 0;
271 }
272 
HudGauge(int _gauge_object,int _gauge_config,bool _slew,bool _message,int _disabled_views,int r,int g,int b)273 HudGauge::HudGauge(int _gauge_object, int _gauge_config, bool _slew, bool _message, int _disabled_views, int r, int g, int b):
274 base_w(0), base_h(0), gauge_config(_gauge_config), gauge_object(_gauge_object), font_num(font::FONT1), lock_color(false), sexp_lock_color(false),
275 reticle_follow(_slew), active(false), off_by_default(false), sexp_override(false), pop_up(false), message_gauge(_message),
276 disabled_views(_disabled_views), only_render_in_chase_view(false), custom_gauge(false), textoffset_x(0), textoffset_y(0), texture_target(-1),
277 canvas_w(-1), canvas_h(-1), target_w(-1), target_h(-1)
278 {
279 	Assert(gauge_config <= NUM_HUD_GAUGES && gauge_config >= 0);
280 
281 	position[0] = 0;
282 	position[1] = 0;
283 
284 	if(r >= 0 && r <= 255 &&
285 		g >= 0 && g <= 255 &&
286 		b >= 0 && b <= 255) {
287 		gr_init_alphacolor(&gauge_color, r, g, b, (HUD_color_alpha+1)*16);
288 	} else {
289 		gr_init_alphacolor(&gauge_color, 255, 255, 255, (HUD_color_alpha+1)*16);
290 	}
291 
292 	flash_duration = timestamp(1);
293 	flash_next = timestamp(1);
294 	flash_status = false;
295 
296 	popup_timer = timestamp(1);
297 
298 	texture_target_fname[0] = '\0';
299 
300 	custom_name[0] = '\0';
301 	custom_text = "";
302 	default_text = "";
303 	custom_frame.first_frame = -1;
304 	custom_frame.num_frames = 0;
305 	custom_frame_offset = 0;
306 }
307 
308 // constructor for custom gauges
HudGauge(int _gauge_config,bool _slew,int r,int g,int b,char * _custom_name,char * _custom_text,char * frame_fname,int txtoffset_x,int txtoffset_y)309 HudGauge::HudGauge(int _gauge_config, bool _slew, int r, int g, int b, char* _custom_name, char* _custom_text, char* frame_fname, int txtoffset_x, int txtoffset_y):
310 base_w(0), base_h(0), gauge_config(_gauge_config), gauge_object(HUD_OBJECT_CUSTOM), font_num(font::FONT1), lock_color(false), sexp_lock_color(false),
311 reticle_follow(_slew), active(false), off_by_default(false), sexp_override(false), pop_up(false), message_gauge(false),
312 disabled_views(VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY), only_render_in_chase_view(false), custom_gauge(true), textoffset_x(txtoffset_x),
313 textoffset_y(txtoffset_y), texture_target(-1), canvas_w(-1), canvas_h(-1), target_w(-1), target_h(-1)
314 {
315 	position[0] = 0;
316 	position[1] = 0;
317 
318 	if(r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255) {
319 		gr_init_alphacolor(&gauge_color, r, g, b, (HUD_color_alpha+1)*16);
320 	} else {
321 		gr_init_alphacolor(&gauge_color, 255, 255, 255, (HUD_color_alpha+1)*16);
322 	}
323 
324 	flash_duration = timestamp(1);
325 	flash_next = timestamp(1);
326 	flash_status = false;
327 
328 	popup_timer = timestamp(1);
329 
330 	texture_target_fname[0] = '\0';
331 
332 	if(_custom_name) {
333 		strcpy_s(custom_name, _custom_name);
334 	} else {
335 		custom_name[0] = '\0';
336 	}
337 
338 	if(_custom_text) {
339 		custom_text = _custom_text;
340 		default_text = _custom_text;
341 	} else {
342 		custom_text = "";
343 		default_text = "";
344 	}
345 
346 	custom_frame.first_frame = -1;
347 	custom_frame.num_frames = 0;
348 	custom_frame_offset = 0;
349 
350 	if(frame_fname) {
351 		custom_frame.first_frame = bm_load_animation(frame_fname, &custom_frame.num_frames);
352 		if (custom_frame.first_frame < 0 ) {
353 			Warning(LOCATION,"Cannot load hud ani: %s\n", frame_fname);
354 		}
355 	}
356 }
357 
~HudGauge()358 HudGauge::~HudGauge() {};
359 
initPosition(int x,int y)360 void HudGauge::initPosition(int x, int y)
361 {
362 	position[0] = x;
363 	position[1] = y;
364 }
365 
getPosition(int * x,int * y)366 void HudGauge::getPosition(int *x, int *y)
367 {
368 	*x = position[0];
369 	*y = position[1];
370 }
371 
initBaseResolution(int w,int h)372 void HudGauge::initBaseResolution(int w, int h)
373 {
374 	Assert(w >= 640 && h >= 480);
375 
376 	base_w = w;
377 	base_h = h;
378 }
379 
initSlew(bool slew)380 void HudGauge::initSlew(bool slew)
381 {
382 	reticle_follow = slew;
383 }
384 
initFont(int input_font_num)385 void HudGauge::initFont(int input_font_num)
386 {
387 	if (input_font_num >= 0 && input_font_num < font::FontManager::numberOfFonts()) {
388 		font_num = input_font_num;
389 	}
390 }
391 
getCustomGaugeName()392 const char* HudGauge::getCustomGaugeName()
393 {
394 	return custom_name;
395 }
396 
getCustomGaugeText()397 const char* HudGauge::getCustomGaugeText()
398 {
399 	return custom_text.c_str();
400 }
401 
updateCustomGaugeCoords(int _x,int _y)402 void HudGauge::updateCustomGaugeCoords(int _x, int _y)
403 {
404 	if(!custom_gauge) {
405 		return;
406 	}
407 
408 	position[0] = _x;
409 	position[1] = _y;
410 }
411 
updateCustomGaugeFrame(int frame_offset)412 void HudGauge::updateCustomGaugeFrame(int frame_offset)
413 {
414 	if(!custom_gauge) {
415 		return;
416 	}
417 
418 	if (frame_offset < 0 ||frame_offset > custom_frame.num_frames) {
419 		return;
420 	}
421 
422 	custom_frame_offset = frame_offset;
423 }
424 
updateCustomGaugeText(const char * txt)425 void HudGauge::updateCustomGaugeText(const char* txt)
426 {
427 	if(!custom_gauge) {
428 		return;
429 	}
430 
431 	custom_text = txt;
432 }
433 
updateCustomGaugeText(const SCP_string & txt)434 void HudGauge::updateCustomGaugeText(const SCP_string& txt)
435 {
436 	if(!custom_gauge) {
437 		return;
438 	}
439 
440 	custom_text = txt;
441 }
442 
setFont()443 void HudGauge::setFont()
444 {
445 	font::set_font(font_num);
446 }
447 
setGaugeColor(int bright_index)448 void HudGauge::setGaugeColor(int bright_index)
449 {
450 	int alpha;
451 
452 	// if we're drawing it as bright
453 	if(bright_index != HUD_C_NONE){
454 		switch(bright_index){
455 		case HUD_C_DIM:
456 			alpha = HUD_contrast ? HUD_NEW_ALPHA_DIM_HI : HUD_NEW_ALPHA_DIM;
457 			gr_init_alphacolor(&gauge_color, gauge_color.red, gauge_color.green, gauge_color.blue, alpha);
458 			break;
459 
460 		case HUD_C_NORMAL:
461 			alpha = HUD_contrast ? HUD_NEW_ALPHA_NORMAL_HI : HUD_NEW_ALPHA_NORMAL;
462 			gr_init_alphacolor(&gauge_color, gauge_color.red, gauge_color.green, gauge_color.blue, alpha);
463 			break;
464 
465 		case HUD_C_BRIGHT:
466 			alpha = HUD_contrast ? HUD_NEW_ALPHA_BRIGHT_HI : HUD_NEW_ALPHA_BRIGHT;
467 			gr_init_alphacolor(&gauge_color, gauge_color.red, gauge_color.green, gauge_color.blue, alpha);
468 			break;
469 
470 		// intensity
471 		default:
472 			Assert((bright_index >= 0) && (bright_index < HUD_NUM_COLOR_LEVELS));
473 			if(bright_index < 0){
474 				bright_index = 0;
475 			}
476 			if(bright_index >= HUD_NUM_COLOR_LEVELS){
477 				bright_index = HUD_NUM_COLOR_LEVELS - 1;
478 			}
479 
480 			// alpha = 255 - (255 / (bright_index + 1));
481 			// alpha = (int)((float)alpha * 1.5f);
482 			int level = 255 / (HUD_NUM_COLOR_LEVELS);
483 			alpha = level * bright_index;
484 			if(alpha > 255){
485 				alpha = 255;
486 			}
487 			if(alpha < 0){
488 				alpha = 0;
489 			}
490 			gr_init_alphacolor(&gauge_color, gauge_color.red, gauge_color.green, gauge_color.blue, alpha);
491 			break;
492 		}
493 	} else {
494 		switch(maybeFlashSexp()) {
495 		case 0:
496 			alpha = HUD_contrast ? HUD_NEW_ALPHA_DIM_HI : HUD_NEW_ALPHA_DIM;
497 			gr_init_alphacolor(&gauge_color, gauge_color.red, gauge_color.green, gauge_color.blue, alpha);
498 			break;
499 		case 1:
500 			alpha = HUD_contrast ? HUD_NEW_ALPHA_BRIGHT_HI : HUD_NEW_ALPHA_BRIGHT;
501 			gr_init_alphacolor(&gauge_color, gauge_color.red, gauge_color.green, gauge_color.blue, alpha);
502 			break;
503 		default:
504 			alpha = HUD_contrast ? HUD_NEW_ALPHA_NORMAL_HI : HUD_NEW_ALPHA_NORMAL;
505 			gr_init_alphacolor(&gauge_color, gauge_color.red, gauge_color.green, gauge_color.blue, alpha);
506 		}
507 	}
508 
509 	gr_set_color_fast(&gauge_color);
510 }
511 
getConfigType()512 int HudGauge::getConfigType()
513 {
514 	//return gauge_type;
515 	return gauge_config;
516 }
517 
getObjectType()518 int HudGauge::getObjectType()
519 {
520 	return gauge_object;
521 }
522 
lockConfigColor(bool lock)523 void HudGauge::lockConfigColor(bool lock)
524 {
525 	lock_color = lock;
526 }
527 
sexpLockConfigColor(bool lock)528 void HudGauge::sexpLockConfigColor(bool lock)
529 {
530 	sexp_lock_color = lock;
531 }
532 
updateColor(int r,int g,int b,int a)533 void HudGauge::updateColor(int r, int g, int b, int a)
534 {
535 	if(sexp_lock_color || lock_color)
536 		return;
537 
538 	gr_init_alphacolor(&gauge_color, r, g, b, a);
539 }
540 
getColor()541  const color& HudGauge::getColor()
542 {
543 	return gauge_color;
544 }
545 
updateActive(bool show)546 void HudGauge::updateActive(bool show)
547 {
548 	active = show;
549 }
550 
initRenderStatus(bool do_render)551 void HudGauge::initRenderStatus(bool do_render)
552 {
553 	off_by_default = !do_render;
554 }
555 
initChase_view_only(bool chase_view_only)556 void HudGauge::initChase_view_only(bool chase_view_only)
557 {
558 	only_render_in_chase_view = chase_view_only;
559 }
560 
isOffbyDefault()561 bool HudGauge::isOffbyDefault()
562 {
563 	return off_by_default;
564 }
565 
isActive()566 bool HudGauge::isActive()
567 {
568 	return active && !sexp_override;
569 }
570 
updateSexpOverride(bool sexp)571 void HudGauge::updateSexpOverride(bool sexp)
572 {
573 	sexp_override = sexp;
574 }
575 
updatePopUp(bool pop_up_flag)576 void HudGauge::updatePopUp(bool pop_up_flag)
577 {
578 	pop_up = pop_up_flag;
579 }
580 
startPopUp(int time)581 void HudGauge::startPopUp(int time)
582 {
583 	//Assert(gauge_index >=0 && gauge_index < NUM_HUD_GAUGES);
584 	if ( !pop_up ) {
585 		return;
586 	}
587 
588 	popup_timer = timestamp(time);
589 }
590 
popUpActive()591 int HudGauge::popUpActive()
592 {
593 	//Assert(gauge_index >=0 && gauge_index < NUM_HUD_GAUGES);
594 	if ( !pop_up ) {
595 		return 0;
596 	}
597 
598 	if ( !timestamp_elapsed(popup_timer) ) {
599 		return 1;
600 	} else {
601 		return 0;
602 	}
603 }
604 
resetTimers()605 void HudGauge::resetTimers()
606 {
607 	flash_duration = timestamp(1);
608 	flash_next = timestamp(1);
609 	flash_status = false;
610 
611 	popup_timer = timestamp(1);
612 }
613 
startFlashSexp()614 void HudGauge::startFlashSexp()
615 {
616 	flash_duration = timestamp(HUD_GAUGE_FLASH_DURATION);
617 	flash_next = timestamp(1);
618 	flash_status = false;
619 }
620 
flashExpiredSexp()621 bool HudGauge::flashExpiredSexp()
622 {
623 	if(timestamp_elapsed(flash_duration)) {
624 		return true;
625 	}
626 
627 	return false;
628 }
629 
maybeFlashSexp()630 int HudGauge::maybeFlashSexp()
631 {
632 	if ( !timestamp_elapsed(flash_duration) ) {
633 		if ( timestamp_elapsed(flash_next) ) {
634 			flash_next = timestamp(HUD_GAUGE_FLASH_INTERVAL);
635 
636 			// toggle between default and bright frames
637 			flash_status = !flash_status;
638 		}
639 		return (int)flash_status;
640 	}
641 
642 	return -1;
643 }
644 
preprocess()645 void HudGauge::preprocess()
646 {
647 
648 }
649 
onFrame(float)650 void HudGauge::onFrame(float  /*frametime*/)
651 {
652 
653 }
654 
render(float)655 void HudGauge::render(float  /*frametime*/)
656 {
657 	if(!custom_gauge) {
658 		return;
659 	}
660 
661 	setGaugeColor();
662 
663 	if( !custom_text.empty() ) {
664 		char *text = new char[custom_text.size()+1];
665 		strcpy(text, custom_text.c_str());
666 
667 		hud_num_make_mono(text, font_num);
668 		renderString(position[0] + textoffset_x, position[1] + textoffset_y, text);
669 
670 		delete[] text;
671 	}
672 
673 	if(custom_frame.first_frame > -1) {
674 		renderBitmap(custom_frame.first_frame + custom_frame_offset, position[0], position[1]);
675 	}
676 }
677 
renderString(int x,int y,const char * str)678 void HudGauge::renderString(int x, int y, const char *str)
679 {
680 	int nx = 0, ny = 0;
681 
682 	if ( gr_screen.rendering_to_texture != -1 ) {
683 		gr_set_screen_scale(canvas_w, canvas_h, -1, -1, target_w, target_h, target_w, target_h, true);
684 	} else {
685 		if ( reticle_follow ) {
686 			nx = HUD_nose_x;
687 			ny = HUD_nose_y;
688 
689 			gr_resize_screen_pos(&nx, &ny);
690 			gr_set_screen_scale(base_w, base_h);
691 			gr_unsize_screen_pos(&nx, &ny);
692 		} else {
693 			gr_set_screen_scale(base_w, base_h);
694 		}
695 	}
696 
697 	gr_string(x + nx, y + ny, str);
698 	gr_reset_screen_scale();
699 }
700 
renderString(int x,int y,int gauge_id,const char * str)701 void HudGauge::renderString(int x, int y, int gauge_id, const char *str)
702 {
703 	int nx = 0, ny = 0;
704 
705 	if ( gr_screen.rendering_to_texture != -1 ) {
706 		gr_set_screen_scale(canvas_w, canvas_h, -1, -1, target_w, target_h, target_w, target_h, true);
707 	} else {
708 		if ( reticle_follow ) {
709 			nx = HUD_nose_x;
710 			ny = HUD_nose_y;
711 
712 			gr_resize_screen_pos(&nx, &ny);
713 			gr_set_screen_scale(base_w, base_h);
714 			gr_unsize_screen_pos(&nx, &ny);
715 		} else {
716 			gr_set_screen_scale(base_w, base_h);
717 		}
718 	}
719 
720 	if ( gauge_id > -2 ) {
721 		emp_hud_string(x + nx, y + ny, gauge_id, str, GR_RESIZE_FULL);
722 	} else {
723 		gr_string(x + nx, y + ny, str);
724 	}
725 
726 	gr_reset_screen_scale();
727 }
728 
renderStringAlignCenter(int x,int y,int area_width,const char * s)729 void HudGauge::renderStringAlignCenter(int x, int y, int area_width, const char *s)
730 {
731 	int w, h;
732 
733 	gr_get_string_size(&w, &h, s);
734 	renderString(x + ((area_width - w) / 2), y, s);
735 }
736 
renderPrintf(int x,int y,const char * format,...)737 void HudGauge::renderPrintf(int x, int y, const char* format, ...)
738 {
739 	char tmp[256] = "";
740 	va_list args;
741 
742 	// format the text
743 	va_start(args, format);
744 	vsnprintf(tmp, sizeof(tmp)-1, format, args);
745 	va_end(args);
746 	tmp[sizeof(tmp)-1] = '\0';
747 
748 	renderString(x, y, tmp);
749 }
750 
renderPrintf(int x,int y,int gauge_id,const char * format,...)751 void HudGauge::renderPrintf(int x, int y, int gauge_id, const char* format, ...)
752 {
753 	char tmp[256] = "";
754 	va_list args;
755 
756 	// format the text
757 	va_start(args, format);
758 	vsnprintf(tmp, sizeof(tmp)-1, format, args);
759 	va_end(args);
760 	tmp[sizeof(tmp)-1] = '\0';
761 
762 	renderString(x, y, gauge_id, tmp);
763 }
764 
renderBitmapColor(int frame,int x,int y)765 void HudGauge::renderBitmapColor(int frame, int x, int y)
766 {
767 	int nx = 0, ny = 0;
768 
769 	if( !emp_should_blit_gauge() ) {
770 		return;
771 	}
772 
773 	emp_hud_jitter(&x, &y);
774 
775 	if ( gr_screen.rendering_to_texture != -1 ) {
776 		gr_set_screen_scale(canvas_w, canvas_h, -1, -1, target_w, target_h, target_w, target_h, true);
777 	} else {
778 		if ( reticle_follow ) {
779 			nx = HUD_nose_x;
780 			ny = HUD_nose_y;
781 
782 			gr_resize_screen_pos(&nx, &ny);
783 			gr_set_screen_scale(base_w, base_h);
784 			gr_unsize_screen_pos(&nx, &ny);
785 		} else {
786 			gr_set_screen_scale(base_w, base_h);
787 		}
788 	}
789 
790 	gr_set_bitmap(frame);
791 	gr_bitmap(x + nx, y + ny);
792 	gr_reset_screen_scale();
793 }
794 
renderBitmap(int x,int y)795 void HudGauge::renderBitmap(int x, int y)
796 {
797 	int nx = 0, ny = 0;
798 
799 	if( !emp_should_blit_gauge() ) {
800 		return;
801 	}
802 
803 	emp_hud_jitter(&x, &y);
804 
805 	if ( gr_screen.rendering_to_texture != -1 ) {
806 		gr_set_screen_scale(canvas_w, canvas_h, -1, -1, target_w, target_h, target_w, target_h, true);
807 	} else {
808 		if ( reticle_follow ) {
809 			nx = HUD_nose_x;
810 			ny = HUD_nose_y;
811 
812 			gr_resize_screen_pos(&nx, &ny);
813 			gr_set_screen_scale(base_w, base_h);
814 			gr_unsize_screen_pos(&nx, &ny);
815 		} else {
816 			gr_set_screen_scale(base_w, base_h);
817 		}
818 	}
819 
820 	gr_aabitmap(x + nx, y + ny);
821 
822 	gr_reset_screen_scale();
823 }
824 
renderBitmap(int frame,int x,int y)825 void HudGauge::renderBitmap(int frame, int x, int y)
826 {
827 	gr_set_bitmap(frame);
828 	renderBitmap(x, y);
829 }
830 
renderBitmapEx(int frame,int x,int y,int w,int h,int sx,int sy)831 void HudGauge::renderBitmapEx(int frame, int x, int y, int w, int h, int sx, int sy)
832 {
833 	int nx = 0, ny = 0;
834 
835 	if( !emp_should_blit_gauge() ) {
836 		return;
837 	}
838 
839 	emp_hud_jitter(&x, &y);
840 
841 	gr_set_bitmap(frame);
842 
843 	if( gr_screen.rendering_to_texture != -1 ) {
844 		gr_set_screen_scale(canvas_w, canvas_h, -1, -1, target_w, target_h, target_w, target_h, true);
845 	} else {
846 		if ( reticle_follow ) {
847 			nx = HUD_nose_x;
848 			ny = HUD_nose_y;
849 
850 			gr_resize_screen_pos(&nx, &ny);
851 			gr_set_screen_scale(base_w, base_h);
852 			gr_unsize_screen_pos(&nx, &ny);
853 		} else {
854 			gr_set_screen_scale(base_w, base_h);
855 		}
856 	}
857 
858 	gr_aabitmap_ex(x + nx, y + ny, w, h, sx, sy);
859 
860 	gr_reset_screen_scale();
861 }
862 
renderLine(int x1,int y1,int x2,int y2)863 void HudGauge::renderLine(int x1, int y1, int x2, int y2)
864 {
865 	int nx = 0, ny = 0;
866 
867 	if ( gr_screen.rendering_to_texture != -1 ) {
868 		gr_set_screen_scale(canvas_w, canvas_h, -1, -1, target_w, target_h, target_w, target_h, true);
869 	} else {
870 		if ( reticle_follow ) {
871 			nx = HUD_nose_x;
872 			ny = HUD_nose_y;
873 
874 			gr_resize_screen_pos(&nx, &ny);
875 			gr_set_screen_scale(base_w, base_h);
876 			gr_unsize_screen_pos(&nx, &ny);
877 		} else {
878 			gr_set_screen_scale(base_w, base_h);
879 		}
880 	}
881 
882 	gr_line(x1+nx, y1+ny, x2+nx, y2+ny);
883 	gr_reset_screen_scale();
884 }
885 
renderGradientLine(int x1,int y1,int x2,int y2)886 void HudGauge::renderGradientLine(int x1, int y1, int x2, int y2)
887 {
888 	int nx = 0, ny = 0;
889 
890 	if ( gr_screen.rendering_to_texture != -1 ) {
891 		gr_set_screen_scale(canvas_w, canvas_h, -1, -1, target_w, target_h, target_w, target_h, true);
892 	} else {
893 		if(reticle_follow) {
894 			nx = HUD_nose_x;
895 			ny = HUD_nose_y;
896 
897 			gr_resize_screen_pos(&nx, &ny);
898 			gr_set_screen_scale(base_w, base_h);
899 			gr_unsize_screen_pos(&nx, &ny);
900 		} else {
901 			gr_set_screen_scale(base_w, base_h);
902 		}
903 	}
904 
905 	gr_gradient(x1+nx, y1+ny, x2+nx, y2+ny);
906 	gr_reset_screen_scale();
907 }
908 
renderRect(int x,int y,int w,int h)909 void HudGauge::renderRect(int x, int y, int w, int h)
910 {
911 	int nx = 0, ny = 0;
912 
913 	if ( gr_screen.rendering_to_texture != -1 ) {
914 		gr_set_screen_scale(canvas_w, canvas_h, -1, -1, target_w, target_h, target_w, target_h, true);
915 	} else {
916 		if ( reticle_follow ) {
917 			nx = HUD_nose_x;
918 			ny = HUD_nose_y;
919 
920 			gr_resize_screen_pos(&nx, &ny);
921 			gr_set_screen_scale(base_w, base_h);
922 			gr_unsize_screen_pos(&nx, &ny);
923 		} else {
924 			gr_set_screen_scale(base_w, base_h);
925 		}
926 	}
927 
928 	gr_rect(x+nx, y+ny, w, h);
929 	gr_reset_screen_scale();
930 }
931 
renderCircle(int x,int y,int diameter,bool filled)932 void HudGauge::renderCircle(int x, int y, int diameter, bool filled)
933 {
934 	int nx = 0, ny = 0;
935 
936 	if ( gr_screen.rendering_to_texture != -1 ) {
937 		gr_set_screen_scale(canvas_w, canvas_h, -1, -1, target_w, target_h, target_w, target_h, true);
938 	} else {
939 		if ( reticle_follow ) {
940 			nx = HUD_nose_x;
941 			ny = HUD_nose_y;
942 
943 			gr_resize_screen_pos(&nx, &ny);
944 			gr_set_screen_scale(base_w, base_h);
945 			gr_unsize_screen_pos(&nx, &ny);
946 		} else {
947 			gr_set_screen_scale(base_w, base_h);
948 		}
949 	}
950 	if (filled) {
951 		gr_circle(x + nx, y + ny, diameter);
952 	} else {
953 		gr_unfilled_circle(x + nx, y + ny, diameter);
954 	}
955 
956 	gr_reset_screen_scale();
957 }
958 
setClip(int x,int y,int w,int h)959 void HudGauge::setClip(int x, int y, int w, int h)
960 {
961 	int hx = fl2i(HUD_offset_x);
962 	int hy = fl2i(HUD_offset_y);
963 
964 	if ( gr_screen.rendering_to_texture != -1 ) {
965 		gr_set_screen_scale(canvas_w, canvas_h, -1, -1, target_w, target_h, target_w, target_h, true);
966 
967 		hx = display_offset_x;
968 		hy = display_offset_y;
969 
970 		gr_resize_screen_pos(&x, &y);
971 
972 		hx += x;
973 		hy += y;
974 
975 		gr_unsize_screen_pos(&hx, &hy);
976 
977 		gr_set_clip(hx, hy, w, h);
978 	} else {
979 		if ( reticle_follow ) {
980 			hx += HUD_nose_x;
981 			hy += HUD_nose_y;
982 		}
983 
984 		gr_resize_screen_pos(&hx, &hy);
985 
986 		gr_set_screen_scale(base_w, base_h);
987 		gr_resize_screen_pos(&x, &y, &w, &h);
988 
989 		gr_set_clip(hx+x, hy+y, w, h, GR_RESIZE_NONE);
990 	}
991 
992 	gr_reset_screen_scale();
993 }
994 
resetClip()995 void HudGauge::resetClip()
996 {
997 	int hx = 0, hy = 0;
998 	int w, h;
999 
1000 	if ( gr_screen.rendering_to_texture != -1 ) {
1001 		gr_set_screen_scale(canvas_w, canvas_h, -1, -1, target_w, target_h, target_w, target_h, true);
1002 
1003 		hx = display_offset_x;
1004 		hy = display_offset_y;
1005 
1006 		gr_unsize_screen_pos(&hx, &hy);
1007 
1008 		w = canvas_w;
1009 		h = canvas_h;
1010 
1011 		gr_set_clip(hx, hy, w, h);
1012 	} else {
1013 		hx = fl2i(HUD_offset_x);
1014 		hy = fl2i(HUD_offset_y);
1015 
1016 		gr_resize_screen_pos(&hx, &hy);
1017 		gr_set_screen_scale(base_w, base_h);
1018 
1019 		w = gr_screen.max_w;
1020 		h = gr_screen.max_h;
1021 
1022 		// clip the screen based on the actual resolution
1023 		gr_set_clip(hx, hy, w, h, GR_RESIZE_NONE);
1024 	}
1025 
1026 	gr_reset_screen_scale();
1027 }
1028 
unsize(int * x,int * y)1029 void HudGauge::unsize(int *x, int *y)
1030 {
1031 	gr_set_screen_scale(base_w, base_h);
1032 	gr_unsize_screen_pos(x, y);
1033 	gr_reset_screen_scale();
1034 }
1035 
unsize(float * x,float * y)1036 void HudGauge::unsize(float *x, float *y)
1037 {
1038 	gr_set_screen_scale(base_w, base_h);
1039 	gr_unsize_screen_posf(x, y);
1040 	gr_reset_screen_scale();
1041 }
1042 
resize(int * x,int * y)1043 void HudGauge::resize(int *x, int *y)
1044 {
1045 	gr_set_screen_scale(base_w, base_h);
1046 	gr_resize_screen_pos(x, y);
1047 	gr_reset_screen_scale();
1048 }
1049 
resize(float * x,float * y)1050 void HudGauge::resize(float *x, float *y)
1051 {
1052 	gr_set_screen_scale(base_w, base_h);
1053 	gr_resize_screen_posf(x, y);
1054 	gr_reset_screen_scale();
1055 }
1056 
pageIn()1057 void HudGauge::pageIn()
1058 {
1059 	if(custom_gauge) {
1060 		if(custom_frame.first_frame > -1 && custom_frame.num_frames > 0) {
1061 			bm_page_in_aabitmap( custom_frame.first_frame, custom_frame.num_frames );
1062 		}
1063 	}
1064 }
1065 
initialize()1066 void HudGauge::initialize()
1067 {
1068 	//Reset text to default
1069 	custom_text = default_text;
1070 
1071 	custom_frame_offset = 0;
1072 
1073 	sexp_lock_color = false;
1074 }
1075 
canRender()1076 bool HudGauge::canRender()
1077 {
1078 	if (sexp_override) {
1079 		return false;
1080 	}
1081 
1082 	if(hud_disabled_except_messages() && !message_gauge) {
1083 		return false;
1084 	}
1085 
1086 	if (hud_disabled() && !hud_disabled_except_messages()) {
1087 		return false;
1088 	}
1089 
1090 	if(!active)
1091 		return false;
1092 
1093 	if ( !(Game_detail_flags & DETAIL_FLAG_HUD) ) {
1094 		return false;
1095 	}
1096 
1097 	if ((Viewer_mode & disabled_views)) {
1098 		return false;
1099 	}
1100 
1101 	if ((Viewer_mode & (VM_CHASE)) == 0 && only_render_in_chase_view) {
1102 		return false;
1103 	}
1104 
1105 	if(pop_up) {
1106 		if(!popUpActive()) {
1107 			return false;
1108 		}
1109 	}
1110 
1111 	if (gauge_config == HUD_ETS_GAUGE) {
1112 		if (Ships[Player_obj->instance].flags[Ship::Ship_Flags::No_ets]) {
1113 			return false;
1114 		}
1115 	}
1116 
1117 	return true;
1118 }
1119 
initCockpitTarget(const char * display_name,int _target_x,int _target_y,int _target_w,int _target_h,int _canvas_w,int _canvas_h)1120 void HudGauge::initCockpitTarget(const char* display_name, int _target_x, int _target_y, int _target_w, int _target_h, int _canvas_w, int _canvas_h)
1121 {
1122 	if ( strlen(display_name) <= 0 ) {
1123 		return;
1124 	}
1125 
1126 	target_x = _target_x;
1127 	target_y = _target_y;
1128 
1129 	strcpy_s(texture_target_fname, display_name);
1130 	target_w = _target_w;
1131 	target_h = _target_h;
1132 
1133 	if ( _canvas_w > 0 && _canvas_h > 0 ) {
1134 		canvas_w = _canvas_w;
1135 		canvas_h = _canvas_h;
1136 	} else {
1137 		canvas_w = _target_w;
1138 		canvas_h = _target_h;
1139 	}
1140 }
1141 
setupRenderCanvas(int render_target)1142 bool HudGauge::setupRenderCanvas(int render_target)
1143 {
1144 	if (texture_target_fname[0] != '\0') {
1145 		if ( render_target >= 0 && render_target == texture_target ) {
1146 			return true;
1147 		}
1148 	} else {
1149 		if ( render_target < 0 ) {
1150 			return true;
1151 		}
1152 	}
1153 
1154 	return false;
1155 }
1156 
setCockpitTarget(const cockpit_display * display)1157 void HudGauge::setCockpitTarget(const cockpit_display *display)
1158 {
1159 	if ( display == NULL ) {
1160 		return;
1161 	}
1162 
1163 	if ( strcmp(texture_target_fname, display->name) != 0 ) {
1164 		return;
1165 	}
1166 
1167 	if ( display->target < 0 ) {
1168 		return;
1169 	}
1170 
1171 	texture_target = display->target;
1172 	display_offset_x = display->offset[0] + target_x;
1173 	display_offset_y = display->offset[1] + target_y;
1174 }
1175 
resetCockpitTarget()1176 void HudGauge::resetCockpitTarget()
1177 {
1178 	texture_target = -1;
1179 }
1180 
1181 // ----------------------------------------------------------------------
1182 /**
1183  * @brief Called each level to initialize HUD systems
1184  */
HUD_init()1185 void HUD_init()
1186 {
1187 	HUD_init_colors();
1188 	hud_init_msg_window();
1189 	hud_init_targeting();
1190 	hud_init_reticle();
1191 	hud_shield_level_init();
1192 	hud_targetbox_init_flash();
1193 	hud_escort_init();
1194 	hud_damage_popup_init();
1195 	hud_support_view_init();
1196 	hud_init_squadmsg();		// initialize the vars needed for squadmate messaging
1197 	hud_objective_message_init();
1198 	hud_init_wingman_status_gauge();
1199 	hud_init_target_static();
1200 	hud_init_text_flash_gauge();
1201 	hud_stop_subspace_notify();
1202 	hud_stop_objective_notify();
1203 	hud_target_last_transmit_level_init();
1204 
1205 	throttle_sound_check_id = timestamp(THROTTLE_SOUND_CHECK_INTERVAL);
1206 	HUD_abort_subspace_timer = 1;
1207 	Hud_last_can_target = 1;
1208 	Hud_can_target_timer = 1;
1209 	last_percent_throttle = 0.0f;
1210 
1211 	// default to high contrast in the nebula
1212 	HUD_contrast = 0;
1213 	HUD_draw     = 1;
1214 	HUD_disable_except_messages = 0;
1215 
1216 	if(The_mission.flags[Mission::Mission_Flags::Fullneb]){
1217 		HUD_contrast = 1;
1218 	}
1219 
1220 	// reset to infinite
1221 	Hud_max_targeting_range = 0;
1222 
1223 	size_t j, num_gauges;
1224 
1225 	// go through all HUD gauges and call their initialization functions
1226 	for (auto it = Ship_info.cbegin(); it != Ship_info.cend(); ++it) {
1227 		if(it->hud_enabled) {
1228 			num_gauges = it->hud_gauges.size();
1229 
1230 			for(j = 0; j < num_gauges; j++) {
1231 				it->hud_gauges[j]->initialize();
1232 				it->hud_gauges[j]->resetTimers();
1233 				it->hud_gauges[j]->updateSexpOverride(false);
1234 			}
1235 		}
1236 	}
1237 
1238 	num_gauges = default_hud_gauges.size();
1239 
1240 	for(j = 0; j < num_gauges; j++) {
1241 		default_hud_gauges[j]->initialize();
1242 		default_hud_gauges[j]->resetTimers();
1243 		default_hud_gauges[j]->updateSexpOverride(false);
1244 	}
1245 
1246 	Script_system.OnStateDestroy.add(hud_scripting_close);
1247 }
1248 
hud_scripting_close(lua_State *)1249 void hud_scripting_close(lua_State*) {
1250 	// Clean up Lua references so that we don't have dangling references on to the lua state in the HUD gauges
1251 	for (auto it = Ship_info.begin(); it != Ship_info.end(); ++it) {
1252 		for (const auto& gauge : it->hud_gauges) {
1253 			if (gauge->getObjectType() == HUD_OBJECT_SCRIPTING) {
1254 				static_cast<HudGaugeScripting*>(gauge.get())->setRenderFunction(luacpp::LuaFunction());
1255 			}
1256 		}
1257 	}
1258 
1259 	for (const auto& gauge : default_hud_gauges) {
1260 		if (gauge->getObjectType() == HUD_OBJECT_SCRIPTING) {
1261 			static_cast<HudGaugeScripting*>(gauge.get())->setRenderFunction(luacpp::LuaFunction());
1262 		}
1263 	}
1264 }
1265 
1266 /**
1267  * @brief Do post mission cleanup of HUD
1268  */
hud_level_close()1269 void hud_level_close()
1270 {
1271 	size_t j, num_gauges;
1272 
1273 	for ( auto it = Ship_info.cbegin(); it != Ship_info.cend(); ++it ) {
1274 		if(it->hud_enabled) {
1275 			num_gauges = it->hud_gauges.size();
1276 
1277 			for(j = 0; j < num_gauges; j++) {
1278 				it->hud_gauges[j]->resetCockpitTarget();
1279 			}
1280 		}
1281 	}
1282 }
1283 
1284 /**
1285  * @brief Delete all HUD gauge objects, for all ships
1286  */
hud_close()1287 void hud_close()
1288 {
1289 	for (auto it = Ship_info.begin(); it != Ship_info.end(); ++it) {
1290 		it->hud_gauges.clear();
1291 	}
1292 
1293 	default_hud_gauges.clear();
1294 }
1295 
hud_toggle_draw()1296 void hud_toggle_draw()
1297 {
1298 	HUD_draw = !HUD_draw;
1299 }
1300 
1301 // Goober5000
hud_set_draw(int draw)1302 void hud_set_draw(int draw)
1303 {
1304 	HUD_draw = draw;
1305 }
1306 
1307 //WMC
hud_get_draw()1308 int hud_get_draw()
1309 {
1310 	return HUD_draw;
1311 }
1312 
1313 // Goober5000
hud_disable_except_messages(int disable)1314 void hud_disable_except_messages(int disable)
1315 {
1316 	HUD_disable_except_messages = disable;
1317 }
1318 
1319 // Goober5000
1320 /**
1321  * @brief Like ::hud_disabled(), except messages are still drawn
1322  */
hud_disabled_except_messages()1323 int hud_disabled_except_messages()
1324 {
1325 	return HUD_disable_except_messages;
1326 }
1327 
1328 /**
1329  * @brief Checks if HUD disabled
1330  * @return !0 if HUD is disabled (ie no gauges are shown/usable), otherwise return 0
1331  */
hud_disabled()1332 int hud_disabled()
1333 {
1334 	return !HUD_draw;
1335 }
1336 
1337 /**
1338  * @brief Determine if we should popup the weapons gauge on the HUD.
1339  */
hud_maybe_popup_weapons_gauge()1340 void hud_maybe_popup_weapons_gauge()
1341 {
1342 	if ( hud_gauge_is_popup(HUD_WEAPONS_GAUGE) ) {
1343 		ship_weapon *swp = &Player_ship->weapons;
1344 		int			i;
1345 
1346 		for ( i = 0; i < swp->num_secondary_banks; i++ ) {
1347 			if ( swp->secondary_bank_ammo[i] > 0 ) {
1348 				int ms_till_fire = timestamp_until(swp->next_secondary_fire_stamp[i]);
1349 				if ( ms_till_fire >= 1000 ) {
1350 					hud_gauge_popup_start(HUD_WEAPONS_GAUGE, 2500);
1351 				}
1352 			}
1353 		}
1354 	}
1355 }
1356 
1357 /**
1358  * @brief Updates HUD systems each frame
1359  *
1360  * @details This function updates those parts of the HUD that are not dependant on the rendering of the HUD.
1361  */
hud_update_frame(float)1362 void hud_update_frame(float  /*frametime*/)
1363 {
1364 	object	*targetp;
1365 	vec3d target_pos;
1366 	int		can_target;
1367 
1368 	update_throttle_sound();
1369 	hud_check_reticle_list();
1370 	hud_wingman_status_update();
1371 
1372 	// Check hotkey selections to see if any ships need to be removed
1373 	hud_prune_hotkeys();
1374 
1375 	// Remove dead/departed ships from the escort list
1376 	hud_escort_cull_list();
1377 	hud_escort_update_list();
1378 
1379 	hud_update_reticle( Player );
1380 
1381 	hud_shield_hit_update();
1382 	hud_update_weapon_flash();
1383 	hud_maybe_popup_weapons_gauge();
1384 	hud_update_objective_message();
1385 	hud_support_view_update();
1386 	message_training_update_frame();
1387 
1388 	// if emp is active we have to allow targeting by the "random emp" system
1389 	// we will intercept player targeting requests in hud_sensors_ok() when checking key commands
1390 	// DB - 8/24/98
1391 	can_target = hud_sensors_ok(Player_ship, 0);
1392 	if(emp_active_local()){
1393 		can_target = 1;
1394 	}
1395 	if ( !can_target && Hud_last_can_target ) {
1396 		Hud_can_target_timer = timestamp(1200);
1397 	}
1398 	Hud_last_can_target = can_target;
1399 
1400 	if ( timestamp_elapsed(Hud_can_target_timer) ) {
1401 		if ( (Player_ai->target_objnum != -1) && !can_target ){
1402 			Player_ai->target_objnum = -1;
1403 		}
1404 	}
1405 
1406 	// if there is no target, check if auto-targeting is enabled, and select new target
1407 	int retarget = 0;
1408 	int retarget_turret = 0;
1409 
1410 	if (Player_ai->target_objnum == -1){
1411 		retarget = 1;
1412 	} else if (Objects[Player_ai->target_objnum].type == OBJ_SHIP) {
1413 		if (Ships[Objects[Player_ai->target_objnum].instance].flags[Ship::Ship_Flags::Dying]){
1414 			if (timestamp_elapsed(Ships[Objects[Player_ai->target_objnum].instance].final_death_time)) {
1415 				retarget = 1;
1416 			}
1417 		}
1418 	}
1419 
1420 	// check if big ship and currently selected subsys is turret and turret is dead
1421 	// only do this is not retargeting
1422 	if ((!retarget) && (Player_ai->target_objnum != -1)) {
1423 		if (Objects[Player_ai->target_objnum].type == OBJ_SHIP) {
1424 			if ( !(Ships[Objects[Player_ai->target_objnum].instance].flags[Ship::Ship_Flags::Dying]) ) {
1425 				if ( Ship_info[Ships[Objects[Player_ai->target_objnum].instance].ship_info_index].is_big_or_huge() ) {
1426 					ship_subsys *ss = Player_ai->targeted_subsys;
1427 					if (ss != NULL) {
1428 						if ((ss->system_info->type == SUBSYSTEM_TURRET) && (ss->current_hits == 0)) {
1429 							retarget_turret = 1;
1430 						}
1431 					}
1432 				}
1433 			}
1434 		}
1435 	}
1436 
1437 	if ( retarget && can_target ) {
1438 		Player_ai->current_target_is_locked = 0;
1439 		if ( Players[Player_num].flags & PLAYER_FLAGS_AUTO_TARGETING ) {
1440 			Player_ai->target_objnum = -1;
1441 			hud_target_auto_target_next();
1442 		}
1443 	}
1444 
1445 	if (retarget_turret && can_target) {
1446 		Assert(!retarget);
1447 		void hud_update_closest_turret();
1448 		hud_update_closest_turret();
1449 	}
1450 
1451 	hud_target_change_check();
1452 
1453 	// check to see if we are in messaging mode.  If so, send the key to the code
1454 	// to deal with the message.  hud_sqaudmsg_do_frame will return 0 if the key
1455 	// wasn't used in messaging mode, otherwise 1.  In the event the key was used,
1456 	// return immediately out of this function.
1457 	if ( Players[Player_num].flags & PLAYER_FLAGS_MSG_MODE ) {
1458 		hud_squadmsg_do_frame();
1459 	}
1460 
1461 	if (Player_ai->target_objnum == -1) {
1462 		if (Target_static_looping.isValid()) {
1463 			snd_stop(Target_static_looping);
1464 		}
1465 		return;
1466 	}
1467 
1468 	targetp = &Objects[Player_ai->target_objnum];
1469 
1470 	if ( Player_ai->targeted_subsys != NULL ) {
1471 		get_subsystem_world_pos(targetp, Player_ai->targeted_subsys, &target_pos);
1472 
1473 		// get new distance of current target
1474 		Player_ai->current_target_distance = vm_vec_dist_quick(&target_pos,&Player_obj->pos);
1475 
1476 		float shield_strength = Player_ai->targeted_subsys->current_hits/Player_ai->targeted_subsys->max_hits * 100.0f;
1477 		int screen_integrity = (int)std::lround(shield_strength);
1478 
1479 		if ( screen_integrity < 0 ) {
1480 			screen_integrity = 0;
1481 		}
1482 
1483 		if ( screen_integrity == 0 ) {
1484 			if ( shield_strength > 0 ) {
1485 				screen_integrity = 1;
1486 			}
1487 		}
1488 
1489 		// Goober5000 - don't flash if this subsystem can't be destroyed
1490 		if ( ship_subsys_takes_damage(Player_ai->targeted_subsys) )
1491 		{
1492 			if ( screen_integrity <= 0 ){
1493 				hud_targetbox_start_flash(TBOX_FLASH_SUBSYS);	// need to flash 0% continuously
1494 			}
1495 		}
1496 	} else {
1497 		target_pos = targetp->pos;
1498 
1499 		Player_ai->current_target_distance = hud_find_target_distance(targetp,Player_obj);
1500 	}
1501 
1502 	int stop_targetting_this_thing = 0;
1503 
1504 	// check to see if the target is still alive
1505 	if ( targetp->flags[Object::Object_Flags::Should_be_dead] ) {
1506 		stop_targetting_this_thing = 1;
1507 	}
1508 
1509 	Player->target_is_dying = FALSE;
1510 	ship	*target_shipp = NULL;
1511 
1512 	if ( targetp->type == OBJ_SHIP ) {
1513 		Assert ( targetp->instance >=0 && targetp->instance < MAX_SHIPS );
1514 		target_shipp = &Ships[targetp->instance];
1515 		Player->target_is_dying = target_shipp->flags[Ship::Ship_Flags::Dying];
1516 
1517 		// If it is warping out (or exploded), turn off targeting
1518 		if ( target_shipp->flags[Ship::Ship_Flags::Depart_warp] || target_shipp->flags[Ship::Ship_Flags::Exploded] ) {
1519 			stop_targetting_this_thing = 1;
1520 		}
1521 	}
1522 
1523 	// Check if can still be seen in Nebula
1524 	if ( hud_target_invalid_awacs(targetp) ) {
1525 		stop_targetting_this_thing = 1;
1526 	}
1527 
1528 	// If this was found to be something we shouldn't
1529 	// target anymore, just remove it
1530 	if ( stop_targetting_this_thing )	{
1531 		Player_ai->target_objnum = -1;
1532 		Player_ai->targeted_subsys = NULL;
1533 		hud_stop_looped_locking_sounds();
1534 	}
1535 
1536 	if (Player->target_is_dying) {
1537 		hud_stop_looped_locking_sounds();
1538 		if ( Players[Player_num].flags & PLAYER_FLAGS_AUTO_TARGETING ) {
1539 			hud_target_auto_target_next();
1540 		}
1541 	}
1542 
1543 	// Switch to battle track when a targeted ship is hostile (it attacks you) and within BATTLE_START_MIN_TARGET_DIST
1544 	if (targetp->type == OBJ_SHIP && Event_Music_battle_started == 0 ) {
1545 		Assert( target_shipp != NULL );
1546 
1547 		// Goober5000
1548 		if (iff_x_attacks_y(target_shipp->team, Player_ship->team))
1549 		{
1550 			float	dist_to_target;
1551 
1552 			dist_to_target = vm_vec_dist_quick(&targetp->pos, &Player_obj->pos);
1553 			if (dist_to_target < BATTLE_START_MIN_TARGET_DIST) {
1554 
1555 				// If the target has an AI class of none, it is a Cargo, NavBuoy or other non-aggressive
1556 				// ship, so don't start the battle music
1557 				if (stricmp(Ai_class_names[Ai_info[target_shipp->ai_index].ai_class], NOX("none")) != 0)
1558 					event_music_battle_start();
1559 			}
1560 		}
1561 	}
1562 
1563 	// Make sure that the player isn't targeting a 3rd stage local ssm
1564 	if (Player_ai->target_objnum >= 0 && Objects[Player_ai->target_objnum].type == OBJ_WEAPON)
1565 	{
1566 		if (Weapons[Objects[Player_ai->target_objnum].instance].lssm_stage==3)
1567 		{
1568 			set_target_objnum( Player_ai, -1 );
1569 			hud_lock_reset();
1570 		}
1571 	}
1572 
1573 	// Since we need to reference the player's target integrity in several places this upcoming
1574 	// frame, only calculate once here
1575 	if ( target_shipp ) {
1576 		Pl_target_integrity = get_hull_pct(targetp);
1577 	}
1578 
1579 	// Update cargo scanning
1580 	hud_update_cargo_scan_sound();
1581 
1582 	if ( Viewer_mode & ( VM_EXTERNAL | VM_WARP_CHASE ) ) {
1583 		Player->cargo_inspect_time = 0;
1584 		player_stop_cargo_scan_sound();
1585 	}
1586 
1587 	hud_update_target_static();
1588 	if ( (targetp->instance >=0) && (targetp->instance < MAX_SHIPS) ) {
1589 		hud_update_ship_status(targetp);
1590 	}
1591 }
1592 
1593 /**
1594  * @brief Draw white brackets around asteroids which has the AF_DRAW_BRACKETS flag set
1595  */
hud_show_asteroid_brackets()1596 void hud_show_asteroid_brackets()
1597 {
1598 	if ( hud_sensors_ok(Player_ship, 0) ) {
1599 		asteroid_show_brackets();
1600 	}
1601 }
1602 
1603 /**
1604  * @brief Render gauges that need to be between a ::g3_start_frame() and a ::g3_end_frame()
1605  */
hud_render_preprocess(float frametime)1606 void hud_render_preprocess(float frametime)
1607 {
1608 	Player->subsys_in_view = -1;
1609 	hud_target_clear_display_list();
1610 
1611 	if ( (Game_detail_flags & DETAIL_FLAG_HUD) && (supernova_active() >= 3) ) {
1612 		return;
1613 	}
1614 
1615 	if ( hud_disabled() ) {
1616 		// if the hud is disabled, we still need to make sure that the indicators are properly handled
1617 		hud_do_lock_indicators(flFrametime);
1618 		return;
1619 	}
1620 
1621 	if ( Viewer_mode & ( VM_DEAD_VIEW ) ) {
1622 		return;
1623 	}
1624 
1625 	if ( Viewer_mode & (VM_EXTERNAL | VM_WARP_CHASE | VM_PADLOCK_ANY ) ) {
1626 		// If the player is warping out, don't draw the targeting gauges
1627 		Assert(Player != NULL);
1628 		if ( Player->control_mode != PCM_NORMAL ) {
1629 			return;
1630 		}
1631 	}
1632 
1633 	// process targeting data around any message sender
1634 	hud_show_message_sender();
1635 
1636 	// if messages are disabled then skip everything else
1637 	if ( hud_disabled_except_messages() ) {
1638 		return;
1639 	}
1640 
1641 	// process navigation stuff
1642 	hud_draw_navigation();
1643 
1644 	// process current selection set, if any
1645 	hud_show_selection_set();
1646 
1647 	// process asteroid brackets if necessary
1648 	hud_show_asteroid_brackets();
1649 
1650 	// process targeting data around the current target
1651 	hud_show_targeting_gauges(frametime);
1652 
1653 	// process brackets and distance to remote detonate missile
1654 	hud_process_remote_detonate_missile();
1655 
1656 	// process any incoming missiles
1657 	hud_process_homing_missiles();
1658 }
1659 
HudGaugeMissionTime()1660 HudGaugeMissionTime::HudGaugeMissionTime():
1661 HudGauge(HUD_OBJECT_MISSION_TIME, HUD_MISSION_TIME, false, false, (VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY | VM_OTHER_SHIP), 255, 255, 255)
1662 {
1663 }
1664 
initTextOffsets(int x,int y)1665 void HudGaugeMissionTime::initTextOffsets(int x, int y)
1666 {
1667 	time_text_offsets[0] = x;
1668 	time_text_offsets[1] = y;
1669 }
1670 
initValueOffsets(int x,int y)1671 void HudGaugeMissionTime::initValueOffsets(int x, int y)
1672 {
1673 	time_val_offsets[0] = x;
1674 	time_val_offsets[1] = y;
1675 }
1676 
initBitmaps(const char * fname)1677 void HudGaugeMissionTime::initBitmaps(const char *fname)
1678 {
1679 	time_gauge.first_frame = bm_load_animation(fname, &time_gauge.num_frames);
1680 
1681 	if ( time_gauge.first_frame == -1 ) {
1682 		Warning(LOCATION, "Could not load in ani: %s\n", fname);
1683 	}
1684 }
1685 
pageIn()1686 void HudGaugeMissionTime::pageIn()
1687 {
1688 	bm_page_in_aabitmap(time_gauge.first_frame, time_gauge.num_frames );
1689 }
1690 
render(float)1691 void HudGaugeMissionTime::render(float  /*frametime*/)
1692 {
1693 	float mission_time, time_comp;
1694 	int minutes=0;
1695 	int seconds=0;
1696 
1697 	mission_time = f2fl(Missiontime);  // convert to seconds
1698 
1699 	minutes=(int)(mission_time/60);
1700 	seconds=(int)mission_time%60;
1701 
1702 	setGaugeColor();
1703 
1704 	// blit background frame
1705 	if ( time_gauge.first_frame >= 0 ) {
1706 		renderBitmap(time_gauge.first_frame, position[0], position[1]);
1707 	}
1708 
1709 	// print out mission time in MM:SS format
1710 	renderPrintf(position[0] + time_text_offsets[0], position[1] + time_text_offsets[1], NOX("%02d:%02d"), minutes, seconds);
1711 
1712 	// display time compression as xN
1713 	time_comp = f2fl(Game_time_compression);
1714 	if ( time_comp < 1 ) {
1715 		renderPrintf(position[0] + time_val_offsets[0], position[1] + time_val_offsets[1], /*XSTR( "x%.1f", 215), time_comp)*/ NOX("%.2f"), time_comp);
1716 	} else {
1717 		renderPrintf(position[0] + time_val_offsets[0], position[1] + time_val_offsets[1], XSTR( "x%.0f", 216), time_comp);
1718 	}
1719 }
1720 
1721 /**
1722  * @brief Show supernova warning it there's a supernova coming
1723  */
hud_maybe_display_supernova()1724 void hud_maybe_display_supernova()
1725 {
1726 	float time_left;
1727 
1728 	time_left = supernova_time_left();
1729 	if(time_left < 0.0f){
1730 		return;
1731 	}
1732 
1733 	gr_set_color_fast(&Color_bright_red);
1734 	if (Lcl_pl) {
1735 		gr_printf(Supernova_coords[gr_screen.res][0], Supernova_coords[gr_screen.res][1], "Wybuch supernowej: %.2f s", time_left);
1736 	} else {
1737 		gr_printf(Supernova_coords[gr_screen.res][0], Supernova_coords[gr_screen.res][1], "Supernova Warning: %.2f s", time_left);
1738 	}
1739 }
1740 
1741 /**
1742  * @brief Undertakes main HUD render.
1743  */
hud_render_all()1744 void hud_render_all()
1745 {
1746 	int i;
1747 
1748 	hud_render_gauges();
1749 
1750 	// start rendering cockpit dependent gauges if possible
1751 	for ( i = 0; i < (int)Player_displays.size(); ++i ) {
1752 		hud_render_gauges(i);
1753 	}
1754 
1755 	hud_clear_msg_buffer();
1756 
1757 	// set font back the way it was
1758 	font::set_font(font::FONT1);
1759 }
1760 
hud_render_gauges(int cockpit_display_num)1761 void hud_render_gauges(int cockpit_display_num)
1762 {
1763 	size_t j, num_gauges;
1764 	ship_info* sip = &Ship_info[Player_ship->ship_info_index];
1765 	int render_target = -1;
1766 
1767 	if ( cockpit_display_num >= 0 ) {
1768 		if ( sip->cockpit_model_num < 0 ) {
1769 			return;
1770 		}
1771 
1772 		if ( !sip->hud_enabled ) {
1773 			return;
1774 		}
1775 
1776 		render_target = ship_start_render_cockpit_display(cockpit_display_num);
1777 
1778 		if ( render_target <= 0 ) {
1779 			return;
1780 		}
1781 	} else {
1782 		if( supernova_active() >= 3 ) {
1783 			return;
1784 		}
1785 	}
1786 
1787 	// Check if this ship has its own HUD gauges.
1788 	if ( sip->hud_enabled ) {
1789 		num_gauges = sip->hud_gauges.size();
1790 
1791 		for(j = 0; j < num_gauges; j++) {
1792 			GR_DEBUG_SCOPE("Render HUD gauge");
1793 
1794 			// only preprocess gauges if we're not rendering to cockpit
1795 			if ( cockpit_display_num < 0 ) {
1796 				sip->hud_gauges[j]->preprocess();
1797 			}
1798 
1799 			sip->hud_gauges[j]->onFrame(flFrametime);
1800 
1801 			if ( !sip->hud_gauges[j]->setupRenderCanvas(render_target) ) {
1802 				continue;
1803 			}
1804 
1805 			if ( !sip->hud_gauges[j]->canRender() ) {
1806 				continue;
1807 			}
1808 
1809 			TRACE_SCOPE(tracing::RenderHUDGauge);
1810 
1811 			sip->hud_gauges[j]->resetClip();
1812 			sip->hud_gauges[j]->setFont();
1813 			sip->hud_gauges[j]->render(flFrametime);
1814 		}
1815 	} else {
1816 		num_gauges = default_hud_gauges.size();
1817 
1818 		for(j = 0; j < num_gauges; j++) {
1819 			GR_DEBUG_SCOPE("Render HUD gauge");
1820 
1821 			default_hud_gauges[j]->preprocess();
1822 
1823 			default_hud_gauges[j]->onFrame(flFrametime);
1824 
1825 			if ( !default_hud_gauges[j]->canRender() ) {
1826 				continue;
1827 			}
1828 
1829 			TRACE_SCOPE(tracing::RenderHUDGauge);
1830 
1831 			default_hud_gauges[j]->resetClip();
1832 			default_hud_gauges[j]->setFont();
1833 			default_hud_gauges[j]->render(flFrametime);
1834 		}
1835 	}
1836 
1837 	if ( cockpit_display_num >= 0 ) {
1838 		ship_end_render_cockpit_display(cockpit_display_num);
1839 
1840 		if ( gr_screen.rendering_to_texture != -1 ) {
1841 			// are we still are rendering to a texture at this point? uh oh.
1842 			bm_set_render_target(-1);
1843 		}
1844 	}
1845 }
1846 
1847 /**
1848  * @brief Called when the game decides to stop all looping sounds
1849  *
1850  * @details This function will set the loop id's for the engine noises to -1, this will force any looping engine sounds to stop.
1851  */
hud_stop_looped_engine_sounds()1852 void hud_stop_looped_engine_sounds()
1853 {
1854 	if (Player_engine_snd_loop.isValid()) {
1855 		snd_stop(Player_engine_snd_loop);
1856 		Player_engine_snd_loop = sound_handle::invalid();
1857 	}
1858 }
1859 
1860 #define ZERO_PERCENT			0.01f
1861 #define ENGINE_MAX_VOL		1.0f
1862 #define ENGINE_MAX_PITCH	44100
1863 
1864 /**
1865  * @brief If the throttle has changed, modify the sound
1866  *
1867  * @details Determine what engine sound to play, based upon the percentage throttle set.
1868  * If we're a multiplayer observer, stop any engine sounds from playing and return.
1869  */
update_throttle_sound()1870 void update_throttle_sound()
1871 {
1872 	float percent_throttle;
1873 
1874 	if((Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_OBSERVER)){
1875 		if (Player_engine_snd_loop.isValid()) {
1876 			snd_stop(Player_engine_snd_loop);
1877 			Player_engine_snd_loop = sound_handle::invalid();
1878 		}
1879 
1880 		return;
1881 	}
1882 
1883 	if ( timestamp_elapsed(throttle_sound_check_id) ) {
1884 
1885 		auto enginesnd_id = ship_get_sound(Player_obj, GameSounds::ENGINE);
1886 		if (!enginesnd_id.isValid())
1887 		{
1888 			throttle_sound_check_id = timestamp(-1);	// never time out
1889 			return;
1890 		}
1891 
1892 		throttle_sound_check_id = timestamp(THROTTLE_SOUND_CHECK_INTERVAL);
1893 
1894 		if ( object_get_gliding(Player_obj) ) {	// Backslash
1895 			percent_throttle = Player_obj->phys_info.forward_thrust;
1896 		} else if ( Ships[Player_obj->instance].current_max_speed == 0 ) {
1897 			percent_throttle = Player_obj->phys_info.fspeed / Ship_info[Ships[Player_obj->instance].ship_info_index].max_speed;
1898 		} else {
1899 			percent_throttle = Player_obj->phys_info.fspeed / Ships[Player_obj->instance].current_max_speed;
1900 		}
1901 
1902 		if (percent_throttle != last_percent_throttle || !Player_engine_snd_loop.isValid()) {
1903 
1904 			if ( percent_throttle < ZERO_PERCENT ) {
1905 				if (Player_engine_snd_loop.isValid()) {
1906 					snd_stop(Player_engine_snd_loop); // Backslash - otherwise, long engine loops keep playing
1907 					Player_engine_snd_loop = sound_handle::invalid();
1908 				}
1909 			}
1910 			else {
1911 				if (!Player_engine_snd_loop.isValid()) {
1912 					Player_engine_snd_loop = snd_play_looping( gamesnd_get_game_sound(enginesnd_id), 0.0f , -1, -1, percent_throttle * ENGINE_MAX_VOL, FALSE);
1913 				} else {
1914 					// The sound may have been trashed at the low-level if sound channel overflow.
1915 					// TODO: implement system where certain sounds cannot be interrupted (priority?)
1916 					if ( snd_is_playing(Player_engine_snd_loop) ) {
1917 						snd_set_volume(Player_engine_snd_loop, percent_throttle * ENGINE_MAX_VOL);
1918 					}
1919 					else {
1920 						Player_engine_snd_loop = sound_handle::invalid();
1921 					}
1922 				}
1923 			}
1924 		}	// end if (percent_throttle != last_percent_throttle)
1925 
1926 		last_percent_throttle = percent_throttle;
1927 
1928 	}	// end if ( timestamp_elapsed(throttle_sound_check_id) )
1929 }
1930 
1931 /**
1932  * @brief Called at the beginning of each level. Loads frame data in once, and initializes any damage
1933  * gauge specific data.
1934  */
hud_damage_popup_init()1935 void hud_damage_popup_init()
1936 {
1937 	int i;
1938 
1939 	Damage_flash_bright = 0;
1940 	Damage_flash_timer =	1;
1941 
1942 	for ( i = 0; i < SUBSYSTEM_MAX; i++ ) {
1943 		Pl_hud_subsys_info[i].last_str = 1000.0f;
1944 		Pl_hud_subsys_info[i].flash_duration_timestamp = 1;
1945 		Pl_hud_next_flash_timestamp = 1;
1946 		Pl_hud_is_bright = 0;
1947 	}
1948 }
1949 
HudGaugeDamage()1950 HudGaugeDamage::HudGaugeDamage():
1951 HudGauge(HUD_OBJECT_DAMAGE, HUD_DAMAGE_GAUGE, false, false, (VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY | VM_OTHER_SHIP), 255, 255, 255)
1952 {
1953 }
1954 
initHeaderOffsets(int x,int y)1955 void HudGaugeDamage::initHeaderOffsets(int x, int y)
1956 {
1957 	header_offsets[0] = x;
1958 	header_offsets[1] = y;
1959 }
1960 
initHullIntegOffsets(int x,int y)1961 void HudGaugeDamage::initHullIntegOffsets(int x, int y)
1962 {
1963 	hull_integ_offsets[0] = x;
1964 	hull_integ_offsets[1] = y;
1965 }
1966 
initHullIntegValueOffsetX(int x)1967 void HudGaugeDamage::initHullIntegValueOffsetX(int x)
1968 {
1969 	hull_integ_val_offset_x = x;
1970 }
1971 
initMiddleFrameStartOffsetY(int y)1972 void HudGaugeDamage::initMiddleFrameStartOffsetY(int y)
1973 {
1974 	middle_frame_start_offset_y = y;
1975 }
1976 
initSubsysIntegStartOffsets(int x,int y)1977 void HudGaugeDamage::initSubsysIntegStartOffsets(int x, int y)
1978 {
1979 	subsys_integ_start_offsets[0] = x;
1980 	subsys_integ_start_offsets[1] = y;
1981 }
1982 
initSubsysIntegValueOffsetX(int x)1983 void HudGaugeDamage::initSubsysIntegValueOffsetX(int x)
1984 {
1985 	subsys_integ_val_offset_x = x;
1986 }
1987 
initBottomBgOffset(int offset)1988 void HudGaugeDamage::initBottomBgOffset(int offset)
1989 {
1990 	bottom_bg_offset = offset;
1991 }
1992 
initLineHeight(int h)1993 void HudGaugeDamage::initLineHeight(int h)
1994 {
1995 	line_h = h;
1996 }
1997 
initBitmaps(const char * fname_top,const char * fname_middle,const char * fname_bottom)1998 void HudGaugeDamage::initBitmaps(const char *fname_top, const char *fname_middle, const char *fname_bottom)
1999 {
2000 	damage_top.first_frame = bm_load_animation(fname_top, &damage_top.num_frames);
2001 	if ( damage_top.first_frame == -1 ) {
2002 		Warning(LOCATION, "Could not load in the ani: %s\n", fname_top);
2003 	}
2004 
2005 	damage_middle.first_frame = bm_load_animation(fname_middle, &damage_middle.num_frames);
2006 	if ( damage_middle.first_frame == -1 ) {
2007 		Warning(LOCATION, "Could not load in the ani: %s\n", fname_middle);
2008 	}
2009 
2010 	damage_bottom.first_frame = bm_load_animation(fname_bottom, &damage_bottom.num_frames);
2011 	if ( damage_bottom.first_frame == -1 ) {
2012 		Warning(LOCATION, "Could not load in the ani: %s\n", fname_bottom);
2013 	}
2014 }
2015 
initialize()2016 void HudGaugeDamage::initialize()
2017 {
2018 	Damage_flash_bright = false;
2019 	Damage_flash_timer = timestamp(1);
2020 	next_flash = timestamp(1);
2021 	flash_status = false;
2022 
2023 	HudGauge::initialize();
2024 }
2025 
pageIn()2026 void HudGaugeDamage::pageIn()
2027 {
2028 	bm_page_in_aabitmap(damage_top.first_frame, damage_top.num_frames);
2029 	bm_page_in_aabitmap(damage_middle.first_frame, damage_middle.num_frames);
2030 	bm_page_in_aabitmap(damage_bottom.first_frame, damage_bottom.num_frames);
2031 }
2032 
render(float)2033 void HudGaugeDamage::render(float  /*frametime*/)
2034 {
2035 	model_subsystem	*psub;
2036 	ship_subsys			*pss;
2037 	int					screen_integrity, num, best_str, best_index;
2038 	float					strength, shield, integrity;
2039 	hud_subsys_damage	hud_subsys_list[SUBSYSTEM_MAX];
2040 
2041 	if ( damage_top.first_frame == -1 ) {
2042 		return;
2043 	}
2044 
2045 	if ( (The_mission.game_type & MISSION_TYPE_TRAINING) && Training_message_visible ){
2046 		return;
2047 	}
2048 
2049 	if ( timestamp_elapsed(Damage_flash_timer) ) {
2050 		Damage_flash_timer = timestamp(DAMAGE_FLASH_TIME);
2051 		Damage_flash_bright = !Damage_flash_bright;
2052 	}
2053 
2054 	num = 0;
2055 	for ( pss = GET_FIRST(&Player_ship->subsys_list); pss !=END_OF_LIST(&Player_ship->subsys_list); pss = GET_NEXT(pss) ) {
2056 		psub = pss->system_info;
2057 		strength = ship_get_subsystem_strength(Player_ship, psub->type);
2058 		if ( strength < 1 ) {
2059 			screen_integrity = fl2i(strength*100);
2060 			if ( screen_integrity == 0 ) {
2061 				if ( strength > 0 ) {
2062 					screen_integrity = 1;
2063 				}
2064 			}
2065 
2066 			if (strlen(psub->alt_dmg_sub_name))
2067 				hud_subsys_list[num].name = psub->alt_dmg_sub_name;
2068 			else {
2069 				hud_subsys_list[num].name = ship_subsys_get_name(pss);
2070 			}
2071 
2072 			hud_subsys_list[num].str  = screen_integrity;
2073 			hud_subsys_list[num].type = psub->type;
2074 			num++;
2075 
2076 			if ( strength < Pl_hud_subsys_info[psub->type].last_str ) {
2077 				Pl_hud_subsys_info[psub->type].flash_duration_timestamp = timestamp(SUBSYS_DAMAGE_FLASH_DURATION);
2078 			}
2079 			Pl_hud_subsys_info[psub->type].last_str = strength;
2080 
2081 			// Don't display more than the max number of damaged subsystems.
2082 			if (num >= SUBSYSTEM_MAX)
2083 			{
2084 				break;
2085 			}
2086 		}
2087 	}
2088 
2089 	// Build a list of damage values to display and then actually display them in a second pass
2090 	// This allows to hide the gauge when there is no damage
2091 	SCP_vector<DamageInfo> info_lines;
2092 
2093 	auto sx = position[0] + subsys_integ_start_offsets[0];
2094 	auto sy = position[1] + subsys_integ_start_offsets[1];
2095 	auto bx = position[0];
2096 	auto by = position[1] + middle_frame_start_offset_y;
2097 
2098 	int type;
2099 	for ( int i = 0; i < num; i++ ) {
2100 		best_str = 1000;
2101 		best_index = -1;
2102 		for ( int j = 0; j < num-i; j++ ) {
2103 			if ( hud_subsys_list[j].str < best_str ) {
2104 				best_str = hud_subsys_list[j].str;
2105 				best_index = j;
2106 			}
2107 		}
2108 
2109 		Assert(best_index >= 0);
2110 		Assert(best_str >= 0);
2111 
2112 		DamageInfo info;
2113 
2114 		info.draw_background = true;
2115 		info.background_x = bx;
2116 		info.background_y = by;
2117 
2118 		type = hud_subsys_list[best_index].type;
2119 		if ( !timestamp_elapsed( Pl_hud_subsys_info[type].flash_duration_timestamp ) ) {
2120 			if ( timestamp_elapsed(next_flash) ) {
2121 				flash_status = !flash_status;
2122 				next_flash = timestamp(SUBSYS_DAMAGE_FLASH_INTERVAL);
2123 			}
2124 
2125 			if ( flash_status ) {
2126 				int alpha_color = MIN(HUD_COLOR_ALPHA_MAX,HUD_color_alpha+HUD_BRIGHT_DELTA);
2127 				info.bright_index = alpha_color;
2128 			}
2129 		}
2130 
2131 		// Draw the text
2132 		if ( best_str < 30 ) {
2133 			if ( best_str <= 0 ) {
2134 				if ( Damage_flash_bright ) {
2135 					info.color_override = &Color_bright_red;
2136 				} else {
2137 					info.color_override = &Color_red;
2138 				}
2139 			} else {
2140 				info.color_override = &Color_red;
2141 			}
2142 		}
2143 
2144 		const char *n_firstline;
2145 		n_firstline = strrchr(hud_subsys_list[best_index].name, '|');
2146 		if (n_firstline) {
2147 			// Print only the last line
2148 			n_firstline++;
2149 			info.name = n_firstline;
2150 		} else {
2151 			char temp_name[NAME_LENGTH];
2152 			strcpy_s(temp_name, hud_subsys_list[best_index].name);
2153 			hud_targetbox_truncate_subsys_name(temp_name);
2154 			info.name = temp_name;
2155 		}
2156 
2157 		char buf[128];
2158 		sprintf(buf, XSTR( "%d%%", 219), best_str);
2159 		hud_num_make_mono(buf, font_num);
2160 
2161 		int w, h;
2162 		gr_get_string_size(&w, &h, buf);
2163 
2164 		info.value_x = position[0] + subsys_integ_val_offset_x - w;
2165 		info.value_y = sy;
2166 		info.strength = best_str;
2167 
2168 		info.name_x = sx;
2169 		info.name_y = sy;
2170 
2171 		by += line_h;
2172 		sy += line_h;
2173 
2174 		info_lines.push_back(info);
2175 
2176 		// Remove it from hud_subsys_list
2177 		if ( best_index < (num-i-1) ) {
2178 			hud_subsys_list[best_index] = hud_subsys_list[num-i-1];
2179 		}
2180 	}
2181 
2182 	hud_get_target_strength(Player_obj, &shield, &integrity);
2183 	screen_integrity = fl2i(integrity*100);
2184 
2185 	// Show hull integrity if it's below 100% or if a subsystem is damaged
2186 	// The second case is just to make the display look complete
2187 	// The third case is there to make the gauge appear only if needed if the right option is set
2188 	if ( screen_integrity < 100 || !info_lines.empty() ) {
2189 		DamageInfo info;
2190 
2191 		info.name = XSTR( "Hull Integrity", 220);
2192 
2193 		if ( screen_integrity == 0 ) {
2194 			screen_integrity = 1;
2195 		}
2196 		info.strength = screen_integrity;
2197 
2198 		char buf[128];
2199 		sprintf(buf, XSTR( "%d%%", 219), screen_integrity);
2200 		hud_num_make_mono(buf, font_num);
2201 
2202 		int w, h;
2203 		gr_get_string_size(&w, &h, buf);
2204 
2205 		if ( screen_integrity < 30 ) {
2206 			info.color_override = &Color_red;
2207 		}
2208 
2209 		info.name_x = position[0] + hull_integ_offsets[0];
2210 		info.name_y = position[1] + hull_integ_offsets[1];
2211 
2212 		info.value_x = position[0] + hull_integ_val_offset_x - w;
2213 		info.value_y = position[1] + hull_integ_offsets[1];
2214 
2215 		// Insert at the top since hull is always first
2216 		info_lines.insert(info_lines.begin(), info);
2217 	}
2218 
2219 	if (info_lines.empty()) {
2220 		// Nothing to display, return before dawing anything
2221 		return;
2222 	}
2223 
2224 	setGaugeColor();
2225 
2226 	// Draw the top of the damage pop-up
2227 	renderBitmap(damage_top.first_frame, position[0], position[1]);
2228 	renderString(position[0] + header_offsets[0], position[1] + header_offsets[1], XSTR( "damage", 218));
2229 
2230 	// These variables keep track of where the background was drawn last so we can draw the bottom correctly
2231 	int last_bx = position[0];
2232 	int last_by = position[1] + middle_frame_start_offset_y;
2233 	for (auto& line : info_lines) {
2234 		if (line.draw_background) {
2235 			renderBitmap(damage_middle.first_frame, line.background_x, line.background_y);
2236 			last_bx = line.background_x;
2237 			last_by = line.background_y + line_h; // Add line_h here so that the footer is properly aligned
2238 		}
2239 
2240 		char buf[128];
2241 		sprintf(buf, XSTR( "%d%%", 219), line.strength);
2242 		hud_num_make_mono(buf, font_num);
2243 
2244 		if (line.color_override != nullptr) {
2245 			gr_set_color_fast(line.color_override);
2246 		} else {
2247 			setGaugeColor(line.bright_index);
2248 		}
2249 
2250 		renderString(line.name_x, line.name_y, line.name.c_str());
2251 		renderString(line.value_x, line.value_y, buf);
2252 
2253 		setGaugeColor();
2254 	}
2255 
2256 	setGaugeColor();
2257 	renderBitmap(damage_bottom.first_frame, last_bx, last_by + bottom_bg_offset);
2258 }
2259 
2260 /**
2261  * @brief Initialise the members of the ::hud_anim struct to default values
2262  */
hud_anim_init(hud_anim * ha,int sx,int sy,const char * filename)2263 void hud_anim_init(hud_anim *ha, int sx, int sy, const char *filename)
2264 {
2265 	ha->first_frame		= -1;
2266 	ha->num_frames		= 0;
2267 	ha->total_time		= 0.0f;
2268 	ha->time_elapsed	= 0.0f;
2269 	ha->sx				= sx;
2270 	ha->sy				= sy;
2271 	strcpy_s(ha->filename, filename);
2272 }
2273 
2274 /**
2275  * @brief Initialise the members of the ::hud_frames struct to default values
2276  */
hud_frames_init(hud_frames * hf)2277 void hud_frames_init(hud_frames *hf)
2278 {
2279 	hf->first_frame		= -1;
2280 	hf->num_frames		= 0;
2281 }
2282 
2283 /**
2284  * @brief Load a ::hud_anim
2285  * @return If successful return 0, otherwise return -1
2286  */
hud_anim_load(hud_anim * ha)2287 int hud_anim_load(hud_anim *ha)
2288 {
2289 	int		fps;
2290 
2291 	ha->first_frame = bm_load_animation(ha->filename, &ha->num_frames, &fps);
2292 
2293 	// Goober5000 - try to bypass the Volition bug
2294 	if ( (ha->first_frame == -1) && !stricmp(ha->filename, "FadeIconS-FreighterCW") )
2295 	{
2296 		ha->first_frame = bm_load_animation("FadeIconS-FreighterWC", &ha->num_frames, &fps);
2297 	}
2298 
2299 	if ( ha->first_frame == -1 )
2300 	{
2301 		Warning(LOCATION, "Couldn't load hud animation for file '%s'", ha->filename);
2302 		return -1;
2303 	}
2304 
2305 	Assert(fps != 0);
2306 	ha->total_time = i2fl(ha->num_frames)/fps;
2307 	return 0;
2308 }
2309 
2310 /**
2311  * @brief Render out a frame of a hud or briefing animation, based on how much time has elapsed
2312  * @note targetbox static was not implemented by :v:, also used for briefing icons & hud lock icons
2313  *
2314  * @param ha			Pointer to ::hud_anim info
2315  * @param frametime		Seconds elapsed since last frame
2316  * @param draw_alpha	Draw bitmap as alpha-bitmap (default 0)
2317  * @param loop			Anim should loop (default 1)
2318  * @param hold_last		Should last frame be held (default 0)
2319  * @param reverse		Play animation in reverse (default 0)
2320  * @param resize_mode		Resize for non-standard resolutions
2321  * @param mirror		Mirror along y-axis so icon points left instead of right
2322  *
2323  * @returns  1 on success, 0 on failure
2324  */
hud_anim_render(hud_anim * ha,float frametime,int draw_alpha,int loop,int hold_last,int reverse,int resize_mode,bool mirror)2325 int hud_anim_render(hud_anim *ha, float frametime, int draw_alpha, int loop, int hold_last, int reverse, int resize_mode, bool mirror)
2326 {
2327 	int framenum;
2328 
2329 	if ( ha->num_frames <= 0 ) {
2330 		if ( hud_anim_load(ha) == -1 )
2331 			return 0;
2332 	}
2333 
2334 	ha->time_elapsed += frametime;
2335 	if ( ha->time_elapsed > ha->total_time && loop == 0 && hold_last == 0) {
2336 		return 0;
2337 	}
2338 
2339 	// Note; total_time for hud_anim is derived only from the fps, no need to pass it in
2340 	framenum = bm_get_anim_frame(ha->first_frame, ha->time_elapsed, 0.0f, loop != 0);
2341 	if (reverse) {
2342 		framenum = (ha->num_frames-1) - framenum;
2343 	}
2344 
2345 	// Blit the bitmap for this frame
2346 	if(emp_should_blit_gauge()){
2347 		gr_set_bitmap(ha->first_frame + framenum);
2348 		if ( draw_alpha ){
2349 			gr_aabitmap(ha->sx, ha->sy, resize_mode, mirror);
2350 		} else {
2351 			gr_bitmap(ha->sx, ha->sy, resize_mode);
2352 		}
2353 	}
2354 
2355 	return 1;
2356 }
2357 
2358 /**
2359  * @brief Convert a number string to use mono-spaced 1 character
2360  */
hud_num_make_mono(char * num_str,int font_num)2361 void hud_num_make_mono(char *num_str, int font_num)
2362 {
2363 	font::FSFont* fsFont = font::get_font(font_num);
2364 
2365 	if (fsFont->getType() != font::VFNT_FONT)
2366 	{
2367 		// Only old volition fonts need this
2368 		return;
2369 	}
2370 
2371 	ubyte sc;
2372 
2373 	sc = lcl_get_font_index(font_num);
2374 	if (sc == 0) {
2375 		// specified font has no mono-spaced 1, make do with non-mono-spaced 1
2376 		return;
2377 	}
2378 
2379 	size_t len = strlen(num_str);
2380 	for (size_t i = 0; i < len; i++ ) {
2381 		if ( num_str[i] == '1' ) {
2382 			num_str[i] = (char)(sc + 1);
2383 		}
2384 	}
2385 }
2386 
2387 /**
2388  * @brief Flashing text gauge
2389  */
hud_init_text_flash_gauge()2390 void hud_init_text_flash_gauge()
2391 {
2392 	strcpy_s(Hud_text_flash, "");
2393 	Hud_text_flash_timer = timestamp(0);
2394 	Hud_text_flash_interval = 0;
2395 }
2396 
hud_start_text_flash(const char * txt,int t,int interval)2397 void hud_start_text_flash(const char *txt, int t, int interval)
2398 {
2399 	// bogus
2400 	if(txt == NULL){
2401 		strcpy_s(Hud_text_flash, "");
2402 		return;
2403 	}
2404 
2405 	// HACK. don't override EMP if its still going    :)
2406 	// An additional hack: don't interrupt other warnings if this is a missile launch alert (Swifty)
2407 	if(!timestamp_elapsed(Hud_text_flash_timer))
2408 		if( !stricmp(Hud_text_flash, NOX("Emp")) || !stricmp(txt, NOX("Launch")) )
2409 			return;
2410 
2411 	strncpy(Hud_text_flash, txt, 500);
2412 	Hud_text_flash_timer = timestamp(t);
2413 	Hud_text_flash_interval = interval;
2414 }
2415 
HudGaugeTextWarnings()2416 HudGaugeTextWarnings::HudGaugeTextWarnings():
2417 HudGauge(HUD_OBJECT_TEXT_WARNINGS, HUD_TEXT_FLASH, true, false, (VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY | VM_OTHER_SHIP), 255, 255, 255)
2418 {
2419 
2420 }
2421 
initialize()2422 void HudGaugeTextWarnings::initialize()
2423 {
2424 	next_flash = timestamp(0);
2425 	flash_flags = false;
2426 
2427 	HudGauge::initialize();
2428 }
2429 
maybeTextFlash()2430 int HudGaugeTextWarnings::maybeTextFlash()
2431 {
2432 	if ( !timestamp_elapsed(Hud_text_flash_timer) ) {
2433 		if ( timestamp_elapsed(next_flash) ) {
2434 			next_flash = timestamp(Hud_text_flash_interval);
2435 
2436 			// toggle between default and bright frames
2437 			flash_flags = !flash_flags;
2438 		}
2439 	}
2440 
2441 	return flash_flags;
2442 }
2443 
render(float)2444 void HudGaugeTextWarnings::render(float  /*frametime*/)
2445 {
2446 	// note: Hud_text_flash globally allocated, address can't be NULL
2447 	if ( timestamp_elapsed(Hud_text_flash_timer) || Hud_text_flash[0] == '\0' ) {
2448 		return;
2449 	}
2450 
2451 	int w, h;
2452 
2453 	// string size
2454 	gr_get_string_size(&w, &h, Hud_text_flash);
2455 
2456 	// set color
2457 	if(maybeTextFlash()){
2458 		setGaugeColor(HUD_C_DIM);
2459 
2460 		// draw the box
2461 		renderRect( (int)( (float)position[0] - (float)w / 2.0f - 1.0f), (int)((float)position[1] - 1.0f), w + 2, h + 1);
2462 	}
2463 
2464 	// string
2465 	setGaugeColor(HUD_C_BRIGHT);
2466 	renderString(fl2i((float)position[0] - ((float)w / 2.0f)), position[1], Hud_text_flash);
2467 }
2468 
HudGaugeKills()2469 HudGaugeKills::HudGaugeKills():
2470 HudGauge(HUD_OBJECT_KILLS, HUD_KILLS_GAUGE, false, false, (VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY | VM_OTHER_SHIP), 255, 255, 255)
2471 {
2472 }
2473 
initBitmaps(const char * fname)2474 void HudGaugeKills::initBitmaps(const char *fname)
2475 {
2476 	Kills_gauge.first_frame = bm_load_animation(fname, &Kills_gauge.num_frames);
2477 	if ( Kills_gauge.first_frame == -1 ) {
2478 		Warning(LOCATION, "Could not load in the ani: %s\n", fname);
2479 	}
2480 }
2481 
initTextOffsets(int x,int y)2482 void HudGaugeKills::initTextOffsets(int x, int y)
2483 {
2484 	text_offsets[0] = x;
2485 	text_offsets[1] = y;
2486 }
2487 
initTextValueOffsets(int x,int y)2488 void HudGaugeKills::initTextValueOffsets(int x, int y)
2489 {
2490 	text_value_offsets[0] = x;
2491 	text_value_offsets[1] = y;
2492 }
2493 
pageIn()2494 void HudGaugeKills::pageIn()
2495 {
2496 	bm_page_in_aabitmap(Kills_gauge.first_frame, Kills_gauge.num_frames);
2497 }
2498 
2499 /**
2500  * @brief Display the kills gauge on the HUD
2501  */
render(float)2502 void HudGaugeKills::render(float  /*frametime*/)
2503 {
2504 	if ( Kills_gauge.first_frame < 0 ) {
2505 		return;
2506 	}
2507 
2508 	setGaugeColor();
2509 
2510 	// Draw background
2511 	renderBitmap(Kills_gauge.first_frame, position[0], position[1]);
2512 	renderString(position[0] + text_offsets[0], position[1] + text_offsets[1], XSTR( "kills:", 223));
2513 
2514 	// Display how many kills the player has so far
2515 	char num_kills_string[32];
2516 	int	w,h;
2517 
2518 	if ( !Player ) {
2519 		Int3();
2520 		return;
2521 	}
2522 
2523 	sprintf(num_kills_string, "%d", Player->stats.m_kill_count_ok);
2524 
2525 	gr_get_string_size(&w, &h, num_kills_string);
2526 	renderString(position[0]+text_value_offsets[0]-w, position[1]+text_value_offsets[1], num_kills_string);
2527 }
2528 
HudGaugeLag()2529 HudGaugeLag::HudGaugeLag():
2530 HudGauge(HUD_OBJECT_LAG, HUD_LAG_GAUGE, false, false, (VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY | VM_OTHER_SHIP), 255, 255, 255)
2531 {
2532 
2533 }
2534 
initBitmaps(const char * fname)2535 void HudGaugeLag::initBitmaps(const char *fname)
2536 {
2537 	Netlag_icon.first_frame = bm_load_animation(fname, &Netlag_icon.num_frames);
2538 
2539 	if ( Netlag_icon.first_frame == -1 ) {
2540 		Warning(LOCATION, "Could not load in the netlag ani: %s\n", fname);
2541 	}
2542 }
2543 
pageIn()2544 void HudGaugeLag::pageIn()
2545 {
2546 	bm_page_in_aabitmap(Netlag_icon.first_frame, Netlag_icon.num_frames);
2547 }
2548 
startFlashLag(int duration)2549 void HudGaugeLag::startFlashLag(int duration)
2550 {
2551 	flash_timer[0] = timestamp(duration);
2552 }
2553 
maybeFlashLag(bool flash_fast)2554 bool HudGaugeLag::maybeFlashLag(bool flash_fast)
2555 {
2556 	bool draw_bright = false;
2557 
2558 	if(!timestamp_elapsed(flash_timer[0])) {
2559 		if(timestamp_elapsed(flash_timer[1])) {
2560 			if(flash_fast) {
2561 				flash_timer[1] = timestamp(fl2i(TBOX_FLASH_INTERVAL/2.0f));
2562 			} else {
2563 				flash_timer[1] = timestamp(TBOX_FLASH_INTERVAL);
2564 			}
2565 			flash_flag = !flash_flag;
2566 		}
2567 
2568 		if(flash_flag) {
2569 			draw_bright = true;
2570 		}
2571 	}
2572 
2573 	return draw_bright;
2574 }
2575 
render(float)2576 void HudGaugeLag::render(float  /*frametime*/)
2577 {
2578 	int lag_status;
2579 
2580 	if( !(Game_mode & GM_MULTIPLAYER) ){
2581 		return;
2582 	}
2583 
2584 	if ( Netlag_icon.first_frame == -1 ) {
2585 		Int3();
2586 		return;
2587 	}
2588 
2589 	lag_status = multi_query_lag_status();
2590 
2591 	switch(lag_status) {
2592 	case 0:
2593 		// Draw the net lag icon flashing
2594 		startFlashLag();
2595 		if(maybeFlashLag()){
2596 			setGaugeColor(HUD_C_BRIGHT);
2597 		} else {
2598 			setGaugeColor();
2599 		}
2600 		renderBitmap(Netlag_icon.first_frame, position[0], position[1]);
2601 		break;
2602 	case 1:
2603 		// Draw the disconnected icon flashing fast
2604 		if(maybeFlashLag(true)){
2605 			setGaugeColor(HUD_C_BRIGHT);
2606 		} else {
2607 			setGaugeColor();
2608 		}
2609 		renderBitmap(Netlag_icon.first_frame+1, position[0], position[1]);
2610 		break;
2611 	default:
2612 		// Nothing to draw
2613 		return;
2614 	}
2615 }
2616 
2617 /**
2618  * @brief Called at mission start to init data, and load support view bitmap if required
2619  */
hud_support_view_init()2620 void hud_support_view_init()
2621 {
2622 	Hud_support_view_fade = 1;
2623 	Hud_support_obj_sig = -1;
2624 	Hud_support_target_sig = -1;
2625 	Hud_support_objnum = -1;
2626 	Hud_support_view_active = 0;
2627 	Hud_support_view_abort = 0;
2628 }
2629 
2630 /**
2631  * @brief Start displaying the support view pop-up. This will remain up until ::hud_support_view_stop() is called.
2632  */
hud_support_view_start()2633 void hud_support_view_start()
2634 {
2635 	Hud_support_view_active = 1;
2636 	Hud_support_view_fade = 1;
2637 }
2638 
2639 /**
2640  * @brief Stop displaying the support view pop-up.
2641  *
2642  * @param stop_now If set, stop now, otherwise in 2 seconds.
2643  */
hud_support_view_stop(int stop_now)2644 void hud_support_view_stop(int stop_now)
2645 {
2646 	if ( stop_now ) {
2647 		Hud_support_view_active = 0;
2648 		Hud_support_view_fade = 1;
2649 		Hud_support_view_abort = 0;
2650 	} else {
2651 		Hud_support_view_fade = timestamp(2000);
2652 	}
2653 
2654 	Hud_support_obj_sig = -1;
2655 	Hud_support_target_sig = -1;
2656 	Hud_support_objnum = -1;
2657 }
2658 
hud_support_view_abort()2659 void hud_support_view_abort()
2660 {
2661 	hud_support_view_stop(0);
2662 	Hud_support_view_abort = 1;
2663 }
2664 
2665 /**
2666  * @brief Get the number of seconds until repair ship will dock with ther player
2667  * @details mwa made this function more general purpose. Goober5000 made clearer
2668  *
2669  * @param docker_objp The object which is attempting to dock with the player
2670  * @return The number of seconds, 0 if already docked, -1 if error
2671  *
2672  * @todo This function is pretty stupid now.  It just assumes the dockee is sitting still, and
2673  * the docker is moving directly to the dockee.
2674  */
hud_get_dock_time(object * docker_objp)2675 int hud_get_dock_time( object *docker_objp )
2676 {
2677 	ai_info	*aip;
2678 	object	*dockee_objp;
2679 	float		dist, rel_speed, docker_speed;
2680 	vec3d	rel_vel;
2681 
2682 	aip = &Ai_info[Ships[docker_objp->instance].ai_index];
2683 
2684 	// Get the dockee object pointer
2685 	if (aip->goal_objnum == -1) {
2686 		// This can happen when you target a support ship as it warps in
2687 		// just give a debug warning instead of a fault - taylor
2688 		mprintf(("'aip->goal_objnum == -1' in hud_get_dock_time(), line %i\n", __LINE__));
2689 		return 0;
2690 	}
2691 
2692 	dockee_objp = &Objects[aip->goal_objnum];
2693 
2694 	// If the ship is docked, return 0
2695 	if ( dock_check_find_direct_docked_object(docker_objp, dockee_objp) )
2696 		return 0;
2697 
2698 	vm_vec_sub(&rel_vel, &docker_objp->phys_info.vel, &dockee_objp->phys_info.vel);
2699 	rel_speed = vm_vec_mag_quick(&rel_vel);
2700 
2701 	dist = vm_vec_dist_quick(&dockee_objp->pos, &docker_objp->pos);
2702 
2703 	docker_speed = docker_objp->phys_info.speed;
2704 
2705 	if ( rel_speed <= docker_speed/2.0f) {	// This means the player is moving away fast from the support ship.
2706 		return (int) (dist/docker_speed);
2707 	} else {
2708 		float	d1;
2709 		float	d = dist;
2710 		float	time = 0.0f;
2711 
2712 		if (rel_speed < 20.0f)
2713 			rel_speed = 20.0f;
2714 
2715 		// When far away, use max speed, not current speed.  Might not have sped up yet.
2716 		if (d > 100.0f) {
2717 			time += (d - 100.0f)/docker_objp->phys_info.max_vel.xyz.z;
2718 		}
2719 
2720 		// For mid-range, use current speed.
2721 		if (d > 60.0f) {
2722 			d1 = MIN(d, 100.0f);
2723 
2724 			time += (d1 - 60.0f)/rel_speed;
2725 		}
2726 
2727 		// For nearby, ship will have to slow down a bit for docking maneuver.
2728 		if (d > 30.0f) {
2729 			d1 = MIN(d, 60.0f);
2730 
2731 			time += (d1 - 30.0f)/5.0f;
2732 		}
2733 
2734 		// For very nearby, ship moves quite slowly.
2735 		d1 = MIN(d, 30.0f);
2736 		time += d1/7.5f;
2737 
2738 		return fl2i(time);
2739 	}
2740 }
2741 
2742 /**
2743  * @brief Locate the closest support ship which is trying to dock with player
2744  *
2745  * @param objnum Object number of player
2746  * @return Number of support ship, -1 if there is no support ship currently trying to dock
2747  */
hud_support_find_closest(int objnum)2748 int hud_support_find_closest( int objnum )
2749 {
2750 	ship_obj		*sop;
2751 	ai_info		*aip;
2752 	object		*objp;
2753 	int i;
2754 
2755 	objp = &Objects[objnum];
2756 
2757 	sop = GET_FIRST(&Ship_obj_list);
2758 	while(sop != END_OF_LIST(&Ship_obj_list)){
2759 		if ( Ship_info[Ships[Objects[sop->objnum].instance].ship_info_index].flags[Ship::Info_Flags::Support] ) {
2760 			int pship_index, sindex;
2761 
2762 			// make sure support ship is not dying
2763             auto shipp = &Ships[Objects[sop->objnum].instance];
2764 
2765 			if ( !(shipp->flags[Ship::Ship_Flags::Dying] || shipp->flags[Ship::Ship_Flags::Exploded]) ) {
2766 
2767 				Assert( objp->type == OBJ_SHIP );
2768 				aip = &Ai_info[Ships[Objects[sop->objnum].instance].ai_index];
2769 				pship_index = objp->instance;
2770 
2771 				// we must check all goals for this support ship -- not just the first one
2772 				for ( i = 0; i < MAX_AI_GOALS; i++ ) {
2773 
2774 					// we can use == in the next statement (and should) since a ship will only ever be
2775 					// following one order at a time.
2776 					if ( aip->goals[i].ai_mode == AI_GOAL_REARM_REPAIR ) {
2777 						Assert( aip->goals[i].target_name );
2778 						sindex = ship_name_lookup( aip->goals[i].target_name );
2779 						if ( sindex == pship_index )
2780 							return sop->objnum;
2781 					}
2782 				}
2783 			}
2784 		}
2785 		sop = GET_NEXT(sop);
2786 	}
2787 
2788 	return -1;
2789 }
2790 
hud_support_view_update()2791 void hud_support_view_update()
2792 {
2793 	if ( !Hud_support_view_active ) {
2794 		return;
2795 	}
2796 
2797 	if((Game_mode & GM_MULTIPLAYER) && ((Net_player->flags & NETINFO_FLAG_OBSERVER) || (Player_obj->type == OBJ_OBSERVER))){
2798 		return;
2799 	}
2800 
2801 	// If we haven't determined yet who the rearm ship is, try to!
2802 	if (Hud_support_objnum == -1) {
2803 		Hud_support_objnum = hud_support_find_closest( OBJ_INDEX(Player_obj) );
2804 		if ( Hud_support_objnum >= 0 ) {
2805 			Hud_support_obj_sig = Objects[Hud_support_objnum].signature;
2806 			Hud_support_target_sig = Player_obj->signature;
2807 		}
2808 	} else {
2809 		// Check to see if support ship is still alive
2810 		if ( (Objects[Hud_support_objnum].signature != Hud_support_obj_sig) || (Hud_support_target_sig != Player_obj->signature) ) {
2811 			hud_support_view_stop(1);
2812 			return;
2813 		}
2814 	}
2815 
2816 	if ( Hud_support_view_fade > 1 ) {
2817 		if ( timestamp_elapsed(Hud_support_view_fade) ) {
2818 
2819 			Hud_support_view_abort = 0;
2820 			Hud_support_view_active = 0;
2821 			Hud_support_view_fade = 1;
2822 			Hud_support_objnum = -1;
2823 		}
2824 	}
2825 }
2826 
HudGaugeSupport()2827 HudGaugeSupport::HudGaugeSupport():
2828 HudGauge(HUD_OBJECT_SUPPORT, HUD_SUPPORT_GAUGE, false, false, (VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY | VM_OTHER_SHIP), 255, 255, 255)
2829 {
2830 }
2831 
initHeaderOffsets(int x,int y)2832 void HudGaugeSupport::initHeaderOffsets(int x, int y)
2833 {
2834 	Header_offsets[0] = x;
2835 	Header_offsets[1] = y;
2836 }
2837 
initTextValueOffsetY(int y)2838 void HudGaugeSupport::initTextValueOffsetY(int y)
2839 {
2840 	text_val_offset_y = y;
2841 }
2842 
initTextDockOffsetX(int x)2843 void HudGaugeSupport::initTextDockOffsetX(int x)
2844 {
2845 	text_dock_offset_x = x;
2846 }
2847 
initTextDockValueOffsetX(int x)2848 void HudGaugeSupport::initTextDockValueOffsetX(int x)
2849 {
2850 	text_dock_val_offset_x = x;
2851 }
2852 
initBitmaps(const char * fname)2853 void HudGaugeSupport::initBitmaps(const char *fname)
2854 {
2855 	background.first_frame = bm_load_animation(fname, &background.num_frames);
2856 	if ( background.first_frame == -1 ) {
2857 		Warning(LOCATION, "Could not load in ani: %s\n", fname);
2858 	}
2859 }
2860 
pageIn()2861 void HudGaugeSupport::pageIn()
2862 {
2863 	bm_page_in_aabitmap( background.first_frame, background.num_frames);
2864 }
2865 
render(float)2866 void HudGaugeSupport::render(float  /*frametime*/)
2867 {
2868 	int	show_time, w, h;
2869 	char	outstr[64];
2870 
2871 	if ( !Hud_support_view_active ) {
2872 		return;
2873 	}
2874 
2875 	// Don't render this gauge for multiplayer observers
2876 	if((Game_mode & GM_MULTIPLAYER) && ((Net_player->flags & NETINFO_FLAG_OBSERVER) || (Player_obj->type == OBJ_OBSERVER))){
2877 		return;
2878 	}
2879 
2880 	if ( Hud_support_objnum >= 0 ) {
2881 		// Check to see if support ship is still alive
2882 		if ( (Objects[Hud_support_objnum].signature != Hud_support_obj_sig) || (Hud_support_target_sig != Player_obj->signature) ) {
2883 			return;
2884 		}
2885 	}
2886 
2887 	bm_get_info(background.first_frame, &w, &h);
2888 
2889 	// Set hud color
2890 	setGaugeColor();
2891 
2892 	renderBitmap(background.first_frame, position[0], position[1]);
2893 
2894 	renderString(position[0] + Header_offsets[0], position[1] + Header_offsets[1], XSTR( "support", 224));
2895 
2896 	if ( Hud_support_view_fade > 1 ) {
2897 		if ( !timestamp_elapsed(Hud_support_view_fade) ) {
2898 			if ( Hud_support_view_abort){
2899 				renderStringAlignCenter(position[0], position[1] + text_val_offset_y, w, XSTR( "aborted", 225));
2900 			} else {
2901 				renderStringAlignCenter(position[0], position[1] + text_val_offset_y, w, XSTR( "complete", 1407));
2902 			}
2903 		}
2904 		return;
2905 	}
2906 
2907 	show_time = 0;
2908 	if (Player_ai->ai_flags[AI::AI_Flags::Being_repaired]) {
2909 		Assert(Player_ship->ship_max_hull_strength > 0);
2910 
2911 		if (!Cmdline_rearm_timer)
2912 		{
2913 			int i;
2914 			bool repairing = false;
2915 			for (i = 0; i < SUBSYSTEM_MAX; i++)
2916 			{
2917 				if (Player_ship->subsys_info[i].type_count > 0)
2918 				{
2919 					if (ship_get_subsystem_strength(Player_ship, i) < 1.0f)
2920 					{
2921 						repairing = true;
2922 						break;
2923 					}
2924 				}
2925 			}
2926 
2927 			if (repairing)
2928 				strcpy_s(outstr, XSTR("repairing", 227));
2929 			else
2930 				strcpy_s(outstr, XSTR("rearming", 228));
2931 		}
2932 		else
2933 		{
2934 			if (Player_rearm_eta > 0)
2935 			{
2936 				int min, sec, hund;
2937 
2938 				min = (int)Player_rearm_eta / 60;
2939 				sec = (int)Player_rearm_eta % 60;
2940 				hund = (int)(Player_rearm_eta * 100) % 100;
2941 
2942 				sprintf(outstr, "%02d:%02d.%02d", min, sec, hund);
2943 			}
2944 			else
2945 			{
2946 				strcpy_s(outstr, XSTR("Waiting...", 1603));
2947 			}
2948 		}
2949 		renderStringAlignCenter(position[0], position[1] + text_val_offset_y, w, outstr);
2950 	}
2951 	else if (Player_ai->ai_flags[AI::AI_Flags::Repair_obstructed]) {
2952 		strcpy_s(outstr, XSTR( "obstructed", 229));
2953 		renderStringAlignCenter(position[0], position[1] + text_val_offset_y, w, outstr);
2954 	} else {
2955 		if ( Hud_support_objnum == -1 ) {
2956 			if (The_mission.support_ships.arrival_location == ARRIVE_FROM_DOCK_BAY)
2957 			{
2958 				strcpy_s(outstr, XSTR( "exiting hangar", 1622));
2959 			}
2960 			else
2961 			{
2962 				strcpy_s(outstr, XSTR( "warping in", 230));
2963 			}
2964 			renderStringAlignCenter(position[0], position[1] + text_val_offset_y, w, outstr);
2965 		} else {
2966 			ai_info *aip;
2967 
2968 			// Display "busy" when support ship isn't actually enroute to me
2969 			aip = &Ai_info[Ships[Objects[Hud_support_objnum].instance].ai_index];
2970 			if ( aip->goal_objnum != OBJ_INDEX(Player_obj) ) {
2971 				strcpy_s(outstr, XSTR( "busy", 231));
2972 				show_time = 0;
2973 
2974 			} else {
2975 				strcpy_s(outstr, XSTR( "dock in:", 232));
2976 				show_time = 1;
2977 			}
2978 
2979 			renderString(position[0] + text_dock_offset_x, position[1] + text_val_offset_y, outstr);
2980 		}
2981 	}
2982 
2983 	if ( show_time ) {
2984 		int seconds, minutes;
2985 
2986 		Assert( Hud_support_objnum != -1 );
2987 
2988 		// Ensure support ship is still alive
2989 		if ( (Objects[Hud_support_objnum].signature != Hud_support_obj_sig) || (Hud_support_target_sig != Player_obj->signature) ) {
2990 			seconds = 0;
2991 		} else {
2992 			seconds = hud_get_dock_time( &Objects[Hud_support_objnum] );
2993 		}
2994 
2995 		if ( seconds >= 0 ) {
2996 			minutes = seconds/60;
2997 			seconds = seconds%60;
2998 			if ( minutes > 99 ) {
2999 				minutes = 99;
3000 				seconds = 99;
3001 			}
3002 		} else {
3003 			minutes = 99;
3004 			seconds = 99;
3005 		}
3006 		renderPrintf(position[0] + text_dock_val_offset_x, position[1] + text_val_offset_y, NOX("%02d:%02d"), minutes, seconds);
3007 	}
3008 }
3009 
3010 /**
3011  * @brief Set the current color to the default HUD color (with default alpha)
3012  */
hud_set_default_color()3013 void hud_set_default_color()
3014 {
3015 	Assert(HUD_color_alpha >= 0 && HUD_color_alpha < HUD_NUM_COLOR_LEVELS);
3016 	gr_set_color_fast(&HUD_color_defaults[HUD_color_alpha]);
3017 }
3018 
3019 /**
3020  * @brief Set the current color to a bright HUD color (ie high alpha)
3021  */
hud_set_bright_color()3022 void hud_set_bright_color()
3023 {
3024 	int alpha_color;
3025 	alpha_color = MIN(HUD_COLOR_ALPHA_MAX,HUD_color_alpha+HUD_BRIGHT_DELTA);
3026 	gr_set_color_fast(&HUD_color_defaults[alpha_color]);
3027 }
3028 
3029 /**
3030  * @brief Set the current color to a dim HUD color (ie low alpha)
3031  */
hud_set_dim_color()3032 void hud_set_dim_color()
3033 {
3034 	if ( HUD_color_alpha > 2 ) {
3035 		gr_set_color_fast(&HUD_color_defaults[2]);
3036 	}
3037 }
3038 
3039 /**
3040  * @brief Will set the color to the IFF color based on the team
3041  *
3042  * @param objp			Object to test for team color to base on
3043  * @param is_bright		Default parameter (value 0) which uses bright version of IFF color
3044  */
hud_set_iff_color(object * objp,int is_bright)3045 void hud_set_iff_color(object *objp, int is_bright)
3046 {
3047 	color *use_color;
3048 
3049 	if (ship_is_tagged(objp))
3050 	{
3051 		use_color = iff_get_color(IFF_COLOR_TAGGED, is_bright);
3052 	}
3053 	else
3054 	{
3055 		if (objp->type == OBJ_ASTEROID)
3056 		{
3057 			if (OBJ_INDEX(objp) == Player_ai->target_objnum)
3058 			{
3059 				use_color = iff_get_color_by_team(Iff_traitor, Player_ship->team, is_bright);
3060 			}
3061 			else
3062 			{
3063 				use_color = iff_get_color(IFF_COLOR_SELECTION, is_bright);
3064 			}
3065 		}
3066 		else
3067 		{
3068 			use_color = iff_get_color_by_team_and_object(obj_team(objp), Player_ship->team, is_bright, objp);
3069 		}
3070 	}
3071 
3072 	gr_set_color_fast(use_color);
3073 }
3074 
3075 /**
3076  * @brief Reset gauge flashing data
3077  */
hud_gauge_flash_init()3078 void hud_gauge_flash_init()
3079 {
3080 	int i;
3081 	for ( i=0; i<NUM_HUD_GAUGES; i++ ) {
3082 		HUD_gauge_flash_duration[i]=timestamp(0);
3083 		HUD_gauge_flash_next[i]=timestamp(0);
3084 	}
3085 	HUD_gauge_bright=0;
3086 }
3087 
3088 #define NUM_VM_OTHER_SHIP_GAUGES 5
3089 static int Vm_other_ship_gauges[NUM_VM_OTHER_SHIP_GAUGES] =
3090 {
3091 	HUD_CENTER_RETICLE,
3092 	HUD_TARGET_MONITOR,
3093 	HUD_TARGET_MONITOR_EXTRA_DATA,
3094 	HUD_MESSAGE_LINES,
3095 	HUD_TALKING_HEAD
3096 };
3097 
3098 /**
3099  * @brief Determine if the specified HUD gauge should be displayed
3100  */
hud_gauge_active(int gauge_index)3101 int hud_gauge_active(int gauge_index)
3102 {
3103 	Assert(gauge_index >=0 && gauge_index < NUM_HUD_GAUGES);
3104 
3105 	// AL: Special code: Only show two gauges when not viewing from own ship
3106 	if ( Viewer_mode & VM_OTHER_SHIP ) {
3107 		for ( int i = 0; i < NUM_VM_OTHER_SHIP_GAUGES; i++ ) {
3108 			if ( gauge_index == Vm_other_ship_gauges[i] ) {
3109 				return 1;
3110 			}
3111 		}
3112 		return 0;
3113 	}
3114 
3115 	return hud_config_show_flag_is_set(gauge_index);
3116 }
3117 
3118 /**
3119  * @brief Determine if gauge is in pop-up mode or not
3120  */
hud_gauge_is_popup(int gauge_index)3121 int hud_gauge_is_popup(int gauge_index)
3122 {
3123 	Assert(gauge_index >=0 && gauge_index < NUM_HUD_GAUGES);
3124 	return hud_config_popup_flag_is_set(gauge_index);
3125 }
3126 
3127 /**
3128  * @brief Start a gauge to pop-up
3129  * @details Load gauge properties defined in the HUD config if gauge is not customized.
3130  */
hud_gauge_popup_start(int gauge_index,int time)3131 void hud_gauge_popup_start(int gauge_index, int time)
3132 {
3133 	Assert(gauge_index >=0 && gauge_index < NUM_HUD_GAUGES);
3134 	if ( !hud_gauge_is_popup(gauge_index) ) {
3135 		return;
3136 	}
3137 
3138 	size_t num_gauges, i;
3139 
3140 	if(!Ship_info[Player_ship->ship_info_index].hud_gauges.empty()) {
3141 		num_gauges = Ship_info[Player_ship->ship_info_index].hud_gauges.size();
3142 
3143 		for(i = 0; i < num_gauges; i++) {
3144 			if(Ship_info[Player_ship->ship_info_index].hud_gauges[i]->getConfigType() == gauge_index)
3145 				Ship_info[Player_ship->ship_info_index].hud_gauges[i]->startPopUp(time);
3146 		}
3147 	} else {
3148 		num_gauges = default_hud_gauges.size();
3149 
3150 		for(i = 0; i < num_gauges; i++) {
3151 			if(default_hud_gauges[i]->getConfigType() == gauge_index)
3152 				default_hud_gauges[i]->startPopUp(time);
3153 		}
3154 	}
3155 }
3156 
3157 /**
3158  * @brief Call HUD function to flash gauge
3159  * @details Load gauge properties defined in the HUD config if gauge is not customized.
3160  */
hud_gauge_start_flash(int gauge_index)3161 void hud_gauge_start_flash(int gauge_index)
3162 {
3163 	Assert(gauge_index >=0 && gauge_index < NUM_HUD_GAUGES);
3164 
3165 	size_t num_gauges, i;
3166 
3167 	HUD_gauge_flash_duration[gauge_index] = timestamp(HUD_GAUGE_FLASH_DURATION);
3168 	HUD_gauge_flash_next[gauge_index] = 1;
3169 
3170 	if(!Ship_info[Player_ship->ship_info_index].hud_gauges.empty()) {
3171 		num_gauges = Ship_info[Player_ship->ship_info_index].hud_gauges.size();
3172 
3173 		for(i = 0; i < num_gauges; i++) {
3174 			if(Ship_info[Player_ship->ship_info_index].hud_gauges[i]->getConfigType() == gauge_index)
3175 				Ship_info[Player_ship->ship_info_index].hud_gauges[i]->startFlashSexp();
3176 		}
3177 	} else {
3178 		num_gauges = default_hud_gauges.size();
3179 
3180 		for(i = 0; i < num_gauges; i++) {
3181 			if(default_hud_gauges[i]->getConfigType() == gauge_index)
3182 				default_hud_gauges[i]->startFlashSexp();
3183 		}
3184 	}
3185 }
3186 
3187 /**
3188  * @brief Set the HUD color for the gauge, based on whether it is flashing or not
3189  */
hud_set_gauge_color(int gauge_index,int bright_index)3190 void hud_set_gauge_color(int gauge_index, int bright_index)
3191 {
3192 	int flash_status = hud_gauge_maybe_flash(gauge_index);
3193 	color *use_color = &HUD_config.clr[gauge_index];
3194 	int alpha;
3195 
3196 	// If we're drawing it as bright
3197 	if(bright_index != HUD_C_NONE){
3198 		switch(bright_index){
3199 		case HUD_C_DIM:
3200 			alpha = HUD_contrast ? HUD_NEW_ALPHA_DIM_HI : HUD_NEW_ALPHA_DIM;
3201 			gr_init_alphacolor(use_color, use_color->red, use_color->green, use_color->blue, alpha);
3202 			break;
3203 
3204 		case HUD_C_NORMAL:
3205 			alpha = HUD_contrast ? HUD_NEW_ALPHA_NORMAL_HI : HUD_NEW_ALPHA_NORMAL;
3206 			gr_init_alphacolor(use_color, use_color->red, use_color->green, use_color->blue, alpha);
3207 			break;
3208 
3209 		case HUD_C_BRIGHT:
3210 			alpha = HUD_contrast ? HUD_NEW_ALPHA_BRIGHT_HI : HUD_NEW_ALPHA_BRIGHT;
3211 			gr_init_alphacolor(use_color, use_color->red, use_color->green, use_color->blue, alpha);
3212 			break;
3213 
3214 		// Intensity
3215 		default:
3216 			Assert((bright_index >= 0) && (bright_index < HUD_NUM_COLOR_LEVELS));
3217 			if(bright_index < 0){
3218 				bright_index = 0;
3219 			}
3220 			if(bright_index >= HUD_NUM_COLOR_LEVELS){
3221 				bright_index = HUD_NUM_COLOR_LEVELS - 1;
3222 			}
3223 
3224 			// alpha = 255 - (255 / (bright_index + 1));
3225 			// alpha = (int)((float)alpha * 1.5f);
3226 			int level = 255 / (HUD_NUM_COLOR_LEVELS);
3227 			alpha = level * bright_index;
3228 			if(alpha > 255){
3229 				alpha = 255;
3230 			}
3231 			if(alpha < 0){
3232 				alpha = 0;
3233 			}
3234 			gr_init_alphacolor(use_color, use_color->red, use_color->green, use_color->blue, alpha);
3235 			break;
3236 		}
3237 	} else {
3238 		switch(flash_status) {
3239 		case 0:
3240 			alpha = HUD_contrast ? HUD_NEW_ALPHA_DIM_HI : HUD_NEW_ALPHA_DIM;
3241 			gr_init_alphacolor(use_color, use_color->red, use_color->green, use_color->blue, alpha);
3242 			break;
3243 		case 1:
3244 			alpha = HUD_contrast ? HUD_NEW_ALPHA_BRIGHT_HI : HUD_NEW_ALPHA_BRIGHT;
3245 			gr_init_alphacolor(use_color, use_color->red, use_color->green, use_color->blue, alpha);
3246 			break;
3247 		default:
3248 			alpha = HUD_contrast ? HUD_NEW_ALPHA_NORMAL_HI : HUD_NEW_ALPHA_NORMAL;
3249 			gr_init_alphacolor(use_color, use_color->red, use_color->green, use_color->blue, alpha);
3250 			break;
3251 		}
3252 	}
3253 
3254 	gr_set_color_fast(use_color);
3255 }
3256 
3257 /**
3258  * @brief Set the color for a gauge that may be flashing
3259  * @param gauge_index Gauge to test
3260  * @return Gauge is not flashing -1; gauge is flashing, draw dim 0; gauge is flashing, draw bright 1
3261  */
hud_gauge_maybe_flash(int gauge_index)3262 int hud_gauge_maybe_flash(int gauge_index)
3263 {
3264 	Assert(gauge_index >=0 && gauge_index < NUM_HUD_GAUGES);
3265 	int flash_status=-1;
3266 	if ( !timestamp_elapsed(HUD_gauge_flash_duration[gauge_index]) ) {
3267 		if ( timestamp_elapsed(HUD_gauge_flash_next[gauge_index]) ) {
3268 			HUD_gauge_flash_next[gauge_index] = timestamp(HUD_GAUGE_FLASH_INTERVAL);
3269 			HUD_gauge_bright ^= (1<<gauge_index);	// toggle between default and bright frames
3270 		}
3271 
3272 		if ( HUD_gauge_bright & (1<<gauge_index) ) {
3273 			flash_status=1;
3274 		} else {
3275 			flash_status=0;
3276 		}
3277 	}
3278 	return flash_status;
3279 }
3280 
hud_alignment_lookup(const char * name)3281 HudAlignment hud_alignment_lookup(const char *name)
3282 {
3283 	if (!stricmp(name, "left"))
3284 		return HudAlignment::LEFT;
3285 	if (!stricmp(name, "right"))
3286 		return HudAlignment::RIGHT;
3287 	return HudAlignment::NONE;
3288 }
3289 
3290 /**
3291  * @brief Initialise the objective message display data
3292  */
hud_objective_message_init()3293 void hud_objective_message_init()
3294 {
3295 	Objective_display.display_timer=timestamp(0);
3296 }
3297 
hud_update_objective_message()3298 void hud_update_objective_message()
3299 {
3300 	// Find out if we should display the subspace status notification
3301 	if ( (Player->control_mode == PCM_WARPOUT_STAGE1) || (Player->control_mode == PCM_WARPOUT_STAGE2) || (Player->control_mode == PCM_WARPOUT_STAGE3)
3302 		|| (Sexp_hud_display_warpout > 0) ) {
3303 		if (!hud_subspace_notify_active()) {
3304 			// Keep sound from being played 1e06 times
3305 			hud_start_subspace_notify();
3306 		}
3307 	} else {
3308 		if ( timestamp_elapsed(HUD_abort_subspace_timer) ) {
3309 			hud_stop_subspace_notify();
3310 		}
3311 	}
3312 
3313 	if ( (Sexp_hud_display_warpout > 1) && hud_subspace_notify_active() ) {
3314 		if ( Sexp_hud_display_warpout < timestamp()) {
3315 			Sexp_hud_display_warpout = 0;
3316 		}
3317 	}
3318 
3319 	// Find out if we should display the objective status notification
3320 	if ( timestamp_elapsed(Objective_display.display_timer) ) {
3321 		hud_stop_objective_notify();
3322 	} else if (!hud_objective_notify_active() && !hud_subspace_notify_active()) {
3323 		hud_start_objective_notify();
3324 	}
3325 }
3326 
3327 /**
3328  * @brief Add objective status on the HUD
3329  *
3330  * @param type		Type of goal, one of: ::PRIMARY_GOAL, ::SECONDARY_GOAL, ::BONUS_GOAL
3331  * @param status	Status of goal, one of:	::GOAL_FAILED, ::GOAL_COMPLETE, ::GOAL_INCOMPLETE
3332  * @todo Play a sound?
3333  */
hud_add_objective_messsage(int type,int status)3334 void hud_add_objective_messsage(int type, int status)
3335 {
3336 	Objective_display.display_timer=timestamp(7000);
3337 	Objective_display.goal_type=type;
3338 	Objective_display.goal_status=status;
3339 
3340 	// if this is a multiplayer tvt game
3341 	if(MULTI_TEAM && (Net_player != NULL)) {
3342 		mission_goal_fetch_num_resolved(type, &Objective_display.goal_nresolved, &Objective_display.goal_ntotal, Net_player->p_info.team);
3343 	} else {
3344 		mission_goal_fetch_num_resolved(type, &Objective_display.goal_nresolved, &Objective_display.goal_ntotal);
3345 	}
3346 
3347 	// TODO: play a sound?
3348 }
3349 
HudGaugeObjectiveNotify()3350 HudGaugeObjectiveNotify::HudGaugeObjectiveNotify():
3351 HudGauge(HUD_OBJECT_OBJ_NOTIFY, HUD_OBJECTIVES_NOTIFY_GAUGE, false, false, VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY | VM_OTHER_SHIP, 255, 255, 255)
3352 {
3353 
3354 }
3355 
initObjTextOffsetY(int y)3356 void HudGaugeObjectiveNotify::initObjTextOffsetY(int y)
3357 {
3358 	Objective_text_offset_y = y;
3359 }
3360 
initObjValueOffsetY(int y)3361 void HudGaugeObjectiveNotify::initObjValueOffsetY(int y)
3362 {
3363 	Objective_text_val_offset_y = y;
3364 }
3365 
initSubspaceTextOffsetY(int y)3366 void HudGaugeObjectiveNotify::initSubspaceTextOffsetY(int y)
3367 {
3368 	Subspace_text_offset_y = y;
3369 }
3370 
initSubspaceValueOffsetY(int y)3371 void HudGaugeObjectiveNotify::initSubspaceValueOffsetY(int y)
3372 {
3373 	Subspace_text_val_offset_y = y;
3374 }
3375 
initRedAlertTextOffsetY(int y)3376 void HudGaugeObjectiveNotify::initRedAlertTextOffsetY(int y)
3377 {
3378 	Red_text_offset_y = y;
3379 }
3380 
initRedAlertValueOffsetY(int y)3381 void HudGaugeObjectiveNotify::initRedAlertValueOffsetY(int y)
3382 {
3383 	Red_text_val_offset_y = y;
3384 }
3385 
initBitmaps(const char * fname)3386 void HudGaugeObjectiveNotify::initBitmaps(const char *fname)
3387 {
3388 	Objective_display_gauge.first_frame = bm_load_animation(fname, &Objective_display_gauge.num_frames);
3389 	if ( Objective_display_gauge.first_frame == -1 ) {
3390 		Warning(LOCATION, "Could not load in ani: %s\n", fname);
3391 	}
3392 }
3393 
initialize()3394 void HudGaugeObjectiveNotify::initialize()
3395 {
3396 	flash_timer[0] = timestamp(1);
3397 	flash_timer[1] = timestamp(1);
3398 	flash_flag = false;
3399 
3400 	HudGauge::initialize();
3401 }
3402 
pageIn()3403 void HudGaugeObjectiveNotify::pageIn()
3404 {
3405 	bm_page_in_aabitmap( Objective_display_gauge.first_frame, Objective_display_gauge.num_frames);
3406 }
3407 
startFlashNotify(int duration)3408 void HudGaugeObjectiveNotify::startFlashNotify(int duration)
3409 {
3410 	flash_timer[0] = timestamp(duration);
3411 }
3412 
maybeFlashNotify(bool flash_fast)3413 bool HudGaugeObjectiveNotify::maybeFlashNotify(bool flash_fast)
3414 {
3415 	bool draw_bright = false;
3416 
3417 	if(!timestamp_elapsed(flash_timer[0])) {
3418 		if(timestamp_elapsed(flash_timer[1])) {
3419 			if(flash_fast) {
3420 				flash_timer[1] = timestamp(fl2i(TBOX_FLASH_INTERVAL/2.0f));
3421 			} else {
3422 				flash_timer[1] = timestamp(TBOX_FLASH_INTERVAL);
3423 			}
3424 			flash_flag = !flash_flag;
3425 		}
3426 
3427 		if(flash_flag) {
3428 			draw_bright = true;
3429 		}
3430 	}
3431 
3432 	return draw_bright;
3433 }
3434 
render(float)3435 void HudGaugeObjectiveNotify::render(float  /*frametime*/)
3436 {
3437 	renderSubspace();
3438 	renderRedAlert();
3439 	renderObjective();
3440 }
3441 
renderSubspace()3442 void HudGaugeObjectiveNotify::renderSubspace()
3443 {
3444 	int w, h;
3445 	int warp_aborted = 0;
3446 
3447 	if ( (Player->control_mode != PCM_WARPOUT_STAGE1) && (Player->control_mode != PCM_WARPOUT_STAGE2) && (Player->control_mode != PCM_WARPOUT_STAGE3)
3448 		&& (Sexp_hud_display_warpout <= 0) ) {
3449 		if ( !timestamp_elapsed(HUD_abort_subspace_timer) ) {
3450 			warp_aborted = 1;
3451 		}
3452 	}
3453 
3454 	if ( !hud_subspace_notify_active() ) {
3455 		return;
3456 	}
3457 
3458 	if ( Objective_display_gauge.first_frame < 0 ) {
3459 		return;
3460 	}
3461 
3462 	// Blit the background
3463 	setGaugeColor();
3464 	renderBitmap(Objective_display_gauge.first_frame, position[0], position[1]);
3465 
3466 	startFlashNotify();
3467 	maybeFlashNotify() ? setGaugeColor(HUD_C_BRIGHT) : setGaugeColor();
3468 
3469 	bm_get_info(Objective_display_gauge.first_frame, &w, &h);
3470 
3471 	renderStringAlignCenter(position[0], position[1] + Subspace_text_offset_y, w, XSTR( "subspace drive", 233));
3472 	if ( warp_aborted ) {
3473 		renderStringAlignCenter(position[0], position[1] + Subspace_text_val_offset_y, w, XSTR( "aborted", 225));
3474 	} else {
3475 		renderStringAlignCenter(position[0], position[1] + Subspace_text_val_offset_y, w, XSTR( "engaged", 234));
3476 	}
3477 }
3478 
3479 /**
3480  * @todo Play a sound?
3481  */
renderRedAlert()3482 void HudGaugeObjectiveNotify::renderRedAlert()
3483 {
3484 	int w, h;
3485 
3486 	if ( !red_alert_in_progress() ) {
3487 		return;
3488 	}
3489 
3490 	if ( Objective_display_gauge.first_frame < 0 ) {
3491 		return;
3492 	}
3493 
3494 	if ( hud_subspace_notify_active() ) {
3495 		return;
3496 	}
3497 
3498 	if ( hud_objective_notify_active() ) {
3499 		return;
3500 	}
3501 
3502 	// Blit the background
3503 	gr_set_color_fast(&Color_red);		// Color box red, because it's an emergency
3504 
3505 	renderBitmap(Objective_display_gauge.first_frame, position[0], position[1]);
3506 
3507 	startFlashNotify();
3508 	if(maybeFlashNotify()) {
3509 		gr_set_color_fast(&Color_red);
3510 	} else {
3511 		gr_set_color_fast(&Color_bright_red);
3512 	}
3513 
3514 	bm_get_info(Objective_display_gauge.first_frame, &w, &h);
3515 
3516 	renderStringAlignCenter(position[0], position[1] + Red_text_offset_y, w, XSTR( "downloading new", 235));
3517 	renderStringAlignCenter(position[0], position[1] + Red_text_val_offset_y, w, XSTR( "orders...", 236));
3518 
3519 	// TODO: play a sound?
3520 }
3521 
renderObjective()3522 void HudGaugeObjectiveNotify::renderObjective()
3523 {
3524 	int w, h;
3525 	char buf[128];
3526 
3527 	if ( timestamp_elapsed(Objective_display.display_timer) ) {
3528 		return;
3529 	}
3530 
3531 	if ( Objective_display_gauge.first_frame < 0 ) {
3532 		return;
3533 	}
3534 
3535 	if ( hud_subspace_notify_active() ) {
3536 		return;
3537 	}
3538 
3539 	// Blit the background
3540 	setGaugeColor();
3541 	renderBitmap(Objective_display_gauge.first_frame, position[0], position[1]);
3542 
3543 	startFlashNotify();
3544 	if(maybeFlashNotify()){
3545 		setGaugeColor(HUD_C_BRIGHT);
3546 	} else {
3547 		setGaugeColor();
3548 	}
3549 
3550 	bm_get_info(Objective_display_gauge.first_frame, &w, &h);
3551 
3552 	// Draw the correct goal type
3553 	switch(Objective_display.goal_type) {
3554 	case PRIMARY_GOAL:
3555 		renderStringAlignCenter(position[0], position[1] + Objective_text_offset_y, w, XSTR( "primary objective", 237));
3556 		break;
3557 	case SECONDARY_GOAL:
3558 		renderStringAlignCenter(position[0], position[1] + Objective_text_offset_y, w, XSTR( "secondary objective", 238));
3559 		break;
3560 	case BONUS_GOAL:
3561 		renderStringAlignCenter(position[0], position[1] + Objective_text_offset_y, w, XSTR( "bonus objective", 239));
3562 		break;
3563 	}
3564 
3565 	// Show the status
3566 	switch(Objective_display.goal_type) {
3567 	case PRIMARY_GOAL:
3568 	case SECONDARY_GOAL:
3569 		switch(Objective_display.goal_status) {
3570 		case GOAL_FAILED:
3571 			sprintf(buf, XSTR( "failed (%d/%d)", 240), Objective_display.goal_nresolved, Objective_display.goal_ntotal);
3572 			renderStringAlignCenter(position[0], position[1] + Objective_text_val_offset_y, w, buf);
3573 			break;
3574 		default:
3575 			sprintf(buf, XSTR( "complete (%d/%d)", 241), Objective_display.goal_nresolved, Objective_display.goal_ntotal);
3576 			renderStringAlignCenter(position[0], position[1] + Objective_text_val_offset_y, w, buf);
3577 			break;
3578 		}
3579 		break;
3580 	case BONUS_GOAL:
3581 		switch(Objective_display.goal_status) {
3582 		case GOAL_FAILED:
3583 			renderStringAlignCenter(position[0], position[1] + Objective_text_val_offset_y, w, XSTR( "failed", 242));
3584 			break;
3585 		default:
3586 			renderStringAlignCenter(position[0], position[1] + Objective_text_val_offset_y, w, XSTR( "complete", 226));
3587 			break;
3588 		}
3589 		break;
3590 	}
3591 }
3592 
hud_subspace_notify_abort()3593 void hud_subspace_notify_abort()
3594 {
3595 	HUD_abort_subspace_timer = timestamp(1500);
3596 }
3597 
hud_stop_subspace_notify()3598 void hud_stop_subspace_notify()
3599 {
3600 	Subspace_notify_active=0;
3601 }
3602 
hud_start_subspace_notify()3603 void hud_start_subspace_notify()
3604 {
3605 
3606 	Subspace_notify_active=1;
3607 }
3608 
hud_subspace_notify_active()3609 int hud_subspace_notify_active()
3610 {
3611 	return Subspace_notify_active;
3612 }
3613 
hud_stop_objective_notify()3614 void hud_stop_objective_notify()
3615 {
3616 	Objective_notify_active = 0;
3617 }
3618 
hud_start_objective_notify()3619 void hud_start_objective_notify()
3620 {
3621 	snd_play(gamesnd_get_game_sound(GameSounds::DIRECTIVE_COMPLETE));
3622 	Objective_notify_active = 1;
3623 }
3624 
hud_objective_notify_active()3625 int hud_objective_notify_active()
3626 {
3627 	return Objective_notify_active;
3628 }
3629 
3630 /**
3631  * @brief Set the offset values for this render frame
3632  * @details Since the player's view vector may be different from the ship's forward vector,
3633  * we calculate the offset of those two in pixels and store the x and y offsets in
3634  * variables HUD_nose_x and HUD_nose_y (Swifty)
3635  *
3636  * @param viewer_obj Object, likely to be player
3637  * @param wiggedy_wack
3638  * @param eye_orient
3639  */
HUD_set_offsets(object * viewer_obj,int wiggedy_wack,matrix * eye_orient)3640 void HUD_set_offsets(object *viewer_obj, int wiggedy_wack, matrix *eye_orient)
3641 {
3642 	if ( (viewer_obj == Player_obj) && wiggedy_wack ){
3643 		vec3d tmp;
3644 		vertex pt;
3645 
3646 		HUD_offset_x = 0.0f;
3647 		HUD_offset_y = 0.0f;
3648 
3649 		vm_vec_scale_add( &tmp, &Eye_position, &eye_orient->vec.fvec, 100.0f );
3650 
3651 		(void) g3_rotate_vertex(&pt,&tmp);
3652 
3653 		g3_project_vertex(&pt);
3654 
3655 		gr_unsize_screen_posf( &pt.screen.xyw.x, &pt.screen.xyw.y );
3656 		HUD_offset_x -= 0.45f * (i2fl(gr_screen.clip_width_unscaled)*0.5f - pt.screen.xyw.x);
3657 		HUD_offset_y -= 0.45f * (i2fl(gr_screen.clip_height_unscaled)*0.5f - pt.screen.xyw.y);
3658 
3659 		if ( HUD_offset_x > 100.0f )	{
3660 			HUD_offset_x = 100.0f;
3661 		} else if ( HUD_offset_x < -100.0f )	{
3662 			HUD_offset_x += 100.0f;
3663 		}
3664 
3665 		if ( HUD_offset_y > 100.0f )	{
3666 			HUD_offset_y = 100.0f;
3667 		} else if ( HUD_offset_y < -100.0f )	{
3668 			HUD_offset_y += 100.0f;
3669 		}
3670 
3671 	} else {
3672 		HUD_offset_x = 0.0f;
3673 		HUD_offset_y = 0.0f;
3674 	}
3675 
3676 	if ( Viewer_mode & ( VM_TOPDOWN | VM_CHASE ) ) {
3677 		HUD_nose_x = 0;
3678 		HUD_nose_y = 0;
3679 	} else {
3680 		HUD_get_nose_coordinates(&HUD_nose_x, &HUD_nose_y);
3681 	}
3682 }
3683 
3684 /**
3685  * @brief Returns the offset between the player's view vector and the forward vector of the ship in pixels (Swifty)
3686  */
HUD_get_nose_coordinates(int * x,int * y)3687 void HUD_get_nose_coordinates(int *x, int *y)
3688 {
3689 	vertex	v0;
3690 	vec3d	p0;
3691 
3692 	float x_nose;
3693 	float y_nose;
3694 	float x_center = gr_screen.clip_center_x;
3695 	float y_center = gr_screen.clip_center_y;
3696 
3697 	*x = 0;
3698 	*y = 0;
3699 
3700 	vm_vec_scale_add(&p0, &Player_obj->pos, &Player_obj->orient.vec.fvec, 10000.0f);
3701 	g3_rotate_vertex(&v0, &p0);
3702 
3703 	if (v0.codes == 0) {
3704 		g3_project_vertex(&v0);
3705 
3706 		if ( !(v0.codes & PF_OVERFLOW) ) {
3707 			x_nose = v0.screen.xyw.x;
3708 			y_nose = v0.screen.xyw.y;
3709 		} else {
3710 			// Means that the ship forward vector is not going through the frame buffer.
3711 			// We're assigning a high negative value so that the the bitmaps will be drawn offscreen so that
3712 			// we can give the illusion that the player is looking away from the slewable HUD reticle.
3713 			*x = -100000;
3714 			*y = -100000;
3715 			return;
3716 		}
3717 	} else {
3718 		// Means that the ship forward vector is not going through the frame buffer.
3719 		// We're assigning a high negative value so that the the bitmaps will be drawn offscreen so that
3720 		// we can give the illusion that the player is looking away from the slewable HUD reticle.
3721 		*x = -100000;
3722 		*y = -100000;
3723 		return;
3724 	}
3725 
3726 	gr_unsize_screen_posf(&x_nose, &y_nose);
3727 	gr_unsize_screen_posf(&x_center, &y_center);
3728 
3729 	*x = fl2i(x_nose - x_center);
3730 	*y = fl2i(y_nose - y_center);
3731 	return;
3732 }
3733 
3734 /**
3735  * @brief Like ::gr_reset_clip() only it accounts for HUD jittering
3736  */
HUD_reset_clip()3737 void HUD_reset_clip()
3738 {
3739 	int hx = fl2i(HUD_offset_x);
3740 	int hy = fl2i(HUD_offset_y);
3741 
3742 	gr_set_clip(hx, hy, gr_screen.max_w_unscaled, gr_screen.max_h_unscaled);
3743 }
3744 
3745 /**
3746  * @brief Like ::gr_set_clip() only it accounts for HUD jittering
3747  */
HUD_set_clip(int x,int y,int w,int h)3748 void HUD_set_clip(int x, int y, int w, int h)
3749 {
3750 	int hx = fl2i(HUD_offset_x);
3751 	int hy = fl2i(HUD_offset_y);
3752 
3753 	gr_set_clip(hx+x, hy+y, w, h);
3754 }
3755 
3756 /**
3757  * @brief Called to save and restore the 3D camera settings.
3758  * @param save Save global view variables if 1, restore them if not 1
3759  */
hud_save_restore_camera_data(int save)3760 void hud_save_restore_camera_data(int save)
3761 {
3762 	static vec3d	save_view_position;
3763 	static float	save_view_zoom;
3764 	static matrix	save_view_matrix;
3765 	static matrix	save_eye_matrix;
3766 	static vec3d	save_eye_position;
3767 
3768 	if ( save ) {
3769 		save_view_position		= View_position;
3770 		save_view_zoom			= View_zoom;
3771 		save_view_matrix		= View_matrix;
3772 		save_eye_matrix			= Eye_matrix;
3773 		save_eye_position		= Eye_position;
3774 	}
3775 	else {
3776 		// restore global view variables
3777 		View_position	= save_view_position;
3778 		View_zoom		= save_view_zoom;
3779 		View_matrix		= save_view_matrix;
3780 		Eye_matrix		= save_eye_matrix;
3781 		Eye_position	= save_eye_position;
3782 	}
3783 }
3784 
3785 
hud_toggle_contrast()3786 void hud_toggle_contrast()
3787 {
3788 	HUD_contrast = !HUD_contrast;
3789 }
3790 
hud_set_contrast(int high)3791 void hud_set_contrast(int high)
3792 {
3793 	HUD_contrast = high;
3794 }
3795 
3796 // Paging functions for the rest of the HUD code
3797 extern void hudtarget_page_in();
3798 
3799 /**
3800  * @brief Page in all HUD bitmaps
3801  */
hud_page_in()3802 void hud_page_in()
3803 {
3804 	// Go through all hud gauges to page them in
3805 	size_t j, num_gauges = 0;
3806 	for (auto it = Ship_info.cbegin(); it != Ship_info.cend(); ++it) {
3807 		if(it->hud_enabled) {
3808 			if(!it->hud_gauges.empty()) {
3809 				num_gauges = it->hud_gauges.size();
3810 
3811 				for(j = 0; j < num_gauges; j++) {
3812 					it->hud_gauges[j]->pageIn();
3813 				}
3814 			}
3815 		}
3816 	}
3817 
3818 	num_gauges = default_hud_gauges.size();
3819 
3820 	for(j = 0; j < num_gauges; j++) {
3821 		default_hud_gauges[j]->pageIn();
3822 	}
3823 }
3824 
hud_get_gauge(const char * name)3825 HudGauge* hud_get_gauge(const char* name)
3826 {
3827 	const char* gauge_name;
3828 	size_t j;
3829 
3830 	// go through all gauges and return the gauge that matches
3831 	if(!Ship_info[Player_ship->ship_info_index].hud_gauges.empty()) {
3832 		for(j = 0; j < Ship_info[Player_ship->ship_info_index].hud_gauges.size(); j++) {
3833 
3834 			gauge_name = Ship_info[Player_ship->ship_info_index].hud_gauges[j]->getCustomGaugeName();
3835 			if(!strcmp(name, gauge_name)) {
3836 				return Ship_info[Player_ship->ship_info_index].hud_gauges[j].get();
3837 			}
3838 		}
3839 	} else {
3840 		for(j = 0; j < default_hud_gauges.size(); j++) {
3841 
3842 			gauge_name = default_hud_gauges[j]->getCustomGaugeName();
3843 			if(!strcmp(name, gauge_name)) {
3844 				return default_hud_gauges[j].get();
3845 			}
3846 		}
3847 	}
3848 
3849 	return NULL;
3850 }
3851 
HudGaugeMultiMsg()3852 HudGaugeMultiMsg::HudGaugeMultiMsg():
3853 HudGauge(HUD_OBJECT_MULTI_MSG, HUD_MESSAGE_LINES, false, true, 0, 255, 255, 255)
3854 {
3855 }
3856 
canRender()3857 bool HudGaugeMultiMsg::canRender()
3858 {
3859 	return true;
3860 }
3861 
3862 /**
3863  * @brief Render multiplayer text message currently being entered, if any
3864  */
render(float)3865 void HudGaugeMultiMsg::render(float  /*frametime*/)
3866 {
3867 	char txt[MULTI_MSG_MAX_TEXT_LEN+20];
3868 
3869 	// clear the text
3870 	memset(txt,0,MULTI_MSG_MAX_TEXT_LEN+20);
3871 
3872 	// if there is valid multiplayer message text to be displayed
3873 	if(multi_msg_message_text(txt)){
3874 		gr_set_color_fast(&Color_normal);
3875 		renderString(position[0], position[1], txt);
3876 	}
3877 }
3878 
HudGaugeVoiceStatus()3879 HudGaugeVoiceStatus::HudGaugeVoiceStatus():
3880 HudGauge(HUD_OBJECT_VOICE_STATUS, HUD_MESSAGE_LINES, false, true, VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY, 255, 255, 255)
3881 {
3882 }
3883 
render(float)3884 void HudGaugeVoiceStatus::render(float  /*frametime*/)
3885 {
3886 	if(!(Game_mode & GM_MULTIPLAYER)){
3887 		return;
3888 	}
3889 
3890 	// if we are currently playing a rtvoice sound stream from another player back
3891 	switch(multi_voice_status()){
3892 	// the player has been denied the voice token
3893 	case MULTI_VOICE_STATUS_DENIED:
3894 		// show a red indicator or something
3895 		renderString(position[0], position[1], XSTR( "[voice denied]", 243));
3896 		break;
3897 
3898 	// the player is currently recording
3899 	case MULTI_VOICE_STATUS_RECORDING:
3900 		renderString(position[0], position[1], XSTR( "[recording voice]", 244));
3901 		break;
3902 
3903 	// the player is current playing back voice from someone
3904 	case MULTI_VOICE_STATUS_PLAYING:
3905 		renderString(position[0], position[1], XSTR( "[playing voice]", 245));
3906 		break;
3907 
3908 	// nothing voice related is happening on my machine
3909 	case MULTI_VOICE_STATUS_IDLE:
3910 		// probably shouldn't be displaying anything
3911 		break;
3912 	}
3913 }
3914 
HudGaugePing()3915 HudGaugePing::HudGaugePing():
3916 HudGauge(HUD_OBJECT_PING, HUD_LAG_GAUGE, false, false, 0, 255, 255, 255)
3917 {
3918 
3919 }
3920 
3921 /**
3922  * @brief Render multiplayer ping time to the server, if appropriate
3923  */
render(float)3924 void HudGaugePing::render(float  /*frametime*/)
3925 {
3926 	// If we shouldn't be displaying a ping time, return here
3927 	if(!multi_show_ingame_ping()){
3928 		return;
3929 	}
3930 
3931 	// If we're in multiplayer mode, display our ping time to the server
3932 	if(MULTIPLAYER_CLIENT && (Net_player != NULL)){
3933 		char ping_str[50];
3934 		memset(ping_str,0,50);
3935 
3936 		// If our ping is positive, display it
3937 		if((Netgame.server != NULL) && (Netgame.server->s_info.ping.ping_avg > 0)){
3938 			// Get the string
3939 			if(Netgame.server->s_info.ping.ping_avg >= 1000){
3940 				strcpy_s(ping_str,XSTR("> 1 sec",628));
3941 			} else {
3942 				sprintf(ping_str,XSTR("%d ms",629),Netgame.server->s_info.ping.ping_avg);
3943 			}
3944 
3945 			// Blit the string out
3946 			hud_set_default_color();
3947 			renderString(position[0], position[1], ping_str);
3948 		}
3949 	}
3950 }
3951 
HudGaugeSupernova()3952 HudGaugeSupernova::HudGaugeSupernova():
3953 HudGauge(HUD_OBJECT_SUPERNOVA, HUD_DIRECTIVES_VIEW, false, false, 0, 255, 255, 255)
3954 {
3955 }
3956 
render(float)3957 void HudGaugeSupernova::render(float  /*frametime*/)
3958 {
3959 	float time_left;
3960 
3961 	// if there's a supernova coming
3962 	time_left = supernova_time_left();
3963 	if(time_left < 0.0f){
3964 		return;
3965 	}
3966 
3967 	gr_set_color_fast(&Color_bright_red);
3968 	if (Lcl_pl) {
3969 		renderPrintf(position[0], position[1], "Wybuch supernowej: %.2f s", time_left);
3970 	} else {
3971 		renderPrintf(position[0], position[1], XSTR( "Supernova Warning: %.2f s", 1639), time_left);
3972 	}
3973 }
3974 
HudGaugeFlightPath()3975 HudGaugeFlightPath::HudGaugeFlightPath():
3976 HudGauge(HUD_OBJECT_FLIGHT_PATH, HUD_CENTER_RETICLE, false, false, VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY, 255, 255, 255)
3977 {
3978 }
3979 
initBitmap(const char * fname)3980 void HudGaugeFlightPath::initBitmap(const char *fname)
3981 {
3982 	Marker.first_frame = bm_load_animation(fname, &Marker.num_frames);
3983 
3984 	if ( Marker.first_frame < 0 ) {
3985 		Warning(LOCATION,"Cannot load hud ani: %s\n", fname);
3986 	}
3987 }
3988 
initHalfSize(int w,int h)3989 void HudGaugeFlightPath::initHalfSize(int w, int h)
3990 {
3991 	Marker_half[0] = w;
3992 	Marker_half[1] = h;
3993 }
3994 
render(float)3995 void HudGaugeFlightPath::render(float  /*frametime*/)
3996 {
3997 	object *obj;
3998 	vec3d p0,v;
3999 	vertex v0;
4000 	int sx, sy;
4001 
4002 	bool in_frame = g3_in_frame() > 0;
4003 	if(!in_frame) {
4004 		g3_start_frame(0);
4005 	}
4006 
4007 	obj = Player_obj;
4008 
4009 	vm_vec_scale_add( &v, &obj->phys_info.vel, &obj->orient.vec.fvec, 1.0f );
4010 	vm_vec_normalize( &v );
4011 
4012 	vm_vec_scale_add( &p0, &obj->pos, &v, 1000000.0f );
4013 
4014 	g3_rotate_vertex( &v0, &p0 );
4015 
4016 	if (v0.codes == 0) { // on screen
4017 		g3_project_vertex(&v0);
4018 
4019 		if (!(v0.flags & PF_OVERFLOW)) {
4020 			if ( Marker.first_frame >= 0 ) {
4021 				sx = fl2i(v0.screen.xyw.x);
4022 				sy = fl2i(v0.screen.xyw.y);
4023 
4024 				unsize(&sx, &sy);
4025 				renderBitmap(Marker.first_frame, sx - Marker_half[0], sy - Marker_half[1]);
4026 			}
4027 		}
4028 	}
4029 
4030 	if(!in_frame) {
4031 		g3_end_frame();
4032 	}
4033 }
4034