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 
12 #include "asteroid/asteroid.h"
13 #include "cmdline/cmdline.h"
14 #include "debris/debris.h"
15 #include "freespace.h"
16 #include "gamesnd/gamesnd.h"
17 #include "graphics/matrix.h"
18 #include "hud/hudbrackets.h"
19 #include "hud/hudtargetbox.h"
20 #include "iff_defs/iff_defs.h"
21 #include "io/timer.h"
22 #include "jumpnode/jumpnode.h"
23 #include "localization/localize.h"
24 #include "mission/missionparse.h"
25 #include "model/model.h"
26 #include "network/multi.h"
27 #include "object/object.h"
28 #include "object/objectdock.h"
29 #include "parse/parselo.h"
30 #include "playerman/player.h"
31 #include "ship/ship.h"
32 #include "ship/subsysdamage.h"
33 #include "species_defs/species_defs.h"
34 #include "weapon/emp.h"
35 #include "weapon/weapon.h"
36 
37 #ifndef NDEBUG
38 #include "hud/hudets.h"
39 #endif
40 
41 
42 extern float View_zoom;
43 
44 int Target_window_coords[GR_NUM_RESOLUTIONS][4] =
45 {
46 	{ // GR_640
47 		8, 362, 131, 112
48 	},
49 	{ // GR_1024
50 		8, 629, 131, 112
51 	}
52 };
53 
54 object *Enemy_attacker = NULL;
55 
56 static int Target_static_next;
57 static int Target_static_playing;
58 sound_handle Target_static_looping = sound_handle::invalid();
59 
60 int Target_display_cargo;
61 char Cargo_string[256] = "";
62 
63 #ifndef NDEBUG
64 extern int Show_target_debug_info;
65 extern int Show_target_weapons;
66 #endif
67 
68 // used to print out + or - after target distance and speed
69 const  char* modifiers[] = {
70 //XSTR:OFF
71 "+",
72 "-",
73 ""
74 //XSTR:ON
75 };
76 
77 #define NUM_TBOX_COORDS			11	// keep up to date
78 #define TBOX_BACKGROUND			0
79 #define TBOX_NAME					1
80 #define TBOX_CLASS				2
81 #define TBOX_DIST					3
82 #define TBOX_SPEED				4
83 #define TBOX_CARGO				5
84 #define TBOX_HULL					6
85 #define TBOX_EXTRA				7
86 #define TBOX_EXTRA_ORDERS		8
87 #define TBOX_EXTRA_TIME			9
88 #define TBOX_EXTRA_DOCK			10
89 
90 // cargo scanning extents
91 int Cargo_scan_coords[GR_NUM_RESOLUTIONS][4] = {
92 	{ // GR_640
93 		7, 364, 130, 109
94 	},
95 	{ // GR_1024
96 		7, 635, 130, 109
97 	}
98 };
99 
100 // first element is time flashing expires
101 int Targetbox_flash_timers[NUM_TBOX_FLASH_TIMERS];
102 
103 int Targetbox_wire = 0;
104 int Targetbox_shader_effect = -1;
105 bool Lock_targetbox_mode = false;
106 
107 // Different target states.  This drives the text display right below the hull integrity on the targetbox.
108 #define TS_DIS		0
109 #define TS_OK		1
110 #define TS_DMG		2
111 #define TS_CRT		3
112 
113 static int Current_ts; // holds current target status
114 static int Last_ts;	// holds last target status.
115 
116 /**
117  * @note Cut down long subsystem names to a more manageable length
118  */
hud_targetbox_truncate_subsys_name(char * outstr)119 void hud_targetbox_truncate_subsys_name(char *outstr)
120 {
121 	if(Lcl_gr){
122 		if ( strstr(outstr, "communication") )	{
123 			strcpy(outstr, "Komm");
124 		} else if ( !stricmp(outstr, "weapons") ) {
125 			strcpy(outstr, "Waffen");
126 		} else if ( strstr(outstr, "engine") || strstr(outstr, "Engine")) {
127 			strcpy(outstr, "Antrieb");
128 		} else if ( !stricmp(outstr, "sensors") ) {
129 			strcpy(outstr, "Sensoren");
130 		} else if ( strstr(outstr, "navigat") ) {
131 			strcpy(outstr, "Nav");
132 		} else if ( strstr(outstr, "fighterbay") || strstr(outstr, "Fighterbay") ) {
133 			strcpy(outstr, "J\x84gerhangar");
134 		} else if ( strstr(outstr, "missile") ) {
135 			strcpy(outstr, "Raketenwerfer");
136 		} else if ( strstr(outstr, "laser") || strstr(outstr, "turret") ) {
137 			strcpy(outstr, "Gesch\x81tzturm");
138 		} else if ( strstr(outstr, "Command Tower") || strstr(outstr, "Bridge") ) {
139 			strcpy(outstr, "Br\x81""cke");
140 		} else if ( strstr(outstr, "Barracks") ) {
141 			strcpy(outstr, "Quartiere");
142 		} else if ( strstr(outstr, "Reactor") ) {
143 			strcpy(outstr, "Reaktor");
144 		} else if ( strstr(outstr, "RadarDish") ) {
145 			strcpy(outstr, "Radarantenne");
146 		} else if (!stricmp(outstr, "Gas Collector")) {
147 			strcpy(outstr, "Sammler");
148 		}
149 	} else if(Lcl_fr){
150 		if ( strstr(outstr, "communication") )	{
151 			strcpy(outstr, "comm");
152 		} else if ( !stricmp(outstr, "weapons") ) {
153 			strcpy(outstr, "armes");
154 		} else if ( strstr(outstr, "engine") ) {
155 			strcpy(outstr, "moteur");
156 		} else if ( !stricmp(outstr, "sensors") ) {
157 			strcpy(outstr, "detecteurs");
158 		} else if ( strstr(outstr, "navi") ) {
159 			strcpy(outstr, "nav");
160 		} else if ( strstr(outstr, "missile") ) {
161 			strcpy(outstr, "lanceur de missiles");
162 		} else if ( strstr(outstr, "fighter") ) {
163 			strcpy(outstr, "baie de chasse");
164 		} else if ( strstr(outstr, "laser") || strstr(outstr, "turret") || strstr(outstr, "missile") ) {
165 			strcpy(outstr, "tourelle");
166 		}
167 	} else if(Lcl_pl){
168 		if ( strstr(outstr, "communication") )	{
169 			strcpy(outstr, "komunikacja");
170 		} else if ( !stricmp(outstr, "weapons") ) {
171 			strcpy(outstr, "uzbrojenie");
172 		} else if ( strstr(outstr, "engine") || strstr(outstr, "Engine")) {
173 			strcpy(outstr, "silnik");
174 		} else if ( !stricmp(outstr, "sensors") ) {
175 			strcpy(outstr, "sensory");
176 		} else if ( strstr(outstr, "navigat") ) {
177 			strcpy(outstr, "nawigacja");
178 		} else if ( strstr(outstr, "fighterbay") || strstr(outstr, "Fighterbay") ) {
179 			strcpy(outstr, "dok my\x9Cliw.");
180 		} else if ( strstr(outstr, "missile") ) {
181 			strcpy(outstr, "wie\xBF. rakiet.");
182 		} else if ( strstr(outstr, "laser") || strstr(outstr, "turret") ) {
183 			strcpy(outstr, "wie\xBFyczka");
184 		} else if ( strstr(outstr, "Command Tower") || strstr(outstr, "Bridge") ) {
185 			strcpy(outstr, "mostek");
186 		} else if ( strstr(outstr, "Barracks") ) {
187 			strcpy(outstr, "koszary");
188 		} else if ( strstr(outstr, "Reactor") ) {
189 			strcpy(outstr, "reaktor");
190 		} else if ( strstr(outstr, "RadarDish") || strstr(outstr, "Radar Dish") ) {
191 			strcpy(outstr, "antena radaru");
192 		} else if (!stricmp(outstr, "Gas Collector")) {
193 			strcpy(outstr, "zbieracz gazu");
194 		}
195 	} else {
196 		if (strstr(outstr, XSTR("communication", 333)))	{
197 			strcpy(outstr, XSTR("comm", 334));
198 		} else if (strstr(outstr, XSTR("navigation", 335)))	{
199 			strcpy(outstr, XSTR("nav", 336));
200 		} else if (strstr(outstr, "gas collector")) {
201 			strcpy(outstr, "collector");
202 		}
203 	}
204 }
205 
HudGaugeTargetBox()206 HudGaugeTargetBox::HudGaugeTargetBox():
207 	HudGauge(HUD_OBJECT_TARGET_MONITOR, HUD_TARGET_MONITOR, false, false, (VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY), 255, 255, 255),
208 	Monitor_mask(-1),
209 	Use_subsys_name_offsets(false),
210 	Use_subsys_integrity_offsets(false),
211 	Use_disabled_status_offsets(false)
212 {
213 }
214 
initViewportOffsets(int x,int y)215 void HudGaugeTargetBox::initViewportOffsets(int x, int y)
216 {
217 	Viewport_offsets[0] = x;
218 	Viewport_offsets[1] = y;
219 }
220 
initViewportSize(int w,int h)221 void HudGaugeTargetBox::initViewportSize(int w, int h)
222 {
223 	Viewport_w = w;
224 	Viewport_h = h;
225 }
226 
initIntegrityOffsets(int x,int y)227 void HudGaugeTargetBox::initIntegrityOffsets(int x, int y)
228 {
229 	Integrity_bar_offsets[0] = x;
230 	Integrity_bar_offsets[1] = y;
231 }
232 
initIntegrityHeight(int h)233 void HudGaugeTargetBox::initIntegrityHeight(int h)
234 {
235 	integrity_bar_h = h;
236 }
237 
initStatusOffsets(int x,int y)238 void HudGaugeTargetBox::initStatusOffsets(int x, int y)
239 {
240 	Status_offsets[0] = x;
241 	Status_offsets[1] = y;
242 }
243 
initNameOffsets(int x,int y)244 void HudGaugeTargetBox::initNameOffsets(int x, int y)
245 {
246 	Name_offsets[0] = x;
247 	Name_offsets[1] = y;
248 }
249 
initClassOffsets(int x,int y)250 void HudGaugeTargetBox::initClassOffsets(int x, int y)
251 {
252 	Class_offsets[0] = x;
253 	Class_offsets[1] = y;
254 }
255 
initDistOffsets(int x,int y)256 void HudGaugeTargetBox::initDistOffsets(int x, int y)
257 {
258 	Dist_offsets[0] = x;
259 	Dist_offsets[1] = y;
260 }
261 
initSpeedOffsets(int x,int y)262 void HudGaugeTargetBox::initSpeedOffsets(int x, int y)
263 {
264 	Speed_offsets[0] = x;
265 	Speed_offsets[1] = y;
266 }
267 
initCargoStringOffsets(int x,int y)268 void HudGaugeTargetBox::initCargoStringOffsets(int x, int y)
269 {
270 	Cargo_string_offsets[0] = x;
271 	Cargo_string_offsets[1] = y;
272 }
273 
initHullOffsets(int x,int y)274 void HudGaugeTargetBox::initHullOffsets(int x, int y)
275 {
276 	Hull_offsets[0] = x;
277 	Hull_offsets[1] = y;
278 }
279 
initCargoScanStartOffsets(int x,int y)280 void HudGaugeTargetBox::initCargoScanStartOffsets(int x, int y)
281 {
282 	Cargo_scan_start_offsets[0] = x;
283 	Cargo_scan_start_offsets[1] = y;
284 }
285 
initCargoScanSize(int w,int h)286 void HudGaugeTargetBox::initCargoScanSize(int w, int h)
287 {
288 	Cargo_scan_w = w;
289 	Cargo_scan_h = h;
290 }
291 
initSubsysNameOffsets(int x,int y,bool activate)292 void HudGaugeTargetBox::initSubsysNameOffsets(int x, int y, bool activate)
293 {
294 	Subsys_name_offsets[0] = x;
295 	Subsys_name_offsets[1] = y;
296 	Use_subsys_name_offsets = activate;
297 }
298 
initSubsysIntegrityOffsets(int x,int y,bool activate)299 void HudGaugeTargetBox::initSubsysIntegrityOffsets(int x, int y, bool activate)
300 {
301 	Subsys_integrity_offsets[0] = x;
302 	Subsys_integrity_offsets[1] = y;
303 	Use_subsys_integrity_offsets = activate;
304 }
305 
initDisabledStatusOffsets(int x,int y,bool activate)306 void HudGaugeTargetBox::initDisabledStatusOffsets(int x, int y, bool activate)
307 {
308 	Disabled_status_offsets[0] = x;
309 	Disabled_status_offsets[1] = y;
310 	Use_disabled_status_offsets = activate;
311 }
312 
initDesaturate(bool desaturate)313 void HudGaugeTargetBox::initDesaturate(bool desaturate)
314 {
315 	Desaturated = desaturate;
316 }
317 
initBitmaps(char * fname_monitor,char * fname_monitor_mask,char * fname_integrity,char * fname_static)318 void HudGaugeTargetBox::initBitmaps(char *fname_monitor, char *fname_monitor_mask, char *fname_integrity, char *fname_static)
319 {
320 	Monitor_frame.first_frame = bm_load_animation(fname_monitor, &Monitor_frame.num_frames);
321 	if ( Monitor_frame.first_frame < 0 ) {
322 		Warning(LOCATION,"Cannot load hud ani: %s\n", fname_monitor);
323 	}
324 
325 	Integrity_bar.first_frame = bm_load_animation(fname_integrity, &Integrity_bar.num_frames);
326 	if ( Integrity_bar.first_frame < 0 ) {
327 		Warning(LOCATION,"Cannot load hud ani: %s\n", fname_integrity);
328 	}
329 
330 	if ( strlen(fname_monitor_mask) > 0 ) {
331 		Monitor_mask = bm_load_animation(fname_monitor_mask);
332 
333 		if ( Monitor_mask < 0 ) {
334 			Warning(LOCATION, "Cannot load bitmap hud mask: %s\n", fname_monitor_mask);
335 		}
336 	}
337 
338 	strcpy_s(static_fname, fname_static);
339 }
340 
initialize()341 void HudGaugeTargetBox::initialize()
342 {
343 	hud_anim_init(&Monitor_static, position[0] + Viewport_offsets[0], position[1] + Viewport_offsets[1], NOX(static_fname));
344 
345 	for(int i = 0; i < NUM_TBOX_FLASH_TIMERS; i++) {
346 		initFlashTimer(i);
347 	}
348 
349 	HudGauge::initialize();
350 }
351 
initFlashTimer(int index)352 void HudGaugeTargetBox::initFlashTimer(int index)
353 {
354 	Next_flash_timers[index] = 1;
355 	flash_flags &= ~(1<<index);
356 }
357 
render(float frametime)358 void HudGaugeTargetBox::render(float frametime)
359 {
360 	object	*target_objp;
361 
362 	if ( Player_ai->target_objnum == -1)
363 		return;
364 
365 	if ( Target_static_playing )
366 		return;
367 
368 	target_objp = &Objects[Player_ai->target_objnum];
369 
370 	setGaugeColor();
371 
372 	// blit the background frame
373 	renderBitmap(Monitor_frame.first_frame, position[0], position[1]);
374 
375 	if ( Monitor_mask >= 0 ) {
376 		// render the alpha mask
377 		gr_alpha_mask_set(1, 0.5f);
378 		gr_stencil_clear();
379 		gr_stencil_set(GR_STENCIL_WRITE);
380 		gr_set_color_buffer(0);
381 
382 		renderBitmapColor(Monitor_mask, position[0], position[1]);
383 
384 		gr_set_color_buffer(1);
385 		gr_stencil_set(GR_STENCIL_NONE);
386 		gr_alpha_mask_set(0, 1.0f);
387 	}
388 
389 	switch ( target_objp->type ) {
390 		case OBJ_SHIP:
391 			renderTargetShip(target_objp);
392 			break;
393 
394 		case OBJ_DEBRIS:
395 			renderTargetDebris(target_objp);
396 			break;
397 
398 		case OBJ_WEAPON:
399 			renderTargetWeapon(target_objp);
400 			break;
401 
402 		case OBJ_ASTEROID:
403 			renderTargetAsteroid(target_objp);
404 			break;
405 
406 		case OBJ_JUMP_NODE:
407 			renderTargetJumpNode(target_objp);
408 			break;
409 
410 		default:
411 			hud_cease_targeting();
412 			break;
413 	} // end switch
414 
415 	if ( Target_static_playing ) {
416 		setGaugeColor();
417 		gr_set_screen_scale(base_w, base_h);
418 		hud_anim_render(&Monitor_static, frametime, 1);
419 		gr_reset_screen_scale();
420 	} else {
421 		showTargetData(frametime);
422 	}
423 
424 	if(Target_display_cargo) {
425 		// Print out what the cargo is
426 		if ( maybeFlashSexp() == 1 ) {
427 			setGaugeColor(HUD_C_BRIGHT);
428 		} else {
429 			maybeFlashElement(TBOX_FLASH_CARGO);
430 		}
431 
432 		renderString(position[0] + Cargo_string_offsets[0], position[1] + Cargo_string_offsets[1], EG_TBOX_CARGO, Cargo_string);
433 	}
434 }
435 
renderTargetForeground()436 void HudGaugeTargetBox::renderTargetForeground()
437 {
438 	setGaugeColor();
439 
440 	renderBitmap(Monitor_frame.first_frame+1, position[0], position[1]);
441 }
442 
443 /**
444  * Draw the integrity bar that is on the right of the target monitor
445  */
renderTargetIntegrity(int disabled,int force_obj_num)446 void HudGaugeTargetBox::renderTargetIntegrity(int disabled,int force_obj_num)
447 {
448 	int		clip_h,w,h;
449 	char		buf[16];
450 
451 	if ( Integrity_bar.first_frame == -1 )
452 		return;
453 
454 	if ( disabled ) {
455 		renderBitmap(Integrity_bar.first_frame, position[0] + Integrity_bar_offsets[0], position[1] + Integrity_bar_offsets[1]);
456 		return;
457 	}
458 
459 	if(force_obj_num == -1)
460 		Assert(Player_ai->target_objnum >= 0 );
461 
462 	clip_h = fl2i( (1 - Pl_target_integrity) * integrity_bar_h );
463 
464 	// print out status of ship
465 	switch(Current_ts) {
466 		case TS_DIS:
467 			strcpy_s(buf,XSTR( "dis", 344));
468 			break;
469 		case TS_OK:
470 			strcpy_s(buf,XSTR( "ok", 345));
471 			break;
472 		case TS_DMG:
473 			strcpy_s(buf,XSTR( "dmg", 346));
474 			break;
475 		case TS_CRT:
476 			strcpy_s(buf,XSTR( "crt", 347));
477 			break;
478 	}
479 
480 	maybeFlashElement(TBOX_FLASH_STATUS);
481 
482 	// finally print out the status of this ship
483 	renderString(position[0] + Status_offsets[0], position[1] + Status_offsets[1], EG_TBOX_INTEG, buf);
484 
485 	setGaugeColor();
486 
487 	bm_get_info(Integrity_bar.first_frame,&w,&h);
488 
489 	if ( clip_h > 0 ) {
490 		// draw the dark portion
491 		renderBitmapEx(Integrity_bar.first_frame, position[0] + Integrity_bar_offsets[0], position[1] + Integrity_bar_offsets[1], w, clip_h,0,0);
492 	}
493 
494 	if ( clip_h <= integrity_bar_h ) {
495 		// draw the bright portion
496 		renderBitmapEx(Integrity_bar.first_frame+1, position[0] + Integrity_bar_offsets[0], position[1] + Integrity_bar_offsets[1]+clip_h,w,h-clip_h,0,clip_h);
497 	}
498 }
499 
renderTargetSetup(vec3d * camera_eye,matrix * camera_orient,float zoom)500 void HudGaugeTargetBox::renderTargetSetup(vec3d *camera_eye, matrix *camera_orient, float zoom)
501 {
502 	// JAS: g3_start_frame uses clip_width and clip_height to determine the
503 	// size to render to.  Normally, you would set this by using gr_set_clip,
504 	// but because of the hacked in hud jittering, I couldn't.  So come talk
505 	// to me before modifying or reusing the following code. Thanks.
506 
507 	int clip_width = Viewport_w;
508 	int clip_height = Viewport_h;
509 
510 	gr_screen.clip_width = clip_width;
511 	gr_screen.clip_height = clip_height;
512 	g3_start_frame(1);		// Turn on zbuffering
513 	hud_save_restore_camera_data(1);
514 	g3_set_view_matrix( camera_eye, camera_orient, zoom);
515 	model_set_detail_level(1);		// use medium detail level
516 
517 	setClip(position[0] + Viewport_offsets[0], position[1] + Viewport_offsets[1], Viewport_w, Viewport_h);
518 
519 	// account for gauge RTT with cockpit here --wookieejedi
520 	float clip_aspect;
521 	if (gr_screen.rendering_to_texture != -1) {
522 		clip_aspect = (i2fl(clip_width) / i2fl(clip_height));
523 	} else {
524 		clip_aspect = gr_screen.clip_aspect;
525 	}
526 
527 	gr_set_proj_matrix(Proj_fov, clip_aspect, Min_draw_distance, Max_draw_distance);
528 	gr_set_view_matrix(&Eye_position, &Eye_matrix);
529 }
530 
renderTargetShip(object * target_objp)531 void HudGaugeTargetBox::renderTargetShip(object *target_objp)
532 {
533 	vec3d		obj_pos = ZERO_VECTOR;
534 	vec3d		camera_eye = ZERO_VECTOR;
535 	matrix		camera_orient = IDENTITY_MATRIX;
536 	ship		*target_shipp;
537 	ship_info	*target_sip;
538 	vec3d		orient_vec, up_vector;
539 	int			sx, sy;
540 	int			subsys_in_view;
541 	float		factor;
542 
543 	target_shipp	= &Ships[target_objp->instance];
544 	target_sip		= &Ship_info[target_shipp->ship_info_index];
545 
546 	int flags=0;
547 	if ( Detail.targetview_model )	{
548 		// take the forward orientation to be the vector from the player to the current target
549 		vm_vec_sub(&orient_vec, &target_objp->pos, &Player_obj->pos);
550 		vm_vec_normalize(&orient_vec);
551 
552 		factor = -target_sip->closeup_pos_targetbox.xyz.z;
553 
554 		// use the player's up vector, and construct the viewers orientation matrix
555 		if (Player_obj->type == OBJ_SHIP) {
556 			vec3d tempv;
557 			ship_get_eye(&tempv, &camera_orient, Player_obj, false, false);
558 		} else {
559 			camera_orient = Player_obj->orient;
560 		}
561 
562 		up_vector = camera_orient.vec.uvec;
563 		vm_vector_2_matrix(&camera_orient,&orient_vec,&up_vector,NULL);
564 
565 		// normalize the vector from the player to the current target, and scale by a factor to calculate
566 		// the objects position
567 		vm_vec_copy_scale(&obj_pos,&orient_vec,factor);
568 
569 		// RT, changed scaling here
570 		renderTargetSetup(&camera_eye, &camera_orient, target_sip->closeup_zoom_targetbox);
571 
572 		// IMPORTANT NOTE! Code handling the case 'missile_view == TRUE' in rendering section of renderTargetWeapon()
573 		//                 is largely copied over from renderTargetShip(). To keep the codes similar please update
574 		//                 both if and when needed
575 		model_render_params render_info;
576 		render_info.set_object_number(OBJ_INDEX(target_objp));
577 
578 		switch (Targetbox_wire) {
579 			case 0:
580 				flags |= MR_NO_LIGHTING;
581 
582 				break;
583 			case 1:
584 				if (ship_is_tagged(target_objp))
585 					render_info.set_color(*iff_get_color(IFF_COLOR_TAGGED, 1));
586 				else
587 					render_info.set_color(*iff_get_color_by_team_and_object(target_shipp->team, Player_ship->team, 1, target_objp));
588 
589 				if (target_sip->uses_team_colors) {
590 					render_info.set_team_color(target_shipp->team_name, target_shipp->secondary_team_name, target_shipp->team_change_timestamp, target_shipp->team_change_time);
591 				}
592 
593 				flags = MR_SHOW_OUTLINE_HTL | MR_NO_POLYS | MR_NO_LIGHTING | MR_NO_TEXTURING;
594 
595 				break;
596 			case 2:
597 				break;
598 			case 3:
599 				if (ship_is_tagged(target_objp))
600 					render_info.set_color(*iff_get_color(IFF_COLOR_TAGGED, 1));
601 				else
602 					render_info.set_color(*iff_get_color_by_team_and_object(target_shipp->team, Player_ship->team, 1, target_objp));
603 
604 				flags |= MR_NO_LIGHTING | MR_NO_TEXTURING;
605 
606 				break;
607 		}
608 
609 		if (target_sip->hud_target_lod >= 0) {
610 			render_info.set_detail_level_lock(target_sip->hud_target_lod);
611 		}
612 
613 		if(Targetbox_shader_effect > -1) {
614 			render_info.set_animated_effect(Targetbox_shader_effect, 0.0f);
615 		}
616 
617 		if ( Monitor_mask >= 0 ) {
618 			gr_stencil_set(GR_STENCIL_READ);
619 		}
620 
621 		if ( Desaturated ) {
622 			flags |= MR_DESATURATED;
623 			render_info.set_color(gauge_color);
624 		}
625 
626 		if (!Glowpoint_override)
627 			Glowpoint_override = true;
628 
629 		// set glowmap flag here since model_render (etc) require an objnum to handle glowmaps
630 		// if we did pass the objnum, we'd also have thrusters drawn in the targetbox
631 		if (target_shipp->flags[Ship::Ship_Flags::Glowmaps_disabled]) {
632 			flags |= MR_NO_GLOWMAPS;
633 		}
634 
635 		render_info.set_flags(flags | MR_AUTOCENTER | MR_NO_FOGGING);
636 
637 		// maybe render a special hud-target-only model
638 		if(target_sip->model_num_hud >= 0){
639 			model_render_immediate( &render_info, target_sip->model_num_hud, &target_objp->orient, &obj_pos);
640 		} else {
641 			render_info.set_replacement_textures(target_shipp->ship_replacement_textures);
642 
643 			model_render_immediate( &render_info, target_sip->model_num, &target_objp->orient, &obj_pos);
644 		}
645 
646 		Glowpoint_override = false;
647 
648 		if ( Monitor_mask >= 0 ) {
649 			gr_stencil_set(GR_STENCIL_NONE);
650 		}
651 
652 		sx = 0;
653 		sy = 0;
654 		// check if subsystem target has changed
655 		if ( Player_ai->targeted_subsys == Player_ai->last_subsys_target ) {
656 			vec3d save_pos;
657 
658 			if (gr_screen.rendering_to_texture != -1) {
659 				gr_set_screen_scale(canvas_w, canvas_h, -1, -1, target_w, target_h, target_w, target_h, true);
660 			} else {
661 				gr_set_screen_scale(base_w, base_h);
662 			}
663 
664 			save_pos = target_objp->pos;
665 			target_objp->pos = obj_pos;
666 			subsys_in_view = hud_targetbox_subsystem_in_view(target_objp, &sx, &sy);
667 			target_objp->pos = save_pos;
668 
669 			if ( subsys_in_view != -1 ) {
670 
671 				// AL 29-3-98: If subsystem is destroyed, draw gray brackets
672 				// Goober5000 - hm, caught a tricky bug for destroyable fighterbays
673 				if ( (Player_ai->targeted_subsys->current_hits <= 0) && ship_subsys_takes_damage(Player_ai->targeted_subsys) ) {
674 					gr_set_color_fast(iff_get_color(IFF_COLOR_MESSAGE, 1));
675 				} else {
676 					hud_set_iff_color( target_objp, 1 );
677 				}
678 
679 				graphics::line_draw_list line_draw_list;
680 				if ( subsys_in_view ) {
681 					draw_brackets_square_quick(&line_draw_list, sx - 10, sy - 10, sx + 10, sy + 10);
682 				} else {
683 					draw_brackets_diamond_quick(&line_draw_list, sx - 10, sy - 10, sx + 10, sy + 10);
684 				}
685 				line_draw_list.flush();
686 			}
687 		}
688 		renderTargetClose();
689 	}
690 	renderTargetForeground();
691 	renderTargetIntegrity(0,OBJ_INDEX(target_objp));
692 
693 	setGaugeColor();
694 
695 	renderTargetShipInfo(target_objp);
696 	maybeRenderCargoScan(target_sip);
697 }
698 
699 /**
700  * @note formerly hud_render_target_debris(object *target_objp) (Swifty)
701  */
renderTargetDebris(object * target_objp)702 void HudGaugeTargetBox::renderTargetDebris(object *target_objp)
703 {
704 	vec3d	obj_pos = ZERO_VECTOR;
705 	vec3d	camera_eye = ZERO_VECTOR;
706 	matrix	camera_orient = IDENTITY_MATRIX;
707 	debris	*debrisp;
708 	vec3d	orient_vec, up_vector;
709 	float		factor;
710 	int flags=0;
711 
712 	debrisp = &Debris[target_objp->instance];
713 
714 	if ( Detail.targetview_model )	{
715 		// take the forward orientation to be the vector from the player to the current target
716 		vm_vec_sub(&orient_vec, &target_objp->pos, &Player_obj->pos);
717 		vm_vec_normalize(&orient_vec);
718 
719 		factor = 2*target_objp->radius;
720 
721 		// use the player's up vector, and construct the viewers orientation matrix
722 		if (Player_obj->type == OBJ_SHIP) {
723 			vec3d tempv;
724 			ship_get_eye(&tempv, &camera_orient, Player_obj, false, false);
725 		} else {
726 			camera_orient = Player_obj->orient;
727 		}
728 
729 		up_vector = camera_orient.vec.uvec;
730 		vm_vector_2_matrix(&camera_orient,&orient_vec,&up_vector,NULL);
731 
732 		// normalize the vector from the player to the current target, and scale by a factor to calculate
733 		// the objects position
734 		vm_vec_copy_scale(&obj_pos,&orient_vec,factor);
735 
736 		renderTargetSetup(&camera_eye, &camera_orient, 0.5f);
737 		model_clear_instance(debrisp->model_num);
738 
739 		model_render_params render_info;
740 
741 		switch (Targetbox_wire) {
742 			case 0:
743 				flags |= MR_NO_LIGHTING;
744 
745 				break;
746 			case 1:
747 				render_info.set_color(255, 255, 255);
748 
749 				flags = MR_SHOW_OUTLINE_HTL | MR_NO_POLYS | MR_NO_LIGHTING | MR_NO_TEXTURING;
750 
751 				break;
752 			case 2:
753 				break;
754 			case 3:
755 				render_info.set_color(255, 255, 255);
756 
757 				flags |= MR_NO_LIGHTING | MR_NO_TEXTURING;
758 
759 				break;
760 		}
761 
762 		if(Targetbox_shader_effect > -1) {
763 			render_info.set_animated_effect(Targetbox_shader_effect, 0.0f);
764 		}
765 
766 		if ( Monitor_mask >= 0 ) {
767 			gr_stencil_set(GR_STENCIL_READ);
768 		}
769 
770 		if ( Desaturated ) {
771 			flags |= MR_DESATURATED;
772 			render_info.set_color(gauge_color);
773 		}
774 
775 		render_info.set_flags(flags | MR_NO_FOGGING);
776 
777 		auto pmi = model_get_instance(debrisp->model_instance_num);
778 		auto pm = model_get(pmi->model_num);
779 
780 		// This calls the colour that doesn't get reset
781 		submodel_render_immediate( &render_info, pm, pmi, debrisp->submodel_num, &target_objp->orient, &obj_pos);
782 
783 		if ( Monitor_mask >= 0 ) {
784 			gr_stencil_set(GR_STENCIL_NONE);
785 		}
786 
787 		renderTargetClose();
788 	}
789 	renderTargetForeground();
790 	renderTargetIntegrity(1);
791 
792 	// print out ship class that debris came from
793 	const char *printable_ship_class;
794 	if (debrisp->parent_alt_name >= 0)
795 		printable_ship_class = mission_parse_lookup_alt_index(debrisp->parent_alt_name);
796 	else
797 		printable_ship_class = Ship_info[debrisp->ship_info_index].get_display_name();
798 
799 	renderString(position[0] + Class_offsets[0], position[1] + Class_offsets[1], EG_TBOX_CLASS, printable_ship_class);
800 	renderString(position[0] + Name_offsets[0], position[1] + Name_offsets[1], EG_TBOX_NAME, XSTR("debris", 348));
801 }
802 
803 /**
804  * @note Formerly hud_render_target_weapon(object *target_objp)
805  */
renderTargetWeapon(object * target_objp)806 void HudGaugeTargetBox::renderTargetWeapon(object *target_objp)
807 {
808 	vec3d		obj_pos = ZERO_VECTOR;
809 	vec3d		camera_eye = ZERO_VECTOR;
810 	matrix		camera_orient = IDENTITY_MATRIX;
811 	vec3d		orient_vec, up_vector;
812 	vec3d		projection_vec;
813 	weapon_info	*target_wip = NULL;
814 	weapon		*wp = NULL;
815 	object		*viewer_obj, *viewed_obj;
816 	int *replacement_textures = NULL;
817 	int			target_team, is_homing, is_player_missile, missile_view, viewed_model_num, hud_target_lod, w, h;
818 	int flags=0;
819 
820 	target_team = obj_team(target_objp);
821 
822 	wp = &Weapons[target_objp->instance];
823 	target_wip = &Weapon_info[wp->weapon_info_index];
824 
825 	if (target_wip->model_num == -1)
826 		return;
827 
828 	is_homing = FALSE;
829 	if ( target_wip->is_homing() && wp->homing_object != &obj_used_list )
830 		is_homing = TRUE;
831 
832 	is_player_missile = FALSE;
833 	if ( target_objp->parent_sig == Player_obj->signature ) {
834 		is_player_missile = TRUE;
835 	}
836 
837 	if ( Detail.targetview_model )	{
838 		ship *homing_shipp = NULL;
839 		ship_info *homing_sip = NULL;
840 
841 		viewer_obj			= Player_obj;
842 		viewed_obj			= target_objp;
843 		missile_view		= FALSE;
844 		viewed_model_num	= target_wip->model_num;
845 		hud_target_lod		= target_wip->hud_target_lod;
846 		// adding a check here to make sure the homing object is a ship, technically it could be some other object just as well
847 		if ( is_homing && is_player_missile && (wp->homing_object->type == OBJ_SHIP)) {
848 			homing_shipp = &Ships[wp->homing_object->instance];
849 			homing_sip = &Ship_info[homing_shipp->ship_info_index];
850 
851 			viewer_obj			= target_objp;
852 			viewed_obj			= wp->homing_object;
853 			missile_view		= TRUE;
854 			viewed_model_num	= homing_sip->model_num;
855 			replacement_textures = homing_shipp->ship_replacement_textures;
856 			hud_target_lod		= homing_sip->hud_target_lod;
857 		}
858 
859 		// take the forward orientation to be the vector from the player to the current target
860 		vm_vec_sub(&orient_vec, &viewed_obj->pos, &viewer_obj->pos);
861 		vm_vec_normalize(&orient_vec);
862 
863 		if (missile_view == TRUE) {
864 			vm_vec_sub(&projection_vec, &wp->homing_pos, &viewer_obj->pos);
865 			vm_vec_normalize(&projection_vec);
866 		}
867 
868 		// use the viewer's up vector, and construct the viewers orientation matrix
869 		if (viewer_obj == Player_obj && Player_obj->type == OBJ_SHIP) {
870 			vec3d tempv;
871 			ship_get_eye(&tempv, &camera_orient, Player_obj, false, false);
872 		} else {
873 			camera_orient = viewer_obj->orient;
874 		}
875 
876 		up_vector = camera_orient.vec.uvec;
877 
878 		if (missile_view == FALSE)
879 			vm_vector_2_matrix(&camera_orient,&orient_vec,&up_vector,NULL);
880 		else
881 			vm_vector_2_matrix(&camera_orient,&projection_vec,&up_vector,NULL);
882 
883 		// normalize the vector from the viewer to the viwed target, and scale by a factor to calculate
884 		// the objects position
885 		if (missile_view == FALSE) {
886 			float factor = 2*target_objp->radius;
887 			// small radius missiles need a bigger factor otherwise they are rendered larger than the targetbox
888 			if (factor < 8.0f) {
889 				factor = 8.0f;
890 			}
891 			vm_vec_copy_scale(&obj_pos,&orient_vec,factor);
892 		} else {
893 			vm_vec_sub(&obj_pos, &viewed_obj->pos, &viewer_obj->pos);
894 		}
895 
896 		renderTargetSetup(&camera_eye, &camera_orient, View_zoom/3);
897 		model_clear_instance(viewed_model_num);
898 
899 		model_render_params render_info;
900 
901 		// IMPORTANT NOTE! Code handling the rendering when 'missile_view == TRUE' is largely copied over from
902 		//                 renderTargetShip(). To keep the codes similar please update both if and when needed
903 		if (missile_view == FALSE) {
904 			switch (Targetbox_wire) {
905 				case 0:
906 					flags |= MR_NO_LIGHTING;
907 
908 					break;
909 				case 1:
910 					render_info.set_color(*iff_get_color_by_team_and_object(target_team, Player_ship->team, 0, target_objp));
911 
912 					flags = MR_SHOW_OUTLINE_HTL | MR_NO_POLYS | MR_NO_LIGHTING | MR_NO_TEXTURING;
913 
914 					break;
915 				case 2:
916 					break;
917 				case 3:
918 					render_info.set_color(*iff_get_color_by_team_and_object(target_team, Player_ship->team, 0, target_objp));
919 
920 					flags |= MR_NO_LIGHTING | MR_NO_TEXTURING;
921 
922 					break;
923 			}
924 		} else {
925 			render_info.set_object_number(OBJ_INDEX(viewed_obj));
926 
927 			switch (Targetbox_wire) {
928 				case 0:
929 					flags |= MR_NO_LIGHTING;
930 
931 					break;
932 				case 1:
933 					if (ship_is_tagged(viewed_obj))
934 						render_info.set_color(*iff_get_color(IFF_COLOR_TAGGED, 1));
935 					else
936 						render_info.set_color(*iff_get_color_by_team_and_object(homing_shipp->team, Player_ship->team, 1, viewed_obj));
937 
938 					if (homing_sip->uses_team_colors) {
939 						render_info.set_team_color(homing_shipp->team_name, homing_shipp->secondary_team_name, homing_shipp->team_change_timestamp, homing_shipp->team_change_time);
940 					}
941 
942 					flags = MR_SHOW_OUTLINE_HTL | MR_NO_POLYS | MR_NO_LIGHTING | MR_NO_TEXTURING;
943 
944 					break;
945 				case 2:
946 					break;
947 				case 3:
948 					if (ship_is_tagged(viewed_obj))
949 						render_info.set_color(*iff_get_color(IFF_COLOR_TAGGED, 1));
950 					else
951 						render_info.set_color(*iff_get_color_by_team_and_object(homing_shipp->team, Player_ship->team, 1, viewed_obj));
952 
953 					flags |= MR_NO_LIGHTING | MR_NO_TEXTURING;
954 
955 					break;
956 			}
957 		}
958 
959 		if (hud_target_lod >= 0) {
960 			render_info.set_detail_level_lock(hud_target_lod);
961 		}
962 
963 		if(Targetbox_shader_effect > -1) {
964 			render_info.set_animated_effect(Targetbox_shader_effect, 0.0f);
965 		}
966 
967 		if ( Monitor_mask >= 0 ) {
968 			gr_stencil_set(GR_STENCIL_READ);
969 		}
970 
971 		if ( Desaturated ) {
972 			flags |= MR_DESATURATED;
973 			render_info.set_color(gauge_color);
974 		}
975 
976 		if (missile_view == TRUE) {
977 			if (!Glowpoint_override)
978 				Glowpoint_override = true;
979 
980 			// set glowmap flag here since model_render (etc) require an objnum to handle glowmaps
981 			// if we did pass the objnum, we'd also have thrusters drawn in the targetbox
982 			if (homing_shipp->flags[Ship::Ship_Flags::Glowmaps_disabled]) {
983 				flags |= MR_NO_GLOWMAPS;
984 			}
985 		}
986 
987 		if (missile_view == FALSE ) {
988 			render_info.set_flags(flags | MR_AUTOCENTER | MR_IS_MISSILE | MR_NO_FOGGING);
989 			render_info.set_replacement_textures(replacement_textures);
990 
991 			model_render_immediate( &render_info, viewed_model_num, &viewed_obj->orient, &obj_pos );
992 		} else {
993 			// maybe render a special hud-target-only model
994 			// autocentering is bad in this one
995 			if(homing_sip->model_num_hud >= 0){
996 				render_info.set_flags(flags | MR_NO_FOGGING);
997 
998 				model_render_immediate( &render_info, homing_sip->model_num_hud, &viewed_obj->orient, &obj_pos);
999 			} else {
1000 				render_info.set_flags(flags | MR_NO_FOGGING);
1001 				render_info.set_replacement_textures(homing_shipp->ship_replacement_textures);
1002 
1003 				model_render_immediate( &render_info, homing_sip->model_num, &viewed_obj->orient, &obj_pos );
1004 			}
1005 		}
1006 
1007 		if (missile_view == TRUE) {
1008 			Glowpoint_override = false;
1009 		}
1010 
1011 		if ( Monitor_mask >= 0 ) {
1012 			gr_stencil_set(GR_STENCIL_NONE);
1013 		}
1014 
1015 		renderTargetClose();
1016 	}
1017 	renderTargetForeground();
1018 
1019 	renderTargetIntegrity(1);
1020 	setGaugeColor();
1021 
1022 	// print out the weapon class name
1023 	auto weapon_name = target_wip->get_display_name();
1024 	gr_get_string_size(&w,&h,weapon_name);
1025 
1026 	renderString(position[0] + Name_offsets[0], position[1] + Name_offsets[1], EG_TBOX_NAME, weapon_name);
1027 
1028 	// If a homing weapon, show time to impact
1029 	if ( is_homing ) {
1030 		float dist, speed;
1031 		char			outstr[100];				// temp buffer
1032 
1033 		speed = vm_vec_mag(&target_objp->phys_info.vel);
1034 
1035 		// do the extra math only if it won't lead to null vec issues
1036 		if(!(IS_VEC_NULL_SQ_SAFE(&target_objp->phys_info.vel))){
1037 			vec3d unit_vec, component_vec;
1038 
1039 			// in other words substract the magnitude of the target's velocity vectors parallel component from the speed of the weapon
1040 			vm_vec_copy_normalize(&unit_vec, &target_objp->phys_info.vel);
1041 			speed -= vm_vec_projection_parallel(&component_vec, &wp->homing_object->phys_info.vel, &unit_vec);
1042 		}
1043 
1044 		dist = vm_vec_dist(&target_objp->pos, &wp->homing_pos);
1045 
1046 		if ( speed > 0 ) {
1047 			sprintf(outstr, XSTR("impact: %.1f sec", 1596), dist/speed);
1048 		} else {
1049 			strcpy_s(outstr, XSTR( "unknown", 349));
1050 		}
1051 
1052 		renderString(position[0] + Class_offsets[0], position[1] + Class_offsets[1], EG_TBOX_CLASS, outstr);
1053 	}
1054 }
1055 
1056 /**
1057  * @note Formerly hud_render_target_asteroid(object *target_objp)
1058  */
renderTargetAsteroid(object * target_objp)1059 void HudGaugeTargetBox::renderTargetAsteroid(object *target_objp)
1060 {
1061 	vec3d		obj_pos = ZERO_VECTOR;
1062 	vec3d		camera_eye = ZERO_VECTOR;
1063 	matrix		camera_orient = IDENTITY_MATRIX;
1064 	asteroid		*asteroidp;
1065 	vec3d		orient_vec, up_vector;
1066 	float			time_to_impact, factor;
1067 	int			pof;
1068 
1069 	int flags=0;									//draw flags for wireframe
1070 	asteroidp = &Asteroids[target_objp->instance];
1071 
1072 	pof = asteroidp->asteroid_subtype;
1073 
1074 	time_to_impact = asteroid_time_to_impact(target_objp);
1075 
1076 	if ( Detail.targetview_model )	{
1077 		// take the forward orientation to be the vector from the player to the current target
1078 		vm_vec_sub(&orient_vec, &target_objp->pos, &Player_obj->pos);
1079 		vm_vec_normalize(&orient_vec);
1080 
1081 		factor = 2*target_objp->radius;
1082 
1083 		// use the player's up vector, and construct the viewers orientation matrix
1084 		if (Player_obj->type == OBJ_SHIP) {
1085 			vec3d tempv;
1086 			ship_get_eye(&tempv, &camera_orient, Player_obj, false, false);
1087 		} else {
1088 			camera_orient = Player_obj->orient;
1089 		}
1090 
1091 		up_vector = camera_orient.vec.uvec;
1092 		vm_vector_2_matrix(&camera_orient,&orient_vec,&up_vector,NULL);
1093 
1094 		// normalize the vector from the player to the current target, and scale by a factor to calculate
1095 		// the objects position
1096 		vm_vec_copy_scale(&obj_pos,&orient_vec,factor);
1097 
1098 		renderTargetSetup(&camera_eye, &camera_orient, 0.5f);
1099 		model_clear_instance(Asteroid_info[asteroidp->asteroid_type].model_num[pof]);
1100 
1101 		model_render_params render_info;
1102 
1103 		switch (Targetbox_wire) {
1104 			case 0:
1105 				flags |= MR_NO_LIGHTING;
1106 
1107 				break;
1108 			case 1:
1109 				if (time_to_impact>=0)
1110 					render_info.set_color(255,255,255);
1111 				else
1112 					render_info.set_color(64,64,0);
1113 
1114 				flags = MR_SHOW_OUTLINE_HTL | MR_NO_POLYS | MR_NO_LIGHTING | MR_NO_TEXTURING;
1115 
1116 				break;
1117 			case 2:
1118 				break;
1119 			case 3:
1120 				if (time_to_impact>=0)
1121 					render_info.set_color(255,255,255);
1122 				else
1123 					render_info.set_color(64,64,0);
1124 
1125 				flags |= MR_NO_LIGHTING | MR_NO_TEXTURING;
1126 
1127 				break;
1128 		}
1129 
1130 		if(Targetbox_shader_effect > -1) {
1131 			render_info.set_animated_effect(Targetbox_shader_effect, 0.0f);
1132 		}
1133 
1134 		if ( Monitor_mask >= 0 ) {
1135 			gr_stencil_set(GR_STENCIL_READ);
1136 		}
1137 
1138 		if ( Desaturated ) {
1139 			flags |= MR_DESATURATED;
1140 			render_info.set_color(gauge_color);
1141 		}
1142 
1143 		render_info.set_flags(flags | MR_NO_FOGGING);
1144 
1145 		model_render_immediate( &render_info, Asteroid_info[asteroidp->asteroid_type].model_num[pof], &target_objp->orient, &obj_pos );
1146 
1147 		if ( Monitor_mask >= 0 ) {
1148 			gr_stencil_set(GR_STENCIL_NONE);
1149 		}
1150 
1151 		renderTargetClose();
1152 	}
1153 	renderTargetForeground();
1154 	renderTargetIntegrity(1);
1155 	setGaugeColor();
1156 
1157 	// hud print type of Asteroid (debris)
1158 	char hud_name[64];
1159 	switch (asteroidp->asteroid_type) {
1160 		case ASTEROID_TYPE_SMALL:
1161 		case ASTEROID_TYPE_MEDIUM:
1162 		case ASTEROID_TYPE_LARGE:
1163 			strcpy_s(hud_name, XSTR("asteroid", 431));
1164 			break;
1165 
1166 		default:
1167 			strcpy_s(hud_name, Asteroid_info[asteroidp->asteroid_type].name);
1168 			break;
1169 	}
1170 
1171 	renderString(position[0] + Name_offsets[0], position[1] + Name_offsets[1], EG_TBOX_NAME, hud_name);
1172 
1173 
1174 	if ( time_to_impact >= 0.0f ) {
1175 		renderPrintf(position[0] + Class_offsets[0], position[1] + Class_offsets[1], EG_TBOX_CLASS, XSTR("impact: %.1f sec", 1596), time_to_impact);
1176 	}
1177 }
1178 
1179 /**
1180  * Render a jump node on the target monitor
1181  * @note Formerly hud_render_target_jump_node(object *target_objp)
1182  */
renderTargetJumpNode(object * target_objp)1183 void HudGaugeTargetBox::renderTargetJumpNode(object *target_objp)
1184 {
1185 	char			outstr[256];
1186 	vec3d		obj_pos = ZERO_VECTOR;
1187 	vec3d		camera_eye = ZERO_VECTOR;
1188 	matrix		camera_orient = IDENTITY_MATRIX;
1189 	vec3d		orient_vec, up_vector;
1190 	float			factor, dist;
1191 	int			hx, hy, w, h;
1192 	SCP_list<CJumpNode>::iterator jnp;
1193 
1194 	for (jnp = Jump_nodes.begin(); jnp != Jump_nodes.end(); ++jnp) {
1195 		if(jnp->GetSCPObject() != target_objp)
1196 			continue;
1197 
1198 		if ( jnp->IsHidden() ) {
1199 			set_target_objnum( Player_ai, -1 );
1200 			return;
1201 		}
1202 
1203 		if ( Detail.targetview_model )	{
1204 			// take the forward orientation to be the vector from the player to the current target
1205 			vm_vec_sub(&orient_vec, &target_objp->pos, &Player_obj->pos);
1206 			vm_vec_normalize(&orient_vec);
1207 
1208 			factor = target_objp->radius*4.0f;
1209 
1210 			// use the player's up vector, and construct the viewers orientation matrix
1211 			if (Player_obj->type == OBJ_SHIP) {
1212 				vec3d tempv;
1213 				ship_get_eye(&tempv, &camera_orient, Player_obj, false, false);
1214 			} else {
1215 				camera_orient = Player_obj->orient;
1216 			}
1217 
1218 			up_vector = camera_orient.vec.uvec;
1219 			vm_vector_2_matrix(&camera_orient,&orient_vec,&up_vector,NULL);
1220 
1221 			// normalize the vector from the player to the current target, and scale by a factor to calculate
1222 			// the objects position
1223 			vm_vec_copy_scale(&obj_pos,&orient_vec,factor);
1224 
1225 			renderTargetSetup(&camera_eye, &camera_orient, 0.5f);
1226 
1227 			if ( Monitor_mask >= 0 ) {
1228 				gr_stencil_set(GR_STENCIL_READ);
1229 			}
1230 
1231 			jnp->Render( &obj_pos );
1232 
1233 			if ( Monitor_mask >= 0 ) {
1234 				gr_stencil_set(GR_STENCIL_NONE);
1235 			}
1236 
1237 			renderTargetClose();
1238 		}
1239 
1240 		renderTargetForeground();
1241 		renderTargetIntegrity(1);
1242 		setGaugeColor();
1243 
1244 		strcpy_s(outstr, jnp->GetName());
1245 		end_string_at_first_hash_symbol(outstr);
1246 		renderString(position[0] + Name_offsets[0], position[1] + Name_offsets[1], EG_TBOX_NAME, outstr);
1247 
1248 		dist = Player_ai->current_target_distance;
1249 		if ( Hud_unit_multiplier > 0.0f ) {	// use a different displayed distance scale
1250 			dist = dist * Hud_unit_multiplier;
1251 		}
1252 
1253 		// account for hud shaking
1254 		hx = fl2i(HUD_offset_x);
1255 		hy = fl2i(HUD_offset_y);
1256 
1257 		sprintf(outstr,XSTR( "d: %.0f", 340), dist);
1258 		hud_num_make_mono(outstr, font_num);
1259 		gr_get_string_size(&w,&h,outstr);
1260 
1261 		renderPrintf(position[0] + Dist_offsets[0]+hx, position[1] + Dist_offsets[1]+hy, EG_TBOX_DIST, "%s", outstr);
1262 	}
1263 }
1264 
1265 /**
1266  * Toggle through the valid targetbox modes
1267  *
1268  * @note 0==standard
1269  * @note 1==wireframe only
1270  * @note 2==standard with lighting
1271  */
hud_targetbox_switch_wireframe_mode()1272 void hud_targetbox_switch_wireframe_mode()
1273 {
1274 
1275 	Targetbox_wire++;
1276 		if (Targetbox_wire==3)
1277 			Targetbox_wire=0;
1278 }
1279 
1280 /**
1281  * Init a specific targetbox timer
1282  */
hud_targetbox_init_flash_timer(int index)1283 void hud_targetbox_init_flash_timer(int index)
1284 {
1285 	Targetbox_flash_timers[index] = 1;
1286 }
1287 
1288 /**
1289  * Init the timers used to flash different parts of the targetbox.
1290  *
1291  * @note This needs to get called whenever the current target changes.
1292  * @note Need to call initFlashTimers for any TargetBox gauges and call initDockFlashTimer() for Extra Target Info gauges (Switfty)
1293  */
hud_targetbox_init_flash()1294 void hud_targetbox_init_flash()
1295 {
1296 	for(int i = 0; i < NUM_TBOX_FLASH_TIMERS; i++) {
1297 		hud_targetbox_init_flash_timer(i);
1298 	}
1299 
1300 	Last_ts = -1;
1301 	Current_ts = -1;
1302 }
1303 
maybeFlashElement(int index,int flash_fast)1304 int HudGaugeTargetBox::maybeFlashElement(int index, int flash_fast)
1305 {
1306 	int draw_bright=0;
1307 
1308 	setGaugeColor();
1309 	if ( !timestamp_elapsed(Targetbox_flash_timers[index]) ) {
1310 		if ( timestamp_elapsed(Next_flash_timers[index]) ) {
1311 			if ( flash_fast ) {
1312 				Next_flash_timers[index] = timestamp(fl2i(TBOX_FLASH_INTERVAL/2.0f));
1313 			} else {
1314 				Next_flash_timers[index] = timestamp(TBOX_FLASH_INTERVAL);
1315 			}
1316 			flash_flags ^= (1<<index);	// toggle between default and bright frames
1317 		}
1318 
1319 		if ( flash_flags & (1<<index) ) {
1320 			setGaugeColor(HUD_C_BRIGHT);
1321 			draw_bright=1;
1322 		} else {
1323 			setGaugeColor(HUD_C_DIM);
1324 		}
1325 	}
1326 
1327 	return draw_bright;
1328 }
1329 
renderTargetClose()1330 void HudGaugeTargetBox::renderTargetClose()
1331 {
1332 	gr_end_view_matrix();
1333 	gr_end_proj_matrix();
1334 
1335 	g3_end_frame();
1336 	hud_save_restore_camera_data(0);
1337 	resetClip();
1338 }
1339 
1340 /**
1341  * Get the shield and hull percentages for a given ship object
1342  *
1343  * @param objp		Pointer to ship object that you want strength values for
1344  * @param shields	OUTPUT parameter:	percentage value of shields (0->1.0)
1345  * @param integrity OUTPUT parameter: percentage value of integrity (0->1.0)
1346  */
hud_get_target_strength(object * objp,float * shields,float * integrity)1347 void hud_get_target_strength(object *objp, float *shields, float *integrity)
1348 {
1349 	*shields = get_shield_pct(objp);
1350 	*integrity = get_hull_pct(objp);
1351 }
1352 
HudGaugeExtraTargetData()1353 HudGaugeExtraTargetData::HudGaugeExtraTargetData():
1354 HudGauge(HUD_OBJECT_EXTRA_TARGET_DATA, HUD_TARGET_MONITOR_EXTRA_DATA, false, false, (VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY), 255, 255, 255)
1355 {
1356 	initDockFlashTimer();
1357 }
1358 
initialize()1359 void HudGaugeExtraTargetData::initialize()
1360 {
1361 	initDockFlashTimer();
1362 
1363 	HudGauge::initialize();
1364 }
1365 
initBracketOffsets(int x,int y)1366 void HudGaugeExtraTargetData::initBracketOffsets(int x, int y)
1367 {
1368 	bracket_offsets[0] = x;
1369 	bracket_offsets[1] = y;
1370 }
1371 
initDockOffsets(int x,int y)1372 void HudGaugeExtraTargetData::initDockOffsets(int x, int y)
1373 {
1374 	dock_offsets[0] = x;
1375 	dock_offsets[1] = y;
1376 }
1377 
initDockMaxWidth(int width)1378 void HudGaugeExtraTargetData::initDockMaxWidth(int width)
1379 {
1380 	dock_max_w = width;
1381 }
1382 
initTimeOffsets(int x,int y)1383 void HudGaugeExtraTargetData::initTimeOffsets(int x, int y)
1384 {
1385 	time_offsets[0] = x;
1386 	time_offsets[1] = y;
1387 }
1388 
initOrderOffsets(int x,int y)1389 void HudGaugeExtraTargetData::initOrderOffsets(int x, int y)
1390 {
1391 	order_offsets[0] = x;
1392 	order_offsets[1] = y;
1393 }
1394 
initOrderMaxWidth(int width)1395 void HudGaugeExtraTargetData::initOrderMaxWidth(int width)
1396 {
1397 	order_max_w = width;
1398 }
1399 
initBitmaps(char * fname)1400 void HudGaugeExtraTargetData::initBitmaps(char *fname)
1401 {
1402 	bracket.first_frame = bm_load_animation(fname, &bracket.num_frames);
1403 	if ( bracket.first_frame < 0 ) {
1404 		Warning(LOCATION,"Cannot load hud ani: %s\n", fname);
1405 	}
1406 }
1407 
pageIn()1408 void HudGaugeExtraTargetData::pageIn()
1409 {
1410 	bm_page_in_aabitmap( bracket.first_frame, bracket.num_frames );
1411 }
1412 
1413 /**
1414  * @note Formerly hud_targetbox_show_extra_ship_info(target_shipp, target_objp) (Swifty)
1415  */
render(float)1416 void HudGaugeExtraTargetData::render(float  /*frametime*/)
1417 {
1418 	char tmpbuf[256];
1419 	int has_orders = 0;
1420 	int not_training;
1421 	int extra_data_shown=0;
1422 
1423 	if(!canRender())
1424 		return;
1425 
1426 	if ( Player_ai->target_objnum == -1)
1427 		return;
1428 
1429 	if ( Target_static_playing )
1430 		return;
1431 
1432 	object	*target_objp;
1433 	target_objp = &Objects[Player_ai->target_objnum];
1434 
1435 	// only render if this the current target is type OBJ_SHIP
1436 	if(target_objp->type != OBJ_SHIP)
1437 		return;
1438 
1439 	ship* target_shipp	= &Ships[target_objp->instance];
1440 
1441 	setGaugeColor();
1442 
1443 	not_training = !(The_mission.game_type & MISSION_TYPE_TRAINING);
1444 	if ( not_training) {
1445 		// Print out current orders if the targeted ship is friendly
1446 		// AL 12-26-97: only show orders and time to target for friendly ships
1447 		// Backslash: actually let's consult the IFF table.  Maybe we want to show orders for certain teams, or hide orders for friendlies
1448 		if (	((Player_ship->team == target_shipp->team) ||
1449 					((Iff_info[target_shipp->team].flags & IFFF_ORDERS_SHOWN) && !(Iff_info[target_shipp->team].flags & IFFF_ORDERS_HIDDEN)))
1450 				&& Ship_info[target_shipp->ship_info_index].is_flyable() ) {
1451 			extra_data_shown = 1;
1452 			auto orders = ship_return_orders(target_shipp);
1453 			if (!orders.empty()) {
1454 				char outstr[256];
1455 				strcpy_s(outstr, orders.c_str());
1456 				font::force_fit_string(outstr, 255, order_max_w);
1457 				orders = outstr;
1458 				has_orders = 1;
1459 			} else {
1460 				orders = XSTR("no orders", 337);
1461 			}
1462 
1463 			renderString(position[0] + order_offsets[0], position[1] + order_offsets[1], EG_TBOX_EXTRA1, orders.c_str());
1464 		}
1465 
1466 		if ( has_orders ) {
1467 			char outstr[256];
1468 			strcpy_s(outstr, XSTR( "time to: ", 338));
1469 			if ( ship_return_time_to_goal(tmpbuf, target_shipp) ) {
1470 				strcat_s(outstr, tmpbuf);
1471 
1472 				renderString(position[0] + time_offsets[0], position[1] + time_offsets[1], EG_TBOX_EXTRA2, outstr);
1473 			}
1474 		}
1475 	}
1476 
1477 	if (Player_ai->last_target != Player_ai->target_objnum) {
1478 		endFlashDock();
1479 	}
1480 
1481 	// Print out dock status
1482 	if ( object_is_docked(target_objp) )
1483 	{
1484 		startFlashDock(2000);
1485 		// count the objects directly docked to me
1486 		int dock_count = dock_count_direct_docked_objects(target_objp);
1487 
1488 		char outstr[256];
1489 		// docked to only one object
1490 		if (dock_count == 1)
1491 		{
1492 			sprintf(outstr, XSTR("Docked: %s", 339), Ships[dock_get_first_docked_object(target_objp)->instance].get_display_name());
1493 		}
1494 		// docked to multiple objects
1495 		else
1496 		{
1497 			sprintf(outstr, XSTR("Docked: %d objects", 1623), dock_count);
1498 		}
1499 
1500 		font::force_fit_string(outstr, 255, dock_max_w);
1501 		maybeFlashDock();
1502 
1503 		renderString(position[0] + dock_offsets[0], position[1] + dock_offsets[1], EG_TBOX_EXTRA3, outstr);
1504 		extra_data_shown=1;
1505 	}
1506 
1507 	if ( extra_data_shown ) {
1508 		renderBitmap(bracket.first_frame, position[0] + bracket_offsets[0], position[1] + bracket_offsets[1]);
1509 	}
1510 }
1511 
initDockFlashTimer()1512 void HudGaugeExtraTargetData::initDockFlashTimer()
1513 {
1514 	flash_timer[0] = 1;
1515 	flash_timer[1] = 1;
1516 	flash_flags = false;
1517 }
1518 
startFlashDock(int duration)1519 void HudGaugeExtraTargetData::startFlashDock(int duration)
1520 {
1521 	flash_timer[0] = timestamp(duration);
1522 }
1523 
maybeFlashDock(int flash_fast)1524 int HudGaugeExtraTargetData::maybeFlashDock(int flash_fast)
1525 {
1526 	int draw_bright=0;
1527 
1528 	setGaugeColor();
1529 	if ( !timestamp_elapsed(flash_timer[0]) ) {
1530 		if ( timestamp_elapsed(flash_timer[1]) ) {
1531 			if ( flash_fast ) {
1532 				flash_timer[1] = timestamp(fl2i(TBOX_FLASH_INTERVAL/2.0f));
1533 			} else {
1534 				flash_timer[1] = timestamp(TBOX_FLASH_INTERVAL);
1535 			}
1536 
1537 			// toggle between default and bright frames
1538 			if(flash_flags)
1539 				flash_flags = false;
1540 			else
1541 				flash_flags = true;
1542 		}
1543 
1544 		if (flash_flags) {
1545 			setGaugeColor(HUD_C_BRIGHT);
1546 			draw_bright=1;
1547 		} else {
1548 			setGaugeColor(HUD_C_DIM);
1549 		}
1550 	}
1551 
1552 	return draw_bright;
1553 }
1554 
endFlashDock()1555 void HudGaugeExtraTargetData::endFlashDock()
1556 {
1557 	flash_timer[0] = timestamp(0);
1558 }
1559 
1560 //from aicode.cpp. Less include...problems...this way.
1561 extern flagset<Weapon::Info_Flags> turret_weapon_aggregate_flags(ship_weapon *swp);
1562 extern bool turret_weapon_has_subtype(ship_weapon *swp, int subtype);
get_turret_subsys_name(ship_weapon * swp,char * outstr)1563 void get_turret_subsys_name(ship_weapon *swp, char *outstr)
1564 {
1565 	Assert(swp != NULL);	// Goober5000 //WMC
1566 
1567 	//WMC - find the first weapon, if there is one
1568 	if (swp->num_primary_banks || swp->num_secondary_banks) {
1569 		auto flags = turret_weapon_aggregate_flags(swp);
1570 
1571 		// check if beam or flak using weapon flags
1572 		if (flags[Weapon::Info_Flags::Beam]) {
1573 			sprintf(outstr, "%s", XSTR("Beam turret", 1567));
1574 		} else if (flags[Weapon::Info_Flags::Flak]) {
1575 			sprintf(outstr, "%s", XSTR("Flak turret", 1566));
1576 		} else {
1577 			if (turret_weapon_has_subtype(swp, WP_MISSILE)) {
1578 				sprintf(outstr, "%s", XSTR("Missile lnchr", 1569));
1579 			} else if (turret_weapon_has_subtype(swp, WP_LASER)) {
1580 				// ballistic too! - Goober5000
1581 				if (flags[Weapon::Info_Flags::Ballistic])
1582 				{
1583 					sprintf(outstr, "%s", XSTR("Turret", 1487));
1584 				}
1585 				// the TVWP has some primaries flagged as bombs
1586 				else if (flags[Weapon::Info_Flags::Bomb])
1587 				{
1588 					sprintf(outstr, "%s", XSTR("Missile lnchr", 1569));
1589 				}
1590 				else
1591 				{
1592 					sprintf(outstr, "%s", XSTR("Laser turret", 1568));
1593 				}
1594 			} else {
1595 				// Mantis #2226: find out if there are any weapons here at all
1596 				if (flags.none_set()) {
1597 					sprintf(outstr, "%s", NOX("Unused"));
1598 				} else {
1599 					// Illegal subtype
1600 					static bool Turret_illegal_subtype_warned = false;
1601 					if (!Turret_illegal_subtype_warned) {
1602 						Turret_illegal_subtype_warned = true;
1603 						Warning(LOCATION, "This turret has an illegal subtype!  Trace out and fix!");
1604 					}
1605 					sprintf(outstr, "%s", XSTR("Turret", 1487));
1606 				}
1607 			}
1608 		}
1609 	} else if(swp->num_tertiary_banks) {
1610 		//TODO: add tertiary turret code stuff here
1611 		sprintf(outstr, "%s", NOX("Unknown"));
1612 	} else {
1613 		// This should not happen
1614 		sprintf(outstr, "%s", NOX("Unused"));
1615 	}
1616 }
1617 
renderTargetShipInfo(object * target_objp)1618 void HudGaugeTargetBox::renderTargetShipInfo(object *target_objp)
1619 {
1620 	ship			*target_shipp;
1621 	int			w, h, screen_integrity = 1;
1622 	char			outstr[NAME_LENGTH];
1623 	char			outstr_name[NAME_LENGTH*2+3];
1624 	char			outstr_class[NAME_LENGTH];
1625 	float			ship_integrity, shield_strength;
1626 
1627 	Assert(target_objp);	// Goober5000
1628 	Assert(target_objp->type == OBJ_SHIP);
1629 	target_shipp = &Ships[target_objp->instance];
1630 
1631 	// set up colors
1632 	if ( HudGauge::maybeFlashSexp() == 1 ) {
1633 		hud_set_iff_color(target_objp, 1);
1634 	} else {
1635 		// Print out ship name, with wing name if it exists
1636 		if ( maybeFlashElement(TBOX_FLASH_NAME) ) {
1637 			hud_set_iff_color(target_objp, 1);
1638 		} else {
1639 			hud_set_iff_color(target_objp);
1640 		}
1641 	}
1642 
1643 	// set up lines
1644 	hud_stuff_ship_name(outstr_name, target_shipp);
1645 	hud_stuff_ship_class(outstr_class, target_shipp);
1646 
1647 	// maybe concatenate the callsign
1648 	if (*outstr_name)
1649 	{
1650 		char outstr_callsign[NAME_LENGTH];
1651 
1652 		hud_stuff_ship_callsign(outstr_callsign, target_shipp);
1653 		if (*outstr_callsign)
1654 			sprintf(&outstr_name[strlen(outstr_name)], " (%s)", outstr_callsign);
1655 	}
1656 	// maybe substitute the callsign
1657 	else
1658 	{
1659 		hud_stuff_ship_callsign(outstr_name, target_shipp);
1660 	}
1661 
1662 	// print lines based on current coords
1663 	renderString(position[0] + Name_offsets[0], position[1] + Name_offsets[1], EG_TBOX_NAME, outstr_name);
1664 	renderString(position[0] + Class_offsets[0], position[1] + Class_offsets[1], EG_TBOX_CLASS, outstr_class);
1665 
1666 	// ----------
1667 
1668 	ship_integrity = 1.0f;
1669 	shield_strength = 1.0f;
1670 	hud_get_target_strength(target_objp, &shield_strength, &ship_integrity);
1671 
1672 	// convert to values of 0->100
1673 	shield_strength *= 100.0f;
1674 	ship_integrity *= 100.0f;
1675 
1676 	screen_integrity = (int)std::lround(ship_integrity);
1677 	if ( screen_integrity == 0 ) {
1678 		if ( ship_integrity > 0 ) {
1679 			screen_integrity = 1;
1680 		}
1681 	}
1682 	// Print out right-justified integrity
1683 	sprintf(outstr, XSTR( "%d%%", 341), screen_integrity);
1684 	gr_get_string_size(&w,&h,outstr);
1685 
1686 	if ( HudGauge::maybeFlashSexp() == 1 ) {
1687 		setGaugeColor(HUD_C_BRIGHT);
1688 	} else {
1689 		maybeFlashElement(TBOX_FLASH_HULL);
1690 	}
1691 
1692 	renderPrintf(position[0] + Hull_offsets[0]-w, position[1] + Hull_offsets[1], EG_TBOX_HULL, "%s", outstr);
1693 	setGaugeColor();
1694 
1695 	// print out the targeted sub-system and % integrity
1696 	if (Player_ai->targeted_subsys != NULL) {
1697 		shield_strength = Player_ai->targeted_subsys->current_hits/Player_ai->targeted_subsys->max_hits * 100.0f;
1698 		screen_integrity = (int)std::lround(shield_strength);
1699 
1700 		if ( screen_integrity < 0 ) {
1701 			screen_integrity = 0;
1702 		}
1703 
1704 		if ( screen_integrity == 0 ) {
1705 			if ( shield_strength > 0 ) {
1706 				screen_integrity = 1;
1707 			}
1708 		}
1709 
1710 		maybeFlashElement(TBOX_FLASH_SUBSYS);
1711 
1712 		// get turret subsys name
1713 		if (Player_ai->targeted_subsys->system_info->type == SUBSYSTEM_TURRET && !ship_subsys_has_instance_name(Player_ai->targeted_subsys)) {
1714 			get_turret_subsys_name(&Player_ai->targeted_subsys->weapons, outstr);
1715 		} else {
1716 			sprintf(outstr, "%s", ship_subsys_get_name(Player_ai->targeted_subsys));
1717 		}
1718 
1719 		char *p_line;
1720 		// hence pipe shall be the linebreak
1721 		char linebreak[2] = "|";
1722 		int n_linebreaks = 0;
1723 		p_line = strpbrk(outstr,linebreak);
1724 
1725 		// figure out how many linebreaks we actually have
1726 		while (p_line != NULL) {
1727 			n_linebreaks++;
1728 			p_line = strpbrk(p_line+1,linebreak);
1729 		}
1730 
1731 		int subsys_name_pos_x;
1732 		int subsys_name_pos_y;
1733 
1734 		if ( Use_subsys_name_offsets ) {
1735 			subsys_name_pos_x = position[0] + Subsys_name_offsets[0];
1736 			subsys_name_pos_y = position[1] + Subsys_name_offsets[1];
1737 		} else {
1738 			subsys_name_pos_x = position[0] + Viewport_offsets[0] + 2;
1739 			subsys_name_pos_y = position[1] + Viewport_offsets[1] + Viewport_h;
1740 		}
1741 
1742 		if (n_linebreaks) {
1743 			p_line = strtok(outstr,linebreak);
1744 			while (p_line != NULL) {
1745 				renderPrintf(subsys_name_pos_x, subsys_name_pos_y-h-((h+1)*n_linebreaks), "%s", p_line);
1746 				p_line = strtok(NULL,linebreak);
1747 				n_linebreaks--;
1748 			}
1749 		} else {
1750 			hud_targetbox_truncate_subsys_name(outstr);
1751 			renderPrintf(subsys_name_pos_x, subsys_name_pos_y-h, "%s", outstr);
1752 		}
1753 
1754 		int subsys_integrity_pos_x;
1755 		int subsys_integrity_pos_y;
1756 
1757 		if ( Use_subsys_integrity_offsets ) {
1758 			subsys_integrity_pos_x = position[0] + Subsys_integrity_offsets[0];
1759 			subsys_integrity_pos_y = position[1] + Subsys_integrity_offsets[1];
1760 		} else {
1761 			subsys_integrity_pos_x = position[0] + Viewport_offsets[0] + Viewport_w - 1;
1762 			subsys_integrity_pos_y = position[1] + Viewport_offsets[1] + Viewport_h;
1763 		}
1764 
1765 		// AL 23-3-98: Fighter bays are a special case.  Player cannot destroy them, so don't
1766 		//					show the subsystem strength
1767 		// Goober5000: don't display any strength if we can't destroy this subsystem - but sometimes
1768 		// fighterbays can be destroyed
1769 		if ( ship_subsys_takes_damage(Player_ai->targeted_subsys) )
1770 		{
1771 			sprintf(outstr,XSTR( "%d%%", 341),screen_integrity);
1772 			gr_get_string_size(&w,&h,outstr);
1773 			renderPrintf(subsys_integrity_pos_x - w, subsys_integrity_pos_y - h, "%s", outstr);
1774 		}
1775 
1776 		setGaugeColor();
1777 	}
1778 
1779 	// print out 'disabled' on the monitor if the target is disabled
1780 	if ( (target_shipp->flags[Ship::Ship_Flags::Disabled]) || (ship_subsys_disrupted(target_shipp, SUBSYSTEM_ENGINE)) ) {
1781 		if ( target_shipp->flags[Ship::Ship_Flags::Disabled] ) {
1782 			strcpy_s(outstr, XSTR( "DISABLED", 342));
1783 		} else {
1784 			strcpy_s(outstr, XSTR( "DISRUPTED", 343));
1785 		}
1786 		gr_get_string_size(&w,&h,outstr);
1787 
1788 		int disabled_status_pos_x;
1789 		int disabled_status_pos_y;
1790 
1791 		if ( Use_disabled_status_offsets ) {
1792 			disabled_status_pos_x = position[0] + Disabled_status_offsets[0];
1793 			disabled_status_pos_y = position[1] + Disabled_status_offsets[1];
1794 		} else {
1795 			disabled_status_pos_x = position[0] + Viewport_offsets[0] + Viewport_w/2 - w/2 - 1;
1796 			disabled_status_pos_y = position[1] + Viewport_offsets[1] + Viewport_h - 2*h;
1797 		}
1798 
1799 		renderPrintf(disabled_status_pos_x, disabled_status_pos_y, "%s", outstr);
1800 	}
1801 }
1802 
1803 /**
1804  * Determine if the subsystem is in line-of sight, without taking into account whether the player ship is
1805  * facing the subsystem
1806  */
hud_targetbox_subsystem_in_view(object * target_objp,int * sx,int * sy)1807 int hud_targetbox_subsystem_in_view(object *target_objp, int *sx, int *sy)
1808 {
1809 	ship_subsys	*subsys;
1810 	vec3d		subobj_pos;
1811 	vertex		subobj_vertex;
1812 	int			rval = -1;
1813 	polymodel	*pm;
1814 
1815 	subsys = Player_ai->targeted_subsys;
1816 	if (subsys != NULL ) {
1817 		get_subsystem_pos(&subobj_pos, target_objp, subsys);
1818 
1819 		// is it subsystem in view
1820 		if ( Player->subsys_in_view == -1 ) {
1821 			rval = ship_subsystem_in_sight(target_objp, subsys, &View_position, &subobj_pos, 0);
1822 		} else {
1823 			rval =  Player->subsys_in_view;
1824 		}
1825 
1826 		// get screen coords, adjusting for autocenter
1827 		Assert(target_objp->type == OBJ_SHIP);
1828 		if (target_objp->type == OBJ_SHIP) {
1829 			pm = model_get(Ship_info[Ships[target_objp->instance].ship_info_index].model_num);
1830 			if (pm->flags & PM_FLAG_AUTOCEN) {
1831 				vec3d temp, delta;
1832 				vm_vec_copy_scale(&temp, &pm->autocenter, -1.0f);
1833 				vm_vec_unrotate(&delta, &temp, &target_objp->orient);
1834 				vm_vec_add2(&subobj_pos, &delta);
1835 			}
1836 		}
1837 
1838 		g3_rotate_vertex(&subobj_vertex, &subobj_pos);
1839 		g3_project_vertex(&subobj_vertex);
1840 		*sx = (int) subobj_vertex.screen.xyw.x;
1841 		*sy = (int) subobj_vertex.screen.xyw.y;
1842 	}
1843 
1844 	return rval;
1845 }
1846 
hud_cargo_scan_update(object * targetp,float frametime)1847 void hud_cargo_scan_update(object *targetp, float frametime)
1848 {
1849 	// update cargo inspection status
1850 	Cargo_string[0] = 0;
1851 	if ( targetp->type == OBJ_SHIP ) {
1852 		Target_display_cargo = player_inspect_cargo(frametime, Cargo_string);
1853 		if ( Target_display_cargo ) {
1854 			if ( Player->cargo_inspect_time > 0 ) {
1855 				hud_targetbox_start_flash(TBOX_FLASH_CARGO);
1856 			}
1857 		}
1858 	}
1859 }
1860 
hud_update_cargo_scan_sound()1861 void hud_update_cargo_scan_sound()
1862 {
1863 	if ( Player->cargo_inspect_time <= 0  ) {
1864 		player_stop_cargo_scan_sound();
1865 		return;
1866 	}
1867 	player_maybe_start_cargo_scan_sound();
1868 
1869 }
1870 
1871 /**
1872  * If the player is scanning for cargo, draw some cool scanning lines on the target monitor
1873  */
maybeRenderCargoScan(ship_info * target_sip)1874 void HudGaugeTargetBox::maybeRenderCargoScan(ship_info *target_sip)
1875 {
1876 	int x1, y1, x2, y2;
1877 	int scan_time;				// time required to scan ship
1878 
1879 	if ( Player->cargo_inspect_time <= 0  ) {
1880 		return;
1881 	}
1882 
1883 	scan_time = target_sip->scan_time;
1884 	setGaugeColor(HUD_C_BRIGHT);
1885 
1886 	// draw horizontal scan line
1887 	x1 = position[0] + Cargo_scan_start_offsets[0]; // Cargo_scan_coords[gr_screen.res][0];
1888 	y1 = fl2i(0.5f + position[1] + Cargo_scan_start_offsets[1] + ( (i2fl(Player->cargo_inspect_time) / scan_time) * Cargo_scan_h ));
1889 	x2 = x1 + Cargo_scan_w;
1890 
1891 	renderLine(x1, y1, x2, y1);
1892 
1893 	// RT Changed this to be optional
1894 	if(Cmdline_dualscanlines) {
1895 		// added 2nd horizontal scan line - phreak
1896 		y1 = fl2i(position[1] + Cargo_scan_start_offsets[1] + Cargo_scan_h - ( (i2fl(Player->cargo_inspect_time) / scan_time) * Cargo_scan_h ));
1897 		renderLine(x1, y1, x2, y1);
1898 	}
1899 
1900 	// draw vertical scan line
1901 	x1 = fl2i(0.5f + position[0] + Cargo_scan_start_offsets[0] + ( (i2fl(Player->cargo_inspect_time) / scan_time) * Cargo_scan_w ));
1902 	y1 = position[1] + Cargo_scan_start_offsets[1];
1903 	y2 = y1 + Cargo_scan_h;
1904 
1905 	renderLine(x1, y1-3, x1, y2-1);
1906 
1907 	// RT Changed this to be optional
1908 	if(Cmdline_dualscanlines) {
1909 		// added 2nd vertical scan line - phreak
1910 		x1 = fl2i(0.5f + Cargo_scan_w + position[0] + Cargo_scan_start_offsets[0] - ( (i2fl(Player->cargo_inspect_time) / scan_time) * Cargo_scan_w ));
1911 		renderLine(x1, y1-3, x1, y2-1);
1912 	}
1913 }
1914 
showTargetData(float)1915 void HudGaugeTargetBox::showTargetData(float  /*frametime*/)
1916 {
1917 	char outstr[256];						// temp buffer for sprintf'ing hud output
1918 	int w,h;									// width and height of string about to print
1919 	object		*target_objp;
1920 	ship			*shipp = NULL;
1921 	debris		*debrisp = NULL;
1922 	ship_info	*sip = NULL;
1923 	int is_ship = 0;
1924 	float		displayed_target_distance, displayed_target_speed, current_target_distance, current_target_speed;
1925 
1926 	setGaugeColor();
1927 
1928 	target_objp = &Objects[Player_ai->target_objnum];
1929 
1930 	current_target_distance = Player_ai->current_target_distance;
1931 
1932 	if ( Hud_unit_multiplier > 0.0f ) {	// use a different displayed distance scale
1933 		displayed_target_distance = current_target_distance * Hud_unit_multiplier;
1934 	} else {
1935 		displayed_target_distance = current_target_distance;
1936 	}
1937 
1938 	switch( Objects[Player_ai->target_objnum].type ) {
1939 		case OBJ_SHIP:
1940 			shipp = &Ships[target_objp->instance];
1941 			sip = &Ship_info[shipp->ship_info_index];
1942 			is_ship = 1;
1943 			break;
1944 
1945 		case OBJ_DEBRIS:
1946 			debrisp = &Debris[target_objp->instance];
1947 			sip = &Ship_info[debrisp->ship_info_index];
1948 			break;
1949 
1950 		case OBJ_WEAPON:
1951 			sip = NULL;
1952 			break;
1953 
1954 		case OBJ_ASTEROID:
1955 			sip = NULL;
1956 			break;
1957 
1958 		case OBJ_JUMP_NODE:
1959 			return;
1960 
1961 		default:
1962 			Int3();	// can't happen
1963 			break;
1964 	}
1965 
1966 	int hx, hy;
1967 
1968 	// Account for HUD shaking
1969 	hx = fl2i(HUD_offset_x);
1970 	hy = fl2i(HUD_offset_y);
1971 
1972 	// print out the target distance and speed
1973 	sprintf(outstr,XSTR( "d: %.0f%s", 350), displayed_target_distance, modifiers[Player_ai->current_target_dist_trend]);
1974 
1975 	hud_num_make_mono(outstr, font_num);
1976 	gr_get_string_size(&w,&h,outstr);
1977 
1978 	renderString(position[0] + Dist_offsets[0]+hx, position[1] + Dist_offsets[1]+hy, EG_TBOX_DIST, outstr);
1979 
1980 #if 0
1981 	current_target_speed = vm_vec_dist(&target_objp->pos, &target_objp->last_pos) / frametime;
1982 #endif
1983 	// 7/28/99 DKA: Do not use vec_mag_quick -- the error is too big
1984 	current_target_speed = vm_vec_mag(&target_objp->phys_info.vel);
1985 	if ( current_target_speed < 0.1f ) {
1986 		current_target_speed = 0.0f;
1987 	}
1988 	// if the speed is 0, determine if we are docked with something -- if so, get the docked velocity
1989 	if ( (current_target_speed == 0.0f) && is_ship ) {
1990 		current_target_speed = dock_calc_docked_fspeed(&Objects[shipp->objnum]);
1991 
1992 		if ( current_target_speed < 0.1f ) {
1993 			current_target_speed = 0.0f;
1994 		}
1995 	}
1996 
1997 	if ( Hud_speed_multiplier > 0.0f ) {	// use a different displayed speed scale
1998 		displayed_target_speed = current_target_speed * Hud_speed_multiplier;
1999 	} else {
2000 		displayed_target_speed = current_target_speed;
2001 	}
2002 
2003 	sprintf(outstr, XSTR( "s: %.0f%s", 351), displayed_target_speed, (displayed_target_speed>1)?modifiers[Player_ai->current_target_speed_trend]:"");
2004 	hud_num_make_mono(outstr, font_num);
2005 
2006 	renderString(position[0] + Speed_offsets[0]+hx, position[1] + Speed_offsets[1]+hy, EG_TBOX_SPEED, outstr);
2007 
2008 	//
2009 	// output target info for debug purposes only, this will be removed later
2010 	//
2011 
2012 #ifndef NDEBUG
2013 	//XSTR:OFF
2014 	char outstr2[256];
2015 	if ( Show_target_debug_info && (is_ship == 1) ) {
2016 		int sx, sy, dy;
2017 		sx = gr_screen.center_offset_x + 5;
2018 		dy = gr_get_font_height() + 1;
2019 		sy = gr_screen.center_offset_y + 300 - 7*dy;
2020 
2021 		gr_set_color_fast(&HUD_color_debug);
2022 
2023 		if ( shipp->ai_index >= 0 ) {
2024 			ai_info	*aip = &Ai_info[shipp->ai_index];
2025 
2026 			sprintf(outstr,"AI: %s",Ai_behavior_names[aip->mode]);
2027 
2028 			switch (aip->mode) {
2029 			case AIM_CHASE:
2030 				Assert(aip->submode <= SM_BIG_PARALLEL);	//	Must be <= largest chase submode value.
2031 				sprintf(outstr2," / %s",Submode_text[aip->submode]);
2032 				strcat_s(outstr,outstr2);
2033 				break;
2034 			case AIM_STRAFE:
2035 				Assert(aip->submode <= AIS_STRAFE_POSITION);	//	Must be <= largest chase submode value.
2036 				sprintf(outstr2," / %s",Strafe_submode_text[aip->submode-AIS_STRAFE_ATTACK]);
2037 				strcat_s(outstr,outstr2);
2038 				break;
2039 			case AIM_WAYPOINTS:
2040 				break;
2041 			default:
2042 				break;
2043 			}
2044 
2045 			gr_printf_no_resize(sx, sy, "%s", outstr);
2046 			sy += dy;
2047 
2048 			gr_printf_no_resize(sx, sy, "Max speed = %d, (%d%%)", (int) shipp->current_max_speed, (int) (100.0f * vm_vec_mag(&target_objp->phys_info.vel)/shipp->current_max_speed));
2049 			sy += dy;
2050 
2051 			// data can be found in target montior
2052 			if (aip->target_objnum != -1) {
2053 				const char *target_str;
2054 				float	dot, dist;
2055 				vec3d	v2t;
2056 
2057 				if (aip->target_objnum == OBJ_INDEX(Player_obj))
2058 					target_str = "Player!";
2059 				else
2060 					target_str = Ships[Objects[aip->target_objnum].instance].get_display_name();
2061 
2062 				gr_printf_no_resize(sx, sy, "Targ: %s", target_str);
2063 				sy += dy;
2064 
2065 				dist = vm_vec_dist_quick(&Objects[Player_ai->target_objnum].pos, &Objects[aip->target_objnum].pos);
2066 				vm_vec_normalized_dir(&v2t,&Objects[aip->target_objnum].pos, &Objects[Player_ai->target_objnum].pos);
2067 
2068 				dot = vm_vec_dot(&v2t, &Objects[Player_ai->target_objnum].orient.vec.fvec);
2069 
2070 				// data can be found in target monitor
2071 				gr_printf_no_resize(sx, sy, "Targ dot: %3.2f", dot);
2072 				sy += dy;
2073 				gr_printf_no_resize(sx, sy, "Targ dst: %3.2f", dist);
2074 				sy += dy;
2075 
2076 				if ( aip->targeted_subsys != NULL ) {
2077 					sprintf(outstr, "Subsys: %s", aip->targeted_subsys->system_info->subobj_name);
2078 					gr_printf_no_resize(sx, sy, "%s", outstr);
2079 				}
2080 				sy += dy;
2081 			}
2082 
2083 			// print out energy transfer information on the ship
2084 			sy = gr_screen.center_offset_y + 70;
2085 
2086 			sprintf(outstr,"MAX G/E: %.0f/%.0f",shipp->weapon_energy,shipp->current_max_speed);
2087 			gr_printf_no_resize(sx, sy, "%s", outstr);
2088 			sy += dy;
2089 
2090 			sprintf(outstr,"G/S/E: %.2f/%.2f/%.2f",Energy_levels[shipp->weapon_recharge_index],Energy_levels[shipp->shield_recharge_index],Energy_levels[shipp->engine_recharge_index]);
2091 			gr_printf_no_resize(sx, sy, "%s", outstr);
2092 			sy += dy;
2093 
2094 			//	Show information about attacker.
2095 			{
2096 				int	found = 0;
2097 
2098 				if (Enemy_attacker != NULL)
2099 					if (Enemy_attacker->type == OBJ_SHIP) {
2100 						ship		*eshipp;
2101 						ai_info	*eaip;
2102 						float		dot, dist;
2103 						vec3d	v2t;
2104 
2105 						eshipp = &Ships[Enemy_attacker->instance];
2106 						eaip = &Ai_info[eshipp->ai_index];
2107 
2108 						if (eaip->target_objnum == OBJ_INDEX(Player_obj)) {
2109 							found = 1;
2110 							dist = vm_vec_dist_quick(&Enemy_attacker->pos, &Player_obj->pos);
2111 							vm_vec_normalized_dir(&v2t,&Objects[eaip->target_objnum].pos, &Enemy_attacker->pos);
2112 
2113 							dot = vm_vec_dot(&v2t, &Enemy_attacker->orient.vec.fvec);
2114 
2115 							gr_printf_no_resize(sx, sy, "#%i: %s", OBJ_INDEX(Enemy_attacker), Ships[Enemy_attacker->instance].get_display_name());
2116 							sy += dy;
2117 							gr_printf_no_resize(sx, sy, "Targ dist: %5.1f", dist);
2118 							sy += dy;
2119 							gr_printf_no_resize(sx, sy, "Targ dot: %3.2f", dot);
2120 							sy += dy;
2121 						}
2122 					}
2123 
2124 				if (Player_ai->target_objnum == Enemy_attacker - Objects)
2125 					found = 0;
2126 
2127 				if (!found) {
2128 					int	i;
2129 
2130 					Enemy_attacker = NULL;
2131 					for (i=0; i<MAX_OBJECTS; i++)
2132 						if (Objects[i].type == OBJ_SHIP) {
2133 							int	enemy;
2134 
2135 							if (i != Player_ai->target_objnum) {
2136 								enemy = Ai_info[Ships[Objects[i].instance].ai_index].target_objnum;
2137 
2138 								if (enemy == Player_obj-Objects) {
2139 									Enemy_attacker = &Objects[i];
2140 									break;
2141 								}
2142 							}
2143 						}
2144 				}
2145 			}
2146 
2147 			// Show target size
2148 			// hud_target_w
2149 			gr_printf_no_resize(sx, sy, "Targ size: %dx%d", Hud_target_w, Hud_target_h );
2150 			sy += dy;
2151 
2152 			polymodel *pm = model_get(sip->model_num);
2153 			gr_printf_no_resize(sx, sy, "POF:%s", pm->filename );
2154 			sy += dy;
2155 
2156 			gr_printf_no_resize(sx, sy, "Mass: %.2f\n", pm->mass);
2157 			sy += dy;
2158 		}
2159 	}
2160 
2161 	// display the weapons for the target on the HUD.  Include ammo counts.
2162 	if ( Show_target_weapons && (is_ship == 1) ) {
2163 		int sx, sy, dy, i;
2164 		ship_weapon *swp;
2165 
2166 		swp = &shipp->weapons;
2167 		sx = gr_screen.center_offset_x + 400;
2168 		sy = gr_screen.center_offset_y + 100;
2169 		dy = gr_get_font_height();
2170 
2171 		sprintf(outstr,"Num primaries: %d", swp->num_primary_banks);
2172 		gr_printf_no_resize(sx,sy,"%s", outstr);
2173 		sy += dy;
2174 		for ( i = 0; i < swp->num_primary_banks; i++ ) {
2175 			sprintf(outstr,"%d. %s", i+1, Weapon_info[swp->primary_bank_weapons[i]].get_display_name());
2176 			gr_printf_no_resize(sx,sy,"%s", outstr);
2177 			sy += dy;
2178 		}
2179 
2180 		sy += dy;
2181 		sprintf(outstr,"Num secondaries: %d", swp->num_secondary_banks);
2182 		gr_printf_no_resize(sx,sy,"%s", outstr);
2183 		sy += dy;
2184 		for ( i = 0; i < swp->num_secondary_banks; i++ ) {
2185 			sprintf(outstr,"%d. %s", i+1, Weapon_info[swp->secondary_bank_weapons[i]].get_display_name());
2186 			gr_printf_no_resize(sx,sy,"%s", outstr);
2187 			sy += dy;
2188 		}
2189 	}
2190 	//XSTR:ON
2191 
2192 #endif
2193 }
2194 
2195 /**
2196  * Called at the start of each level
2197  */
hud_init_target_static()2198 void hud_init_target_static()
2199 {
2200 	Target_static_next = 0;
2201 	Target_static_playing = 0;
2202 }
2203 
2204 /**
2205  * Determine if we should draw static on top of the target box
2206  */
hud_update_target_static()2207 void hud_update_target_static()
2208 {
2209 	float	sensors_str;
2210 
2211 	// on lowest skill level, don't show static on target monitor
2212 	if ( Game_skill_level == 0 )
2213 		return;
2214 
2215 	// if multiplayer observer, don't show static
2216 	if((Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_OBSERVER))
2217 		return;
2218 
2219 	sensors_str = ship_get_subsystem_strength( Player_ship, SUBSYSTEM_SENSORS );
2220 
2221 	if ( ship_subsys_disrupted(Player_ship, SUBSYSTEM_SENSORS) ) {
2222 		sensors_str = SENSOR_STR_TARGET_NO_EFFECTS-1;
2223 	}
2224 
2225 	if ( sensors_str > SENSOR_STR_TARGET_NO_EFFECTS ) {
2226 		Target_static_playing = 0;
2227 		Target_static_next = 0;
2228 	} else {
2229 		if ( Target_static_next == 0 )
2230 			Target_static_next = 1;
2231 	}
2232 
2233 	if ( timestamp_elapsed(Target_static_next) ) {
2234 		Target_static_playing ^= 1;
2235 		Target_static_next = timestamp_rand(50, 750);
2236 	}
2237 
2238 	if ( Target_static_playing ) {
2239 		if (!Target_static_looping.isValid()) {
2240 			Target_static_looping = snd_play_looping(gamesnd_get_game_sound(GameSounds::STATIC));
2241 		}
2242 	} else {
2243 		if (Target_static_looping.isValid()) {
2244 			snd_stop(Target_static_looping);
2245 			Target_static_looping = sound_handle::invalid();
2246 		}
2247 	}
2248 }
2249 
2250 /**
2251  * Updates the HUD status description of a particular ship
2252  *
2253  * Checks for disabled or ships with disrupted engines, as well as damage levels
2254  * of the target ship. If status has changed, then the HUD will flash
2255  *
2256  * @param targetp Instance of the ship target -- note the targetp->instance cannot be negative
2257  *
2258  */
hud_update_ship_status(object * targetp)2259 void hud_update_ship_status(object *targetp)
2260 {
2261     Assert( targetp != NULL );
2262     Assert( (targetp->instance >= 0) && (targetp->instance < MAX_SHIPS) );
2263 
2264     if ( (targetp->instance >= 0) && (targetp->instance < MAX_SHIPS) ) {
2265     	// print out status of ship for the targetbox
2266 		if ( (Ships[targetp->instance].flags[Ship::Ship_Flags::Disabled]) || (ship_subsys_disrupted(&Ships[targetp->instance], SUBSYSTEM_ENGINE)) ) {
2267 			Current_ts = TS_DIS;
2268 		} else {
2269 			if ( Pl_target_integrity > 0.9 ) {
2270 				Current_ts = TS_OK;
2271 			} else if ( Pl_target_integrity > 0.2 ) {
2272 				Current_ts = TS_DMG;
2273 			} else {
2274 				Current_ts = TS_CRT;
2275 			}
2276 		}
2277 
2278 		if ( Last_ts != -1 && Current_ts != Last_ts ) {
2279 			hud_targetbox_start_flash(TBOX_FLASH_STATUS);
2280 		}
2281     }
2282 
2283 	Last_ts = Current_ts;
2284 }
2285 
2286 /**
2287  * Start the targetbox item flashing for duration ms
2288  *
2289  * @param index		TBOX_FLASH_ define
2290  * @param duration	optional param (default value TBOX_FLASH_DURATION), how long to flash in ms
2291  */
hud_targetbox_start_flash(int index,int duration)2292 void hud_targetbox_start_flash(int index, int duration)
2293 {
2294 	Targetbox_flash_timers[index] = timestamp(duration);
2295 }
2296 
2297 /**
2298  * Stop flashing a specific targetbox item
2299  */
hud_targetbox_end_flash(int index)2300 void hud_targetbox_end_flash(int index)
2301 {
2302 	Targetbox_flash_timers[index] = timestamp(0);
2303 }
2304 
pageIn()2305 void HudGaugeTargetBox::pageIn()
2306 {
2307 	bm_page_in_aabitmap( Monitor_frame.first_frame, Monitor_frame.num_frames);
2308 
2309 	bm_page_in_aabitmap( Integrity_bar.first_frame, Integrity_bar.num_frames );
2310 }
2311