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