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 
13 #include "asteroid/asteroid.h"
14 #include "cmdline/cmdline.h"
15 #include "debris/debris.h"
16 #include "freespace.h"	// for flFrametime
17 #include "gamesnd/gamesnd.h"
18 #include "globalincs/alphacolors.h"
19 #include "globalincs/linklist.h"
20 #include "graphics/matrix.h"
21 #include "hud/hudartillery.h"
22 #include "hud/hudbrackets.h"
23 #include "hud/hudlock.h"
24 #include "hud/hudmessage.h"
25 #include "hud/hudparse.h"
26 #include "hud/hudreticle.h"
27 #include "hud/hudshield.h"
28 #include "hud/hudtarget.h"
29 #include "hud/hudtargetbox.h"
30 #include "iff_defs/iff_defs.h"
31 #include "io/timer.h"
32 #include "jumpnode/jumpnode.h"
33 #include "localization/localize.h"
34 #include "mission/missionhotkey.h"
35 #include "mission/missionmessage.h"
36 #include "model/model.h"
37 #include "network/multi.h"
38 #include "network/multiutil.h"
39 #include "object/object.h"
40 #include "libs/renderdoc/renderdoc.h"
41 #include "parse/parselo.h"
42 #include "playerman/player.h"
43 #include "render/3dinternal.h"
44 #include "ship/awacs.h"
45 #include "ship/ship.h"
46 #include "ship/subsysdamage.h"
47 #include "weapon/emp.h"
48 #include "weapon/weapon.h"
49 
50 // Global values for the target bracket width and height, used for debugging
51 int Hud_target_w, Hud_target_h;
52 
53 // offscreen triangle that point the the off-screen target
54 float Offscreen_tri_base[GR_NUM_RESOLUTIONS] = {
55 	6.0f,
56 	9.5f
57 };
58 float Offscreen_tri_height[GR_NUM_RESOLUTIONS] = {
59 	7.0f,
60 	11.0f
61 };
62 float Max_offscreen_tri_seperation[GR_NUM_RESOLUTIONS] = {
63 	10.0f,
64 	16.0f
65 };
66 float Max_front_seperation[GR_NUM_RESOLUTIONS] = {
67 	10.0f,
68 	16.0f
69 };
70 
71 SCP_vector<target_display_info> target_display_list;
72 
73 // The following variables are global to this file, and do not need to be persistent from frame-to-frame
74 // This means the variables are not player-specific
75 object* hostile_obj = NULL;
76 
77 static int ballistic_hud_index = 0;	// Goober5000
78 
79 extern object obj_used_list;		// dummy node in linked list of active objects
80 extern char *Cargo_names[];
81 
82 // shader is used to shade the target box
83 shader Training_msg_glass;
84 
85 // the target triangle (that orbits the reticle) dimensions
86 float Target_triangle_base[GR_NUM_RESOLUTIONS] = {
87 	6.0f,
88 	9.5f
89 };
90 float Target_triangle_height[GR_NUM_RESOLUTIONS] = {
91 	7.0f,
92 	11.0f
93 };
94 
95 // stuff for hotkey targeting lists
96 htarget_list htarget_items[MAX_HOTKEY_TARGET_ITEMS];
97 htarget_list htarget_free_list;
98 
99 /*
100 // coordinates and widths used to render the HUD afterburner energy gauge
101 int current_hud->Aburn_coords[GR_NUM_RESOLUTIONS][4] = {
102 	{ // GR_640
103 		171, 265, 60, 60
104 	},
105 	{ // GR_1024
106 		274, 424, 86, 96
107 	}
108 };
109 
110 // coordinates and widths used to render the HUD weapons energy gauge
111 int current_hud->Wenergy_coords[GR_NUM_RESOLUTIONS][4] = {
112 	{ // GR_640
113 		416, 265, 60, 60
114 	},
115 	{ // GR_1024
116 		666, 424, 86, 96
117 	}
118 };*/
119 
120 #define MIN_DISTANCE_TO_CONSIDER_THREAT	1500	// min distance to show hostile warning triangle
121 
122 //////////////////////////////////////////////////////////////////////////
123 // lists for target in reticle cycling
124 //////////////////////////////////////////////////////////////////////////
125 #define	RL_USED		(1<<0)
126 #define	RL_USE_DOT	(1<<1)	// use dot product result, not distance
127 
128 typedef struct _reticle_list {
129 	_reticle_list	*next, *prev;
130 	object			*objp;
131 	float				dist, dot;
132 	int				flags;
133 } reticle_list;
134 
135 #define			RESET_TARGET_IN_RETICLE	750
136 int				Reticle_save_timestamp;
137 reticle_list	Reticle_cur_list;
138 reticle_list	Reticle_save_list;
139 #define			MAX_RETICLE_TARGETS	50
140 reticle_list	Reticle_list[MAX_RETICLE_TARGETS];
141 
142 //////////////////////////////////////////////////////////////////////////
143 // used for closest target cycling
144 //////////////////////////////////////////////////////////////////////////
145 #define	TL_RESET			1500
146 #define	TURRET_RESET	1000
147 static int Tl_hostile_reset_timestamp;
148 static int Tl_friendly_reset_timestamp;
149 static int Target_next_uninspected_object_timestamp;
150 static int Target_newest_ship_timestamp;
151 static int Target_next_turret_timestamp;
152 
153 // animation frames for the hud targeting gauges
154 // frames:	0	=>		out of range lead
155 //				1	=>		in range lead
156 float Lead_indicator_half[NUM_HUD_RETICLE_STYLES][GR_NUM_RESOLUTIONS][2] =
157 {
158 	{
159 		{ // GR_640
160 			12.5f,		// half-width
161 			12.5f			// half-height
162 		},
163 		{ // GR_1024
164 			20.0f,		// half-width
165 			20.0f			// half-height
166 		}
167 	},
168 	{
169 		{ // GR_640
170 			8.0f,		// half-width
171 			8.0f			// half-height
172 		},
173 		{ // GR_1024
174 			13.0f,		// half-width
175 			13.0f			// half-height
176 		}
177 	}
178 };
179 hud_frames Lead_indicator_gauge;
180 int Lead_indicator_gauge_loaded = 0;
181 char Lead_fname[NUM_HUD_RETICLE_STYLES][GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN] =
182 {
183 	{ "lead1_fs1", "2_lead1_fs1" },
184 	{ "lead1", "2_lead1" }
185 };
186 
187 // animation frames for the countermeasures gauge
188 // frames:	0	=>		background
189 hud_frames Cmeasure_gauge;
190 int Cmeasure_gauge_loaded = 0;
191 int Cm_coords[GR_NUM_RESOLUTIONS][2] = {
192 	{ // GR_640
193 		497, 343
194 	},
195 	{ // GR_1024
196 		880, 602
197 	}
198 };
199 int Cm_text_coords[GR_NUM_RESOLUTIONS][2] = {
200 	{ // GR_640
201 		533, 347
202 	},
203 	{ // GR_1024
204 		916, 606
205 	}
206 };
207 int Cm_text_val_coords[GR_NUM_RESOLUTIONS][2] = {
208 	{ // GR_640
209 		506, 347
210 	},
211 	{ // GR_1024
212 		889, 606
213 	}
214 };
215 char Cm_fname[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN] = {
216 	"countermeasure1",
217 	"countermeasure1"
218 };
219 
220 #define TOGGLE_TEXT_AUTOT		0
221 #define TOGGLE_TEXT_TARGET		1
222 #define TOGGLE_TEXT_AUTOS		2
223 #define TOGGLE_TEXT_SPEED		3
224 
225 int Toggle_text_alpha = 255;
226 
227 
228 // animation files for the weapons gauge
229 #define NUM_WEAPON_GAUGES	5
230 hud_frames Weapon_gauges[NUM_HUD_SETTINGS][NUM_WEAPON_GAUGES];
231 hud_frames New_weapon;
232 int Weapon_gauges_loaded = 0;
233 // for primaries
234 int Weapon_gauge_primary_coords[NUM_HUD_SETTINGS][GR_NUM_RESOLUTIONS][3][2] =
235 {
236 	{ // normal HUD
237 		{ // GR_640
238 			// based on the # of primaries
239 			{509, 273},				// top of weapon gauge, first frame, always
240 			{497, 293},				// for the first primary
241 			{497, 305}				// for the second primary
242 		},
243 		{ // GR_1024
244 			// based on the # of primaries
245 			{892, 525},				// top of weapon gauge, first frame, always
246 			{880, 545},				// for the first primary
247 			{880, 557}				// for the second primary
248 		}
249 	},
250 	{ // ballistic HUD - slightly different alignment
251 		{ // GR_640
252 			// based on the # of primaries
253 			{485, 273},				// top of weapon gauge, first frame, always
254 			{485, 293},				// for the first primary
255 			{485, 305}				// for the second primary
256 		},
257 		{ // GR_1024
258 			// based on the # of primaries
259 			{868, 525},				// top of weapon gauge, first frame, always
260 			{868, 545},				// for the first primary
261 			{868, 557}				// for the second primary
262 		}
263 	}
264 };
265 int Weapon_gauge_secondary_coords[NUM_HUD_SETTINGS][GR_NUM_RESOLUTIONS][5][2] =
266 {
267 	{ // normal HUD
268 		{ // GR_640
269 			// based on the # of secondaries
270 			{497, 318},				// bottom of gauge, 0 secondaries
271 			{497, 318},				// bottom of gauge, 1 secondaries
272 			{497, 317},				// middle of gauge, 2 secondaries AND middle of gauge, 3 secondaries
273 			{497, 326},				// bottom of gauge, 2 secondaries AND middle of gauge, 3 secondaries
274 			{497, 335}				// bottom of gauge, 3 secondaries
275 		},
276 		{ // GR_1024
277 			// based on the # of secondaries
278 			{880, 570},				// bottom of gauge, 0 secondaries
279 			{880, 570},				// bottom of gauge, 1 secondaries
280 			{880, 569},				// middle of gauge, 2 secondaries AND middle of gauge, 3 secondaries
281 			{880, 578},				// bottom of gauge, 2 secondaries AND middle of gauge, 3 secondaries
282 			{880, 587}				// bottom of gauge, 3 secondaries
283 		}
284 	},
285 	{ // ballistic HUD - slightly different alignment
286 		{ // GR_640
287 			// based on the # of secondaries
288 			{485, 318},				// bottom of gauge, 0 secondaries
289 			{485, 318},				// bottom of gauge, 1 secondaries
290 			{485, 317},				// middle of gauge, 2 secondaries AND middle of gauge, 3 secondaries
291 			{485, 326},				// bottom of gauge, 2 secondaries AND middle of gauge, 3 secondaries
292 			{485, 335}				// bottom of gauge, 3 secondaries
293 		},
294 		{ // GR_1024
295 			// based on the # of secondaries
296 			{868, 570},				// bottom of gauge, 0 secondaries
297 			{868, 570},				// bottom of gauge, 1 secondaries
298 			{868, 569},				// middle of gauge, 2 secondaries AND middle of gauge, 3 secondaries
299 			{868, 578},				// bottom of gauge, 2 secondaries AND middle of gauge, 3 secondaries
300 			{868, 587}				// bottom of gauge, 3 secondaries
301 		}
302 	}
303 };
304 int Weapon_title_coords[NUM_HUD_SETTINGS][GR_NUM_RESOLUTIONS][2] =
305 {
306 	{ // normal HUD
307 		{ // GR_640
308 			518, 274
309 		},
310 		{ // GR_1024
311 			901, 527
312 		}
313 	},
314 	{ // ballistic HUD - slightly different alignment
315 		{ // GR_640
316 			487, 274
317 		},
318 		{ // GR_1024
319 			870, 527
320 		}
321 	}
322 };
323 int Weapon_plink_coords[GR_NUM_RESOLUTIONS][2][2] = {
324 	{ // GR_640
325 		{530, 285},				// fire-linked thingie, for the first primary
326 		{530, 295}				// fire-linked thingie, for the second primary
327 	},
328 	{ // GR_1024
329 		{913, 537},				// fire-linked thingie, for the first primary
330 		{913, 547}				// fire-linked thingie, for the second primary
331 	}
332 };
333 int Weapon_pname_coords[GR_NUM_RESOLUTIONS][2][2] = {
334 	{ // GR_640
335 		{536, 285},				// weapon name, first primary
336 		{536, 295}				// weapon name, second primary
337 	},
338 	{ // GR_1024
339 		{919, 537},				// weapon name, first primary
340 		{919, 547}				// weapon name, second primary
341 	}
342 };
343 int Weapon_slinked_x[GR_NUM_RESOLUTIONS] = {
344 	525,							// where to draw the second thingie if this weapon is fire-linked
345 	908
346 };
347 int Weapon_sunlinked_x[GR_NUM_RESOLUTIONS] = {
348 	530,							// where to draw the first thingie if this weapon is selected at all (fire-linked or not)
349 	913
350 };
351 int Weapon_secondary_y[GR_NUM_RESOLUTIONS][3] = {
352 	{ // GR_640
353 		309,						// y location of where to draw text for the first secondary
354 		318,						// y location of where to draw text for the second secondary
355 		327						// y location of where to draw text for the third secondary
356 	},
357 	{ // GR_1024
358 		561,						// y location of where to draw text for the third secondary
359 		570,						// y location of where to draw text for the third secondary
360 		579						// y location of where to draw text for the third secondary
361 	}
362 };
363 int Weapon_primary_y[GR_NUM_RESOLUTIONS][2] = {
364 	{ // GR_640
365 		285,						// y location of where to draw text for the first primary
366 		295							// y location of where to draw text for the second primary
367 	},
368 	{ // GR_1024
369 		537,						// y location of where to draw text for the first primary
370 		547							// y location of where to draw text for the second primary
371 	}
372 };
373 int Weapon_secondary_name_x[GR_NUM_RESOLUTIONS] = {
374 	536,							// x location of where to draw weapon name
375 	919
376 };
377 int Weapon_secondary_ammo_x[GR_NUM_RESOLUTIONS] = {
378 	525,							// x location of where to draw weapon ammo count
379 	908
380 };
381 int Weapon_primary_ammo_x[GR_NUM_RESOLUTIONS] = {
382 	525,							// x location of where to draw primary weapon ammo count
383 	908
384 };
385 int Weapon_secondary_reload_x[GR_NUM_RESOLUTIONS] = {
386 	615,							// x location of where to draw the weapon reload time
387 	998
388 };
389 const char *Weapon_gauge_fnames[NUM_HUD_SETTINGS][GR_NUM_RESOLUTIONS][NUM_WEAPON_GAUGES] =
390 {
391 //XSTR:OFF
392 	{ // normal HUD
393 		{ // GR_640
394 			"weapons1",
395 			"weapons2",
396 			"weapons3",
397 			"weapons4",
398 			"weapons5"
399 		},
400 		{ // GR_1024
401 			"weapons1",
402 			"weapons2",
403 			"weapons3",
404 			"weapons4",
405 			"weapons5"
406 		}
407 	},
408 	{ // ballistic HUD - slightly different alignment
409 		{ // GR_640
410 			"weapons1_b",
411 			"weapons2_b",
412 			"weapons3_b",
413 			"weapons4_b",
414 			"weapons5_b"
415 		},
416 		{ // GR_1024
417 			"weapons1_b",
418 			"weapons2_b",
419 			"weapons3_b",
420 			"weapons4_b",
421 			"weapons5_b"
422 		}
423 	}
424 //XSTR:ON
425 };
426 
427 // Flash the line for a weapon.  This normally occurs when the player tries to fire that
428 // weapon, but the firing fails (due to lack of energy or damaged weapons subsystem).
429 #define MAX_WEAPON_FLASH_LINES 7		// 3 primary and 4 secondary
430 typedef struct weapon_flash
431 {
432 	int flash_duration[MAX_WEAPON_FLASH_LINES];
433 	int flash_next[MAX_WEAPON_FLASH_LINES];
434 	int is_bright;
435 } weapon_flash;
436 weapon_flash Weapon_flash_info;
437 
438 // Data used for the proximity warning
439 typedef struct homing_beep_info
440 {
441 	sound_handle snd_handle;    // sound handle for last played beep
442 	fix	last_time_played;		//	time beep was last played
443 	int	min_cycle_time;		// time (in ms) for fastest cycling of the sound
444 	int	max_cycle_time;		// time (in ms) for slowest cycling of the sound
445 	float min_cycle_dist;		// distance at which fastest cycling occurs
446 	float max_cycle_dist;		// distance at which slowest cycling occurs
447 	float	precalced_interp;		// a precalculated value used in a linear interpretation
448 } homing_beep_info;
449 
450 homing_beep_info Homing_beep = {sound_handle::invalid(), 0, 150, 1000, 30.0f, 1500.0f, 1.729412f};
451 
452 // Set at the start of a mission, used to decide how to draw the separation for the warning missile indicators
453 float Min_warning_missile_dist;
454 float	Max_warning_missile_dist;
455 
456 void hud_maybe_flash_weapon(int index);
457 
458 // if a given object should be ignored because of AWACS effects
hud_target_invalid_awacs(object * objp)459 int hud_target_invalid_awacs(object *objp)
460 {
461 	// if objp is ship object, first check if can be targeted with team info
462 	if (objp->type == OBJ_SHIP) {
463 		if (Player_ship != NULL) {
464 			if (ship_is_visible_by_team(objp, Player_ship)) {
465 				return 0;
466 			}
467 		}
468 	}
469 
470 	// check for invalid status
471 	if((Player_ship != NULL) && (awacs_get_level(objp, Player_ship) < 1.0f)){
472 		return 1;
473 	}
474 
475 	// valid
476 	return 0;
477 }
478 
advance_subsys(ship_subsys * cur,int next_flag)479 ship_subsys *advance_subsys(ship_subsys *cur, int next_flag)
480 {
481 	if (next_flag) {
482 		return GET_NEXT(cur);
483 	} else {
484 		return GET_LAST(cur);
485 	}
486 }
487 
488 // select a sorted turret subsystem on a ship if no other subsys has been selected
hud_maybe_set_sorted_turret_subsys(ship * shipp)489 void hud_maybe_set_sorted_turret_subsys(ship *shipp)
490 {
491 	Assert((Player_ai->target_objnum >= 0) && (Player_ai->target_objnum < MAX_OBJECTS));
492 	if (!((Player_ai->target_objnum >= 0) && (Player_ai->target_objnum < MAX_OBJECTS))) {
493 		return;
494 	}
495 	Assert(Objects[Player_ai->target_objnum].type == OBJ_SHIP);
496 	if (Objects[Player_ai->target_objnum].type != OBJ_SHIP) {
497 		return;
498 	}
499 
500 	if (Ship_info[shipp->ship_info_index].is_big_or_huge()) {
501 		if (shipp->last_targeted_subobject[Player_num] == NULL) {
502 			hud_target_live_turret(1, 1);
503 		}
504 	}
505 
506 }
507 
508 // -----------------------------------------------------------------------
509 //	clear out the linked list of targets in the reticle
hud_reticle_clear_list(reticle_list * rlist)510 void hud_reticle_clear_list(reticle_list *rlist)
511 {
512 	reticle_list *cur;
513 	for ( cur = GET_FIRST(rlist); cur != END_OF_LIST(rlist); cur = GET_NEXT(cur) ) {
514 		cur->flags = 0;
515 	}
516 	list_init(rlist);
517 }
518 
519 // --------------------------------------------------------------------------------------
520 //	hud_reticle_list_init()
hud_reticle_list_init()521 void hud_reticle_list_init()
522 {
523 	int i;
524 
525 	for ( i = 0; i < MAX_RETICLE_TARGETS; i++ ) {
526 		Reticle_list[i].flags = 0;
527 	}
528 
529 	Reticle_save_timestamp = 1;
530 	list_init(&Reticle_save_list);
531 	list_init(&Reticle_cur_list);
532 }
533 
534 // --------------------------------------------------------------------------------------
535 //	hud_check_reticle_list()
536 //
537 //
hud_check_reticle_list()538 void	hud_check_reticle_list()
539 {
540 	reticle_list	*rl, *temp;
541 
542 	// cull dying objects from reticle list
543 	rl = GET_FIRST(&Reticle_cur_list);
544 	while( rl !=END_OF_LIST(&Reticle_cur_list) )	{
545 		temp = GET_NEXT(rl);
546 		if ( rl->objp->flags[Object::Object_Flags::Should_be_dead] ) {
547 			list_remove( &Reticle_cur_list, rl );
548 			rl->flags = 0;
549 		}
550 		rl = temp;
551 	}
552 
553 	if ( timestamp_elapsed(Reticle_save_timestamp) ) {
554 		hud_reticle_clear_list(&Reticle_save_list);
555 		Reticle_save_timestamp = timestamp(RESET_TARGET_IN_RETICLE);
556 	}
557 }
558 
559 // --------------------------------------------------------------------------------------
560 //	hud_reticle_list_find_free()
561 //
562 //
hud_reticle_list_find_free()563 int hud_reticle_list_find_free()
564 {
565 	int i;
566 
567 	// find a free reticle_list element
568 	for ( i = 0; i < MAX_RETICLE_TARGETS; i++ ) {
569 		if ( !(Reticle_list[i].flags & RL_USED) ) {
570 			break;
571 		}
572 	}
573 
574 	if ( i == MAX_RETICLE_TARGETS ) {
575 //		nprintf(("Warning","Warning ==> Ran out of reticle target elements...\n"));
576 		return -1;
577 	}
578 
579 	return i;
580 }
581 
582 // --------------------------------------------------------------------------------------
583 //	hud_stuff_reticle_list()
584 //
585 //
586 #define	RETICLE_DEFAULT_DIST		100000.0f
587 #define	RETICLE_DEFAULT_DOT		1.0f
hud_stuff_reticle_list(reticle_list * rl,object * objp,float measure,int dot_flag)588 void hud_stuff_reticle_list(reticle_list *rl, object *objp, float measure, int dot_flag)
589 {
590 	if ( dot_flag ) {
591 		rl->dot = measure;
592 		rl->dist = RETICLE_DEFAULT_DIST;
593 		rl->flags |= RL_USE_DOT;
594 	}
595 	else {
596 		rl->dist = measure;
597 		rl->dot = RETICLE_DEFAULT_DOT;
598 	}
599 	rl->objp = objp;
600 }
601 
602 // --------------------------------------------------------------------------------------
603 //	hud_reticle_list_update()
604 //
605 //	Update Reticle_cur_list with an object that lies in the reticle
606 //
607 //	parmeters:	objp		=>		object pointer to target
608 //					measure	=>		distance or dot product, depending on dot_flag
609 //					dot_flag	=>		if 0, measure is distance, if 1 measure is dot
610 //
hud_reticle_list_update(object * objp,float measure,int dot_flag)611 void hud_reticle_list_update(object *objp, float measure, int dot_flag)
612 {
613 	reticle_list	*rl, *new_rl;
614 	int				i;
615 	SCP_list<CJumpNode>::iterator jnp;
616 
617 	if (objp->type == OBJ_JUMP_NODE) {
618 		for (jnp = Jump_nodes.begin(); jnp != Jump_nodes.end(); ++jnp) {
619 			if( jnp->GetSCPObject() != objp )
620 				continue;
621 
622 			if( jnp->IsHidden() )
623 				return;
624 		}
625 	}
626 
627 	for ( rl = GET_FIRST(&Reticle_cur_list); rl != END_OF_LIST(&Reticle_cur_list); rl = GET_NEXT(rl) ) {
628 		if ( rl->objp == objp )
629 			return;
630 	}
631 
632 	i = hud_reticle_list_find_free();
633 	if ( i == -1 )
634 		return;
635 
636 	new_rl = &Reticle_list[i];
637 	new_rl->flags |= RL_USED;
638 	hud_stuff_reticle_list(new_rl, objp, measure, dot_flag);
639 
640 	int was_inserted = 0;
641 
642 	if ( EMPTY(&Reticle_cur_list) ) {
643 		list_insert(&Reticle_cur_list, new_rl);
644 		was_inserted = 1;
645 	}
646 	else {
647 		for ( rl = GET_FIRST(&Reticle_cur_list); rl != END_OF_LIST(&Reticle_cur_list); rl = GET_NEXT(rl) ) {
648 			if ( !dot_flag ) {
649 				// compare based on distance
650 				if ( measure < rl->dist ) {
651 					list_insert_before(rl, new_rl);
652 					was_inserted = 1;
653 					break;
654 				}
655 			}
656 			else {
657 				// compare based on dot
658 				if ( measure > rl->dot ) {
659 					list_insert_before(rl, new_rl);
660 					was_inserted = 1;
661 					break;
662 				}
663 			}
664 		}	// end for
665 	}
666 
667 	if ( !was_inserted ) {
668 		list_append(&Reticle_cur_list, new_rl);
669 	}
670 }
671 
672 // --------------------------------------------------------------------------------------
673 //	hud_reticle_pick_target()
674 //
675 //	Pick a target from Reticle_cur_list, based on what is in Reticle_save_list
676 //
677 //
hud_reticle_pick_target()678 object *hud_reticle_pick_target()
679 {
680 	reticle_list	*cur_rl, *save_rl, *new_rl;
681 	object			*return_objp;
682 	int				in_save_list, i;
683 
684 	return_objp = NULL;
685 
686 	// As a first step, see if both ships and debris are in the list.  If so, cull the debris.
687 	int debris_in_list = 0;
688 	int ship_in_list = 0;
689 	for ( cur_rl = GET_FIRST(&Reticle_cur_list); cur_rl != END_OF_LIST(&Reticle_cur_list); cur_rl = GET_NEXT(cur_rl) ) {
690 		if ( (cur_rl->objp->type == OBJ_SHIP) || (cur_rl->objp->type == OBJ_JUMP_NODE) ) {
691 			ship_in_list = 1;
692 			continue;
693 		}
694 
695 		if ( cur_rl->objp->type == OBJ_WEAPON ) {
696 			if ( Weapon_info[Weapons[cur_rl->objp->instance].weapon_info_index].subtype == WP_MISSILE ) {
697 				ship_in_list = 1;
698 				continue;
699 			}
700 		}
701 
702 		if ( (cur_rl->objp->type == OBJ_DEBRIS) || (cur_rl->objp->type == OBJ_ASTEROID) ) {
703 			debris_in_list = 1;
704 			continue;
705 		}
706 	}
707 
708 	if ( ship_in_list && debris_in_list ) {
709 		// cull debris
710 		reticle_list	*rl, *next;
711 
712 		rl = GET_FIRST(&Reticle_cur_list);
713 		while ( rl != &Reticle_cur_list ) {
714 			next = rl->next;
715 			if ( (rl->objp->type == OBJ_DEBRIS) || (rl->objp->type == OBJ_ASTEROID) ){
716 				list_remove(&Reticle_cur_list,rl);
717 				rl->flags = 0;
718 			}
719 			rl = next;
720 		}
721 	}
722 
723 	for ( cur_rl = GET_FIRST(&Reticle_cur_list); cur_rl != END_OF_LIST(&Reticle_cur_list); cur_rl = GET_NEXT(cur_rl) ) {
724 		in_save_list = 0;
725 		for ( save_rl = GET_FIRST(&Reticle_save_list); save_rl != END_OF_LIST(&Reticle_save_list); save_rl = GET_NEXT(save_rl) ) {
726 			if ( cur_rl->objp == save_rl->objp ) {
727 				in_save_list = 1;
728 				break;
729 			}
730 		}
731 
732 		if ( !in_save_list ) {
733 			return_objp = cur_rl->objp;
734 			i = hud_reticle_list_find_free();
735 			if ( i == -1 )
736 				break;
737 
738 			new_rl = &Reticle_list[i];
739 			new_rl->flags |= RL_USED;
740 			if ( cur_rl->flags & RL_USE_DOT ) {
741 				hud_stuff_reticle_list(new_rl, cur_rl->objp, cur_rl->dot, 1);
742 			}
743 			else {
744 				hud_stuff_reticle_list(new_rl, cur_rl->objp, cur_rl->dist, 0);
745 			}
746 
747 			list_append(&Reticle_save_list, new_rl);
748 			break;
749 		}
750 	}	// end for
751 
752 	if ( return_objp == NULL && !EMPTY(&Reticle_cur_list) ) {
753 			i = hud_reticle_list_find_free();
754 			if ( i == -1 )
755 				return NULL;
756 			new_rl = &Reticle_list[i];
757 			cur_rl = GET_FIRST(&Reticle_cur_list);
758 			*new_rl = *cur_rl;
759 			return_objp = cur_rl->objp;
760 			hud_reticle_clear_list(&Reticle_save_list);
761 			list_append(&Reticle_save_list, new_rl);
762 	}
763 
764 	return return_objp;
765 }
766 
767 // hud_target_hotkey_add_remove takes as its parameter which hotkey (1-0) to add/remove the current
768 // target from.  This function behaves like the Shift-<selection> does in Windows -- using shift # will toggle
769 // the current target in and out of the selection set.
hud_target_hotkey_add_remove(int k,object * ctarget,int how_to_add)770 void hud_target_hotkey_add_remove( int k, object *ctarget, int how_to_add )
771 {
772 	htarget_list *hitem, *plist;
773 
774 	// don't do anything if a standalone multiplayer server
775 	if ( MULTIPLAYER_STANDALONE )
776 		return;
777 
778 	if ( (k < 0) || (k >= MAX_KEYED_TARGETS) ) {
779 		nprintf(("Warning", "Bogus hotkey %d sent to hud_target_hotkey_add_remove\n", k));
780 		return;
781 	}
782 
783 	plist = &(Players[Player_num].keyed_targets[k]);
784 
785 	// we must operate only on ships
786 	if ( ctarget->type != OBJ_SHIP )
787 		return;
788 
789 	// don't allow player into hotkey set
790 	if ( ctarget == Player_obj )
791 		return;
792 
793 	// don't put dying or departing
794 	if ( Ships[ctarget->instance].is_dying_or_departing() )
795 		return;
796 
797 	// don't add mission file added hotkey assignments if there are player added assignments
798 	// already in the list
799 	if ( (how_to_add == HOTKEY_MISSION_FILE_ADDED) && NOT_EMPTY(plist) ) {
800 		for ( hitem = GET_FIRST(plist); hitem != END_OF_LIST(plist); hitem = GET_NEXT(hitem) ) {
801 			if ( hitem->how_added == HOTKEY_USER_ADDED )
802 				return;
803 		}
804 	}
805 
806 	// determine if the current target is currently in the set or not
807 	for ( hitem = GET_FIRST(plist); hitem != END_OF_LIST(plist); hitem = GET_NEXT(hitem) ) {
808 		if ( hitem->objp == ctarget )
809 			break;
810 	}
811 
812 	// if hitem == end of the list, then the target should be added, else it should be removed
813 	if ( hitem == END_OF_LIST(plist) ) {
814 		if ( EMPTY(&htarget_free_list) ) {
815 			Int3();			// get Allender -- no more free hotkey target items
816 			return;
817 		}
818 
819 		nprintf(("network", "Hotkey: Adding %s\n", Ships[ctarget->instance].ship_name));
820 		hitem = GET_FIRST( &htarget_free_list );
821 		list_remove( &htarget_free_list, hitem );
822 		list_append( plist, hitem );
823 		hitem->objp = ctarget;
824 		hitem->how_added = how_to_add;
825 	} else {
826 		nprintf(("network", "Hotkey: Removing %s\n", Ships[ctarget->instance].ship_name));
827 		list_remove( plist, hitem );
828 		list_append( &htarget_free_list, hitem );
829 		hitem->objp = NULL;									// for safety
830 	}
831 }
832 
833 // the following function clears the hotkey set given by parameter passed in
hud_target_hotkey_clear(int k)834 void hud_target_hotkey_clear( int k )
835 {
836 	htarget_list *hitem, *plist, *temp;
837 
838 	plist = &(Players[Player_num].keyed_targets[k]);
839 	hitem = GET_FIRST(plist);
840 	while ( hitem != END_OF_LIST(plist) ) {
841 		temp = GET_NEXT(hitem);
842 		list_remove( plist, hitem );
843 		list_append( &htarget_free_list, hitem );
844 		hitem->objp = NULL;
845 		hitem = temp;
846 	}
847 	if ( Players[Player_num].current_hotkey_set == k )		// clear this variable if we removed the bindings
848 		Players[Player_num].current_hotkey_set = -1;
849 }
850 
851 // the next function sets the current selected set to be N.  If there is just one ship in the selection
852 // set, this ship will become the new target.  If there is more than one ship in the selection set,
853 // then the current_target will remain what it was.
hud_target_hotkey_select(int k)854 void hud_target_hotkey_select( int k )
855 {
856 	int visible_count = 0;
857 	htarget_list *hitem, *plist, *target, *next_target, *first_target;
858 	int target_objnum;
859 
860 	plist = &(Players[Player_num].keyed_targets[k]);
861 
862 	if ( EMPTY( plist ) )			// no items in list, then do nothing
863 		return;
864 
865 	// a simple walk of the list to get the count
866 	for ( hitem = GET_FIRST(plist); hitem != END_OF_LIST(plist); hitem = GET_NEXT(hitem) ){
867 		if (awacs_get_level(hitem->objp, Player_ship, 1) > 1) {
868 			visible_count++;
869 		}
870 	}
871 
872 	// no visible ships in list
873 	if (visible_count == 0) {
874 		return;
875 	}
876 
877 	// set the current target to be the "next" ship in the list.  Scan the list to see if our
878 	// current target is in the set.  If so, target the next ship in the list, otherwise target
879 	// the first
880 	// set	first_target - first visible item in list
881 	//			target - item in list that is the player's currently selected target
882 	//			next_target -	next visible item in list following target
883 	target_objnum = Player_ai->target_objnum;
884 	target = NULL;
885 	next_target = NULL;
886 	first_target = NULL;
887 	for ( hitem = GET_FIRST(plist); hitem != END_OF_LIST(plist); hitem = GET_NEXT(hitem) ) {
888 
889 		if (awacs_get_level(hitem->objp, Player_ship, 1) > 1) {
890 			// get the first valid target
891 			if (first_target == NULL) {
892 				first_target = hitem;
893 			}
894 
895 			// get the next target in the list following the player currently selected target
896 			if (target != NULL) {
897 				next_target = hitem;
898 				break;
899 			}
900 		}
901 
902 		// mark the player currently selected target
903 		if ( OBJ_INDEX(hitem->objp) == target_objnum ) {
904 			target = hitem;
905 		}
906 	}
907 
908 	// if current target is not in list, then target and next_target will be NULL
909 	// so we use the first found target
910 	if (target == NULL) {
911 		Assert(first_target != NULL);
912 		if (first_target != NULL) {
913 			target = first_target;
914 			next_target = first_target;
915 		} else {
916 			// this should not happen
917 			return;
918 		}
919 	// if current target is in the list but this is not our current selection set,
920 	// then we don't want to change target.
921 	} else if (Players[Player_num].current_hotkey_set != k) {
922 		next_target = target;
923 	}
924 
925 	// update target if more than 1 is visible
926 	if (visible_count > 1) {
927 		// next already found (after current target in list)
928 		if (next_target != NULL) {
929 			target = next_target;
930 		} else {
931 
932 		// next is before current target, so search from start of list
933 			for ( hitem = GET_FIRST(plist); hitem != END_OF_LIST(plist); hitem = GET_NEXT(hitem) ) {
934 				if (awacs_get_level(hitem->objp, Player_ship, 1) > 1) {
935 					target = hitem;
936 					break;
937 				}
938 			}
939 		}
940 	}
941 
942 	Assert( target != END_OF_LIST(plist) );
943 
944 	if ( Player_obj != target->objp ){
945 		set_target_objnum( Player_ai, OBJ_INDEX(target->objp) );
946 		hud_shield_hit_reset(target->objp);
947 	}
948 
949 	Players[Player_num].current_hotkey_set = k;
950 }
951 
952 // hud_init_targeting_colors() will initialize the shader and gradient objects used
953 // on the HUD
954 //
955 
956 color HUD_color_homing_indicator;
957 
hud_make_shader(shader * sh,ubyte r,ubyte,ubyte,float dimmer=1000.0f)958 void hud_make_shader(shader *sh, ubyte r, ubyte  /*g*/, ubyte  /*b*/, float dimmer = 1000.0f)
959 {
960 	// The m matrix converts all colors to shades of green
961 	//float tmp = 16.0f*(0.0015625f * i2fl(HUD_color_alpha+1.0f));
962 	float tmp = 0.025f * i2fl(HUD_color_alpha+1.0f);
963 
964 	ubyte R = ubyte(r * tmp);
965 	ubyte G = ubyte(r * tmp);
966 	ubyte B = ubyte(r * tmp);
967 	ubyte A = ubyte((float(r) / dimmer)*(i2fl(HUD_color_alpha) / 15.0f) * 255.0f);
968 
969 	gr_create_shader( sh, R, G, B, A );
970 }
971 
hud_init_targeting_colors()972 void hud_init_targeting_colors()
973 {
974 	gr_init_color( &HUD_color_homing_indicator, 0x7f, 0x7f, 0 );	// yellow
975 
976 	hud_make_shader(&Training_msg_glass, 61, 61, 85, 500.0f);
977 
978 	hud_init_brackets();
979 }
980 
hud_keyed_targets_clear()981 void hud_keyed_targets_clear()
982 {
983 	int i;
984 
985 	// clear out the keyed target list
986 	for (i = 0; i < MAX_KEYED_TARGETS; i++ )
987 		list_init( &(Players[Player_num].keyed_targets[i]) );
988 
989 	Players[Player_num].current_hotkey_set = -1;
990 
991 	// place all of the hoykey target items back onto the free list
992 	list_init( &htarget_free_list );
993 	for ( i = 0; i < MAX_HOTKEY_TARGET_ITEMS; i++ )
994 		list_append( &htarget_free_list, &htarget_items[i] );
995 }
996 
997 // Init data used for the weapons display on the HUD
hud_weapons_init()998 void hud_weapons_init()
999 {
1000 	Weapon_flash_info.is_bright = 0;
1001 	for ( int i = 0; i < MAX_WEAPON_FLASH_LINES; i++ ) {
1002 		Weapon_flash_info.flash_duration[i] = 1;
1003 		Weapon_flash_info.flash_next[i] = 1;
1004 	}
1005 
1006 	// The E: There used to be a number of checks here. They are no longer needed, as the new HUD code handles it on its own.
1007 	Weapon_gauges_loaded = 1;
1008 }
1009 
1010 // init data used to play the homing "proximity warning" sound
hud_init_homing_beep()1011 void hud_init_homing_beep()
1012 {
1013 	Homing_beep.snd_handle        = sound_handle::invalid();
1014 	Homing_beep.last_time_played  = 0;
1015 	Homing_beep.precalced_interp = (Homing_beep.max_cycle_dist-Homing_beep.min_cycle_dist) / (Homing_beep.max_cycle_time - Homing_beep.min_cycle_time );
1016 }
1017 
hud_init_ballistic_index()1018 void hud_init_ballistic_index()
1019 {
1020 	int i;
1021 
1022 	// decide whether to realign HUD for ballistic primaries
1023 	ballistic_hud_index = 0;
1024 	for (i = 0; i <	Player_ship->weapons.num_primary_banks; i++)
1025 	{
1026 		if (Weapon_info[Player_ship->weapons.primary_bank_weapons[i]].wi_flags[Weapon::Info_Flags::Ballistic])
1027 		{
1028 			ballistic_hud_index = 1;
1029 			break;
1030 		}
1031 	}
1032 }
1033 
1034 // hud_init_targeting() will set the current target to point to the dummy node
1035 // in the object used list
1036 //
hud_init_targeting()1037 void hud_init_targeting()
1038 {
1039 	Assert(Player_ai != NULL);
1040 
1041 	// make sure there is no current target
1042 	set_target_objnum( Player_ai, -1 );
1043 	Player_ai->last_target = -1;
1044 	Player_ai->last_subsys_target = NULL;
1045 	Player_ai->last_dist = 0.0f;
1046 	Player_ai->last_speed = 0.0f;
1047 
1048 	hud_keyed_targets_clear();
1049 	hud_init_missile_lock();
1050 	hud_init_artillery();
1051 
1052 	// Init the lists that hold targets in reticle (to allow cycling of targets in reticle)
1053 	hud_reticle_list_init();
1054 	hud_init_homing_beep();
1055 
1056 	hud_weapons_init();
1057 
1058 	Min_warning_missile_dist = 2.5f*Player_obj->radius;
1059 	Max_warning_missile_dist = 1500.0f;
1060 
1061 	Tl_hostile_reset_timestamp = timestamp(0);
1062 	Tl_friendly_reset_timestamp = timestamp(0);
1063 	Target_next_uninspected_object_timestamp = timestamp(0);
1064 	Target_newest_ship_timestamp = timestamp(0);
1065 	Target_next_turret_timestamp = timestamp(0);
1066 
1067 	if(The_mission.flags[Mission::Mission_Flags::Fullneb]) {
1068 		Toggle_text_alpha = TOGGLE_TEXT_NEBULA_ALPHA;
1069 	} else {
1070 		Toggle_text_alpha = TOGGLE_TEXT_NORMAL_ALPHA;
1071 	}
1072 }
1073 
1074 //	Target the next or previous subobject on the currently selected ship, based on next_flag.
hud_target_subobject_common(int next_flag)1075 void hud_target_subobject_common(int next_flag)
1076 {
1077 	if (Player_ai->target_objnum == -1) {
1078 		HUD_sourced_printf(HUD_SOURCE_HIDDEN, "%s", XSTR( "No target selected.", 322));
1079 		snd_play( gamesnd_get_game_sound(GameSounds::TARGET_FAIL) );
1080 		return;
1081 	}
1082 
1083 	if (Objects[Player_ai->target_objnum].type != OBJ_SHIP) {
1084 		snd_play( gamesnd_get_game_sound(GameSounds::TARGET_FAIL));
1085 		return;
1086 	}
1087 
1088 	ship_subsys	*start, *start2, *A;
1089 	ship_subsys	*subsys_to_target=NULL;
1090 	ship			*target_shipp;
1091 
1092 	target_shipp = &Ships[Objects[Player_ai->target_objnum].instance];
1093 
1094 	if (!Player_ai->targeted_subsys) {
1095 		start = END_OF_LIST(&target_shipp->subsys_list);
1096 	} else {
1097 		start = Player_ai->targeted_subsys;
1098 	}
1099 
1100 	start2 = advance_subsys(start, next_flag);
1101 
1102 	for ( A = start2; A != start; A = advance_subsys(A, next_flag) ) {
1103 
1104 		if ( A == &target_shipp->subsys_list ) {
1105 			continue;
1106 		}
1107 
1108 		// ignore turrets
1109 		if ( A->system_info->type == SUBSYSTEM_TURRET ) {
1110 			continue;
1111 		}
1112 
1113 		if ( A->flags[Ship::Subsystem_Flags::Untargetable] ) {
1114 			continue;
1115 		}
1116 
1117 		subsys_to_target = A;
1118 		break;
1119 
1120 	} // end for
1121 
1122 	if ( subsys_to_target == NULL ) {
1123 		snd_play( gamesnd_get_game_sound(GameSounds::TARGET_FAIL));
1124 	} else {
1125 		set_targeted_subsys(Player_ai, subsys_to_target, Player_ai->target_objnum);
1126 		target_shipp->last_targeted_subobject[Player_num] =  Player_ai->targeted_subsys;
1127 	}
1128 }
1129 
advance_fb(object * objp,int next_flag)1130 object *advance_fb(object *objp, int next_flag)
1131 {
1132 	if (next_flag)
1133 		return GET_NEXT(objp);
1134 	else
1135 		return GET_LAST(objp);
1136 }
1137 
1138 //	Target the previous subobject on the currently selected ship.
1139 //
1140 
hud_target_prev_subobject()1141 void hud_target_prev_subobject()
1142 {
1143 	hud_target_subobject_common(0);
1144 }
1145 
hud_target_next_subobject()1146 void hud_target_next_subobject()
1147 {
1148 	hud_target_subobject_common(1);
1149 }
1150 
1151 // hud_target_next() will set the Players[Player_num].current_target to the next target in the object
1152 // used list whose team matches the team parameter.  The player is NOT included in the target list.
1153 //
1154 //	parameters:		team_mask => team(s) of ship to target next
1155 //
1156 
hud_target_common(int team_mask,int next_flag)1157 void hud_target_common(int team_mask, int next_flag)
1158 {
1159 	object	*A, *start, *start2;
1160 	ship		*shipp;
1161 	int		is_ship, target_found = FALSE;
1162 	SCP_list<CJumpNode>::iterator jnp;
1163 
1164 	if (Player_ai->target_objnum == -1)
1165 		start = &obj_used_list;
1166 	else
1167 		start = &Objects[Player_ai->target_objnum];
1168 
1169 	start2 = advance_fb(start, next_flag);
1170 
1171 	for ( A = start2; A != start; A = advance_fb(A, next_flag) ) {
1172 		is_ship = 0;
1173 
1174 		if (A == &obj_used_list)
1175 			continue;
1176 
1177 		if ( (A == Player_obj) || ((A->type != OBJ_SHIP) && (A->type != OBJ_WEAPON) && (A->type != OBJ_JUMP_NODE)) )
1178 			continue;
1179 
1180 		if ( hud_target_invalid_awacs(A) )
1181 			continue;
1182 
1183 		if (A->type == OBJ_WEAPON) {
1184 			if ( !(Weapon_info[Weapons[A->instance].weapon_info_index].wi_flags[Weapon::Info_Flags::Can_be_targeted]) )
1185 				if ( !(Weapon_info[Weapons[A->instance].weapon_info_index].wi_flags[Weapon::Info_Flags::Bomb]) )
1186 					continue;
1187 
1188 			if (Weapons[A->instance].lssm_stage == 3)
1189 				continue;
1190 		}
1191 
1192 		if (A->type == OBJ_SHIP) {
1193 			if (should_be_ignored(&Ships[A->instance]))
1194 				continue;
1195 
1196 			is_ship = 1;
1197 		}
1198 
1199 		if (A->type == OBJ_JUMP_NODE) {
1200 			for (jnp = Jump_nodes.begin(); jnp != Jump_nodes.end(); ++jnp) {
1201 				if( jnp->GetSCPObject() == A )
1202 					break;
1203 			}
1204 
1205 			Assertion(jnp != Jump_nodes.end(), "Failed to find jump node with object index %d; trace out and fix!\n", OBJ_INDEX(A));
1206 
1207 			if( jnp->IsHidden() )
1208 				continue;
1209 		}
1210 
1211 		if ( vm_vec_same(&A->pos, &Eye_position) )
1212 			continue;
1213 
1214 		if ( is_ship ) {
1215 			shipp = &Ships[A->instance];	// get a pointer to the ship information
1216 
1217 			if (!iff_matches_mask(shipp->team, team_mask)) {
1218 				continue;
1219 			}
1220 
1221 			if ( A == Player_obj ) {
1222 				continue;
1223 			}
1224 
1225 			// if we've reached here, it is a valid next target
1226 			if ( Player_ai->target_objnum != OBJ_INDEX(A) ) {
1227 				target_found = TRUE;
1228 				set_target_objnum( Player_ai, OBJ_INDEX(A) );
1229 				hud_shield_hit_reset(A);
1230 				// if ship is BIG|HUGE and last subsys is NULL, get turret
1231 				hud_maybe_set_sorted_turret_subsys(shipp);
1232 				hud_restore_subsystem_target(shipp);
1233 			}
1234 		} else {
1235 			target_found = TRUE;
1236 			set_target_objnum( Player_ai, OBJ_INDEX(A) );
1237 			hud_shield_hit_reset(A);
1238 		}
1239 
1240 		break;
1241 	}
1242 
1243 	if ( target_found == FALSE ) {
1244 		snd_play( gamesnd_get_game_sound(GameSounds::TARGET_FAIL) );
1245 	}
1246 }
1247 
hud_target_next(int team_mask)1248 void hud_target_next(int team_mask)
1249 {
1250 	hud_target_common(team_mask, 1);
1251 }
1252 
hud_target_prev(int team_mask)1253 void hud_target_prev(int team_mask)
1254 {
1255 	hud_target_common(team_mask, 0);
1256 }
1257 
1258 // -------------------------------------------------------------------
1259 // advance_missile_obj()
1260 //
advance_missile_obj(missile_obj * mo,int next_flag)1261 missile_obj *advance_missile_obj(missile_obj *mo, int next_flag)
1262 {
1263 	if (next_flag){
1264 		return GET_NEXT(mo);
1265 	}
1266 
1267 	return GET_LAST(mo);
1268 }
1269 
advance_ship(ship_obj * so,int next_flag)1270 ship_obj *advance_ship(ship_obj *so, int next_flag)
1271 {
1272 	if (next_flag){
1273 		return GET_NEXT(so);
1274 	}
1275 
1276 	return GET_LAST(so);
1277 }
1278 
1279 /// \brief Iterates down to and selects the next target in a linked list
1280 ///        fashion ordered from closest to farthest from the
1281 ///        attacked_object_number, returning the next valid target.
1282 ///
1283 ///
1284 /// \param targeting_from_closest_to_farthest[in]   targets the closest object
1285 ///                                     if true. Targets the farthest
1286 ///                                     away object if false.
1287 /// \param valid_team_mask[in]          A bit mask that defines the desired
1288 ///                                     victim team.
1289 /// \param attacked_object_number[in]   The objectid that is under attack.
1290 ///                                     Defaults to -1.
1291 /// \param target_filters               Applies a bit filter to exclude certain
1292 ///                                     classes of objects from being targeted.
1293 ///                                     Defaults to (SIF_CARGO | SIF_NAVBUOY)
1294 ///
1295 /// \returns         The next object to target if targeting was successful.
1296 ///                  Returns NULL if targeting was unsuccessful.
select_next_target_by_distance(const bool targeting_from_closest_to_farthest,const int valid_team_mask,const int attacked_object_number=-1,flagset<Ship::Info_Flags> * target_filters=NULL)1297 static object* select_next_target_by_distance(const bool targeting_from_closest_to_farthest, const int valid_team_mask, const int attacked_object_number = -1, flagset<Ship::Info_Flags>* target_filters = NULL) {
1298     object *minimum_object_ptr, *maximum_object_ptr, *nearest_object_ptr;
1299     minimum_object_ptr = maximum_object_ptr = nearest_object_ptr = NULL;
1300     float current_distance = hud_find_target_distance(&Objects[Player_ai->target_objnum], Player_obj);
1301     float minimum_distance = 1e20f;
1302     float maximum_distance = 0.0f;
1303     int player_object_index = OBJ_INDEX(Player_obj);
1304     flagset<Ship::Info_Flags> filter;
1305 
1306     if (target_filters != NULL) {
1307         filter = *target_filters;
1308     } else {
1309         filter.set(Ship::Info_Flags::Cargo);
1310         filter.set(Ship::Info_Flags::Navbuoy);
1311     }
1312 
1313     float nearest_distance;
1314     if (targeting_from_closest_to_farthest) {
1315         nearest_distance = 1e20f;
1316     }
1317     else {
1318         nearest_distance = 0.0f;
1319     }
1320 
1321     ship_obj *ship_object_ptr;
1322     object   *prospective_victim_ptr;
1323     ship     *prospective_victim_ship_ptr;
1324     for (ship_object_ptr = GET_FIRST(&Ship_obj_list); ship_object_ptr != END_OF_LIST(&Ship_obj_list); ship_object_ptr = GET_NEXT(ship_object_ptr)) {
1325         prospective_victim_ptr = &Objects[ship_object_ptr->objnum];
1326         // get a pointer to the ship information
1327         prospective_victim_ship_ptr = &Ships[prospective_victim_ptr->instance];
1328 
1329         float new_distance;
1330         if ((prospective_victim_ptr == Player_obj) || (should_be_ignored(prospective_victim_ship_ptr))) {
1331             continue;
1332         }
1333 
1334         // choose from the correct team
1335         if (!iff_matches_mask(prospective_victim_ship_ptr->team, valid_team_mask)) {
1336             continue;
1337         }
1338 
1339         // don't use object if it is already a target
1340         if (OBJ_INDEX(prospective_victim_ptr) == Player_ai->target_objnum) {
1341             continue;
1342         }
1343 
1344 
1345         if (attacked_object_number == -1) {
1346             // always ignore navbuoys and cargo
1347             if ((Ship_info[prospective_victim_ship_ptr->ship_info_index].flags & filter).any_set()) {
1348                 continue;
1349             }
1350 
1351             if (hud_target_invalid_awacs(prospective_victim_ptr)) {
1352                 continue;
1353             }
1354 
1355             new_distance = hud_find_target_distance(prospective_victim_ptr, Player_obj);
1356         }
1357         else {
1358             // Filter out any target that is not targeting the player  --Mastadon
1359             if ((attacked_object_number == player_object_index) && (Ai_info[prospective_victim_ship_ptr->ai_index].target_objnum != player_object_index)) {
1360                 continue;
1361             }
1362             esct eval_ship_as_closest_target_args;
1363             eval_ship_as_closest_target_args.attacked_objnum = attacked_object_number;
1364             eval_ship_as_closest_target_args.check_all_turrets = (attacked_object_number == player_object_index);
1365             eval_ship_as_closest_target_args.check_nearest_turret = FALSE;
1366             // We don't ever filter our target selection to just bombers or fighters
1367             // because the select next attacker logic doesn't.  --Mastadon
1368             eval_ship_as_closest_target_args.filter = 0;
1369             eval_ship_as_closest_target_args.team_mask = valid_team_mask;
1370             // We always get the turret attacking, since that's how the select next
1371             // attacker logic does it.  --Mastadon
1372             eval_ship_as_closest_target_args.turret_attacking_target = 1;
1373             eval_ship_as_closest_target_args.shipp = prospective_victim_ship_ptr;
1374             evaluate_ship_as_closest_target(&eval_ship_as_closest_target_args);
1375 
1376             new_distance = eval_ship_as_closest_target_args.min_distance;
1377         }
1378 
1379 
1380         if (new_distance <= minimum_distance) {
1381             minimum_distance = new_distance;
1382             minimum_object_ptr = prospective_victim_ptr;
1383         }
1384 
1385         if (new_distance >= maximum_distance) {
1386             maximum_distance = new_distance;
1387             maximum_object_ptr = prospective_victim_ptr;
1388         }
1389 
1390         float diff = 0.0f;
1391         if (targeting_from_closest_to_farthest) {
1392             diff = new_distance - current_distance;
1393             if (diff > 0.0f) {
1394                 if (diff < (nearest_distance - current_distance)) {
1395                     nearest_distance = new_distance;
1396                     nearest_object_ptr = prospective_victim_ptr;
1397                 }
1398             }
1399         }
1400         else {
1401             diff = current_distance - new_distance;
1402             if (diff > 0.0f) {
1403                 if (diff < (current_distance - nearest_distance)) {
1404                     nearest_distance = new_distance;
1405                     nearest_object_ptr = prospective_victim_ptr;
1406                 }
1407             }
1408         }
1409     }
1410 
1411     if (nearest_object_ptr == NULL) {
1412 
1413         if (targeting_from_closest_to_farthest) {
1414             if (minimum_object_ptr != NULL) {
1415                 nearest_object_ptr = minimum_object_ptr;
1416             }
1417         }
1418         else {
1419             if (maximum_object_ptr != NULL) {
1420                 nearest_object_ptr = maximum_object_ptr;
1421             }
1422         }
1423     }
1424 
1425     return nearest_object_ptr;
1426 }
1427 
1428 ship_obj *get_ship_obj_ptr_from_index(int index);
1429 // -------------------------------------------------------------------
1430 // hud_target_missile()
1431 //
1432 // Target the closest locked missile that is locked on locked_obj
1433 //
1434 //	input:	source_obj	=>		pointer to object that fired weapon
1435 //				next_flag	=>		0 -> previous 1 -> next
1436 //
1437 // NOTE: this function is only allows targeting bombs
hud_target_missile(object * source_obj,int next_flag)1438 void hud_target_missile(object *source_obj, int next_flag)
1439 {
1440 	missile_obj	*end, *start, *mo;
1441 	object		*A, *target_objp;
1442 	ai_info		*aip;
1443 	weapon		*wp;
1444 	weapon_info	*wip;
1445 	int			target_found = 0;
1446 
1447 	if ( source_obj->type != OBJ_SHIP )
1448 		return;
1449 
1450 	Assert( Ships[source_obj->instance].ai_index != -1 );
1451 	aip = &Ai_info[Ships[source_obj->instance].ai_index];
1452 
1453 	end = &Missile_obj_list;
1454 	if (aip->target_objnum != -1) {
1455 		target_objp = &Objects[aip->target_objnum];
1456 		if ( target_objp->type == OBJ_WEAPON && Weapon_info[Weapons[target_objp->instance].weapon_info_index].subtype == WP_MISSILE )	{	// must be a missile
1457 			end = missile_obj_return_address(Weapons[target_objp->instance].missile_list_index);
1458 		}
1459 	}
1460 
1461 	start = advance_missile_obj(end, next_flag);
1462 
1463 	for ( mo = start; mo != end; mo = advance_missile_obj(mo, next_flag) ) {
1464 		if ( mo == &Missile_obj_list ){
1465 			continue;
1466 		}
1467 
1468 		Assert(mo->objnum >= 0 && mo->objnum < MAX_OBJECTS);
1469 		A = &Objects[mo->objnum];
1470 
1471 		Assert(A->type == OBJ_WEAPON);
1472 		Assert((A->instance >= 0) && (A->instance < MAX_WEAPONS));
1473 		wp = &Weapons[A->instance];
1474 		wip = &Weapon_info[wp->weapon_info_index];
1475 
1476 		// only allow targeting of bombs
1477 		if ( !(wip->wi_flags[Weapon::Info_Flags::Can_be_targeted]) ) {
1478 			if ( !(wip->wi_flags[Weapon::Info_Flags::Bomb]) ) {
1479 				continue;
1480 			}
1481 		}
1482 
1483 		if (wp->lssm_stage==3){
1484 			continue;
1485 		}
1486 
1487 		// only allow targeting of hostile bombs
1488 		if (!iff_x_attacks_y(Player_ship->team, obj_team(A))) {
1489 			continue;
1490 		}
1491 
1492 		if(hud_target_invalid_awacs(A)){
1493 			continue;
1494 		}
1495 
1496 		// if we've reached here, got a new target
1497 		target_found = TRUE;
1498 		set_target_objnum( aip, OBJ_INDEX(A) );
1499 		hud_shield_hit_reset(A);
1500 		break;
1501 	}	// end for
1502 
1503 	if ( !target_found ) {
1504 	// if no bomb is found, search for bombers
1505 		ship_obj *startShip, *so;
1506 
1507 		if ( (aip->target_objnum != -1)
1508 			&& (Objects[aip->target_objnum].type == OBJ_SHIP)
1509 			&& ((Ship_info[Ships[Objects[aip->target_objnum].instance].ship_info_index].flags[Ship::Info_Flags::Bomber])
1510 				|| (Objects[aip->target_objnum].flags[Object::Object_Flags::Targetable_as_bomb]))) {
1511 			int index = Ships[Objects[aip->target_objnum].instance].ship_list_index;
1512 			startShip = get_ship_obj_ptr_from_index(index);
1513 		} else {
1514 			startShip = GET_FIRST(&Ship_obj_list);
1515 		}
1516 
1517 		for (so=advance_ship(startShip, next_flag); so!=startShip; so=advance_ship(so, next_flag)) {
1518 			A = &Objects[so->objnum];
1519 
1520 			// don't look at header
1521 			if (so == &Ship_obj_list) {
1522 				continue;
1523 			}
1524 
1525 			// only allow targeting of hostile bombs
1526 			if (!iff_x_attacks_y(Player_ship->team, obj_team(A))) {
1527 				continue;
1528 			}
1529 
1530 			if(hud_target_invalid_awacs(A)){
1531 				continue;
1532 			}
1533 
1534 			// check if ship type is bomber
1535 			if ( !(Ship_info[Ships[A->instance].ship_info_index].flags[Ship::Info_Flags::Bomber]) && !(A->flags[Object::Object_Flags::Targetable_as_bomb]) ) {
1536 				continue;
1537 			}
1538 
1539 			// check if ignore
1540 			if ( should_be_ignored(&Ships[A->instance]) ){
1541 				continue;
1542 			}
1543 
1544 			// found a good one
1545 			target_found = TRUE;
1546 			set_target_objnum( aip, OBJ_INDEX(A) );
1547 			hud_shield_hit_reset(A);
1548 			break;
1549 		}
1550 	}
1551 
1552 	if ( !target_found ) {
1553 		snd_play( gamesnd_get_game_sound(GameSounds::TARGET_FAIL), 0.0f );
1554 	}
1555 }
1556 
1557 // Return !0 if shipp can be scanned, otherwise return 0
hud_target_ship_can_be_scanned(ship * shipp)1558 int hud_target_ship_can_be_scanned(ship *shipp)
1559 {
1560 	ship_info *sip;
1561 
1562 	sip = &Ship_info[shipp->ship_info_index];
1563 
1564 	// ignore cargo that has already been scanned
1565 	if (shipp->flags[Ship::Ship_Flags::Cargo_revealed])
1566 		return 0;
1567 
1568 	// allow ships with scannable flag set
1569 	if (shipp->flags[Ship::Ship_Flags::Scannable])
1570 		return 1;
1571 
1572 	// ignore ships that don't carry cargo
1573 	if ((sip->class_type < 0) || !(Ship_types[sip->class_type].flags[Ship::Type_Info_Flags::Scannable]))
1574 		return 0;
1575 
1576 	return 1;
1577 }
1578 
1579 // target the next/prev uninspected cargo container
hud_target_uninspected_cargo(int next_flag)1580 void hud_target_uninspected_cargo(int next_flag)
1581 {
1582 	object	*A, *start, *start2;
1583 	ship		*shipp;
1584 	int		target_found = 0;
1585 
1586 	if (Player_ai->target_objnum == -1) {
1587 		start = &obj_used_list;
1588 	}  else {
1589 		start = &Objects[Player_ai->target_objnum];
1590 	}
1591 
1592 	start2 = advance_fb(start, next_flag);
1593 
1594 	for ( A = start2; A != start; A = advance_fb(A, next_flag) ) {
1595 		if ( A == &obj_used_list ) {
1596 			continue;
1597 		}
1598 
1599 		if (A == Player_obj || (A->type != OBJ_SHIP) ) {
1600 			continue;
1601 		}
1602 
1603 		shipp = &Ships[A->instance];	// get a pointer to the ship information
1604 
1605 		if ( should_be_ignored(shipp) ) {
1606 			continue;
1607 		}
1608 
1609 		// ignore all non-cargo carrying craft
1610 		if ( !hud_target_ship_can_be_scanned(shipp) ) {
1611 			continue;
1612 		}
1613 
1614 		if(hud_target_invalid_awacs(A)){
1615 			continue;
1616 		}
1617 
1618 		// if we've reached here, it is a valid next target
1619 		if ( Player_ai->target_objnum != OBJ_INDEX(A) ) {
1620 			target_found = TRUE;
1621 			set_target_objnum( Player_ai, OBJ_INDEX(A) );
1622 			hud_shield_hit_reset(A);
1623 		}
1624 	}
1625 
1626 	if ( target_found == FALSE ) {
1627 		snd_play( gamesnd_get_game_sound(GameSounds::TARGET_FAIL));
1628 	}
1629 }
1630 
1631 // target the newest ship in the area
hud_target_newest_ship()1632 void hud_target_newest_ship()
1633 {
1634 	object	*A, *player_target_objp;
1635 	object	*newest_obj=NULL;
1636 	ship		*shipp;
1637 	ship_obj	*so;
1638 	uint		current_target_arrived_time = 0xffffffff, newest_time = 0;
1639 
1640 	if ( Player_ai->target_objnum >= 0 ) {
1641 		player_target_objp = &Objects[Player_ai->target_objnum];
1642 		if ( player_target_objp->type == OBJ_SHIP ) {
1643 			current_target_arrived_time = Ships[player_target_objp->instance].create_time;
1644 		}
1645 	} else {
1646 		player_target_objp = NULL;
1647 	}
1648 
1649 	// If no target is selected, then simply target the closest uninspected cargo
1650 	if ( Player_ai->target_objnum == -1 || timestamp_elapsed(Target_newest_ship_timestamp) ) {
1651 		current_target_arrived_time = 0xffffffff;
1652 	}
1653 
1654 	Target_newest_ship_timestamp = timestamp(TL_RESET);
1655 
1656 	for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
1657 		A = &Objects[so->objnum];
1658 		shipp = &Ships[A->instance];	// get a pointer to the ship information
1659 
1660 		if ( (A == Player_obj) || (should_be_ignored(shipp)) )
1661 			continue;
1662 
1663 		// ignore navbuoys
1664 		if ( Ship_info[shipp->ship_info_index].flags[Ship::Info_Flags::Navbuoy] ) {
1665 			continue;
1666 		}
1667 
1668 		if ( A == player_target_objp ) {
1669 			continue;
1670 		}
1671 
1672 		if(hud_target_invalid_awacs(A)){
1673 			continue;
1674 		}
1675 
1676 		if ( (shipp->create_time >= newest_time) && (shipp->create_time <= current_target_arrived_time) ) {
1677 			newest_time = shipp->create_time;
1678 			newest_obj = A;
1679 		}
1680 	}
1681 
1682 	if (newest_obj) {
1683 		set_target_objnum( Player_ai, OBJ_INDEX(newest_obj) );
1684 		hud_shield_hit_reset(newest_obj);
1685 		// if BIG|HUGE and no selected subsystem, get sorted turret
1686 		hud_maybe_set_sorted_turret_subsys(&Ships[newest_obj->instance]);
1687 		hud_restore_subsystem_target(&Ships[newest_obj->instance]);
1688 	}
1689 	else {
1690 		snd_play( gamesnd_get_game_sound(GameSounds::TARGET_FAIL));
1691 	}
1692 }
1693 
1694 #define TYPE_NONE						0
1695 #define TYPE_FACING_BEAM			1
1696 #define TYPE_FACING_FLAK			2
1697 #define TYPE_FACING_MISSILE		3
1698 #define TYPE_FACING_LASER			4
1699 #define TYPE_NONFACING_BEAM		5
1700 #define TYPE_NONFACING_FLAK		6
1701 #define TYPE_NONFACING_MISSILE	7
1702 #define TYPE_NONFACING_LASER		8
1703 #define TYPE_NONFACING_INC			4
1704 
1705 typedef struct eval_next_turret {
1706 	ship_subsys *ss;
1707 	int type;
1708 	float dist;
1709 } eval_next_turret;
1710 
turret_compare_func(const void * e1,const void * e2)1711 int turret_compare_func(const void *e1, const void *e2)
1712 {
1713 	eval_next_turret *p1 = (eval_next_turret*)e1;
1714 	eval_next_turret *p2 = (eval_next_turret*)e2;
1715 
1716 	Assert(p1->type != TYPE_NONE);
1717 	Assert(p2->type != TYPE_NONE);
1718 
1719 	if (p1->type != p2->type) {
1720 		return (p1->type - p2->type);
1721 	} else {
1722 		float delta_dist = p1->dist - p2->dist;
1723 		if (delta_dist < 0) {
1724 			return -1;
1725 		} else if (delta_dist > 0) {
1726 			return 1;
1727 		} else {
1728 			return 0;
1729 		}
1730 	}
1731 }
1732 
1733 extern bool turret_weapon_has_flags(ship_weapon *swp, Weapon::Info_Flags flags);
1734 extern bool turret_weapon_has_subtype(ship_weapon *swp, int subtype);
1735 // target the next/prev live turret on the current target
1736 // auto_advance from hud_update_closest_turret
hud_target_live_turret(int next_flag,int auto_advance,int only_player_target)1737 void hud_target_live_turret(int next_flag, int auto_advance, int only_player_target)
1738 {
1739 	ship_subsys	*A;
1740 	ship_subsys	*live_turret=NULL;
1741 	ship			*target_shipp;
1742 	object		*objp;
1743 	eval_next_turret ent[MAX_MODEL_SUBSYSTEMS];
1744 	int num_live_turrets = 0;
1745 
1746 	// make sure we're targeting a ship
1747 	if (Player_ai->target_objnum == -1 && !auto_advance) {
1748 		snd_play(gamesnd_get_game_sound(GameSounds::TARGET_FAIL));
1749 		return;
1750 	}
1751 
1752 	// only targeting subsystems on ship
1753 	if ((Objects[Player_ai->target_objnum].type != OBJ_SHIP) && (!auto_advance)) {
1754 		snd_play( gamesnd_get_game_sound(GameSounds::TARGET_FAIL));
1755 		return;
1756 	}
1757 
1758 	// set some pointers
1759 	objp = &Objects[Player_ai->target_objnum];
1760 	target_shipp = &Ships[objp->instance];
1761 
1762 	// set timestamp
1763 	int timestamp_val = 0;
1764 	if (!auto_advance) {
1765 		timestamp_val = Target_next_turret_timestamp;
1766 		Target_next_turret_timestamp = timestamp(TURRET_RESET);
1767 	}
1768 
1769 	// If no target is selected, then simply target the closest (or facing) turret
1770 	int last_subsys_turret = FALSE;
1771 	if (Player_ai->targeted_subsys != NULL) {
1772 		if (Player_ai->targeted_subsys->system_info->type == SUBSYSTEM_TURRET) {
1773 			if (Player_ai->targeted_subsys->weapons.num_primary_banks > 0 || Player_ai->targeted_subsys->weapons.num_secondary_banks > 0) {
1774 				last_subsys_turret = TRUE;
1775 			}
1776 		}
1777 	}
1778 
1779 	// do we want the closest turret (or the one our ship is pointing at)
1780 	int get_closest_turret = (auto_advance || !last_subsys_turret || timestamp_elapsed(timestamp_val));
1781 
1782 	// initialize eval struct
1783 	memset(ent,0, sizeof(ent));
1784 	int use_straight_ahead_turret = FALSE;
1785 
1786 	// go through list of turrets
1787 	for (A=GET_FIRST(&target_shipp->subsys_list); A!=END_OF_LIST(&target_shipp->subsys_list); A=GET_NEXT(A))  {
1788 		// get a turret
1789 		if (A->system_info->type == SUBSYSTEM_TURRET) {
1790 			// niffiwan: ignore untargetable turrets
1791 			if ( A->flags[Ship::Subsystem_Flags::Untargetable] ) {
1792 				continue;
1793 			}
1794 			// check turret has hit points and has a weapon
1795 			if ( (A->current_hits > 0) && (A->weapons.num_primary_banks > 0 || A->weapons.num_secondary_banks > 0) ) {
1796 				if ( !only_player_target || (A->turret_enemy_objnum == OBJ_INDEX(Player_obj)) ) {
1797 					vec3d gsubpos, vec_to_subsys;
1798 					float distance, dot;
1799 					// get world pos of subsystem and its distance
1800 					get_subsystem_world_pos(objp, A, &gsubpos);
1801 					distance = vm_vec_normalized_dir(&vec_to_subsys, &gsubpos, &View_position);
1802 
1803 					// check if facing and in view
1804 					int facing = ship_subsystem_in_sight(objp, A, &View_position, &gsubpos, 0);
1805 
1806 					if (!auto_advance && get_closest_turret && !only_player_target) {
1807 						// if within 3 degrees and not previous subsys, use subsys in front
1808 						dot = vm_vec_dot(&vec_to_subsys, &Player_obj->orient.vec.fvec);
1809 						if ((dot > 0.9986) && facing) {
1810 							use_straight_ahead_turret = TRUE;
1811 							break;
1812 						}
1813 					}
1814 
1815 					// set weapon_type to allow sort of ent on type
1816 					if (turret_weapon_has_flags(&A->weapons, Weapon::Info_Flags::Beam)) {
1817 						ent[num_live_turrets].type = TYPE_FACING_BEAM;
1818 					} else  if (turret_weapon_has_flags(&A->weapons, Weapon::Info_Flags::Flak)) {
1819 						ent[num_live_turrets].type = TYPE_FACING_FLAK;
1820 					} else {
1821 						if (turret_weapon_has_subtype(&A->weapons, WP_MISSILE)) {
1822 							ent[num_live_turrets].type = TYPE_FACING_MISSILE;
1823 						} else if (turret_weapon_has_subtype(&A->weapons, WP_LASER)) {
1824 							ent[num_live_turrets].type = TYPE_FACING_LASER;
1825 						} else {
1826 							//Turret not live, bail
1827 							continue;
1828 						}
1829 					}
1830 
1831 					// fill out ent struct
1832 					ent[num_live_turrets].ss = A;
1833 					ent[num_live_turrets].dist = distance;
1834 					if (!facing) {
1835 						ent[num_live_turrets].type += TYPE_NONFACING_INC;
1836 					}
1837 					num_live_turrets++;
1838 				}
1839 			}
1840 		}
1841 	}
1842 
1843 	// sort the list if we're not using turret straight ahead of us
1844 	if (!use_straight_ahead_turret) {
1845 		insertion_sort(ent, num_live_turrets, sizeof(eval_next_turret), turret_compare_func);
1846 	}
1847 
1848 	if (use_straight_ahead_turret) {
1849 	// use the straight ahead turret
1850 		live_turret = A;
1851 	} else {
1852 	// check if we have a currently targeted turret and find its position after the sort
1853 		int i, start_index, next_index;
1854 		if (get_closest_turret) {
1855 			start_index = 0;
1856 		} else {
1857 			start_index = -1;
1858 			for (i=0; i<num_live_turrets; i++) {
1859 				if (ent[i].ss == Player_ai->targeted_subsys) {
1860 					start_index = i;
1861 					break;
1862 				}
1863 			}
1864 			// check that we started with a turret
1865 			if (start_index == -1) {
1866 				start_index = 0;
1867 			}
1868 		}
1869 
1870 		// set next live turret
1871 		if (num_live_turrets == 0) {
1872 			// no live turrets
1873 			live_turret = NULL;
1874 		} else if (num_live_turrets == 1 || get_closest_turret) {
1875 			// only 1 live turret, so set it
1876 			live_turret = ent[0].ss;
1877 		} else {
1878 			if (next_flag) {
1879 				// advance to next closest turret
1880 				next_index = start_index + 1;
1881 				if (next_index == num_live_turrets) {
1882 					next_index = 0;
1883 				}
1884 			} else {
1885 				// go to next farther turret
1886 				next_index = start_index - 1;
1887 				if (next_index == -1) {
1888 					next_index = num_live_turrets - 1;
1889 				}
1890 			}
1891 
1892 			// set the next turret to be targeted based on next_index
1893 			live_turret = ent[next_index].ss;
1894 		}
1895 
1896 		//if (live_turret) {
1897 			// debug info
1898 		//	mprintf(("name %s, index: %d, type: %d\n", live_turret->system_info->subobj_name, next_index, ent[next_index].type));
1899 		//}
1900 	}
1901 
1902 	if ( live_turret != NULL ) {
1903 		set_targeted_subsys(Player_ai, live_turret, Player_ai->target_objnum);
1904 		target_shipp->last_targeted_subobject[Player_num] = Player_ai->targeted_subsys;
1905 	} else {
1906 		if (!auto_advance) {
1907 			snd_play( gamesnd_get_game_sound(GameSounds::TARGET_FAIL));
1908 		}
1909 	}
1910 }
1911 
1912 // select a new target, by auto-targeting
hud_target_auto_target_next()1913 void hud_target_auto_target_next()
1914 {
1915 	if ( Framecount < 2 ) {
1916 		return;
1917 	}
1918 
1919 	//	No auto-targeting after dead.
1920 	if (Game_mode & (GM_DEAD | GM_DEAD_BLEW_UP))
1921 		return;
1922 
1923 	// try target next ship in hotkey set, if any -- Backslash
1924 	if ( Player->current_hotkey_set != -1 ) {
1925 		hud_target_hotkey_select(Player->current_hotkey_set);
1926 	}
1927 
1928 	int valid_team_mask = iff_get_attackee_mask(Player_ship->team);
1929 
1930 	// if none, try targeting closest hostile fighter/bomber
1931 	if ( Player_ai->target_objnum == -1 ) { //-V581
1932 		hud_target_closest(valid_team_mask, -1, FALSE, TRUE);
1933 	}
1934 
1935 	// No fighter/bombers exists, so go ahead an target the closest hostile
1936 	if ( Player_ai->target_objnum == -1 ) { //-V581
1937 		hud_target_closest(valid_team_mask, -1, FALSE);
1938 	}
1939 
1940 	// um, ok.  Try targeting asteroids that are on a collision course for an escort ship
1941 	if ( Player_ai->target_objnum == -1 ) { //-V581
1942 		asteroid_target_closest_danger();
1943 	}
1944 }
1945 
1946 
1947 // Given that object 'targeter' is targeting object 'targetee',
1948 // how far are they?   This uses the point closest to the targeter
1949 // object on the targetee's bounding box.  So if targeter is inside
1950 // targtee's bounding box, the distance is 0.
hud_find_target_distance(object * targetee,const vec3d * targeter_pos)1951 float hud_find_target_distance( object *targetee, const vec3d *targeter_pos )
1952 {
1953 	vec3d tmp_pnt;
1954 
1955 	int model_num = -1;
1956 
1957 	// Which model is it?
1958 	switch( targetee->type )	{
1959 	case OBJ_SHIP:
1960 		model_num = Ship_info[Ships[targetee->instance].ship_info_index].model_num;
1961 		break;
1962 	case OBJ_DEBRIS:
1963 //		model_num = Debris[targetee->instance].model_num;
1964 		break;
1965 	case OBJ_WEAPON:
1966 		// Don't find model_num since circles would work better
1967 		//model_num = Weapon_info[Weapons[targetee->instance].weapon_info_index].model_num;
1968 		break;
1969 	case OBJ_ASTEROID:
1970 		// Don't find model_num since circles would work better
1971 		//model_num = Asteroid_info[Asteroids[targetee->instance].type].model_num;
1972 		break;
1973 	case OBJ_JUMP_NODE:
1974 		// Don't find model_num since circles would work better
1975 		//model_num = Jump_nodes[targetee->instance].modelnum;
1976 		break;
1977 	}
1978 
1979 	float dist = 0.0f;
1980 
1981 	// New way, that uses bounding box.
1982 	if ( model_num > -1 )	{
1983 		dist = model_find_closest_point( &tmp_pnt, model_num, -1, &targetee->orient, &targetee->pos, targeter_pos );
1984 	}  else {
1985 		// Old way, that uses radius.
1986 		dist = vm_vec_dist_quick(&targetee->pos, targeter_pos) - targetee->radius;
1987 		if ( dist < 0.0f )	{
1988 			dist = 0.0f;
1989 		}
1990 	}
1991 	return dist;
1992 }
1993 
hud_find_target_distance(object * targetee,object * targeter)1994 float hud_find_target_distance( object *targetee, object *targeter )
1995 {
1996 	return hud_find_target_distance(targetee, &targeter->pos);
1997 }
1998 
1999 //
2000 
2001 /// \brief evaluate a ship (and maybe turrets) as a potential target
2002 ///
2003 /// Check if shipp (or its turrets) is attacking attacked_objnum
2004 /// Provides a special case for player trying to select target (don't check if
2005 /// turrets are aimed at player)
2006 ///
2007 /// \param[in, out] *esct The Evaluate Ship as Closest Target (esct) that will
2008 ///                       be used to determine if a target is a valid, harmful
2009 ///                       target and, if so, sets the min_distance attribute
2010 ///                       to the distance of either the attacker or the
2011 ///                       closest attacker's turret. Otherwise, min_distance
2012 ///                       is set to FLT_MAX
2013 ///
2014 /// \return true if either the ship or one of it's turrets are attacking the
2015 ///                       player. Otherwise, returns false.
evaluate_ship_as_closest_target(esct * esct_p)2016 bool evaluate_ship_as_closest_target(esct *esct_p)
2017 {
2018 	int targeting_player, turret_is_attacking;
2019 	ship_subsys *ss;
2020 	float new_distance;
2021 
2022 	// initialize
2023 	esct_p->min_distance = FLT_MAX;
2024 	esct_p->check_nearest_turret = FALSE;
2025 	turret_is_attacking = FALSE;
2026 
2027 
2028 	object *objp = &Objects[esct_p->shipp->objnum];
2029 	Assert(objp->type == OBJ_SHIP);
2030 	if (objp->type != OBJ_SHIP) {
2031 		return false;
2032 	}
2033 
2034 	// player being targeted, so we will want closest distance from player
2035 	targeting_player = (esct_p->attacked_objnum == OBJ_INDEX(Player_obj));
2036 
2037 	// filter on team
2038 	if ( !iff_matches_mask(esct_p->shipp->team, esct_p->team_mask) ) {
2039 		return false;
2040 	}
2041 
2042 	// check if player or ignore ship
2043 	if ( (esct_p->shipp->objnum == OBJ_INDEX(Player_obj)) || (should_be_ignored(esct_p->shipp)) ) {
2044 		return false;
2045 	}
2046 
2047 	// bail if harmless
2048 	if ( Ship_info[esct_p->shipp->ship_info_index].class_type > -1 && !(Ship_types[Ship_info[esct_p->shipp->ship_info_index].class_type].flags[Ship::Type_Info_Flags::Target_as_threat])) {
2049 		return false;
2050 	}
2051 
2052 	// only look at targets that are AWACS valid
2053 	if (hud_target_invalid_awacs(&Objects[esct_p->shipp->objnum])) {
2054 		return false;
2055 	}
2056 
2057 	// If filter is set, only target fighters and bombers
2058 	if ( esct_p->filter ) {
2059 		if ( !(Ship_info[esct_p->shipp->ship_info_index].is_fighter_bomber()) ) {
2060 			return false;
2061 		}
2062 	}
2063 
2064 	// find closest turret to player if BIG or HUGE ship
2065 	if (Ship_info[esct_p->shipp->ship_info_index].is_big_or_huge()) {
2066 		for (ss=GET_FIRST(&esct_p->shipp->subsys_list); ss!=END_OF_LIST(&esct_p->shipp->subsys_list); ss=GET_NEXT(ss)) {
2067 
2068 			if (ss->flags[Ship::Subsystem_Flags::Untargetable])
2069 				continue;
2070 
2071 			if ( (ss->system_info->type == SUBSYSTEM_TURRET) && (ss->current_hits > 0) ) {
2072 
2073 				if (esct_p->check_all_turrets || (ss->turret_enemy_objnum == esct_p->attacked_objnum)) {
2074 					turret_is_attacking = 1;
2075 					esct_p->check_nearest_turret = TRUE;
2076 
2077 					if ( !esct_p->turret_attacking_target || (esct_p->turret_attacking_target && (ss->turret_enemy_objnum == esct_p->attacked_objnum)) ) {
2078 						vec3d gsubpos;
2079 						// get world pos of subsystem
2080 						vm_vec_unrotate(&gsubpos, &ss->system_info->pnt, &objp->orient);
2081 						vm_vec_add2(&gsubpos, &objp->pos);
2082 						new_distance = vm_vec_dist_quick(&gsubpos, &Player_obj->pos);
2083 
2084 						/*
2085 						// GET TURRET TYPE, FAVOR BEAM, FLAK, OTHER
2086 						int turret_type = ss->system_info->turret_weapon_type;
2087 						if (Weapon_info[turret_type].wi_flags[Weapon::Info_Flags::Beam]) {
2088 							new_distance *= 0.3f;
2089 						} else if (Weapon_info[turret_type].wi_flags[Weapon::Info_Flags::Flak]) {
2090 							new_distance *= 0.6f;
2091 						} */
2092 
2093 						// get the closest distance
2094 						if (new_distance <= esct_p->min_distance) {
2095 							esct_p->min_distance = new_distance;
2096 						}
2097 					}
2098 				}
2099 			}
2100 		}
2101 	}
2102 
2103 	// If no turret is attacking, check if objp is actually targeting attacked_objnum
2104 	// don't bail if targeting is for player
2105 	if ( !targeting_player && !turret_is_attacking ) {
2106 		ai_info *aip = &Ai_info[esct_p->shipp->ai_index];
2107 
2108 		if (aip->target_objnum != esct_p->attacked_objnum) {
2109 			return false;
2110 		}
2111 
2112 		if ( (Game_mode & GM_NORMAL) && ( aip->mode != AIM_CHASE ) && (aip->mode != AIM_STRAFE) && (aip->mode != AIM_EVADE) && (aip->mode != AIM_EVADE_WEAPON) && (aip->mode != AIM_AVOID)) {
2113 			return false;
2114 		}
2115 	}
2116 
2117 	// consider the ship alone if there are no attacking turrets
2118 	if ( !turret_is_attacking ) {
2119 		//new_distance = hud_find_target_distance(objp, Player_obj);
2120 		new_distance = vm_vec_dist_quick(&objp->pos, &Player_obj->pos);
2121 
2122 		if (new_distance <= esct_p->min_distance) {
2123 			esct_p->min_distance = new_distance;
2124 			esct_p->check_nearest_turret = FALSE;
2125 		}
2126 	}
2127 
2128 	return true;
2129 }
2130 
2131 /// \brief Sets the Players[Player_num].current_target to the closest ship to
2132 ///        the player that matches the team passed as a paramater.
2133 ///
2134 /// The current algorithm is to simply iterate through the objects and
2135 /// calculate the magnitude of the vector that connects the player to the
2136 /// target. The smallest magnitude is tracked, and then used to locate the
2137 /// closest hostile ship. Note only the square of the magnitude is required,
2138 /// since we are only comparing magnitudes.
2139 ///
2140 /// \param[in] team_mask       team of closest ship that should be targeted.
2141 ///                            Default value is -1, if team doesn't matter.
2142 ///
2143 /// \param[in] attacked_objnum object number of ship that is being attacked
2144 /// \param[in] play_fail_snd   boolean, whether to play SND_TARGET_FAIL
2145 ///                            (needed, since function called repeatedly when
2146 ///                            auto-targeting is enabled, and we don't want a
2147 ///                            string of fail sounds playing). This is a
2148 ///                            default parameter with a value of TRUE.
2149 /// \param[in] filter          OPTIONAL parameter (default value 0): when set
2150 ///                            to TRUE, only fighters and bombers are
2151 ///                            considered for new targets.
2152 /// \param[in] get_closest_turret_attacking_player Finds the closest turret
2153 ///                            attacking the player if true. Otherwise, only
2154 ///                            finds the closest attacking ship, targeting the
2155 ///                            turret closest to the player.
2156 ///
2157 /// \return: true (non-zero) if a target was acquired. Returns false (zero) if
2158 ///          no target was acquired.
hud_target_closest(int team_mask,int attacked_objnum,int play_fail_snd,int filter,int get_closest_turret_attacking_player)2159 int hud_target_closest(int team_mask, int attacked_objnum, int play_fail_snd, int filter, int get_closest_turret_attacking_player)
2160 {
2161 	object	*A;
2162 	object	*nearest_obj = &obj_used_list;
2163 	ship		*shipp;
2164 	ship_obj	*so;
2165 	int		check_nearest_turret = FALSE;
2166 
2167 	// evaluate ship closest target struct
2168 	esct		eval_ship_as_closest_target_args;
2169 
2170 	float		min_distance = FLT_MAX;
2171 	int		target_found = FALSE;
2172 
2173 	int		player_obj_index = OBJ_INDEX(Player_obj);
2174 	ship_subsys *ss;
2175 
2176 	int initial_attacked_objnum = attacked_objnum;
2177 
2178 	if ( (attacked_objnum >= 0) && (attacked_objnum != player_obj_index) ) {
2179 		// bail if player does not have target
2180 		if ( Player_ai->target_objnum == -1 ) {
2181 			goto Target_closest_done;
2182 		}
2183 
2184 		if ( Objects[attacked_objnum].type != OBJ_SHIP ) {
2185 			goto Target_closest_done;
2186 		}
2187 
2188 		// bail if ship is to be ignored
2189 		if (should_be_ignored(&Ships[Objects[attacked_objnum].instance])) {
2190 			goto Target_closest_done;
2191 		}
2192 	}
2193 
2194 	if (attacked_objnum == -1) {
2195 		attacked_objnum = player_obj_index;
2196 	}
2197 
2198 	// check all turrets if for player.
2199 	eval_ship_as_closest_target_args.check_all_turrets = (attacked_objnum == player_obj_index);
2200 	eval_ship_as_closest_target_args.filter = filter;
2201 	eval_ship_as_closest_target_args.team_mask = team_mask;
2202 	eval_ship_as_closest_target_args.attacked_objnum = attacked_objnum;
2203 	eval_ship_as_closest_target_args.turret_attacking_target = get_closest_turret_attacking_player;
2204 
2205 	for ( so=GET_FIRST(&Ship_obj_list); so!=END_OF_LIST(&Ship_obj_list); so=GET_NEXT(so) ) {
2206 
2207 		A = &Objects[so->objnum];
2208 		shipp = &Ships[A->instance];	// get a pointer to the ship information
2209 
2210 		// fill in rest of eval_ship_as_closest_target_args
2211 		eval_ship_as_closest_target_args.shipp = shipp;
2212 
2213 		// Filter out any target that is not targeting the player  --Mastadon
2214 		if ( (initial_attacked_objnum == player_obj_index) && (Ai_info[shipp->ai_index].target_objnum != player_obj_index) ) {
2215 			continue;
2216 		}
2217 		// check each shipp on list and update nearest obj and subsys
2218 		evaluate_ship_as_closest_target(&eval_ship_as_closest_target_args);
2219 		if (eval_ship_as_closest_target_args.min_distance < min_distance) {
2220 			target_found = TRUE;
2221 			min_distance = eval_ship_as_closest_target_args.min_distance;
2222 			nearest_obj = A;
2223 			check_nearest_turret = eval_ship_as_closest_target_args.check_nearest_turret;
2224 		}
2225 	}
2226 
2227 	Target_closest_done:
2228 
2229 	// maybe ignore target if too far away
2230 	// DKA 9/8/99 Remove distance check
2231 	/*
2232 	if (target_found) {
2233 		// get distance to nearest attacker
2234 		float dist = vm_vec_dist_quick(&Objects[attacked_objnum].pos, &nearest_obj->pos);
2235 
2236 		// no distance limit for player obj
2237 		if ((attacked_objnum != player_obj_index) && (dist > MIN_DISTANCE_TO_CONSIDER_THREAT)) {
2238 			target_found = FALSE;
2239 		}
2240 	} */
2241 
2242 	if (target_found) {
2243 		set_target_objnum(Player_ai, OBJ_INDEX(nearest_obj));
2244 		hud_shield_hit_reset(nearest_obj);
2245 		if ( check_nearest_turret ) {
2246 
2247 			// if former subobject was not a turret do, not change subsystem
2248 			ss = Ships[nearest_obj->instance].last_targeted_subobject[Player_num];
2249 			if (ss == NULL || get_closest_turret_attacking_player) {
2250 				// update nearest turret with later func
2251 				hud_target_live_turret(1, 1, get_closest_turret_attacking_player);
2252 				Ships[nearest_obj->instance].last_targeted_subobject[Player_num] = Player_ai->targeted_subsys;
2253 			}
2254 		} else {
2255 			hud_restore_subsystem_target(&Ships[nearest_obj->instance]);
2256 		}
2257 	} else {
2258 		// no target found, maybe play fail sound
2259 		if (play_fail_snd == TRUE) {
2260 			snd_play(gamesnd_get_game_sound(GameSounds::TARGET_FAIL));
2261 		}
2262 	}
2263 
2264 	return target_found;
2265 }
2266 
2267 // auto update closest turret to attack on big or huge ships
hud_update_closest_turret()2268 void hud_update_closest_turret()
2269 {
2270 	hud_target_live_turret(1, 1);
2271 
2272 /*
2273 	float nearest_distance, new_distance;
2274 	ship_subsys	*ss, *closest_subsys;
2275 	ship	*shipp;
2276 	object *objp;
2277 
2278 	nearest_distance = FLT_MAX;
2279 	objp = &Objects[Player_ai->target_objnum];
2280 	shipp = &Ships[objp->instance];
2281 	closest_subsys = NULL;
2282 
2283 
2284 	Assert(Ship_info[shipp->ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP));
2285 
2286 	for (ss=GET_FIRST(&shipp->subsys_list); ss!=END_OF_LIST(&shipp->subsys_list); ss=GET_NEXT(ss)) {
2287 		if ( (ss->system_info->type == SUBSYSTEM_TURRET) && (ss->current_hits > 0) ) {
2288 			// make sure turret is not "unused"
2289 			if (ss->system_info->turret_weapon_type >= 0) {
2290 				vec3d gsubpos;
2291 				// get world pos of subsystem
2292 				vm_vec_unrotate(&gsubpos, &ss->system_info->pnt, &objp->orient);
2293 				vm_vec_add2(&gsubpos, &objp->pos);
2294 				new_distance = vm_vec_dist_quick(&gsubpos, &Player_obj->pos);
2295 
2296 				// GET TURRET TYPE, FAVOR BEAM, FLAK, OTHER
2297 				int turret_type = ss->system_info->turret_weapon_type;
2298 				if (Weapon_info[turret_type].wi_flags[Weapon::Info_Flags::Beam]) {
2299 					new_distance *= 0.3f;
2300 				} else if (Weapon_info[turret_type].wi_flags[Weapon::Info_Flags::Flak]) {
2301 					new_distance *= 0.6f;
2302 				}
2303 
2304 				// check if facing and in view
2305 				int facing = ship_subsystem_in_sight(objp, ss, &View_position, &gsubpos, 0);
2306 
2307 				if (facing) {
2308 					new_distance *= 0.5f;
2309 				}
2310 
2311 				// get the closest distance
2312 				if (new_distance <= nearest_distance) {
2313 					nearest_distance = new_distance;
2314 					closest_subsys = ss;
2315 				}
2316 			}
2317 		}
2318 	}
2319 
2320 	// check if new subsys to target
2321 	if (Player_ai->targeted_subsys != NULL) {
2322 		set_targeted_subsys(Player_ai, closest_subsys, Player_ai->target_objnum);
2323 		shipp->last_targeted_subobject[Player_num] = Player_ai->targeted_subsys;
2324 	}
2325 	*/
2326 }
2327 
2328 
2329 // --------------------------------------------------------------------
2330 // hud_target_targets_target()
2331 //
2332 // Target your target's target.  Your target is specified by objnum passed
2333 // as a parameter.
2334 //
hud_target_targets_target()2335 void hud_target_targets_target()
2336 {
2337 	object *objp = NULL;
2338 	object *tt_objp = NULL;
2339 	int		tt_objnum;
2340 
2341 	if ( Player_ai->target_objnum < 0 || Player_ai->target_objnum >= MAX_OBJECTS ) {
2342 		goto ttt_fail;
2343 	}
2344 
2345 	objp = &Objects[Player_ai->target_objnum];
2346 	if ( objp->type != OBJ_SHIP ) {
2347 		goto ttt_fail;
2348 	}
2349 
2350 	tt_objnum = Ai_info[Ships[objp->instance].ai_index].target_objnum;
2351 	if ( tt_objnum < 0 || tt_objnum >= MAX_OBJECTS ) {
2352 		goto ttt_fail;
2353 	}
2354 
2355 	if ( tt_objnum == OBJ_INDEX(Player_obj) ) {
2356 		goto ttt_fail;
2357 	}
2358 
2359 	tt_objp = &Objects[tt_objnum];
2360 
2361 	if (hud_target_invalid_awacs(tt_objp)) {
2362 		goto ttt_fail;
2363 	}
2364 
2365 	if ( tt_objp->type != OBJ_SHIP ) {
2366 		goto ttt_fail;
2367 	}
2368 
2369 	if ( should_be_ignored(&Ships[tt_objp->instance]) ) {
2370 		goto ttt_fail;
2371 	}
2372 
2373 	// if we've reached here, found player target's target
2374 	set_target_objnum( Player_ai, tt_objnum );
2375 	hud_shield_hit_reset(&Objects[tt_objnum]);
2376 	if (Objects[tt_objnum].type == OBJ_SHIP) {
2377 		hud_maybe_set_sorted_turret_subsys(&Ships[Objects[tt_objnum].instance]);
2378 	}
2379 	hud_restore_subsystem_target(&Ships[Objects[tt_objnum].instance]);
2380 	return;
2381 
2382 	ttt_fail:
2383 	snd_play( gamesnd_get_game_sound(GameSounds::TARGET_FAIL), 0.0f );
2384 }
2385 
2386 // Return !0 if target_objp is a valid object type for targeting in reticle, otherwise return 0
object_targetable_in_reticle(object * target_objp)2387 int object_targetable_in_reticle(object *target_objp)
2388 {
2389 	int obj_type;
2390 	SCP_list<CJumpNode>::iterator jnp;
2391 
2392 	if (target_objp == Player_obj ) {
2393 		return 0;
2394 	}
2395 
2396 	obj_type = target_objp->type;
2397 
2398 	if ( (obj_type == OBJ_SHIP) || (obj_type == OBJ_DEBRIS) || (obj_type == OBJ_WEAPON) || (obj_type == OBJ_ASTEROID) )
2399 	{
2400 		return 1;
2401 	} else if ( obj_type == OBJ_JUMP_NODE )
2402 	{
2403 		for (jnp = Jump_nodes.begin(); jnp != Jump_nodes.end(); ++jnp) {
2404 			if(jnp->GetSCPObject() == target_objp)
2405 				break;
2406 		}
2407 
2408 		if (!jnp->IsHidden())
2409 			return 1;
2410 	}
2411 
2412 	return 0;
2413 }
2414 
2415 // hud_target_in_reticle_new() will target the object that is closest to the player, and who is
2416 // intersected by a ray passed from the center of the reticle out along the forward vector of the
2417 // player.
2418 //
2419 // targeting of objects of type OBJ_SHIP and OBJ_DEBRIS are supported
2420 //
2421 // Method: A ray is cast from the center of the reticle, and we keep track of any eligible object
2422 //         the ray intersects.  We take the ship closest to us that intersects an object.
2423 //
2424 //         Since this method may work poorly with objects that are far away, hud_target_in_reticle_old()
2425 //         is called if no intersections are found.
2426 //
2427 //
2428 #define TARGET_IN_RETICLE_DISTANCE	10000.0f
2429 
hud_target_in_reticle_new()2430 void hud_target_in_reticle_new()
2431 {
2432 	vec3d	terminus;
2433 	object	*A;
2434 	mc_info	mc;
2435 	float		dist;
2436 	SCP_list<CJumpNode>::iterator jnp;
2437 
2438 	hud_reticle_clear_list(&Reticle_cur_list);
2439 	Reticle_save_timestamp = timestamp(RESET_TARGET_IN_RETICLE);
2440 
2441 	//	Get 3d vector through center of reticle
2442 	vm_vec_scale_add(&terminus, &Eye_position, &Player_obj->orient.vec.fvec, TARGET_IN_RETICLE_DISTANCE);
2443 
2444 	mc_info_init(&mc);
2445 	mc.model_instance_num = -1;
2446 	mc.model_num = 0;
2447 	for ( A = GET_FIRST(&obj_used_list); A !=END_OF_LIST(&obj_used_list); A = GET_NEXT(A) ) {
2448 		if ( !object_targetable_in_reticle(A) ) {
2449 			continue;
2450 		}
2451 
2452 		if ( A->type == OBJ_WEAPON ) {
2453 			if ( !(Weapon_info[Weapons[A->instance].weapon_info_index].wi_flags[Weapon::Info_Flags::Can_be_targeted]) ) {
2454 				if ( !(Weapon_info[Weapons[A->instance].weapon_info_index].wi_flags[Weapon::Info_Flags::Bomb]) ){
2455 					continue;
2456 				}
2457 			}
2458 			if (Weapons[A->instance].lssm_stage==3){
2459 				continue;
2460 			}
2461 		}
2462 
2463 
2464 		if ( A->type == OBJ_SHIP ) {
2465 			if ( should_be_ignored(&Ships[A->instance]) ){
2466 				continue;
2467 			}
2468 		}
2469 
2470 		if(hud_target_invalid_awacs(A)){
2471 			continue;
2472 		}
2473 
2474 		switch (A->type) {
2475 		case OBJ_SHIP:
2476 			mc.model_num = Ship_info[Ships[A->instance].ship_info_index].model_num;
2477 			break;
2478 		case OBJ_DEBRIS:
2479 			mc.model_num = Debris[A->instance].model_num;
2480 			break;
2481 		case OBJ_WEAPON:
2482 			mc.model_num = Weapon_info[Weapons[A->instance].weapon_info_index].model_num;
2483 			break;
2484 		case OBJ_ASTEROID:
2485 			{
2486 				int pof = 0;
2487 				pof = Asteroids[A->instance].asteroid_subtype;
2488 				mc.model_num = Asteroid_info[Asteroids[A->instance].asteroid_type].model_num[pof];
2489 			}
2490 			break;
2491 		case OBJ_JUMP_NODE:
2492 			for (jnp = Jump_nodes.begin(); jnp != Jump_nodes.end(); ++jnp) {
2493 				if(jnp->GetSCPObject() == A)
2494 					break;
2495 			}
2496 
2497 			mc.model_num = jnp->GetModelNumber();
2498 			break;
2499 		default:
2500 			Int3();	//	Illegal object type.
2501 		}
2502 
2503 		if (mc.model_num == -1) {
2504 			// so just check distance of a point
2505 			vec3d temp_v;
2506 			float angle;
2507 			vm_vec_sub(&temp_v, &A->pos, &Eye_position);
2508 			vm_vec_normalize(&temp_v);
2509 			angle = vm_vec_dot(&Player_obj->orient.vec.fvec, &temp_v);
2510 			if (angle > 0.99f) {
2511 				dist = vm_vec_mag_squared(&temp_v);
2512 				hud_reticle_list_update(A, dist, 0);
2513 			}
2514 
2515 		} else {
2516 			mc.orient = &A->orient;										// The object's orient
2517 			mc.pos = &A->pos;												// The object's position
2518 			mc.p0 = &Eye_position;										// Point 1 of ray to check
2519 			mc.p1 = &terminus;											// Point 2 of ray to check
2520 			mc.flags = MC_CHECK_MODEL;	// | MC_ONLY_BOUND_BOX;		// check the model, but only its bounding box
2521 
2522 			model_collide(&mc);
2523 			if ( mc.num_hits ) {
2524 				dist = vm_vec_dist_squared(&mc.hit_point_world, &Eye_position);
2525 				hud_reticle_list_update(A, dist, 0);
2526 			}
2527 		}
2528 	}	// end for (go to next object)
2529 
2530 	hud_target_in_reticle_old();	// try the old method (works well with ships far away)
2531 }
2532 
2533 // hud_target_in_reticle_old() will target the object that is closest to the reticle center and inside
2534 // the reticle
2535 //
2536 // targeting of objects of type OBJ_SHIP and OBJ_DEBRIS are supported
2537 //
2538 //
2539 // Method:  take the dot product of the foward vector and the vector to target.  Take
2540 //          the one that is closest to 1 and at least MIN_DOT_FOR_TARGET
2541 //
2542 //	IMPORTANT:  The MIN_DOT_FOR_TARGET value was arrived at by trial and error and
2543 //             is only valid for the HUD reticle in use at that time.
2544 
2545 #define MIN_DOT_FOR_TARGET		0.9726// fov for targeting in reticle
2546 
hud_target_in_reticle_old()2547 void hud_target_in_reticle_old()
2548 {
2549 	object	*A, *target_obj;
2550 	float	dot;
2551 	vec3d	vec_to_target;
2552 
2553 	for ( A = GET_FIRST(&obj_used_list); A !=END_OF_LIST(&obj_used_list); A = GET_NEXT(A) ) {
2554 		if ( !object_targetable_in_reticle(A) ) {
2555 			continue;
2556 		}
2557 
2558 		if ( A->type == OBJ_WEAPON ) {
2559 			if ( !(Weapon_info[Weapons[A->instance].weapon_info_index].wi_flags[Weapon::Info_Flags::Can_be_targeted]) ){
2560 				if ( !(Weapon_info[Weapons[A->instance].weapon_info_index].wi_flags[Weapon::Info_Flags::Bomb]) ){
2561 					continue;
2562 				}
2563 			}
2564 
2565 			if (Weapons[A->instance].lssm_stage==3){
2566 				continue;
2567 			}
2568 		}
2569 
2570 		if ( A->type == OBJ_SHIP ) {
2571 			if ( should_be_ignored(&Ships[A->instance]) ){
2572 				continue;
2573 			}
2574 		}
2575 
2576 		if(hud_target_invalid_awacs(A)){
2577 			continue;
2578 		}
2579 
2580 		if ( vm_vec_same( &A->pos, &Eye_position ) ) {
2581 			continue;
2582 		}
2583 
2584 		vm_vec_normalized_dir(&vec_to_target, &A->pos, &Eye_position);
2585 		dot = vm_vec_dot(&Player_obj->orient.vec.fvec, &vec_to_target);
2586 
2587 		if ( dot > MIN_DOT_FOR_TARGET ) {
2588 			hud_reticle_list_update(A, dot, 1);
2589 		}
2590 	}
2591 
2592 	target_obj = hud_reticle_pick_target();
2593 	if ( target_obj != NULL ) {
2594 		set_target_objnum( Player_ai, OBJ_INDEX(target_obj) );
2595 		hud_shield_hit_reset(target_obj);
2596 		if ( target_obj->type == OBJ_SHIP ) {
2597 			// if BIG|HUGE, maybe set subsys to turret
2598 			hud_maybe_set_sorted_turret_subsys(&Ships[target_obj->instance]);
2599 			hud_restore_subsystem_target(&Ships[target_obj->instance]);
2600 		}
2601 	}
2602 	else {
2603 			snd_play( gamesnd_get_game_sound(GameSounds::TARGET_FAIL), 0.0f );
2604 	}
2605 }
2606 
2607 // hud_target_subsystem_in_reticle() will target the subsystem that is within the reticle and
2608 // is closest to the reticle center.  The current target is the only object that is searched for
2609 // subsystems
2610 //
2611 // Method:  take the dot product of the foward vector and the vector to target.  Take
2612 //          the one that is closest to 1 and at least MIN_DOT_FOR_TARGET
2613 //
2614 //	IMPORTANT:  The MIN_DOT_FOR_TARGET value was arrived at by trial and error and
2615 //             is only valid for the HUD reticle in use at that time.
2616 //
2617 
hud_target_subsystem_in_reticle()2618 void hud_target_subsystem_in_reticle()
2619 {
2620 	object* targetp;
2621 	ship_subsys	*subsys;
2622 	ship_subsys *nearest_subsys = NULL;
2623 	vec3d subobj_pos;
2624 
2625 	float dot, best_dot;
2626 	vec3d vec_to_target;
2627 	best_dot = -1.0f;
2628 
2629 	if ( Player_ai->target_objnum == -1){
2630 		hud_target_in_reticle_old();
2631 	}
2632 
2633 	if ( Player_ai->target_objnum == -1) { //-V581
2634 		snd_play( gamesnd_get_game_sound(GameSounds::TARGET_FAIL));
2635 		return;
2636 	}
2637 
2638 	targetp = &Objects[Player_ai->target_objnum];
2639 
2640 	if ( targetp->type != OBJ_SHIP ){		// only targeting subsystems on ship
2641 		return;
2642 	}
2643 
2644 	int shipnum = targetp->instance;
2645 
2646 	if ( targetp->type == OBJ_SHIP ) {
2647 		if ( should_be_ignored(&Ships[shipnum]) ) {
2648 			return;
2649 		}
2650 	}
2651 
2652 	for (subsys = GET_FIRST(&Ships[shipnum].subsys_list); subsys != END_OF_LIST(&Ships[shipnum].subsys_list)  ; subsys = GET_NEXT( subsys ) ) {
2653 
2654 		//if the subsystem isn't targetable, skip it
2655 		if (subsys->flags[Ship::Subsystem_Flags::Untargetable])
2656 			continue;
2657 
2658 		get_subsystem_world_pos(targetp, subsys, &subobj_pos);
2659 
2660 		vm_vec_normalized_dir(&vec_to_target, &subobj_pos, &Eye_position);
2661 		dot = vm_vec_dot(&Player_obj->orient.vec.fvec, &vec_to_target);
2662 
2663 		if ( dot > best_dot ) {
2664 			best_dot = dot;
2665 			if ( best_dot > MIN_DOT_FOR_TARGET )
2666 				nearest_subsys = subsys;
2667 		}
2668 	} // end for
2669 
2670 	if ( nearest_subsys != NULL ) {
2671 		set_targeted_subsys(Player_ai, nearest_subsys, Player_ai->target_objnum);
2672 		char r_name[NAME_LENGTH];
2673 		int i;
2674 		strcpy_s(r_name, ship_subsys_get_name(Player_ai->targeted_subsys));
2675 		for (i = 0; r_name[i] > 0; i++) {
2676 			if (r_name[i] == '|')
2677 				r_name[i] = ' ';
2678 		}
2679 		HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Targeting subsystem %s.", 323), r_name);
2680 		Ships[shipnum].last_targeted_subobject[Player_num] =  Player_ai->targeted_subsys;
2681 	}
2682 	else {
2683 		snd_play( gamesnd_get_game_sound(GameSounds::TARGET_FAIL));
2684 	}
2685 }
2686 
2687 #define T_LENGTH					8
2688 #define T_OFFSET_FROM_CIRCLE	-13
2689 #define T_BASE_LENGTH			4
2690 
2691 //	On entry:
2692 //		color set
renderOrientation(object * from_objp,object * to_objp,matrix * from_orientp)2693 void HudGaugeOrientationTee::renderOrientation(object *from_objp, object *to_objp, matrix *from_orientp)
2694 {
2695 	float		dot_product;
2696 	vec3d	target_to_obj;
2697 	float		x1,y1,x2,y2,x3,y3,x4,y4;
2698 
2699 	vm_vec_sub(&target_to_obj, &from_objp->pos, &to_objp->pos);
2700 
2701 	vm_vec_normalize(&target_to_obj);
2702 
2703 	// calculate the dot product between the target_to_player vector and the targets forward vector
2704 	//
2705 	// 0 - vectors are perpendicular
2706 	// 1 - vectors are collinear and in the same direction (target is facing player)
2707 	// -1 - vectors are collinear and in the opposite direction (target is facing away from player)
2708 	dot_product = vm_vec_dot(&from_orientp->vec.fvec, &target_to_obj);
2709 
2710 	if (vm_vec_dot(&from_orientp->vec.rvec, &target_to_obj) >= 0) {
2711 		if (dot_product >= 0){
2712 			dot_product = -PI_2*dot_product + PI;
2713 		} else {
2714 			dot_product = -PI_2*dot_product - PI;
2715 		}
2716 	} else {
2717 		dot_product *= PI_2; //(range is now -PI/2 => PI/2)
2718 	}
2719 
2720 	y1 = sinf(dot_product) * (Radius - T_OFFSET_FROM_CIRCLE);
2721 	x1 = cosf(dot_product) * (Radius - T_OFFSET_FROM_CIRCLE);
2722 
2723 	y1 += position[1];
2724 	x1 += position[0];
2725 
2726 	x1 += HUD_offset_x;
2727 	y1 += HUD_offset_y;
2728 
2729 	y2 = sinf(dot_product) * (Radius - T_OFFSET_FROM_CIRCLE - T_LENGTH);
2730 	x2 = cosf(dot_product) * (Radius - T_OFFSET_FROM_CIRCLE - T_LENGTH);
2731 
2732 	y2 += position[1];
2733 	x2 += position[0];
2734 
2735 	x2 += HUD_offset_x;
2736 	y2 += HUD_offset_y;
2737 
2738 	x3 = x1 - T_BASE_LENGTH * sinf(dot_product);
2739 	y3 = y1 + T_BASE_LENGTH * cosf(dot_product);
2740 	x4 = x1 + T_BASE_LENGTH * sinf(dot_product);
2741 	y4 = y1 - T_BASE_LENGTH * cosf(dot_product);
2742 
2743 	// HACK! Should be antialiased!
2744 	renderLine(fl2i(x3),fl2i(y3),fl2i(x4),fl2i(y4));	// bottom of T
2745 	renderLine(fl2i(x1),fl2i(y1),fl2i(x2),fl2i(y2));	// part of T pointing towards center
2746 }
2747 
hud_tri(float x1,float y1,float x2,float y2,float x3,float y3)2748 void hud_tri(float x1,float y1,float x2,float y2,float x3,float y3)
2749 {
2750 	int i;
2751 
2752 	// Make the triangle always be the correct handiness so
2753 	// the tmapper won't think its back-facing and throw it out.
2754 	float det = (y2-y1)*(x3-x1) - (x2-x1)*(y3-y1);
2755 	if ( det >= 0.0f )	{
2756 		float tmp;
2757 
2758 		// swap y1 & y3
2759 		tmp = y1;
2760 		y1 = y3;
2761 		y3 = tmp;
2762 
2763 		// swap x1 & x3
2764 		tmp = x1;
2765 		x1 = x3;
2766 		x3 = tmp;
2767 	}
2768 
2769 	vertex verts[3];
2770 
2771 	// zero verts[] out, this is a faster way (nods to Kazan) to make sure that
2772 	// the specular colors are set to 0 to avoid rendering problems - taylor
2773 	memset(verts, 0, sizeof(verts));
2774 
2775 
2776 	verts[0].screen.xyw.x = x1;
2777 	verts[0].screen.xyw.y = y1;
2778 	verts[0].screen.xyw.w = 0.0f;
2779 	verts[0].texture_position.u = 0.0f;
2780 	verts[0].texture_position.v = 0.0f;
2781 	verts[0].flags = PF_PROJECTED;
2782 	verts[0].codes = 0;
2783 	verts[0].r = (ubyte)gr_screen.current_color.red;
2784 	verts[0].g = (ubyte)gr_screen.current_color.green;
2785 	verts[0].b = (ubyte)gr_screen.current_color.blue;
2786 	verts[0].a = (ubyte)gr_screen.current_color.alpha;
2787 
2788 	verts[1].screen.xyw.x = x2;
2789 	verts[1].screen.xyw.y = y2;
2790 	verts[1].screen.xyw.w = 0.0f;
2791 	verts[1].texture_position.u = 0.0f;
2792 	verts[1].texture_position.v = 0.0f;
2793 	verts[1].flags = PF_PROJECTED;
2794 	verts[1].codes = 0;
2795 	verts[1].r = (ubyte)gr_screen.current_color.red;
2796 	verts[1].g = (ubyte)gr_screen.current_color.green;
2797 	verts[1].b = (ubyte)gr_screen.current_color.blue;
2798 	verts[1].a = (ubyte)gr_screen.current_color.alpha;
2799 
2800 	verts[2].screen.xyw.x = x3;
2801 	verts[2].screen.xyw.y = y3;
2802 	verts[2].screen.xyw.w = 0.0f;
2803 	verts[2].texture_position.u = 0.0f;
2804 	verts[2].texture_position.v = 0.0f;
2805 	verts[2].flags = PF_PROJECTED;
2806 	verts[2].codes = 0;
2807 	verts[2].r = (ubyte)gr_screen.current_color.red;
2808 	verts[2].g = (ubyte)gr_screen.current_color.green;
2809 	verts[2].b = (ubyte)gr_screen.current_color.blue;
2810 	verts[2].a = (ubyte)gr_screen.current_color.alpha;
2811 
2812 	for (i=0; i<3; i++)
2813 		gr_resize_screen_posf(&verts[i].screen.xyw.x, &verts[i].screen.xyw.y);
2814 
2815 	//uint saved_mode = gr_zbuffer_get();
2816 	//int cull = gr_set_cull(0);
2817 
2818 	//gr_zbuffer_set( GR_ZBUFF_NONE );
2819 
2820 	material material_def;
2821 
2822 	material_def.set_blend_mode(ALPHA_BLEND_NONE);
2823 	material_def.set_depth_mode(ZBUFFER_TYPE_NONE);
2824 	material_def.set_cull_mode(false);
2825 
2826 	g3_render_primitives_colored(&material_def, verts, 3, PRIM_TYPE_TRIFAN, true);
2827 
2828 	//g3_draw_poly_constant_sw(3, vertlist, TMAP_FLAG_GOURAUD | TMAP_FLAG_RGB | TMAP_FLAG_ALPHA, 0.1f);
2829 
2830 	//gr_zbuffer_set( saved_mode );
2831 	//gr_set_cull(cull);
2832 }
2833 
2834 
hud_tri_empty(float x1,float y1,float x2,float y2,float x3,float y3)2835 void hud_tri_empty(float x1,float y1,float x2,float y2,float x3,float y3)
2836 {
2837  	gr_line(fl2i(x1),fl2i(y1),fl2i(x2),fl2i(y2));
2838  	gr_line(fl2i(x2),fl2i(y2),fl2i(x3),fl2i(y3));
2839  	gr_line(fl2i(x3),fl2i(y3),fl2i(x1),fl2i(y1));
2840 }
2841 
HudGaugeReticleTriangle()2842 HudGaugeReticleTriangle::HudGaugeReticleTriangle():
2843 HudGauge(HUD_OBJECT_HOSTILE_TRI, HUD_HOSTILE_TRIANGLE, false, false, VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY | VM_OTHER_SHIP, 255, 255, 255)
2844 {
2845 
2846 }
2847 
HudGaugeReticleTriangle(int _gauge_object,int _gauge_config)2848 HudGaugeReticleTriangle::HudGaugeReticleTriangle(int _gauge_object, int _gauge_config):
2849 HudGauge(_gauge_object, _gauge_config, true, false, VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY | VM_OTHER_SHIP, 255, 255, 255)
2850 {
2851 
2852 }
2853 
initRadius(int length)2854 void HudGaugeReticleTriangle::initRadius(int length)
2855 {
2856 	Radius = length;
2857 }
2858 
initTriBase(float length)2859 void HudGaugeReticleTriangle::initTriBase(float length)
2860 {
2861 	Target_triangle_base = length;
2862 }
2863 
initTriHeight(float h)2864 void HudGaugeReticleTriangle::initTriHeight(float h)
2865 {
2866 	Target_triangle_height = h;
2867 }
2868 
render(float)2869 void HudGaugeReticleTriangle::render(float  /*frametime*/)
2870 {
2871 }
2872 
2873 // Render a missile warning triangle that has a tail on it to indicate distance
renderTriangleMissileTail(float ang,float xpos,float ypos,float cur_dist,int draw_solid,int draw_inside)2874 void HudGaugeReticleTriangle::renderTriangleMissileTail(float ang, float xpos, float ypos, float cur_dist, int draw_solid, int draw_inside)
2875 {
2876 	float x1=0.0f;
2877 	float x2=0.0f;
2878 	float y1=0.0f;
2879 	float y2=0.0f;
2880 	float xtail=0.0f;
2881 	float ytail=0.0f;
2882 
2883 	float sin_ang, cos_ang, tail_len;
2884 
2885 	float max_tail_len=20.0f;
2886 
2887 	sin_ang=sinf(ang);
2888 	cos_ang=cosf(ang);
2889 
2890 	if ( cur_dist < Min_warning_missile_dist ) {
2891 		tail_len = 0.0f;
2892 	} else if ( cur_dist > Max_warning_missile_dist ) {
2893 		tail_len = max_tail_len;
2894 	} else {
2895 		tail_len = cur_dist/Max_warning_missile_dist * max_tail_len;
2896 	}
2897 
2898 	if ( draw_inside ) {
2899 		x1 = xpos - Target_triangle_base * -sin_ang;
2900 		y1 = ypos + Target_triangle_base * cos_ang;
2901 		x2 = xpos + Target_triangle_base * -sin_ang;
2902 		y2 = ypos - Target_triangle_base * cos_ang;
2903 
2904 		xpos -= Target_triangle_height * cos_ang;
2905 		ypos += Target_triangle_height * sin_ang;
2906 
2907 		if ( tail_len > 0 ) {
2908 			xtail = xpos - tail_len * cos_ang;
2909 			ytail = ypos + tail_len * sin_ang;
2910 		}
2911 
2912 	} else {
2913 		x1 = xpos - Target_triangle_base * -sin_ang;
2914 		y1 = ypos + Target_triangle_base * cos_ang;
2915 		x2 = xpos + Target_triangle_base * -sin_ang;
2916 		y2 = ypos - Target_triangle_base * cos_ang;
2917 
2918 		xpos += Target_triangle_height * cos_ang;
2919 		ypos -= Target_triangle_height * sin_ang;
2920 
2921 		if ( tail_len > 0 ) {
2922 			xtail = xpos + tail_len * cos_ang;
2923 			ytail = ypos - tail_len * sin_ang;
2924 		}
2925 	}
2926 
2927 	gr_set_screen_scale(base_w, base_h);
2928 	if (draw_solid) {
2929 		hud_tri(xpos,ypos,x1,y1,x2,y2);
2930 	} else {
2931 		hud_tri_empty(xpos,ypos,x1,y1,x2,y2);
2932 	}
2933 
2934 	// draw the tail indicating length
2935 	if ( tail_len > 0 ) {
2936 		gr_line(fl2i(xpos), fl2i(ypos), fl2i(xtail), fl2i(ytail));
2937 	}
2938 	gr_reset_screen_scale();
2939 }
2940 
2941 //	Render a triangle on the outside of the targeting circle.
2942 //	Must be inside a g3_start_frame().
2943 //	If aspect_flag !0, then render filled, indicating aspect lock.
2944 // If show_interior !0, then point inwards to positions inside reticle
renderTriangle(vec3d * hostile_pos,int aspect_flag,int show_interior,int split_tri)2945 void HudGaugeReticleTriangle::renderTriangle(vec3d *hostile_pos, int aspect_flag, int show_interior, int split_tri)
2946 {
2947 	vertex	hostile_vertex;
2948 	float		ang;
2949 	float		xpos,ypos,cur_dist,sin_ang,cos_ang;
2950 	int		draw_inside=0;
2951 
2952 	// determine if the given object is within the targeting reticle
2953 	// (which means the triangle is not drawn)
2954 
2955 	cur_dist = vm_vec_dist_quick(&Player_obj->pos, hostile_pos);
2956 
2957 	g3_rotate_vertex(&hostile_vertex, hostile_pos);
2958 	g3_project_vertex(&hostile_vertex);
2959 
2960 	if (hostile_vertex.codes == 0)  { // on screen
2961 		int		projected_x, projected_y;
2962 
2963 		if (!(hostile_vertex.flags & PF_OVERFLOW)) {  // make sure point projected
2964 			int mag_squared;
2965 
2966 			projected_x = fl2i(hostile_vertex.screen.xyw.x);
2967 			projected_y = fl2i(hostile_vertex.screen.xyw.y);
2968 
2969 			unsize(&projected_x, &projected_y);
2970 
2971 			mag_squared = (projected_x - position[0]) * (projected_x - position[0]) +
2972 							  (projected_y - position[1]) * (projected_y - position[1]);
2973 
2974 			if ( mag_squared < Radius*Radius ) {
2975 				if ( show_interior ) {
2976 					draw_inside=1;
2977 				} else {
2978 					return;
2979 				}
2980 			}
2981 		}
2982 	}
2983 
2984 	int HUD_nose_scaled_x = HUD_nose_x;
2985 	int HUD_nose_scaled_y = HUD_nose_y;
2986 
2987 	gr_resize_screen_pos(&HUD_nose_scaled_x, &HUD_nose_scaled_y);
2988 
2989 	unsize( &hostile_vertex.world.xyz.x, &hostile_vertex.world.xyz.y );
2990 
2991 	ang = atan2_safe(hostile_vertex.world.xyz.y,hostile_vertex.world.xyz.x);
2992 	sin_ang=sinf(ang);
2993 	cos_ang=cosf(ang);
2994 
2995 	if ( draw_inside ) {
2996 		xpos = position[0] + cos_ang*(Radius-7);
2997 		ypos = position[1] - sin_ang*(Radius-7);
2998 	} else {
2999 		xpos = position[0] + cos_ang*(Radius+4);
3000 		ypos = position[1] - sin_ang*(Radius+4);
3001 	}
3002 
3003 	xpos += HUD_offset_x + HUD_nose_x;
3004 	ypos += HUD_offset_y + HUD_nose_y;
3005 
3006 	if ( split_tri ) {
3007 		// renderTriangleMissileSplit(ang, xpos, ypos, cur_dist, aspect_flag, draw_inside);
3008 		renderTriangleMissileTail(ang, xpos, ypos, cur_dist, aspect_flag, draw_inside);
3009 	} else {
3010 		float x1=0.0f;
3011 		float x2=0.0f;
3012 		float y1=0.0f;
3013 		float y2=0.0f;
3014 
3015 		if ( draw_inside ) {
3016 			x1 = xpos - Target_triangle_base * -sin_ang;
3017 			y1 = ypos + Target_triangle_base * cos_ang;
3018 			x2 = xpos + Target_triangle_base * -sin_ang;
3019 			y2 = ypos - Target_triangle_base * cos_ang;
3020 
3021 			xpos -= Target_triangle_height * cos_ang;
3022 			ypos += Target_triangle_height * sin_ang;
3023 
3024 		} else {
3025 			x1 = xpos - Target_triangle_base * -sin_ang;
3026 			y1 = ypos + Target_triangle_base * cos_ang;
3027 			x2 = xpos + Target_triangle_base * -sin_ang;
3028 			y2 = ypos - Target_triangle_base * cos_ang;
3029 
3030 			xpos += Target_triangle_height * cos_ang;
3031 			ypos -= Target_triangle_height * sin_ang;
3032 		}
3033 
3034 		//renderPrintf(position[0], position[1], "%d", fl2i((360*ang)/(2*PI)));
3035 		gr_set_screen_scale(base_w, base_h);
3036 		if (aspect_flag) {
3037 			hud_tri(xpos,ypos,x1,y1,x2,y2);
3038 		} else {
3039 			hud_tri_empty(xpos,ypos,x1,y1,x2,y2);
3040 		}
3041 		gr_reset_screen_scale();
3042 	}
3043 }
3044 
3045 //	Show all homing missiles locked onto the player.
3046 //	Also, play the beep!
hud_process_homing_missiles()3047 void hud_process_homing_missiles()
3048 {
3049 	object		*A;
3050 	missile_obj	*mo;
3051 	weapon		*wp;
3052 	float			dist, nearest_dist;
3053 	int			closest_is_aspect=0;
3054 
3055 	nearest_dist = Homing_beep.max_cycle_dist;
3056 
3057 	for ( mo = GET_NEXT(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
3058 		A = &Objects[mo->objnum];
3059 		Assert((A->instance >= 0) && (A->instance < MAX_WEAPONS));
3060 
3061 		wp = &Weapons[A->instance];
3062 
3063 		if (wp->homing_object == Player_obj) {
3064 			dist = vm_vec_dist_quick(&A->pos, &Player_obj->pos);
3065 
3066 			if (dist < nearest_dist) {
3067 				nearest_dist = dist;
3068 				if ( Weapon_info[wp->weapon_info_index].is_locked_homing() ) {
3069 					closest_is_aspect=1;
3070 				} else {
3071 					closest_is_aspect=0;
3072 				}
3073 			}
3074 		}
3075 	}
3076 
3077 	//	See if need to play warning beep.
3078 	if (nearest_dist < Homing_beep.max_cycle_dist ) {
3079 		float	delta_time;
3080 		float cycle_time;
3081 
3082 		delta_time = f2fl(Missiontime - Homing_beep.last_time_played);
3083 
3084 		// figure out the cycle time by doing a linear interpretation
3085 		cycle_time = Homing_beep.min_cycle_time + (nearest_dist-Homing_beep.min_cycle_dist) * Homing_beep.precalced_interp;
3086 
3087 		// play a new 'beep' if cycle time has elapsed
3088 		if ( (delta_time*1000) > cycle_time ) {
3089 			Homing_beep.last_time_played = Missiontime;
3090 			if ( snd_is_playing(Homing_beep.snd_handle) ) {
3091 				snd_stop(Homing_beep.snd_handle);
3092 			}
3093 
3094 			if ( closest_is_aspect ) {
3095 				Homing_beep.snd_handle = snd_play(gamesnd_get_game_sound(ship_get_sound(Player_obj, GameSounds::PROXIMITY_ASPECT_WARNING)));
3096 			} else {
3097 				Homing_beep.snd_handle = snd_play(gamesnd_get_game_sound(ship_get_sound(Player_obj, GameSounds::PROXIMITY_WARNING)));
3098 			}
3099 		}
3100 	}
3101 }
3102 
HudGaugeMissileTriangles()3103 HudGaugeMissileTriangles::HudGaugeMissileTriangles():
3104 HudGaugeReticleTriangle(HUD_OBJECT_MISSILE_TRI, HUD_MISSILE_WARNING_ARROW)
3105 {
3106 }
3107 
render(float)3108 void HudGaugeMissileTriangles::render(float  /*frametime*/)
3109 {
3110 	object		*A;
3111 	missile_obj	*mo;
3112 	weapon		*wp;
3113 
3114 	bool in_frame = g3_in_frame() > 0;
3115 	if(!in_frame)
3116 		g3_start_frame(0);
3117 	gr_set_screen_scale(base_w, base_h);
3118 
3119 	gr_set_color_fast(&HUD_color_homing_indicator);
3120 
3121 	for ( mo = GET_NEXT(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
3122 		A = &Objects[mo->objnum];
3123 		Assert((A->instance >= 0) && (A->instance < MAX_WEAPONS));
3124 
3125 		wp = &Weapons[A->instance];
3126 
3127 		if (wp->homing_object == Player_obj) {
3128 			renderTriangle(&A->pos, Weapon_info[wp->weapon_info_index].is_locked_homing(), 1, 1);
3129 		}
3130 	}
3131 
3132 	gr_reset_screen_scale();
3133 	if(!in_frame)
3134 		g3_end_frame();
3135 }
3136 
HudGaugeOrientationTee()3137 HudGaugeOrientationTee::HudGaugeOrientationTee():
3138 HudGauge(HUD_OBJECT_ORIENTATION_TEE, HUD_ORIENTATION_TEE, true, false, VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY | VM_OTHER_SHIP, 255, 255, 255)
3139 {
3140 }
3141 
initRadius(int length)3142 void HudGaugeOrientationTee::initRadius(int length)
3143 {
3144 	Radius = length;
3145 }
3146 
pageIn()3147 void HudGaugeOrientationTee::pageIn()
3148 {
3149 }
3150 
3151 // hud_show_orientation_tee() will draw the orientation gauge that orbits the inside of the
3152 // outer reticle ring.  If the T is at 12 o'clock, the target is facing the player, if the T
3153 // is at 6 o'clock the target is facing away from the player.  If the T is at 3 or 9 o'clock
3154 // the target is facing 90 away from the player.
render(float)3155 void HudGaugeOrientationTee::render(float  /*frametime*/)
3156 {
3157 	object* targetp;
3158 
3159 	if (Player_ai->target_objnum == -1 || Player->target_is_dying)
3160 		return;
3161 
3162 	targetp = &Objects[Player_ai->target_objnum];
3163 
3164 	if ( maybeFlashSexp() == 1 ) {
3165 		hud_set_iff_color( targetp );
3166 	} else {
3167 		hud_set_iff_color( targetp, 1);
3168 	}
3169 	renderOrientation(targetp, Player_obj, &targetp->orient);
3170 }
3171 
3172 // routine to draw a bounding box around a remote detonate missile and distance to
hud_process_remote_detonate_missile()3173 void hud_process_remote_detonate_missile()
3174 {
3175 	missile_obj	*mo;
3176 	object	*mobjp;
3177 	vertex target_point;
3178 
3179     memset(&target_point, 0, sizeof(target_point));
3180 
3181 	// check for currently locked missiles (highest precedence)
3182 	for ( mo = GET_FIRST(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
3183 		Assert(mo->objnum >= 0 && mo->objnum < MAX_OBJECTS);
3184 		mobjp = &Objects[mo->objnum];
3185 
3186 		if ((Player_obj != NULL) && (mobjp->parent_sig == Player_obj->parent_sig)) {
3187 			if (Weapon_info[Weapons[mobjp->instance].weapon_info_index].wi_flags[Weapon::Info_Flags::Remote]) {
3188 				// get box center point
3189 				g3_rotate_vertex(&target_point,&mobjp->pos);
3190 
3191 				// project vertex
3192 				g3_project_vertex(&target_point);
3193 
3194 				if (!(target_point.flags & PF_OVERFLOW)) {  // make sure point projected
3195 					switch ( mobjp->type ) {
3196 					case OBJ_WEAPON:
3197 						hud_target_add_display_list(mobjp, &target_point, &mobjp->pos, 0, iff_get_color(IFF_COLOR_MESSAGE, 1), NULL, TARGET_DISPLAY_DIST);
3198 						break;
3199 
3200 					default:
3201 						Int3();	// should never happen
3202 						return;
3203 					}
3204 				}
3205 			}
3206 		}
3207 	}
3208 }
3209 
3210 // routine to possibly draw a bounding box around a ship sending a message to the player
hud_show_message_sender()3211 void hud_show_message_sender()
3212 {
3213 	object *targetp;
3214 	vertex target_point;					// temp vertex used to find screen position for 3-D object;
3215 
3216 	// don't draw brackets if no ship sending a message
3217 	if ( Message_shipnum == -1 )
3218 		return;
3219 
3220 	targetp = &Objects[Ships[Message_shipnum].objnum];
3221 	Assert ( targetp != NULL );
3222 
3223 	if (targetp->type != OBJ_SHIP) {
3224 		// if it's not a ship (maybe it got ship-vanished in the middle of talking) then clear Message_shipnum
3225 		Message_shipnum = -1;
3226 		return;
3227 	}
3228 
3229 	// Don't do this for the ship you're flying!
3230 	if ( targetp == Player_obj ) {
3231 		return;
3232 	}
3233 
3234 	// Goober5000 - don't draw if primitive sensors
3235 	if ( Ships[Player_obj->instance].flags[Ship::Ship_Flags::Primitive_sensors] ) {
3236 		return;
3237 	}
3238 
3239 	// Karajorma - If we've gone to all the trouble to make our friendly ships stealthed they shouldn't then give away
3240 	// their position cause they're feeling chatty
3241 	// MageKing17 - Make the check see if they're actually stealthed at the time, and may as well include a check for
3242 	// being hidden from sensors, too; logic copied from a similar check in hudescort.cpp
3243 	if ( (Ships[Message_shipnum].flags[Ship::Ship_Flags::Hidden_from_sensors])
3244 		|| ((Ships[Message_shipnum].flags[Ship::Ship_Flags::Stealth]) && ((Ships[Message_shipnum].team != Player_ship->team) || (Ships[Message_shipnum].flags[Ship::Ship_Flags::Friendly_stealth_invis])))
3245 	) {
3246 		return;
3247 	}
3248 
3249 	Assert ( targetp->instance >=0 && targetp->instance < MAX_SHIPS );
3250 
3251 	// check the object flags to see if this ship is gone.  If so, then don't do this stuff anymore
3252 	if ( targetp->flags[Object::Object_Flags::Should_be_dead] ) {
3253 		Message_shipnum = -1;
3254 		return;
3255 	}
3256 
3257     memset(&target_point, 0, sizeof(target_point));
3258 
3259 	// find the current target vertex
3260 	g3_rotate_vertex(&target_point, &targetp->pos);
3261 	g3_project_vertex(&target_point);
3262 
3263 	if (!(target_point.flags & PF_OVERFLOW)) {  // make sure point projected
3264 		hud_target_add_display_list(targetp, &target_point, &targetp->pos, 10, iff_get_color(IFF_COLOR_MESSAGE, 1), NULL, 0);
3265 	} else if (target_point.codes != 0) { // target center is not on screen
3266 		// draw the offscreen indicator at the edge of the screen where the target is closest to
3267 		// AL 11-19-97: only show offscreen indicator if player sensors are functioning
3268 		if ( (OBJ_INDEX(targetp) != Player_ai->target_objnum) || (Message_shipnum == Objects[Player_ai->target_objnum].instance) ) {
3269 			if ( hud_sensors_ok(Player_ship, 0) ) {
3270 				hud_target_add_display_list(targetp, &target_point, &targetp->pos, 0, iff_get_color(IFF_COLOR_MESSAGE, 1), NULL, TARGET_DISPLAY_DIST);
3271 			}
3272 		}
3273 	}
3274 }
3275 
3276 // hud_prune_hotkeys()
3277 //
3278 // Check for ships that are dying, departed or dead.  These should be removed from the player's
3279 // hotkey lists.
hud_prune_hotkeys()3280 void hud_prune_hotkeys()
3281 {
3282 	int				i;
3283 	htarget_list	*hitem, *plist;
3284 	object			*objp;
3285 	ship				*sp;
3286 
3287 	for ( i = 0; i < MAX_KEYED_TARGETS; i++ ) {
3288 		plist = &(Players[Player_num].keyed_targets[i]);
3289 		if ( EMPTY( plist ) )			// no items in list, then do nothing
3290 			continue;
3291 
3292 		hitem = GET_FIRST(plist);
3293 		while ( hitem != END_OF_LIST(plist) ) {
3294 			int remove_item;
3295 
3296 			remove_item = 0;
3297 
3298 			objp = hitem->objp;
3299 			Assert ( objp != NULL );
3300 			if ( objp->type == OBJ_SHIP ) {
3301 				Assert ( objp->instance >=0 && objp->instance < MAX_SHIPS );
3302 				sp = &Ships[objp->instance];
3303 			} else {
3304 				// if the object isn't a ship, it shouldn't be on the list, so remove it without question
3305 				remove_item = 1;
3306 				sp = NULL;
3307 			}
3308 
3309 			// check to see if the object is dying -- if so, remove it from the list
3310 			// check to see if the ship is departing -- if so, remove it from the list
3311 			if ( remove_item || (objp->flags[Object::Object_Flags::Should_be_dead]) || (sp->is_dying_or_departing()) ) {
3312 				if ( sp != NULL ) {
3313 					nprintf(("Network", "Hotkey: Pruning %s\n", sp->ship_name));
3314 				}
3315 
3316 				htarget_list *temp;
3317 				temp = GET_NEXT(hitem);
3318 				list_remove( plist, hitem );
3319 				list_append( &htarget_free_list, hitem );
3320 				hitem->objp = NULL;
3321 				hitem = temp;
3322 				continue;
3323 			}
3324 			hitem = GET_NEXT( hitem );
3325 		}	// end while
3326 	}	// end for
3327 
3328 	// save the hotkey sets with mission time reaches a certain point.  Code was put here because this
3329 	// function always called for both single/multiplayer.  Maybe not the best location, but whatever.
3330 	mission_hotkey_maybe_save_sets();
3331 }
3332 
3333 int HUD_drew_selection_bracket_on_target;
3334 
3335 // hud_show_selection_set draws some indicator around all the ships in the current selection set.  No
3336 // indicators will be drawn if there is only 1 ship in the set.
hud_show_selection_set()3337 void hud_show_selection_set()
3338 {
3339 	htarget_list *hitem, *plist;
3340 	object *targetp;
3341 	int set, count;
3342 	vertex target_point;					// temp vertex used to find screen position for 3-D object;
3343 
3344 	HUD_drew_selection_bracket_on_target = 0;
3345 
3346 	set = Players[Player_num].current_hotkey_set;
3347 	if ( set == -1 )
3348 		return;
3349 
3350 	Assert ( (set >= 0) && (set < MAX_KEYED_TARGETS) );
3351 	plist = &(Players[Player_num].keyed_targets[set]);
3352 
3353 	count = 0;
3354 	for ( hitem = GET_FIRST(plist); hitem != END_OF_LIST(plist); hitem = GET_NEXT(hitem) )
3355 		count++;
3356 
3357 	if ( count == 0 )	{	// only one ship, do nothing
3358 		Players[Player_num].current_hotkey_set = -1;
3359 		return;
3360 	}
3361 
3362     memset(&target_point, 0, sizeof(target_point));
3363 
3364 	for ( hitem = GET_FIRST(plist); hitem != END_OF_LIST(plist); hitem = GET_NEXT(hitem) ) {
3365 		targetp = hitem->objp;
3366 		Assert ( targetp != NULL );
3367 
3368 		ship	*target_shipp = NULL;
3369 
3370 		Assert ( targetp->type == OBJ_SHIP );
3371 		Assert ( targetp->instance >=0 && targetp->instance < MAX_SHIPS );
3372 		target_shipp = &Ships[targetp->instance];
3373 
3374 		if ( (Game_mode & GM_MULTIPLAYER) && (target_shipp == Player_ship) ) {
3375 			continue;
3376 		}
3377 
3378 		// Goober5000 - don't draw indicators for non-visible ships, per Mantis #1972
3379 		// (the only way we could get here is if the hotkey set contained a mix of visible
3380 		// and invisible ships)
3381 		if (awacs_get_level(targetp, Player_ship, 1) < 1) {
3382 			continue;
3383 		}
3384 
3385 		// find the current target vertex
3386 		//
3387 		g3_rotate_vertex(&target_point,&targetp->pos);
3388 		g3_project_vertex(&target_point);
3389 
3390 		if (!(target_point.flags & PF_OVERFLOW)) {  // make sure point projected
3391 
3392 			switch ( targetp->type ) {
3393 			case OBJ_SHIP:
3394 				break;
3395 
3396 			default:
3397 				Int3();	// should never happen
3398 				return;
3399 			}
3400 			if ( OBJ_INDEX(targetp) == Player_ai->target_objnum ) {
3401 				hud_target_add_display_list(targetp, &target_point, &targetp->pos, 5, iff_get_color(IFF_COLOR_SELECTION, 1), NULL, 0);
3402 				HUD_drew_selection_bracket_on_target = 1;
3403 			} else if ( Cmdline_targetinfo ) {		//Backslash -- show the distance and a lead indicator
3404 				hud_target_add_display_list(targetp, &target_point, &targetp->pos, 5, iff_get_color(IFF_COLOR_SELECTION, 1), NULL, TARGET_DISPLAY_DIST | TARGET_DISPLAY_LEAD);
3405 			} else {
3406 				hud_target_add_display_list(targetp, &target_point, &targetp->pos, 5, iff_get_color(IFF_COLOR_SELECTION, 1), NULL, 0);
3407 			}
3408 		}
3409 
3410 		if (target_point.codes != 0) { // target center is not on screen
3411 			// draw the offscreen indicator at the edge of the screen where the target is closest to
3412 			// AL 11-19-97: only show offscreen indicator if player sensors are functioning
3413 
3414 			if ( OBJ_INDEX(targetp) != Player_ai->target_objnum ) {
3415 				if ( hud_sensors_ok(Player_ship, 0) ) {
3416 					hud_target_add_display_list(targetp, &target_point, &targetp->pos, 5, iff_get_color(IFF_COLOR_SELECTION, 1), NULL, 0);
3417 				}
3418 			}
3419 		}
3420 	}
3421 }
3422 
3423 // hud_show_targeting_gauges() will display the targeting information on the HUD.  Called once per frame.
3424 //
3425 // Must be inside a g3_start_frame()
3426 // input:	frametime	=>		time in seconds since last update
3427 //				in_cockpit	=>		flag (default value 1) indicating whether viewpoint is from cockpit or external
hud_show_targeting_gauges(float frametime)3428 void hud_show_targeting_gauges(float frametime)
3429 {
3430 	vertex target_point;					// temp vertex used to find screen position for 3-D object;
3431 	vec3d target_pos;
3432 
3433 	hud_show_hostile_triangle();
3434 	hud_do_lock_indicators(frametime);
3435 
3436 	if (Player_ai->target_objnum == -1)
3437 		return;
3438 
3439 	object * targetp = &Objects[Player_ai->target_objnum];
3440 	Players[Player_num].lead_indicator_active = 0;
3441 
3442 	// check to see if there is even a current target
3443 	if ( targetp == &obj_used_list ) {
3444 		return;
3445 	}
3446 
3447 	// AL 1/20/97: Point to targted subsystem if one exists
3448 	if ( Player_ai->targeted_subsys != NULL ) {
3449 		get_subsystem_world_pos(targetp, Player_ai->targeted_subsys, &target_pos);
3450 	} else {
3451 		target_pos = targetp->pos;
3452 	}
3453 
3454 	// find the current target vertex
3455 	//
3456 	// The 2D screen pos depends on the current viewer position and orientation.
3457 	g3_rotate_vertex(&target_point,&target_pos);
3458 
3459 	hud_set_iff_color( targetp, 1 );
3460 	g3_project_vertex(&target_point);
3461 
3462 	if (!(target_point.flags & PF_OVERFLOW)) {  // make sure point projected
3463 		if (target_point.codes == 0) { // target center is not on screen
3464 			int target_display_flags;
3465 
3466 			if(Cmdline_targetinfo) {
3467 				target_display_flags = TARGET_DISPLAY_DIST | TARGET_DISPLAY_DOTS | TARGET_DISPLAY_SUBSYS | TARGET_DISPLAY_NAME | TARGET_DISPLAY_CLASS;
3468 			} else {
3469 				target_display_flags = TARGET_DISPLAY_DIST | TARGET_DISPLAY_DOTS | TARGET_DISPLAY_SUBSYS;
3470 			}
3471 
3472 			hud_target_add_display_list(targetp, &target_point, &targetp->pos, 0, NULL, NULL, target_display_flags);
3473 		}
3474 	} else {
3475 		Hud_target_w = 0;
3476 		Hud_target_h = 0;
3477 	}
3478 
3479 	// update cargo scanning
3480 	hud_cargo_scan_update(targetp, frametime);
3481 
3482 	// display the lock indicator
3483 	if (!Player->target_is_dying) {
3484 		// update and render artillery
3485 		hud_artillery_update();
3486 		hud_artillery_render();
3487 	}
3488 
3489 	if (target_point.codes != 0) {
3490 		// draw the offscreen indicator at the edge of the screen where the target is closest to
3491 		Assert(Player_ai->target_objnum >= 0);
3492 
3493 		// AL 11-11-97:	don't draw the indicator if the ship is messaging, the indicator is drawn
3494 		// in the message sending color in hud_show_message_sender()
3495 		if ( Message_shipnum != Objects[Player_ai->target_objnum].instance ) {
3496 			hud_target_add_display_list(targetp, &target_point, &targetp->pos, 0, NULL, NULL, 0);
3497 		}
3498 	}
3499 }
3500 
3501 // hud_show_hostile_triangle() will draw an empty triangle that oribits around the outer
3502 // circle of the reticle.  It will point to the closest enemy that is firing on the player.
3503 // Currently, it points to the closest enemy that has the player as its target_objnum and has
3504 // SM_ATTACK or SM_SUPER_ATTACK as its ai submode.
hud_show_hostile_triangle()3505 void hud_show_hostile_triangle()
3506 {
3507 	object* A;
3508 	float min_distance=1e20f;
3509 	float new_distance=0.0f;
3510 	object* nearest_obj = &obj_used_list;
3511 	ai_info *aip;
3512 	ship_obj	*so;
3513 	ship		*sp;
3514 	ship_subsys *ss;
3515 
3516 	int player_obj_index = OBJ_INDEX(Player_obj);
3517 	int turret_is_attacking = 0;
3518 
3519 	hostile_obj = NULL;
3520 
3521 	for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list);  so = GET_NEXT(so) ) {
3522 
3523 		A = &Objects[so->objnum];
3524 		sp = &Ships[A->instance];
3525 
3526 		// only look at ships who attack us
3527 		if ( (A == Player_obj) || !(iff_x_attacks_y(Ships[A->instance].team, Player_ship->team)) ) {
3528 			continue;
3529 		}
3530 
3531 		aip = &Ai_info[Ships[A->instance].ai_index];
3532 
3533 		// don't look at ignore ships
3534 		if ( should_be_ignored(sp) ) {
3535 			continue;
3536 		}
3537 
3538 		// always ignore cargo containers and navbuoys
3539 		if ( Ship_info[sp->ship_info_index].class_type > -1 && !(Ship_types[Ship_info[sp->ship_info_index].class_type].flags[Ship::Type_Info_Flags::Show_attack_direction]) ) {
3540 			continue;
3541 		}
3542 
3543 		// check if ship is stealthy
3544 		if (awacs_get_level(&Objects[sp->objnum], Player_ship, 1) < 1) {
3545 			continue;
3546 		}
3547 
3548 		turret_is_attacking = 0;
3549 
3550 		// check if any turrets on ship are firing at the player (only on non fighter-bombers)
3551 		if ( !(Ship_info[sp->ship_info_index].is_fighter_bomber()) ) {
3552 			for (ss = GET_FIRST(&sp->subsys_list); ss != END_OF_LIST(&sp->subsys_list); ss = GET_NEXT(ss) ) {
3553 				if (ss->flags[Ship::Subsystem_Flags::Untargetable])
3554 					continue;
3555 
3556 				if ( (ss->system_info->type == SUBSYSTEM_TURRET) && (ss->current_hits > 0) ) {
3557 
3558 					if ( ss->turret_enemy_objnum == player_obj_index ) {
3559 						turret_is_attacking = 1;
3560 
3561 						vec3d		gsubpos;
3562 						// get world pos of subsystem
3563 						vm_vec_unrotate(&gsubpos, &ss->system_info->pnt, &A->orient);
3564 						vm_vec_add2(&gsubpos, &A->pos);
3565 						new_distance = vm_vec_dist_quick(&gsubpos, &Player_obj->pos);
3566 
3567 						if (new_distance <= min_distance) {
3568 							min_distance=new_distance;
3569 							nearest_obj = A;
3570 						}
3571 					}
3572 				}
3573 			}
3574 		}
3575 
3576 		if ( !turret_is_attacking ) {
3577 			// check for ships attacking the player
3578 			if ( aip->target_objnum != Player_ship->objnum ) {
3579 				continue;
3580 			}
3581 
3582 			// ignore enemy if not in chase mode
3583 			if ( (Game_mode & GM_NORMAL) && (aip->mode != AIM_CHASE) ) {
3584 				continue;
3585 			}
3586 
3587 			new_distance = vm_vec_dist_quick(&A->pos, &Player_obj->pos);
3588 
3589 			if (new_distance <= min_distance) {
3590 				min_distance=new_distance;
3591 				nearest_obj = A;
3592 			}
3593 		}
3594 	}
3595 
3596 	if ( nearest_obj == &obj_used_list ) {
3597 		return;
3598 	}
3599 
3600 	if ( min_distance > MIN_DISTANCE_TO_CONSIDER_THREAT ) {
3601 		return;
3602 	}
3603 
3604 	hostile_obj = nearest_obj;
3605 
3606 	// hook to maybe warn player about this attacking ship
3607 	ship_maybe_warn_player(&Ships[nearest_obj->instance], min_distance);
3608 }
3609 
HudGaugeHostileTriangle()3610 HudGaugeHostileTriangle::HudGaugeHostileTriangle():
3611 HudGaugeReticleTriangle(HUD_OBJECT_HOSTILE_TRI, HUD_HOSTILE_TRIANGLE)
3612 {
3613 }
3614 
render(float)3615 void HudGaugeHostileTriangle::render(float  /*frametime*/)
3616 {
3617 	if (hostile_obj && maybeFlashSexp() != 1) {
3618 		bool in_frame = g3_in_frame() > 0;
3619 		if(!in_frame)
3620 			g3_start_frame(0);
3621 
3622 		// hud_set_iff_color( TEAM_HOSTILE, 1 );	//	Note: This should really be TEAM_HOSTILE, not opposite of Player_ship->team.
3623 		hud_set_iff_color( hostile_obj, 1 );
3624 		renderTriangle(&hostile_obj->pos, 0, 1, 0);
3625 
3626 		if(!in_frame)
3627 			g3_end_frame();
3628 	}
3629 }
3630 
hud_calculate_lead_pos(vec3d * lead_target_pos,vec3d * target_pos,object * targetp,weapon_info * wip,float dist_to_target,vec3d * rel_pos)3631 void hud_calculate_lead_pos(vec3d *lead_target_pos, vec3d *target_pos, object *targetp, weapon_info	*wip, float dist_to_target, vec3d *rel_pos)
3632 {
3633 	vec3d target_moving_direction;
3634 	vec3d last_delta_vector;
3635 	float time_to_target, target_moved_dist;
3636 
3637 	if(wip->max_speed != 0) {
3638 		time_to_target = dist_to_target / wip->max_speed;
3639 	} else {
3640 		time_to_target = 0;
3641 	}
3642 
3643 	target_moved_dist = targetp->phys_info.speed * time_to_target;
3644 
3645 	target_moving_direction = targetp->phys_info.vel;
3646 
3647 	if(The_mission.ai_profile->flags[AI::Profile_Flags::Use_additive_weapon_velocity])
3648 		vm_vec_scale_sub2(&target_moving_direction, &Player_obj->phys_info.vel, wip->vel_inherit_amount);
3649 
3650 	// test if the target is moving at all
3651 	if ( vm_vec_mag_quick(&target_moving_direction) < 0.1f ) { // Find distance!
3652 		*lead_target_pos =  *target_pos;
3653 	} else {
3654 		vm_vec_normalize(&target_moving_direction);
3655 		vm_vec_scale(&target_moving_direction, target_moved_dist);
3656 		vm_vec_add(lead_target_pos, target_pos, &target_moving_direction );
3657 		polish_predicted_target_pos(wip, targetp, target_pos, lead_target_pos, dist_to_target, &last_delta_vector, 1); // Not used:, float time_to_enemy)
3658 
3659 		if(rel_pos) { // needed for quick lead indicators, not needed for normal lead indicators.
3660 			vm_vec_add2(lead_target_pos, rel_pos);
3661 		}
3662 	}
3663 }
3664 
3665 // Return the bank number for the primary weapon that can fire the farthest, from
3666 // the number of active primary weapons
3667 // input: range	=>	output parameter... it is the range of the selected bank
hud_get_best_primary_bank(float * range)3668 int hud_get_best_primary_bank(float *range)
3669 {
3670 	int	i, best_bank, bank_to_fire, num_to_test;
3671 	float	weapon_range, farthest_weapon_range;
3672 	ship_weapon	*swp;
3673 	weapon_info	*wip;
3674 
3675 	swp = &Player_ship->weapons;
3676 
3677 	farthest_weapon_range = 0.0f;
3678 	best_bank = -1;
3679 
3680 	if ( Player_ship->flags[Ship::Ship_Flags::Primary_linked] ) {
3681 		num_to_test = swp->num_primary_banks;
3682 	} else {
3683 		num_to_test = MIN(1, swp->num_primary_banks);
3684 	}
3685 
3686 	for ( i = 0; i < num_to_test; i++ )
3687 	{
3688 		bank_to_fire = (swp->current_primary_bank + i) % swp->num_primary_banks;
3689 
3690 		// calculate the range of the weapon, and only display the lead target indicator
3691 		// if the weapon can actually hit the target
3692 		Assert(bank_to_fire >= 0 && bank_to_fire < swp->num_primary_banks);
3693 		Assert(swp->primary_bank_weapons[bank_to_fire] < weapon_info_size());
3694 
3695 		if (swp->primary_bank_weapons[bank_to_fire] < 0)
3696 			continue;
3697 
3698 		wip = &Weapon_info[swp->primary_bank_weapons[bank_to_fire]];
3699 		weapon_range = MIN((wip->max_speed * wip->lifetime), wip->weapon_range);
3700 
3701 		// don't consider this primary if it's a ballistic that's out of ammo - Goober5000
3702 		if ( wip->wi_flags[Weapon::Info_Flags::Ballistic] )
3703 		{
3704 			if ( swp->primary_bank_ammo[bank_to_fire] <= 0)
3705 			{
3706 				continue;
3707 			}
3708 		}
3709 
3710 		if ( weapon_range > farthest_weapon_range )
3711 		{
3712 			best_bank = bank_to_fire;
3713 			farthest_weapon_range = weapon_range;
3714 		}
3715 	}
3716 
3717 	*range = farthest_weapon_range;
3718 	return best_bank;
3719 }
3720 
3721 // -----------------------------------------------------------------------------
3722 //	polish_predicted_target_pos()
3723 //
3724 // Called by the draw lead indicator code to predict where the enemy is going to be
3725 //
polish_predicted_target_pos(weapon_info * wip,object * targetp,vec3d * enemy_pos,vec3d * predicted_enemy_pos,float dist_to_enemy,vec3d * last_delta_vec,int num_polish_steps,object * reference_object)3726 void polish_predicted_target_pos(weapon_info *wip, object *targetp, vec3d *enemy_pos, vec3d *predicted_enemy_pos, float dist_to_enemy, vec3d *last_delta_vec, int num_polish_steps, object *reference_object)
3727 {
3728 	Assertion(reference_object != nullptr, "polish_predicted_target_pos received a nullptr for its reference ship, this is a coder mistake, please report!");
3729 
3730 	int	iteration;
3731 	vec3d	reference_pos = reference_object->pos;
3732 	float		time_to_enemy;
3733 	vec3d	last_predicted_enemy_pos = *predicted_enemy_pos;
3734 
3735 	float	weapon_speed = wip->max_speed;
3736 
3737 	vm_vec_zero(last_delta_vec);
3738 
3739 
3740 	vec3d enemy_vel = targetp->phys_info.vel;
3741 	vec3d enemy_acc;
3742 	if (The_mission.ai_profile->second_order_lead_predict_factor > 0) {
3743 		vec3d world_rotvel;
3744 		vm_vec_unrotate(&world_rotvel, &targetp->phys_info.rotvel, &targetp->orient);
3745 		vm_vec_cross(&enemy_acc, &world_rotvel, &enemy_vel);
3746 	}
3747 	// additive velocity stuff
3748 	// not just the player's main target
3749 	if (The_mission.ai_profile->flags[AI::Profile_Flags::Use_additive_weapon_velocity]) {
3750 		vm_vec_scale_sub2( &enemy_vel, &reference_object->phys_info.vel, wip->vel_inherit_amount);
3751 	}
3752 
3753 	for (iteration=0; iteration < num_polish_steps; iteration++) {
3754 		dist_to_enemy = vm_vec_dist_quick(predicted_enemy_pos, &reference_pos);
3755 		time_to_enemy = dist_to_enemy/weapon_speed;
3756 		vm_vec_scale_add(predicted_enemy_pos, enemy_pos, &enemy_vel, time_to_enemy);
3757 		if (The_mission.ai_profile->second_order_lead_predict_factor > 0) {
3758 			vm_vec_scale_add(predicted_enemy_pos, predicted_enemy_pos, &enemy_acc, (time_to_enemy * time_to_enemy / 2) * The_mission.ai_profile->second_order_lead_predict_factor);
3759 		}
3760 		vm_vec_sub(last_delta_vec, predicted_enemy_pos, &last_predicted_enemy_pos);
3761 		last_predicted_enemy_pos= *predicted_enemy_pos;
3762 	}
3763 }
3764 
HudGaugeLeadIndicator()3765 HudGaugeLeadIndicator::HudGaugeLeadIndicator():
3766 HudGauge(HUD_OBJECT_LEAD, HUD_LEAD_INDICATOR, false, false, VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY | VM_OTHER_SHIP, 255, 255, 255)
3767 {
3768 
3769 }
3770 
initHalfSize(float w,float h)3771 void HudGaugeLeadIndicator::initHalfSize(float w, float h)
3772 {
3773 	Lead_indicator_half[0] = w;
3774 	Lead_indicator_half[1] = h;
3775 }
3776 
initBitmaps(char * fname)3777 void HudGaugeLeadIndicator::initBitmaps(char *fname)
3778 {
3779 	Lead_indicator_gauge.first_frame = bm_load_animation(fname, &Lead_indicator_gauge.num_frames);
3780 	if ( Lead_indicator_gauge.first_frame < 0 ) {
3781 		Warning(LOCATION,"Cannot load hud ani: %s\n", fname);
3782 	}
3783 }
3784 
pageIn()3785 void HudGaugeLeadIndicator::pageIn()
3786 {
3787 	bm_page_in_aabitmap(Lead_indicator_gauge.first_frame, Lead_indicator_gauge.num_frames);
3788 }
3789 
3790 // determine the correct frame to draw for the lead indicator
3791 // 0 -> center only	(in secondary range only)
3792 // 1 -> full			(in secondary and primary range)
3793 //	2 -> oustide only	(in primary range only)
3794 //
3795 // input:	prange	=>	range of current primary weapon
3796 //				srange	=>	range of current secondary weapon
3797 //				dist_to_target	=>	current dist to target
3798 //
3799 // exit:		0-2	=>	frame offset
3800 //				-1		=>	don't draw anything
pickFrame(float prange,float srange,float dist_to_target)3801 int HudGaugeLeadIndicator::pickFrame(float prange, float srange, float dist_to_target)
3802 {
3803 	int frame_offset=-1;
3804 	int in_prange=0, in_srange=0;
3805 
3806 	if ( dist_to_target < prange ) {
3807 		in_prange=1;
3808 	}
3809 
3810 	if ( dist_to_target < srange ) {
3811 		in_srange=1;
3812 	}
3813 
3814 	if ( in_prange && in_srange ) {
3815 		frame_offset=1;
3816 	} else if ( in_prange && !in_srange ) {
3817 		frame_offset=2;
3818 	} else if ( !in_prange && in_srange ) {
3819 		frame_offset=0;
3820 	} else {
3821 		frame_offset=-1;
3822 	}
3823 
3824 	return frame_offset;
3825 }
3826 
render(float)3827 void HudGaugeLeadIndicator::render(float  /*frametime*/)
3828 {
3829 	if(Player->target_is_dying) {
3830 		return;
3831 	}
3832 
3833 	bool in_frame = g3_in_frame() > 0;
3834 	if(!in_frame)
3835 		g3_start_frame(0);
3836 
3837 	// first render the current target the player has selected.
3838 	renderLeadCurrentTarget();
3839 
3840 	// if extra targeting info is enabled, render lead indicators for objects in the target display list.
3841 	for(size_t i = 0; i < target_display_list.size(); i++) {
3842 		if ( (target_display_list[i].flags & TARGET_DISPLAY_LEAD) && target_display_list[i].objp ) {
3843 
3844 			// set the color
3845 			if( target_display_list[i].bracket_clr.red && target_display_list[i].bracket_clr.green &&
3846 				target_display_list[i].bracket_clr.blue ) {
3847 				gr_set_color_fast(&target_display_list[i].bracket_clr);
3848 			} else {
3849 				// use IFF colors if none defined.
3850 				hud_set_iff_color(target_display_list[i].objp, 1);
3851 			}
3852 
3853 			renderLeadQuick(&target_display_list[i].target_pos, target_display_list[i].objp);
3854 		}
3855 	}
3856 
3857 	if(!in_frame)
3858 		g3_end_frame();
3859 }
3860 
renderIndicator(int frame_offset,object * targetp,vec3d * lead_target_pos)3861 void HudGaugeLeadIndicator::renderIndicator(int frame_offset, object *targetp, vec3d *lead_target_pos)
3862 {
3863 	vertex lead_target_vertex;
3864 	int sx, sy;
3865 
3866 	g3_rotate_vertex(&lead_target_vertex, lead_target_pos);
3867 
3868 	if (lead_target_vertex.codes == 0) { // on screen
3869 		g3_project_vertex(&lead_target_vertex);
3870 
3871 		if (!(lead_target_vertex.flags & PF_OVERFLOW)) {
3872 			if ( maybeFlashSexp() == 1 ) {
3873 				hud_set_iff_color(targetp, 0);
3874 			} else {
3875 				hud_set_iff_color(targetp, 1);
3876 			}
3877 
3878 			if ( Lead_indicator_gauge.first_frame + frame_offset >= 0 ) {
3879 				sx = fl2i(lead_target_vertex.screen.xyw.x);
3880 				sy = fl2i(lead_target_vertex.screen.xyw.y);
3881 
3882 				unsize(&sx, &sy);
3883 				renderBitmap(Lead_indicator_gauge.first_frame + frame_offset, fl2i(sx - Lead_indicator_half[0]),  fl2i(sy - Lead_indicator_half[1]));
3884 			}
3885 		}
3886 	}
3887 }
3888 
3889 // HudGaugeLeadIndicator::renderTargetLead() determine where to draw the lead target box and display it
renderLeadCurrentTarget()3890 void HudGaugeLeadIndicator::renderLeadCurrentTarget()
3891 {
3892 	vec3d		target_pos;
3893 	vec3d		source_pos;
3894 	vec3d		*rel_pos;
3895 	vec3d		lead_target_pos;
3896 	object		*targetp;
3897 	polymodel	*pm;
3898 	ship_weapon	*swp;
3899 	weapon_info	*wip;
3900 	weapon_info	*tmp=NULL;
3901 	float			dist_to_target, prange, srange;
3902 	int			bank_to_fire, frame_offset;
3903 
3904 	if (Player_ai->target_objnum == -1)
3905 		return;
3906 
3907 	targetp = &Objects[Player_ai->target_objnum];
3908 	if ( (targetp->type != OBJ_SHIP) && (targetp->type != OBJ_WEAPON) && (targetp->type != OBJ_ASTEROID) ) {
3909 		return;
3910 	}
3911 
3912 	// only allow bombs to have lead indicator displayed
3913 	if ( targetp->type == OBJ_WEAPON ) {
3914 		if ( !(Weapon_info[Weapons[targetp->instance].weapon_info_index].wi_flags[Weapon::Info_Flags::Can_be_targeted]) ) {
3915 			if ( !(Weapon_info[Weapons[targetp->instance].weapon_info_index].wi_flags[Weapon::Info_Flags::Bomb]) ) {
3916 				return;
3917 			}
3918 		}
3919 	}
3920 
3921 	// If the target is out of range, then draw the correct frame for the lead indicator
3922 	if ( Lead_indicator_gauge.first_frame == -1 ) {
3923 		Int3();
3924 		return;
3925 	}
3926 
3927 	// AL 1/20/97: Point to targted subsystem if one exists
3928 	if ( Player_ai->targeted_subsys != NULL ) {
3929 		get_subsystem_world_pos(targetp, Player_ai->targeted_subsys, &target_pos);
3930 	} else {
3931 		target_pos = targetp->pos;
3932 	}
3933 
3934 	pm = model_get(Ship_info[Player_ship->ship_info_index].model_num);
3935 	swp = &Player_ship->weapons;
3936 
3937 	// Added to take care of situation where there are no primary banks on the player ship
3938 	// (this may not be possible, depending on what we decide for the weapons loadout rules)
3939 	if ( swp->num_primary_banks == 0 )
3940 		return;
3941 
3942 	bank_to_fire = hud_get_best_primary_bank(&prange);
3943 
3944 	if ( bank_to_fire < 0 )
3945 		return;
3946 
3947 	wip = &Weapon_info[swp->primary_bank_weapons[bank_to_fire]];
3948 
3949 	if (pm->n_guns && bank_to_fire != -1 ) {
3950 		rel_pos = &pm->gun_banks[bank_to_fire].pnt[0];
3951 	} else {
3952 		rel_pos = NULL;
3953 	}
3954 
3955 	// source_pos will contain the world coordinate of where to base the lead indicator prediction
3956 	// from.  Normally, this will be the world pos of the gun turret of the currently selected primary
3957 	// weapon.
3958 	source_pos = Player_obj->pos;
3959 	if (rel_pos != NULL) {
3960 		vec3d	gun_point;
3961 		vm_vec_unrotate(&gun_point, rel_pos, &Player_obj->orient);
3962 		vm_vec_add2(&source_pos, &gun_point);
3963 	}
3964 
3965 	// Determine "accurate" distance to target.
3966 	// This is the distance from the player ship to:
3967 	//   (if targeting a subsystem) the distance to the subsystem centre
3968 	//     (playing it safe, will usually be in range at slightly further away due to subsys radius)
3969 	//   (otherwise) the closest point on the bounding box of the target
3970 	if ( Player_ai->targeted_subsys != NULL ) {
3971 		dist_to_target = vm_vec_dist(&target_pos, &Player_obj->pos);
3972 	} else {
3973 		dist_to_target = hud_find_target_distance(targetp, Player_obj);
3974 	}
3975 
3976 	srange = ship_get_secondary_weapon_range(Player_ship);
3977 
3978 	if ( (swp->current_secondary_bank >= 0) && (swp->secondary_bank_weapons[swp->current_secondary_bank] >= 0) )
3979 	{
3980 		int bank = swp->current_secondary_bank;
3981 		tmp = &Weapon_info[swp->secondary_bank_weapons[bank]];
3982 		if ( !(tmp->is_homing()) && !(tmp->is_locked_homing() && Player->target_in_lock_cone) ) {
3983 			//The secondary lead indicator is handled farther below if it is a non-locking type
3984 			srange = -1.0f;
3985 		}
3986 	}
3987 
3988 	frame_offset = pickFrame(prange, srange, dist_to_target);
3989 	if ( frame_offset < 0 && srange != -1.0f ) {
3990 		return;
3991 	}
3992 
3993 	if (frame_offset >= 0) {
3994 		hud_calculate_lead_pos(&lead_target_pos, &target_pos, targetp, wip, dist_to_target);
3995 		renderIndicator(frame_offset, targetp, &lead_target_pos);
3996 	}
3997 
3998 	//do dumbfire lead indicator - color is orange (255,128,0) - bright, (192,96,0) - dim
3999 	//phreak changed 9/01/02
4000 	if((swp->current_secondary_bank>=0) && (swp->secondary_bank_weapons[swp->current_secondary_bank] >= 0)) {
4001 		int bank=swp->current_secondary_bank;
4002 		wip=&Weapon_info[swp->secondary_bank_weapons[bank]];
4003 
4004 		//get out of here if the secondary weapon is a homer or if its out of range
4005 		if ( wip->is_homing() )
4006 			return;
4007 
4008 		double max_dist = MIN((wip->lifetime * wip->max_speed), wip->weapon_range);
4009 
4010 		if (dist_to_target > max_dist)
4011 			return;
4012 	}
4013 
4014 	hud_calculate_lead_pos(&lead_target_pos, &target_pos, targetp, wip, dist_to_target);
4015 	renderIndicator(0, targetp, &lead_target_pos);
4016 }
4017 
4018 //Backslash
4019 // A stripped-down version of the lead indicator, only shows primary weapons
4020 // and works for a specified target (not just the current selected target).
4021 // Ideally I'd like to later turn this into something (or make a new function) that would actually WORK with gun convergence/normals
4022 // instead of the existing code (copied from above) that does some calculations and then is ignored ;-)
4023 // (Go look, what's it actually DO with source_pos?)
4024 // And also, something that could be called for multiple weapons, ITTS style.
renderLeadQuick(vec3d * target_world_pos,object * targetp)4025 void HudGaugeLeadIndicator::renderLeadQuick(vec3d *target_world_pos, object *targetp)
4026 {
4027 	vec3d		source_pos;
4028 	vec3d		*rel_pos;
4029 	vec3d		lead_target_pos;
4030 	polymodel	*pm;
4031 	ship_weapon	*swp;
4032 	weapon_info	*wip;
4033 	float			dist_to_target, prange;
4034 	int			bank_to_fire, frame_offset;
4035 
4036 	if ( (targetp->type != OBJ_SHIP) && (targetp->type != OBJ_WEAPON) && (targetp->type != OBJ_ASTEROID) ) {
4037 		return;
4038 	}
4039 
4040 	// only allow bombs to have lead indicator displayed
4041 	if ( targetp->type == OBJ_WEAPON ) {
4042 		if ( !(Weapon_info[Weapons[targetp->instance].weapon_info_index].wi_flags[Weapon::Info_Flags::Can_be_targeted]) ) {
4043 			if ( !(Weapon_info[Weapons[targetp->instance].weapon_info_index].wi_flags[Weapon::Info_Flags::Bomb]) ) {
4044 				return;
4045 			}
4046 		}
4047 	}
4048 
4049 	// If the target is out of range, then draw the correct frame for the lead indicator
4050 	if ( Lead_indicator_gauge.first_frame == -1 ) {
4051 		Int3();
4052 		return;
4053 	}
4054 
4055 	pm = model_get(Ship_info[Player_ship->ship_info_index].model_num);
4056 	swp = &Player_ship->weapons;
4057 
4058 	// Added to take care of situation where there are no primary banks on the player ship
4059 	// (this may not be possible, depending on what we decide for the weapons loadout rules)
4060 	if ( swp->num_primary_banks == 0 )
4061 		return;
4062 
4063 	bank_to_fire = hud_get_best_primary_bank(&prange);	//Backslash note: this!
4064 	if ( bank_to_fire < 0 )
4065 		return;
4066 	wip = &Weapon_info[swp->primary_bank_weapons[bank_to_fire]];
4067 
4068 	if (pm->n_guns && bank_to_fire != -1 ) {
4069 		rel_pos = &pm->gun_banks[bank_to_fire].pnt[0];
4070 	} else {
4071 		rel_pos = NULL;
4072 	}
4073 //							vec3d firing_vec;
4074 //							vm_vec_unrotate(&firing_vec, &po->gun_banks[bank_to_fire].norm[pt], &obj->orient);
4075 //							vm_vector_2_matrix(&firing_orient, &firing_vec, NULL, NULL);
4076 
4077 	// source_pos will contain the world coordinate of where to base the lead indicator prediction
4078 	// from.  Normally, this will be the world pos of the gun turret of the currently selected primary
4079 	// weapon.
4080 	source_pos = Player_obj->pos;
4081 	if (rel_pos != NULL) {
4082 		vec3d	gun_point;
4083 		vm_vec_unrotate(&gun_point, rel_pos, &Player_obj->orient);
4084 		vm_vec_add2(&source_pos, &gun_point);
4085 	}
4086 
4087 	// Determine "accurate" distance to target.  This is the distance from the player ship
4088 	// to the closest point on the bounding box of the target
4089 	dist_to_target = hud_find_target_distance(targetp, Player_obj);
4090 
4091 	frame_offset = pickFrame(prange, -1.0f, dist_to_target);
4092 	if ( frame_offset < 0 ) {
4093 		return;
4094 	}
4095 
4096 	hud_calculate_lead_pos(&lead_target_pos, target_world_pos, targetp, wip, dist_to_target, rel_pos);
4097 	renderIndicator(frame_offset, targetp, &lead_target_pos);
4098 }
4099 
HudGaugeLeadSight()4100 HudGaugeLeadSight::HudGaugeLeadSight():
4101 HudGauge(HUD_OBJECT_LEAD_SIGHT, HUD_LEAD_INDICATOR, true, false, VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY | VM_OTHER_SHIP, 255, 255, 255)
4102 {
4103 }
4104 
initBitmaps(char * fname)4105 void HudGaugeLeadSight::initBitmaps(char *fname)
4106 {
4107 	Lead_sight.first_frame = bm_load_animation(fname, &Lead_sight.num_frames);
4108 
4109 	if ( Lead_sight.first_frame < 0 ) {
4110 		Warning(LOCATION,"Cannot load hud ani: %s\n", fname);
4111 	} else {
4112 		int w, h;
4113 
4114 		bm_get_info(Lead_sight.first_frame, &w, &h);
4115 		Lead_sight_half[0] = fl2i(w * 0.5f);
4116 		Lead_sight_half[1] = fl2i(h * 0.5f);
4117 	}
4118 }
4119 
renderSight(int frame_offset,vec3d * target_pos,vec3d * lead_target_pos)4120 void HudGaugeLeadSight::renderSight(int frame_offset, vec3d *target_pos, vec3d *lead_target_pos)
4121 {
4122 	vertex target_vertex;
4123 	float target_sx;
4124 	float target_sy;
4125 
4126 	vertex lead_target_vertex;
4127 	float target_lead_sx;
4128 	float target_lead_sy;
4129 
4130 	// first see if the lead is on screen
4131 	g3_rotate_vertex(&lead_target_vertex, lead_target_pos);
4132 
4133 	if (lead_target_vertex.codes != 0)
4134 		return;
4135 
4136 	g3_project_vertex(&lead_target_vertex);
4137 
4138 	if (lead_target_vertex.flags & PF_OVERFLOW)
4139 		return;
4140 
4141 	target_lead_sx = lead_target_vertex.screen.xyw.x;
4142 	target_lead_sy = lead_target_vertex.screen.xyw.y;
4143 
4144 	// now see if the target is on screen
4145 	g3_rotate_vertex(&target_vertex, target_pos);
4146 
4147 	if (target_vertex.codes != 0)
4148 		return;
4149 
4150 	g3_project_vertex(&target_vertex);
4151 
4152 	if (target_vertex.flags & PF_OVERFLOW)
4153 		return;
4154 
4155 	target_sx = target_vertex.screen.xyw.x;
4156 	target_sy = target_vertex.screen.xyw.y;
4157 
4158 	// render the lead sight
4159 	if ( Lead_sight.first_frame >= 0 ) {
4160 
4161 		unsize(&target_lead_sx, &target_lead_sy);
4162 		unsize(&target_sx, &target_sy);
4163 
4164 		float reticle_target_sx = target_sx - Lead_sight_half[0] - target_lead_sx;
4165 		float reticle_target_sy = target_sy - Lead_sight_half[1] - target_lead_sy;
4166 
4167 		reticle_target_sx += position[0] + 0.5f;
4168 		reticle_target_sy += position[1] + 0.5f;
4169 
4170 		setGaugeColor();
4171 		renderBitmap(Lead_sight.first_frame + frame_offset, fl2i(reticle_target_sx) + fl2i(HUD_offset_x), fl2i(reticle_target_sy) + fl2i(HUD_offset_y));
4172 	}
4173 }
4174 
pageIn()4175 void HudGaugeLeadSight::pageIn()
4176 {
4177 	bm_page_in_aabitmap(Lead_sight.first_frame, Lead_sight.num_frames);
4178 }
4179 
render(float)4180 void HudGaugeLeadSight::render(float  /*frametime*/)
4181 {
4182 	vec3d		target_pos;
4183 	vec3d		source_pos;
4184 	vec3d		*rel_pos;
4185 	vec3d		lead_target_pos;
4186 	object		*targetp;
4187 	polymodel	*pm;
4188 	ship_weapon	*swp;
4189 	weapon_info	*wip;
4190 	float		dist_to_target, prange;
4191 	int			bank_to_fire;
4192 
4193 	if (Player_ai->target_objnum == -1)
4194 		return;
4195 
4196 	targetp = &Objects[Player_ai->target_objnum];
4197 	if ( (targetp->type != OBJ_SHIP) && (targetp->type != OBJ_WEAPON) && (targetp->type != OBJ_ASTEROID) ) {
4198 		return;
4199 	}
4200 
4201 	// only allow bombs to have lead indicator displayed
4202 	if ( targetp->type == OBJ_WEAPON ) {
4203 		if ( !(Weapon_info[Weapons[targetp->instance].weapon_info_index].wi_flags[Weapon::Info_Flags::Bomb]) ) {
4204 			return;
4205 		}
4206 	}
4207 
4208 	// If the target is out of range, then draw the correct frame for the lead indicator
4209 	if ( Lead_sight.first_frame == -1 ) {
4210 		Int3();
4211 		return;
4212 	}
4213 
4214 	// AL 1/20/97: Point to targeted subsystem if one exists
4215 	if ( Player_ai->targeted_subsys != NULL ) {
4216 		get_subsystem_world_pos(targetp, Player_ai->targeted_subsys, &target_pos);
4217 	} else {
4218 		target_pos = targetp->pos;
4219 	}
4220 
4221 	pm = model_get(Ship_info[Player_ship->ship_info_index].model_num);
4222 	swp = &Player_ship->weapons;
4223 
4224 	// Added to take care of situation where there are no primary banks on the player ship
4225 	// (this may not be possible, depending on what we decide for the weapons loadout rules)
4226 	if ( swp->num_primary_banks == 0 )
4227 		return;
4228 
4229 	bank_to_fire = hud_get_best_primary_bank(&prange);
4230 	if ( bank_to_fire < 0 )
4231 		return;
4232 	wip = &Weapon_info[swp->primary_bank_weapons[bank_to_fire]];
4233 
4234 	if (pm->n_guns && bank_to_fire != -1 ) {
4235 		rel_pos = &pm->gun_banks[bank_to_fire].pnt[0];
4236 	} else {
4237 		rel_pos = NULL;
4238 	}
4239 
4240 	// source_pos will contain the world coordinate of where to base the lead indicator prediction
4241 	// from.  Normally, this will be the world pos of the gun turret of the currently selected primary
4242 	// weapon.
4243 	source_pos = Player_obj->pos;
4244 	if (rel_pos != NULL) {
4245 		vec3d	gun_point;
4246 		vm_vec_unrotate(&gun_point, rel_pos, &Player_obj->orient);
4247 		vm_vec_add2(&source_pos, &gun_point);
4248 	}
4249 
4250 	// Determine "accurate" distance to target.  This is the distance from the player ship
4251 	// to the closest point on the bounding box of the target
4252 	dist_to_target = hud_find_target_distance(targetp, Player_obj);
4253 
4254 	bool in_frame;
4255 	if ( dist_to_target < prange ) {
4256 		// fire it up
4257 		in_frame = g3_in_frame() > 0;
4258 		if(!in_frame) {
4259 			g3_start_frame(0);
4260 		}
4261 
4262 		hud_calculate_lead_pos(&lead_target_pos, &target_pos, targetp, wip, dist_to_target);
4263 		renderSight(1, &target_pos, &lead_target_pos); // render the primary weapon lead sight
4264 
4265 		if(!in_frame) {
4266 			g3_end_frame();
4267 		}
4268 	}
4269 
4270 	//do dumbfire lead indicator - color is orange (255,128,0) - bright, (192,96,0) - dim
4271 	//phreak changed 9/01/02
4272 	if((swp->current_secondary_bank>=0) && (swp->secondary_bank_weapons[swp->current_secondary_bank] >= 0))
4273 	{
4274 		int bank=swp->current_secondary_bank;
4275 		wip=&Weapon_info[swp->secondary_bank_weapons[bank]];
4276 
4277 		//get out of here if the secondary weapon is a homer or if its out of range
4278 		if ( wip->is_homing() ) {
4279 			return;
4280 		}
4281 
4282 		double max_dist = MIN((wip->lifetime * wip->max_speed), wip->weapon_range);
4283 
4284 		if (dist_to_target > max_dist) {
4285 			return;
4286 		}
4287 	} else {
4288 		return;
4289 	}
4290 
4291 	// fire it up
4292 	in_frame = g3_in_frame() > 0;
4293 	if(!in_frame) {
4294 		g3_start_frame(0);
4295 	}
4296 
4297 	//give it the "in secondary range frame
4298 
4299 	hud_calculate_lead_pos(&lead_target_pos, &target_pos, targetp, wip, dist_to_target);
4300 	renderSight(0, &target_pos, &lead_target_pos); // now render the secondary weapon lead sight
4301 
4302 	if(!in_frame) {
4303 		g3_end_frame();
4304 	}
4305 }
4306 
hud_cease_subsystem_targeting(bool print_message)4307 void hud_cease_subsystem_targeting(bool print_message)
4308 {
4309 	int ship_index;
4310 
4311 	Assertion(Player_ai->target_objnum < MAX_OBJECTS, "Invalid player target objnum");
4312 
4313 	if (Player_ai->target_objnum < 0) {
4314 		// Nothing selected
4315 		if (print_message) {
4316 			HUD_sourced_printf(HUD_SOURCE_HIDDEN, "%s", XSTR("No target selected.", 322));
4317 			snd_play(gamesnd_get_game_sound(GameSounds::TARGET_FAIL));
4318 		}
4319 		return;
4320 	}
4321 
4322 	if (Objects[Player_ai->target_objnum].type != OBJ_SHIP) {
4323 		// Object isn't a ship, so it doesn't have any subsystems
4324 		if (print_message) {
4325 			snd_play(gamesnd_get_game_sound(GameSounds::TARGET_FAIL));
4326 		}
4327 		return;
4328 	}
4329 
4330 	ship_index = Objects[Player_ai->target_objnum].instance;
4331 
4332 	Assertion(ship_index >= 0 && ship_index < MAX_SHIPS, "Invalid ship index");
4333 	Assertion(Player_num >= 0 && Player_num < MAX_PLAYERS, "Invalid player number");
4334 
4335 	Ships[ship_index].last_targeted_subobject[Player_num] = NULL;
4336 	Player_ai->targeted_subsys = NULL;
4337 	Player_ai->targeted_subsys_parent = -1;
4338 
4339 	if ( print_message ) {
4340 		HUD_sourced_printf(HUD_SOURCE_HIDDEN, "%s", XSTR( "Deactivating sub-system targeting", 324));
4341 	}
4342 
4343 	hud_stop_looped_locking_sounds();
4344 	hud_lock_reset();
4345 }
4346 
hud_cease_targeting(bool deliberate)4347 void hud_cease_targeting(bool deliberate)
4348 {
4349 	// Turn off subsys targeting before we invalidate the player target_objnum...
4350 	hud_cease_subsystem_targeting(false);
4351 	set_target_objnum(Player_ai, -1);
4352 
4353 	if (deliberate) {
4354 		// Player wants to stop targeting, so turn off auto-target so we don't immediately aquire another target
4355 		Players[Player_num].flags &= ~PLAYER_FLAGS_AUTO_TARGETING;
4356 		HUD_sourced_printf(HUD_SOURCE_HIDDEN, "%s", XSTR("Deactivating targeting system", 325));
4357 	}
4358 }
4359 
4360 // hud_restore_subsystem_target() will remember the last targeted subsystem
4361 // on a target.
4362 //
hud_restore_subsystem_target(ship * shipp)4363 void hud_restore_subsystem_target(ship* shipp)
4364 {
4365 	// check if there was a previously targeted sub-system for this target
4366 	if ( shipp->last_targeted_subobject[Player_num] != NULL ) {
4367 		Player_ai->targeted_subsys = shipp->last_targeted_subobject[Player_num];
4368 		Player_ai->targeted_subsys_parent = Player_ai->target_objnum;
4369 	}
4370 	else {
4371 		Player_ai->targeted_subsys = NULL;
4372 		Player_ai->targeted_subsys_parent = -1;
4373 	}
4374 }
4375 
4376 // --------------------------------------------------------------------------------
4377 // get_subsystem_world_pos() returns the world position for a given subsystem on a ship
4378 //
get_subsystem_world_pos(object * parent_obj,ship_subsys * subsys,vec3d * world_pos)4379 vec3d* get_subsystem_world_pos(object* parent_obj, ship_subsys* subsys, vec3d* world_pos)
4380 {
4381 	get_subsystem_pos(world_pos, parent_obj, subsys);
4382 
4383 	return world_pos;
4384 }
4385 
4386 // ----------------------------------------------------------------------------
4387 // hud_target_change_check()
4388 //
4389 // called once per frame to account for when the target changes
4390 //
hud_target_change_check()4391 void hud_target_change_check()
4392 {
4393 	float current_speed=0.0f;
4394 
4395 	// Check if player subsystem target has changed, and reset necessary player flag
4396 	if ( Player_ai->targeted_subsys != Player_ai->last_subsys_target ) {
4397 		Player->subsys_in_view=-1;
4398 	}
4399 
4400 	// check if the main target has changed
4401 	if (Player_ai->last_target != Player_ai->target_objnum) {
4402 
4403 		if ( Player_ai->target_objnum != -1){
4404 			snd_play( gamesnd_get_game_sound(ship_get_sound(Player_obj, GameSounds::TARGET_ACQUIRE)), 0.0f );
4405 		}
4406 
4407 		// if we have a hotkey set active, see if new target is in set.  If not in
4408 		// set, deselect the current hotkey set.
4409 		if (	Player->current_hotkey_set != -1 ) {
4410 			htarget_list *hitem, *plist;
4411 
4412 			plist = &(Player->keyed_targets[Player->current_hotkey_set]);
4413 			for ( hitem = GET_FIRST(plist); hitem != END_OF_LIST(plist); hitem = GET_NEXT(hitem) ) {
4414 				if ( OBJ_INDEX(hitem->objp) == Player_ai->target_objnum ){
4415 					break;
4416 				}
4417 			}
4418 			if ( hitem == END_OF_LIST(plist) ){
4419 				Player->current_hotkey_set = -1;
4420 			}
4421 		}
4422 
4423 		player_stop_cargo_scan_sound();
4424 		if ( (Player_ai->target_objnum >= 0) && (Player_ai->target_objnum < MAX_OBJECTS) ) {
4425 			hud_shield_hit_reset(&Objects[Player_ai->target_objnum]);
4426 		}
4427 		hud_targetbox_init_flash();
4428 		hud_targetbox_start_flash(TBOX_FLASH_NAME);
4429 		hud_gauge_popup_start(HUD_TARGET_MINI_ICON);
4430 		Player->cargo_inspect_time=0;
4431 		Player->locking_subsys=NULL;
4432 		Player->locking_on_center=0;
4433 		Player->locking_subsys_parent=-1;
4434 
4435 		Player_ai->current_target_dist_trend = NO_CHANGE;
4436 		Player_ai->current_target_speed_trend = NO_CHANGE;
4437 
4438 		if ( Players[Player_num].flags & PLAYER_FLAGS_AUTO_MATCH_SPEED ) {
4439 			Players[Player_num].flags &= ~PLAYER_FLAGS_MATCH_TARGET;
4440 			player_match_target_speed();
4441 		}
4442 		else {
4443 			if ( Players[Player_num].flags & PLAYER_FLAGS_MATCH_TARGET )
4444 				Players[Player_num].flags &= ~PLAYER_FLAGS_MATCH_TARGET;		// no more target matching.
4445 		}
4446 
4447 		hud_lock_reset();
4448 
4449 		if ( (Player_ai->target_objnum >= 0) && (Player_ai->target_objnum < MAX_OBJECTS) ) {
4450 			if ( Objects[Player_ai->target_objnum].type == OBJ_SHIP ) {
4451 				hud_restore_subsystem_target(&Ships[Objects[Player_ai->target_objnum].instance]);
4452 			}
4453 		}
4454 
4455 		// reset external cam distance
4456 		if (Viewer_mode & VM_EXTERNAL) {
4457 			if (Viewer_mode & VM_OTHER_SHIP)
4458 				Viewer_external_info.preferred_distance = 2 * Objects[Player_ai->target_objnum].radius;
4459 		}
4460 	}
4461 	else {
4462 		if (Player_ai->current_target_distance < Player_ai->last_dist-0.01){
4463 			Player_ai->current_target_dist_trend = DECREASING;
4464 		} else if (Player_ai->current_target_distance > Player_ai->last_dist+0.01){
4465 			Player_ai->current_target_dist_trend = INCREASING;
4466 		} else {
4467 			Player_ai->current_target_dist_trend = NO_CHANGE;
4468 		}
4469 
4470 		if ( (Player_ai->target_objnum >= 0) && (Player_ai->target_objnum < MAX_OBJECTS) ) {
4471 			current_speed = Objects[Player_ai->target_objnum].phys_info.speed;
4472 		}
4473 
4474 		if (current_speed < Player_ai->last_speed-0.01){
4475 			Player_ai->current_target_speed_trend = DECREASING;
4476 		} else if (current_speed > Player_ai->last_speed+0.01) {
4477 			Player_ai->current_target_speed_trend = INCREASING;
4478 		} else {
4479 			Player_ai->current_target_speed_trend = NO_CHANGE;
4480 		}
4481 
4482 		if ( Players[Player_num].flags & PLAYER_FLAGS_AUTO_MATCH_SPEED ) {
4483 			if ( !(Players[Player_num].flags & PLAYER_FLAGS_MATCH_TARGET) ) {
4484 				player_match_target_speed();
4485 			}
4486 		}
4487 	}
4488 
4489 	Player_ai->last_dist = Player_ai->current_target_distance;
4490 	Player_ai->last_speed = current_speed;
4491 
4492 	Player_ai->last_target = Player_ai->target_objnum;
4493 	Player_ai->last_subsys_target = Player_ai->targeted_subsys;
4494 }
4495 
HudGaugeTargetTriangle()4496 HudGaugeTargetTriangle::HudGaugeTargetTriangle():
4497 HudGaugeReticleTriangle(HUD_OBJECT_TARGET_TRI, HUD_TARGET_TRIANGLE)
4498 {
4499 }
4500 
render(float)4501 void HudGaugeTargetTriangle::render(float  /*frametime*/)
4502 {
4503 	if ( Player_ai->target_objnum == -1)
4504 		return;
4505 
4506 	bool in_frame = g3_in_frame() > 0;
4507 	if(!in_frame)
4508 		g3_start_frame(0);
4509 
4510 	object *targetp = &Objects[Player_ai->target_objnum];
4511 
4512 	// draw the targeting triangle that orbits the outside of the outer circle of the reticle
4513 	if (!Player->target_is_dying && maybeFlashSexp() != 1) {
4514 
4515 		hud_set_iff_color(targetp, 1);
4516 		renderTriangle(&targetp->pos, 1, 0, 0);
4517 	}
4518 
4519 	if(!in_frame)
4520 		g3_end_frame();
4521 }
4522 
4523 // start the weapon line (on the HUD) flashing
hud_start_flash_weapon(int index)4524 void hud_start_flash_weapon(int index)
4525 {
4526 	if ( index >= MAX_WEAPON_FLASH_LINES ) {
4527 		Int3();	// Get Alan
4528 		return;
4529 	}
4530 
4531 	if ( timestamp_elapsed(Weapon_flash_info.flash_duration[index]) ) {
4532 		Weapon_flash_info.flash_next[index] = timestamp(TBOX_FLASH_INTERVAL);
4533 		Weapon_flash_info.is_bright &= ~(1<<index);
4534 	}
4535 
4536 	Weapon_flash_info.flash_duration[index] = timestamp(TBOX_FLASH_DURATION);
4537 }
4538 
4539 // maybe change the text color for the weapon line indicated by index
hud_maybe_flash_weapon(int index)4540 void hud_maybe_flash_weapon(int index)
4541 {
4542 	if ( index >= MAX_WEAPON_FLASH_LINES ) {
4543 		Int3();	// Get Alan
4544 		return;
4545 	}
4546 
4547 	// hud_set_default_color();
4548 	hud_set_gauge_color(HUD_WEAPONS_GAUGE);
4549 	if ( !timestamp_elapsed(Weapon_flash_info.flash_duration[index]) ) {
4550 		if ( timestamp_elapsed(Weapon_flash_info.flash_next[index]) ) {
4551 			Weapon_flash_info.flash_next[index] = timestamp(TBOX_FLASH_INTERVAL);
4552 			Weapon_flash_info.is_bright ^= (1<<index);
4553 		}
4554 
4555 		if ( Weapon_flash_info.is_bright & (1<<index) ) {
4556 			hud_set_gauge_color(HUD_WEAPONS_GAUGE, HUD_C_BRIGHT);
4557 			// hud_set_bright_color();
4558 		} else {
4559 			hud_set_gauge_color(HUD_WEAPONS_GAUGE, HUD_C_DIM);
4560 			// hud_set_dim_color();
4561 		}
4562 	}
4563 }
4564 
4565 /**
4566  * @brief Check if targeting is possible based on sensors strength
4567  */
hud_sensors_ok(ship * sp,int show_msg)4568 int hud_sensors_ok(ship *sp, int show_msg)
4569 {
4570 	float	sensors_str;
4571 
4572 	// If playing on lowest skill level, sensors don't affect targeting
4573 	// If dead, still allow player to target, despite any subsystem damage
4574 	// If i'm a multiplayer observer, allow me to target
4575 	if ( (Game_skill_level == 0) || (Game_mode & GM_DEAD) || ((Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_OBSERVER)) ) {
4576 		return 1;
4577 	}
4578 
4579 	// if the ship is currently being affected by EMP
4580 	if(emp_active_local()){
4581 		return 0;
4582 	}
4583 
4584 	// ensure targeting functions are not disabled through damage
4585 	sensors_str = ship_get_subsystem_strength( sp, SUBSYSTEM_SENSORS );
4586 	if ( (sensors_str < MIN_SENSOR_STR_TO_TARGET) || (ship_subsys_disrupted(sp, SUBSYSTEM_SENSORS)) ) {
4587 		if ( show_msg ) {
4588 			HUD_sourced_printf(HUD_SOURCE_HIDDEN, "%s", XSTR( "Targeting is disabled due to sensors damage", 330));
4589 			snd_play(gamesnd_get_game_sound(GameSounds::TARGET_FAIL));
4590 		}
4591 		return 0;
4592 	} else {
4593 		return 1;
4594 	}
4595 }
4596 
hud_communications_state(ship * sp,bool for_death_scream)4597 int hud_communications_state(ship *sp, bool for_death_scream)
4598 {
4599 	float str;
4600 
4601 	// If playing on the lowest skill level, communications always ok
4602 	// If dead, still allow player to communicate, despite any subsystem damage
4603 	if ( Game_skill_level == 0 || (Game_mode & GM_DEAD) ) {
4604 		return COMM_OK;
4605 	}
4606 
4607 	// Goober5000 - if the ship is the player, and he's dying, return OK (so laments can be played)
4608 	if ((sp == Player_ship) && (sp->flags[Ship::Ship_Flags::Dying]))
4609 		return COMM_OK;
4610 
4611 	str = ship_get_subsystem_strength( sp, SUBSYSTEM_COMMUNICATION, for_death_scream );
4612 
4613 	if ( (str <= 0.01) || ship_subsys_disrupted(sp, SUBSYSTEM_COMMUNICATION) ) {
4614 		return COMM_DESTROYED;
4615 	} else if ( str < MIN_COMM_STR_TO_MESSAGE ) {
4616 		return COMM_DAMAGED;
4617 	}
4618 
4619 	// Goober5000 - check for scrambled communications
4620 	if (emp_active_local() || sp->flags[Ship::Ship_Flags::Scramble_messages])
4621 		return COMM_SCRAMBLED;
4622 
4623 	return COMM_OK;
4624 }
4625 
4626 // target the next or previous hostile/friendly ship
hud_target_next_list(int hostile,int next_flag,int team_mask,int attacked_objnum,int play_fail_snd,int filter,int get_closest_turret_attacking_player)4627 void hud_target_next_list(int hostile, int next_flag, int team_mask, int attacked_objnum, int play_fail_snd, int filter, int get_closest_turret_attacking_player)
4628 {
4629 	int		timestamp_val, valid_team_mask;
4630 
4631 	if ( hostile ) {
4632 		timestamp_val = Tl_hostile_reset_timestamp;
4633 		Tl_hostile_reset_timestamp = timestamp(TL_RESET);
4634 		if ( team_mask == -1 ) {
4635 			valid_team_mask = iff_get_attackee_mask(Player_ship->team);
4636 		} else {
4637 			valid_team_mask = team_mask;
4638 		}
4639 	} else {
4640 		// everyone hates a traitor including other traitors so the friendly target option shouldn't work for them
4641 		if (Player_ship->team == Iff_traitor) {
4642 			snd_play( gamesnd_get_game_sound(GameSounds::TARGET_FAIL), 0.0f );
4643 			return;
4644 		}
4645 
4646 		timestamp_val = Tl_friendly_reset_timestamp;
4647 		Tl_friendly_reset_timestamp = timestamp(TL_RESET);
4648 		valid_team_mask = iff_get_mask(Player_ship->team);
4649 	}
4650 
4651 	// If no target is selected, then simply target the closest ship
4652 	if ( Player_ai->target_objnum == -1 || timestamp_elapsed(timestamp_val) ) {
4653 		hud_target_closest(valid_team_mask, attacked_objnum, play_fail_snd, filter, get_closest_turret_attacking_player);
4654 		return;
4655 	}
4656 
4657 	object *nearest_object = select_next_target_by_distance((next_flag != 0), valid_team_mask, attacked_objnum);
4658 
4659 	if (nearest_object != NULL) {
4660 		// set new target
4661 		set_target_objnum( Player_ai, OBJ_INDEX(nearest_object) );
4662 		hud_shield_hit_reset(nearest_object);
4663 
4664 		// maybe set new turret subsystem
4665 		hud_maybe_set_sorted_turret_subsys(&Ships[nearest_object->instance]);
4666 		hud_restore_subsystem_target(&Ships[nearest_object->instance]);
4667 	}
4668 	else {
4669 		snd_play( gamesnd_get_game_sound(GameSounds::TARGET_FAIL), 0.0f );
4670 	}
4671 }
4672 
HudGaugeAutoTarget()4673 HudGaugeAutoTarget::HudGaugeAutoTarget():
4674 HudGauge(HUD_OBJECT_AUTO_TARGET, HUD_AUTO_TARGET, false, false, (VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY | VM_OTHER_SHIP), 255, 255, 255)
4675 {
4676 }
4677 
initAutoTextOffsets(int x,int y)4678 void HudGaugeAutoTarget::initAutoTextOffsets(int x, int y)
4679 {
4680 	Auto_text_offsets[0] = x;
4681 	Auto_text_offsets[1] = y;
4682 }
4683 
initTargetTextOffsets(int x,int y)4684 void HudGaugeAutoTarget::initTargetTextOffsets(int x, int y)
4685 {
4686 	Target_text_offsets[0] = x;
4687 	Target_text_offsets[1] = y;
4688 }
4689 
initBitmaps(char * fname)4690 void HudGaugeAutoTarget::initBitmaps(char *fname)
4691 {
4692 	Toggle_frame.first_frame = bm_load_animation(fname, &Toggle_frame.num_frames);
4693 	if ( Toggle_frame.first_frame < 0 ) {
4694 		Warning(LOCATION,"Cannot load hud ani: %s\n", fname);
4695 	}
4696 }
4697 
initOnColor(int r,int g,int b,int a)4698 void HudGaugeAutoTarget::initOnColor(int r, int g, int b, int a)
4699 {
4700 	if ( r == -1 || g == -1 || b == -1 || a == -1 ) {
4701 		Use_on_color = false;
4702 		gr_init_alphacolor(&On_color, 0, 0, 0, 0);
4703 		return;
4704 	}
4705 
4706 	Use_on_color = true;
4707 	gr_init_alphacolor(&On_color, r, g, b, a);
4708 }
4709 
initOffColor(int r,int g,int b,int a)4710 void HudGaugeAutoTarget::initOffColor(int r, int g, int b, int a)
4711 {
4712 	if ( r == -1 || g == -1 || b == -1 || a == -1 ) {
4713 		Use_off_color = false;
4714 		gr_init_alphacolor(&Off_color, 0, 0, 0, 0);
4715 		return;
4716 	}
4717 
4718 	Use_off_color = true;
4719 	gr_init_alphacolor(&Off_color, r, g, b, a);
4720 }
4721 
render(float)4722 void HudGaugeAutoTarget::render(float  /*frametime*/)
4723 {
4724 	if (Player_ship->flags[Ship::Ship_Flags::Primitive_sensors])
4725 		return;
4726 
4727 	int frame_offset;
4728 
4729 	if ( Players[Player_num].flags & PLAYER_FLAGS_AUTO_TARGETING ) {
4730 		frame_offset = 1;
4731 	} else {
4732 		frame_offset = 0;
4733 	}
4734 
4735 	// draw the box background
4736 	setGaugeColor();
4737 	renderBitmap(Toggle_frame.first_frame+frame_offset, position[0], position[1]);
4738 
4739 	// draw the text on top
4740 	if (frame_offset == 1) {
4741 		//static color text_color;
4742 		//gr_init_alphacolor(&text_color, 0, 0, 0, Toggle_text_alpha);
4743 		if ( Use_on_color ) {
4744 			gr_set_color_fast(&On_color);
4745 		}
4746 	} else if ( Use_off_color ) {
4747 		gr_set_color_fast(&Off_color);
4748 	}
4749 
4750 	renderString(position[0] + Auto_text_offsets[0], position[1] + Auto_text_offsets[1], XSTR("auto", 1463));
4751 	renderString(position[0] + Target_text_offsets[0], position[1] + Target_text_offsets[1], XSTR("target", 1465));
4752 }
4753 
pageIn()4754 void HudGaugeAutoTarget::pageIn()
4755 {
4756 	bm_page_in_aabitmap(Toggle_frame.first_frame, Toggle_frame.num_frames);
4757 }
4758 
HudGaugeAutoSpeed()4759 HudGaugeAutoSpeed::HudGaugeAutoSpeed():
4760 HudGauge(HUD_OBJECT_AUTO_SPEED, HUD_AUTO_SPEED, false, false, (VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY | VM_OTHER_SHIP), 255, 255, 255)
4761 {
4762 }
4763 
initAutoTextOffsets(int x,int y)4764 void HudGaugeAutoSpeed::initAutoTextOffsets(int x, int y)
4765 {
4766 	Auto_text_offsets[0] = x;
4767 	Auto_text_offsets[1] = y;
4768 }
4769 
initSpeedTextOffsets(int x,int y)4770 void HudGaugeAutoSpeed::initSpeedTextOffsets(int x, int y)
4771 {
4772 	Speed_text_offsets[0] = x;
4773 	Speed_text_offsets[1] = y;
4774 }
4775 
initBitmaps(char * fname)4776 void HudGaugeAutoSpeed::initBitmaps(char *fname)
4777 {
4778 	Toggle_frame.first_frame = bm_load_animation(fname, &Toggle_frame.num_frames);
4779 	if ( Toggle_frame.first_frame < 0 ) {
4780 		Warning(LOCATION,"Cannot load hud ani: %s\n", fname);
4781 	}
4782 }
4783 
initOnColor(int r,int g,int b,int a)4784 void HudGaugeAutoSpeed::initOnColor(int r, int g, int b, int a)
4785 {
4786 	if ( r == -1 || g == -1 || b == -1 || a == -1 ) {
4787 		Use_on_color = false;
4788 		gr_init_alphacolor(&On_color, 0, 0, 0, 0);
4789 		return;
4790 	}
4791 
4792 	Use_on_color = true;
4793 	gr_init_alphacolor(&On_color, r, g, b, a);
4794 }
4795 
initOffColor(int r,int g,int b,int a)4796 void HudGaugeAutoSpeed::initOffColor(int r, int g, int b, int a)
4797 {
4798 	if ( r == -1 || g == -1 || b == -1 || a == -1 ) {
4799 		Use_off_color = false;
4800 		gr_init_alphacolor(&Off_color, 0, 0, 0, 0);
4801 		return;
4802 	}
4803 
4804 	Use_off_color = true;
4805 	gr_init_alphacolor(&Off_color, r, g, b, a);
4806 }
4807 
render(float)4808 void HudGaugeAutoSpeed::render(float  /*frametime*/)
4809 {
4810 	if (Player_ship->flags[Ship::Ship_Flags::Primitive_sensors])
4811 		return;
4812 
4813 	int frame_offset;
4814 
4815 	if ( Players[Player_num].flags & PLAYER_FLAGS_AUTO_MATCH_SPEED ) {
4816 		frame_offset = 3;
4817 	} else {
4818 		frame_offset = 2;
4819 	}
4820 
4821 	setGaugeColor();
4822 
4823 	renderBitmap(Toggle_frame.first_frame+frame_offset, position[0], position[1]);
4824 
4825 	// draw the text on top
4826 	if (frame_offset == 3) {
4827 		//static color text_color;
4828 		//gr_init_alphacolor(&text_color, 0, 0, 0, Toggle_text_alpha);
4829 		if ( Use_on_color ) {
4830 			gr_set_color_fast(&On_color);
4831 		}
4832 	} else if ( Use_off_color ) {
4833 		gr_set_color_fast(&Off_color);
4834 	}
4835 	renderString(position[0] + Auto_text_offsets[0], position[1] + Auto_text_offsets[1], XSTR("auto", 1463));
4836 	renderString(position[0] + Speed_text_offsets[0], position[1] + Speed_text_offsets[1], XSTR("speed", 1464));
4837 }
4838 
pageIn()4839 void HudGaugeAutoSpeed::pageIn()
4840 {
4841 	bm_page_in_aabitmap(Toggle_frame.first_frame, Toggle_frame.num_frames);
4842 }
4843 
4844 // Set the player target to the closest friendly repair ship
4845 // input:	goal_objnum	=>	Try to find repair ship where aip->goal_objnum matches this
4846 // output:	1	=>	A repair ship was targeted
4847 //				0	=>	No targeting change
hud_target_closest_repair_ship(int goal_objnum)4848 int hud_target_closest_repair_ship(int goal_objnum)
4849 {
4850 	object	*A;
4851 	object	*nearest_obj=&obj_used_list;
4852 	ship		*shipp;
4853 	ship_obj	*so;
4854 	float		min_distance=1e20f;
4855 	float		new_distance=0.0f;
4856 	int		rval=0;
4857 
4858 	for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
4859 		A = &Objects[so->objnum];
4860 		shipp = &Ships[A->instance];	// get a pointer to the ship information
4861 
4862 		// ignore all ships that aren't repair ships
4863 		if ( !(Ship_info[shipp->ship_info_index].flags[Ship::Info_Flags::Support]) ) {
4864 			continue;
4865 		}
4866 
4867 		if ( (A == Player_obj) || (should_be_ignored(shipp)) )
4868 			continue;
4869 
4870 		// only consider friendly ships
4871 		if ( !(Player_ship->team == shipp->team)) {
4872 			continue;
4873 		}
4874 
4875 		if(hud_target_invalid_awacs(A)){
4876 			continue;
4877 		}
4878 
4879 		if ( goal_objnum >= 0 ) {
4880 			if ( Ai_info[shipp->ai_index].goal_objnum != goal_objnum ) {
4881 				continue;
4882 			}
4883 		}
4884 
4885 		new_distance = hud_find_target_distance(A,Player_obj);
4886 
4887 		if (new_distance <= min_distance) {
4888 			min_distance=new_distance;
4889 			nearest_obj = A;
4890 		}
4891 	}
4892 
4893 	if (nearest_obj != &obj_used_list) {
4894 		set_target_objnum( Player_ai, OBJ_INDEX(nearest_obj) );
4895 		hud_shield_hit_reset(nearest_obj);
4896 		hud_restore_subsystem_target(&Ships[nearest_obj->instance]);
4897 		rval=1;
4898 	}
4899 	else {
4900 		// inform player how to get a support ship
4901 		if ( goal_objnum == -1 ) {
4902 			HUD_sourced_printf(HUD_SOURCE_HIDDEN, "%s", XSTR( "No support ships in area.  Use messaging to call one in.", 332));
4903 		}
4904 		rval=0;
4905 	}
4906 
4907 	return rval;
4908 }
4909 
hud_target_toggle_hidden_from_sensors()4910 void hud_target_toggle_hidden_from_sensors()
4911 {
4912 	if ( Ignore_List[Ship::Ship_Flags::Hidden_from_sensors] ) {
4913         Ignore_List.remove(Ship::Ship_Flags::Hidden_from_sensors);
4914 		HUD_sourced_printf(HUD_SOURCE_HIDDEN, NOX("Target hiding from sensors disabled"));
4915 	} else {
4916         Ignore_List.set(Ship::Ship_Flags::Hidden_from_sensors);
4917 		HUD_sourced_printf(HUD_SOURCE_HIDDEN, NOX("Target hiding from sensors enabled"));
4918 	}
4919 }
4920 
4921 // target the closest uninspected object
hud_target_closest_uninspected_object()4922 void hud_target_closest_uninspected_object()
4923 {
4924 	object	*A, *nearest_obj = NULL;
4925 	ship		*shipp;
4926 	ship_obj	*so;
4927 	float		min_distance = 1e20f;
4928 	float		new_distance = 0.0f;
4929 
4930 	for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
4931 
4932 		A = &Objects[so->objnum];
4933 		shipp = &Ships[A->instance];	// get a pointer to the ship information
4934 
4935 		if ( (A == Player_obj) || (should_be_ignored(shipp)) ){
4936 			continue;
4937 		}
4938 
4939 		if(hud_target_invalid_awacs(A)){
4940 			continue;
4941 		}
4942 
4943 		// ignore all non-cargo carrying craft
4944 		if ( !hud_target_ship_can_be_scanned(shipp) ) {
4945 			continue;
4946 		}
4947 
4948 		new_distance = hud_find_target_distance(A,Player_obj);
4949 
4950 		if (new_distance <= min_distance) {
4951 			min_distance=new_distance;
4952 			nearest_obj = A;
4953 		}
4954 	}
4955 
4956 	if (nearest_obj != NULL) {
4957 		set_target_objnum( Player_ai, OBJ_INDEX(nearest_obj) );
4958 		hud_shield_hit_reset(nearest_obj);
4959 		hud_restore_subsystem_target(&Ships[nearest_obj->instance]);
4960 	}
4961 	else {
4962 		snd_play( gamesnd_get_game_sound(GameSounds::TARGET_FAIL) );
4963 	}
4964 }
4965 
4966 // target the next or previous uninspected/unscanned object
hud_target_uninspected_object(int next_flag)4967 void hud_target_uninspected_object(int next_flag)
4968 {
4969 	object	*A, *min_obj, *max_obj, *nearest_obj;
4970 	ship		*shipp;
4971 	ship_obj	*so;
4972 	float		cur_dist, min_dist, max_dist, new_dist, nearest_dist, diff;
4973 
4974 	// If no target is selected, then simply target the closest uninspected cargo
4975 	if ( Player_ai->target_objnum == -1 || timestamp_elapsed(Target_next_uninspected_object_timestamp) ) {
4976 		Target_next_uninspected_object_timestamp = timestamp(TL_RESET);
4977 		hud_target_closest_uninspected_object();
4978 		return;
4979 	}
4980 
4981 	Target_next_uninspected_object_timestamp = timestamp(TL_RESET);
4982 
4983 	cur_dist = hud_find_target_distance(&Objects[Player_ai->target_objnum], Player_obj);
4984 
4985 	min_obj = max_obj = nearest_obj = NULL;
4986 	min_dist = 1e20f;
4987 	max_dist = 0.0f;
4988 	if ( next_flag ) {
4989 		nearest_dist = 1e20f;
4990 	} else {
4991 		nearest_dist = 0.0f;
4992 	}
4993 
4994 	for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
4995 		A = &Objects[so->objnum];
4996 		shipp = &Ships[A->instance];	// get a pointer to the ship information
4997 
4998 		if ( (A == Player_obj) || (should_be_ignored(shipp)) )
4999 			continue;
5000 
5001 		// ignore all non-cargo carrying craft
5002 		if ( !hud_target_ship_can_be_scanned(shipp) ) {
5003 			continue;
5004 		}
5005 
5006 		// don't use object if it is already a target
5007 		if ( OBJ_INDEX(A) == Player_ai->target_objnum ) {
5008 			continue;
5009 		}
5010 
5011 		if(hud_target_invalid_awacs(A)){
5012 			continue;
5013 		}
5014 
5015 		new_dist = hud_find_target_distance(A, Player_obj);
5016 
5017 		if (new_dist <= min_dist) {
5018 			min_dist = new_dist;
5019 			min_obj = A;
5020 		}
5021 
5022 		if (new_dist >= max_dist) {
5023 			max_dist = new_dist;
5024 			max_obj = A;
5025 		}
5026 
5027 		if ( next_flag ) {
5028 			diff = new_dist - cur_dist;
5029 			if ( diff > 0 ) {
5030 				if ( diff < ( nearest_dist - cur_dist ) ) {
5031 					nearest_dist = new_dist;
5032 					nearest_obj = A;
5033 				}
5034 			}
5035 		} else {
5036 			diff = cur_dist - new_dist;
5037 			if ( diff > 0 ) {
5038 				if ( diff < ( cur_dist - nearest_dist ) ) {
5039 					nearest_dist = new_dist;
5040 					nearest_obj = A;
5041 				}
5042 			}
5043 		}
5044 	}
5045 
5046 	if ( nearest_obj == NULL ) {
5047 
5048 		if ( next_flag ) {
5049 			if ( min_obj != NULL ) {
5050 				nearest_obj = min_obj;
5051 			}
5052 		} else {
5053 			if ( max_obj != NULL ) {
5054 				nearest_obj = max_obj;
5055 			}
5056 		}
5057 	}
5058 
5059 	if (nearest_obj != NULL) {
5060 		set_target_objnum( Player_ai, OBJ_INDEX(nearest_obj) );
5061 		hud_shield_hit_reset(nearest_obj);
5062 		hud_restore_subsystem_target(&Ships[nearest_obj->instance]);
5063 	}
5064 	else {
5065 		snd_play( gamesnd_get_game_sound(GameSounds::TARGET_FAIL) );
5066 	}
5067 }
5068 
5069 // ----------------------------------------------------------------
5070 //
5071 // Target Last Transmission Sender code START
5072 //
5073 // ----------------------------------------------------------------
5074 
5075 typedef struct transmit_target
5076 {
5077 	int objnum;
5078 	int objsig;
5079 } transmit_target;
5080 
5081 static int Transmit_target_next_slot = 0;
5082 static int Transmit_target_current_slot = -1;
5083 static int Transmit_target_reset_timer = timestamp(0);
5084 
5085 #define MAX_TRANSMIT_TARGETS	10
5086 static transmit_target Transmit_target_list[MAX_TRANSMIT_TARGETS];
5087 
5088 // called once per level to initialize the target last transmission sender list
hud_target_last_transmit_level_init()5089 void hud_target_last_transmit_level_init()
5090 {
5091 	int i;
5092 
5093 	for ( i = 0; i < MAX_TRANSMIT_TARGETS; i++ ) {
5094 		Transmit_target_list[i].objnum = -1;
5095 		Transmit_target_list[i].objsig = -1;
5096 	}
5097 
5098 	Transmit_target_next_slot = 0;
5099 	Transmit_target_current_slot = 0;
5100 	Transmit_target_reset_timer = timestamp(0);
5101 }
5102 
5103 // internal function only.. used to find index for last recorded ship transmission
hud_target_last_transmit_newest()5104 int hud_target_last_transmit_newest()
5105 {
5106 	int latest_slot;
5107 
5108 	latest_slot = Transmit_target_next_slot - 1;
5109 	if ( latest_slot < 0 ) {
5110 		latest_slot = MAX_TRANSMIT_TARGETS - 1;
5111 	}
5112 
5113 	return latest_slot;
5114 }
5115 
5116 // called externally to set the player target to the last ship which sent a transmission to the player
hud_target_last_transmit()5117 void hud_target_last_transmit()
5118 {
5119 	int i;
5120 
5121 	if ( Transmit_target_current_slot < 0 ) {
5122 		Transmit_target_current_slot = hud_target_last_transmit_newest();
5123 	}
5124 
5125 	// If timed out, then simply target the last ship to transmit
5126 	if ( timestamp_elapsed(Transmit_target_reset_timer) ) {
5127 		Transmit_target_current_slot = hud_target_last_transmit_newest();
5128 	}
5129 
5130 	Transmit_target_reset_timer = timestamp(TL_RESET);
5131 
5132 	int play_fail_sound = 1;
5133 	int transmit_index = Transmit_target_current_slot;
5134 	Assert(transmit_index >= 0);
5135 	for ( i = 0; i < MAX_TRANSMIT_TARGETS; i++ ) {
5136 		if ( Transmit_target_list[transmit_index].objnum >= 0 ) {
5137 			int transmit_objnum = Transmit_target_list[transmit_index].objnum;
5138 
5139 			if ( Player_ai->target_objnum == transmit_objnum ) {
5140 				play_fail_sound = 0;
5141 			} else {
5142 				if ( Transmit_target_list[transmit_index].objsig == Objects[Transmit_target_list[transmit_index].objnum].signature ) {
5143 					if ( !(should_be_ignored(&Ships[Objects[transmit_objnum].instance])) ) {
5144 						Transmit_target_current_slot = transmit_index-1;
5145 						if ( Transmit_target_current_slot < 0 ) {
5146 							Transmit_target_current_slot = MAX_TRANSMIT_TARGETS - 1;
5147 						}
5148 						break;
5149 					}
5150 				}
5151 			}
5152 		}
5153 
5154 		transmit_index--;
5155 		if ( transmit_index < 0 ) {
5156 			transmit_index = MAX_TRANSMIT_TARGETS - 1;
5157 		}
5158 	}
5159 
5160 	if ( i == MAX_TRANSMIT_TARGETS ) {
5161 		if ( play_fail_sound ) {
5162 			snd_play( gamesnd_get_game_sound(GameSounds::TARGET_FAIL) );
5163 		}
5164 		Transmit_target_current_slot = -1;
5165 		return;
5166 	}
5167 
5168 	if(hud_target_invalid_awacs(&Objects[Transmit_target_list[transmit_index].objnum])){
5169 		return;
5170 	}
5171 
5172 	// target new ship!
5173 	// Fix bug in targeting due to Alt-Y (target last ship sending transmission).
5174 	// Was just bogus code in the call to hud_restore_subsystem_target(). -- MK, 9/15/99, 1:59 pm.
5175 	int targeted_objnum;
5176 	targeted_objnum = Transmit_target_list[transmit_index].objnum;
5177 	Assert((targeted_objnum >= 0) && (targeted_objnum < MAX_OBJECTS));
5178 
5179 	if ((targeted_objnum >= 0) && (targeted_objnum < MAX_OBJECTS)) {
5180 		set_target_objnum( Player_ai, Transmit_target_list[transmit_index].objnum );
5181 		hud_shield_hit_reset(&Objects[Transmit_target_list[transmit_index].objnum]);
5182 		hud_restore_subsystem_target(&Ships[Objects[Transmit_target_list[transmit_index].objnum].instance]);
5183 	}
5184 }
5185 
5186 // called externally to add a message sender to the list
hud_target_last_transmit_add(int ship_num)5187 void hud_target_last_transmit_add(int ship_num)
5188 {
5189 	object	*ship_objp;
5190 	int		ship_objnum;
5191 
5192 	ship_objnum = Ships[ship_num].objnum;
5193 	Assert(ship_objnum >= 0 && ship_objnum < MAX_OBJECTS);
5194 	ship_objp = &Objects[ship_objnum];
5195 	Assert(ship_objp->type == OBJ_SHIP);
5196 
5197 	// don't add ourselves to the list
5198 	if (Player_obj == ship_objp) {
5199 		return;
5200 	}
5201 
5202 	Transmit_target_list[Transmit_target_next_slot].objnum = ship_objnum;
5203 	Transmit_target_list[Transmit_target_next_slot].objsig = ship_objp->signature;
5204 	Transmit_target_next_slot++;
5205 	if ( Transmit_target_next_slot >= MAX_TRANSMIT_TARGETS ) {
5206 		Transmit_target_next_slot = 0;
5207 	}
5208 }
5209 
5210 // target a random ship (useful for EMP stuff)
hud_target_random_ship()5211 void hud_target_random_ship()
5212 {
5213 	int shipnum;
5214 	int objnum;
5215 
5216 	shipnum = ship_get_random_targetable_ship();
5217 	if((shipnum < 0) || (Ships[shipnum].objnum < 0)){
5218 		return;
5219 	}
5220 	objnum = Ships[shipnum].objnum;
5221 
5222 	if((objnum >= 0) && (Player_ai != NULL) && !hud_target_invalid_awacs(&Objects[objnum])){
5223 		// never target yourself
5224 		if(objnum == OBJ_INDEX(Player_obj)){
5225 			set_target_objnum(Player_ai, -1);
5226 		} else {
5227 			set_target_objnum(Player_ai, objnum);
5228 			hud_shield_hit_reset(&Objects[objnum]);
5229 		}
5230 	}
5231 }
5232 
5233 // ----------------------------------------------------------------
5234 //
5235 // Target Last Transmission Sender code END
5236 //
5237 // ----------------------------------------------------------------
5238 
hudtarget_page_in()5239 void hudtarget_page_in()
5240 {
5241 	int i;
5242 
5243 	for ( i = 0; i < NUM_WEAPON_GAUGES; i++ ) {
5244 		bm_page_in_aabitmap( Weapon_gauges[ballistic_hud_index][i].first_frame, Weapon_gauges[ballistic_hud_index][i].num_frames);
5245 	}
5246 	bm_page_in_aabitmap( New_weapon.first_frame, New_weapon.num_frames );
5247 
5248 	for (auto &wi : Weapon_info)
5249 	{
5250 		if (strlen(wi.hud_filename))
5251 		{
5252 			wi.hud_image_index = bm_load(wi.hud_filename);
5253 		}
5254 	}
5255 }
5256 
hud_stuff_ship_name(char * ship_name_text,const ship * shipp)5257 void hud_stuff_ship_name(char *ship_name_text, const ship *shipp)
5258 {
5259 	strcpy(ship_name_text, hud_get_ship_name(shipp).c_str());
5260 }
5261 
hud_get_ship_name(const ship * shipp)5262 SCP_string hud_get_ship_name(const ship *shipp)
5263 {
5264 	// print ship name
5265 	if ( ((Iff_info[shipp->team].flags & IFFF_WING_NAME_HIDDEN) && (shipp->wingnum != -1)) || (shipp->flags[Ship::Ship_Flags::Hide_ship_name]) ) {
5266 		return "";
5267 	} else if (!Disable_built_in_translations) {
5268 		// handle translation
5269 		if (Lcl_gr) {
5270 			char buf[128];
5271 			strcpy(buf, shipp->get_display_name());
5272 			lcl_translate_targetbox_name_gr(buf);
5273 			return buf;
5274 		} else if (Lcl_pl) {
5275 			char buf[128];
5276 			strcpy(buf, shipp->get_display_name());
5277 			lcl_translate_targetbox_name_pl(buf);
5278 			return buf;
5279 		}
5280 	}
5281 
5282 	return shipp->get_display_name();
5283 }
5284 
hud_stuff_ship_callsign(char * ship_callsign_text,const ship * shipp)5285 void hud_stuff_ship_callsign(char *ship_callsign_text, const ship *shipp)
5286 {
5287 	strcpy(ship_callsign_text, hud_get_ship_callsign(shipp).c_str());
5288 }
5289 
5290 extern char Fred_callsigns[MAX_SHIPS][NAME_LENGTH+1];
hud_get_ship_callsign(const ship * shipp)5291 SCP_string hud_get_ship_callsign(const ship *shipp)
5292 {
5293 	// handle multiplayer callsign
5294 	if (Game_mode & GM_MULTIPLAYER) {
5295 		// get a player num from the object, then get a callsign from the player structure.
5296 		int pn = multi_find_player_by_object( &Objects[shipp->objnum] );
5297 
5298 		if (pn >= 0) {
5299 			return Net_players[pn].m_player->short_callsign;
5300 		}
5301 	}
5302 
5303 	SCP_string ship_callsign_text;
5304 
5305 	// try to get callsign
5306 	if (Fred_running) {
5307 		ship_callsign_text = Fred_callsigns[shipp-Ships];
5308 	} else {
5309 		if (shipp->callsign_index >= 0) {
5310 			ship_callsign_text = mission_parse_lookup_callsign_index(shipp->callsign_index);
5311 		}
5312 	}
5313 
5314 	if (!Disable_built_in_translations) {
5315 		// handle translation
5316 		if (Lcl_gr) {
5317 			char buf[128];
5318 			strcpy(buf, ship_callsign_text.c_str());
5319 			lcl_translate_targetbox_name_gr(buf);
5320 			return buf;
5321 		} else if (Lcl_pl) {
5322 			char buf[128];
5323 			strcpy(buf, ship_callsign_text.c_str());
5324 			lcl_translate_targetbox_name_pl(buf);
5325 			return buf;
5326 		}
5327 	}
5328 
5329 	return ship_callsign_text;
5330 }
5331 
hud_stuff_ship_class(char * ship_class_text,const ship * shipp)5332 void hud_stuff_ship_class(char *ship_class_text, const ship *shipp)
5333 {
5334 	strcpy(ship_class_text, hud_get_ship_class(shipp).c_str());
5335 }
5336 
5337 extern char Fred_alt_names[MAX_SHIPS][NAME_LENGTH + 1];
hud_get_ship_class(const ship * shipp)5338 SCP_string hud_get_ship_class(const ship *shipp)
5339 {
5340 	SCP_string ship_class_text;
5341 
5342 	// try to get alt name
5343 	if (Fred_running) {
5344 		ship_class_text = Fred_alt_names[shipp-Ships];
5345 
5346 		if (ship_class_text.empty()) {
5347 			ship_class_text = Ship_info[shipp->ship_info_index].get_display_name();
5348 		}
5349 	} else {
5350 		if (shipp->alt_type_index >= 0) {
5351 			ship_class_text = mission_parse_lookup_alt_index(shipp->alt_type_index);
5352 		} else {
5353 			ship_class_text = Ship_info[shipp->ship_info_index].get_display_name();
5354 		}
5355 	}
5356 
5357 	if (!Disable_built_in_translations) {
5358 		// handle translation
5359 		if (Lcl_gr) {
5360 			char buf[128];
5361 			strcpy(buf, ship_class_text.c_str());
5362 			lcl_translate_targetbox_name_gr(buf);
5363 			return buf;
5364 		} else if (Lcl_pl) {
5365 			char buf[128];
5366 			strcpy(buf, ship_class_text.c_str());
5367 			lcl_translate_targetbox_name_pl(buf);
5368 			return buf;
5369 		}
5370 	}
5371 
5372 	return ship_class_text;
5373 }
5374 
HudGaugeCmeasures()5375 HudGaugeCmeasures::HudGaugeCmeasures():
5376 HudGauge(HUD_OBJECT_CMEASURES, HUD_CMEASURE_GAUGE, false, false, VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY | VM_OTHER_SHIP, 255, 255, 255)
5377 {
5378 }
5379 
initCountTextOffsets(int x,int y)5380 void HudGaugeCmeasures::initCountTextOffsets(int x, int y)
5381 {
5382 	Cm_text_offsets[0] = x;
5383 	Cm_text_offsets[1] = y;
5384 }
5385 
initCountValueOffsets(int x,int y)5386 void HudGaugeCmeasures::initCountValueOffsets(int x, int y)
5387 {
5388 	Cm_text_val_offsets[0] = x;
5389 	Cm_text_val_offsets[1] = y;
5390 }
5391 
initBitmaps(char * fname)5392 void HudGaugeCmeasures::initBitmaps(char *fname)
5393 {
5394 	Cmeasure_gauge.first_frame = bm_load_animation(fname, &Cmeasure_gauge.num_frames);
5395 	if ( Cmeasure_gauge.first_frame < 0 ) {
5396 		Warning(LOCATION,"Cannot load hud ani: %s\n", fname);
5397 	}
5398 }
5399 
pageIn()5400 void HudGaugeCmeasures::pageIn()
5401 {
5402 	bm_page_in_aabitmap(Cmeasure_gauge.first_frame, Cmeasure_gauge.num_frames);
5403 }
5404 
render(float)5405 void HudGaugeCmeasures::render(float  /*frametime*/)
5406 {
5407 	if ( Cmeasure_gauge.first_frame == -1) {
5408 		Int3();	// failed to load coutermeasure gauge background
5409 		return;
5410 	}
5411 
5412 	ship_info *sip = &Ship_info[Player_ship->ship_info_index];
5413 	if(sip->cmeasure_max < 0 || sip->cmeasure_type < 0){
5414 		return;
5415 	}
5416 
5417 	// hud_set_default_color();
5418 	setGaugeColor();
5419 
5420 	// blit the background
5421 	renderBitmap(Cmeasure_gauge.first_frame, position[0], position[1]);
5422 
5423 	// blit text
5424 	renderString(position[0] + Cm_text_offsets[0], position[1] + Cm_text_offsets[1], XSTR( "cm.", 327));
5425 	if ( !Player_ship ) {
5426 		Int3();	// player ship doesn't exist?
5427 		return;
5428 	}
5429 	renderPrintf(position[0] + Cm_text_val_offsets[0], position[1] + Cm_text_val_offsets[1], NOX("%02d"), Player_ship->cmeasure_count);
5430 }
5431 
HudGaugeAfterburner()5432 HudGaugeAfterburner::HudGaugeAfterburner():
5433 HudGauge(HUD_OBJECT_AFTERBURNER, HUD_AFTERBURNER_ENERGY, true, false, (VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY | VM_OTHER_SHIP), 255, 255, 255)
5434 {
5435 }
5436 
initEnergyHeight(int h)5437 void HudGaugeAfterburner::initEnergyHeight(int h)
5438 {
5439 	Energy_h = h;
5440 }
5441 
initBitmaps(char * fname)5442 void HudGaugeAfterburner::initBitmaps(char *fname)
5443 {
5444 	Energy_bar.first_frame = bm_load_animation(fname, &Energy_bar.num_frames);
5445 
5446 	if ( Energy_bar.first_frame < 0 ) {
5447 		Warning(LOCATION,"Cannot load hud ani: %s\n", fname);
5448 	}
5449 }
5450 
5451 //	Render the HUD afterburner energy gauge
render(float)5452 void HudGaugeAfterburner::render(float  /*frametime*/)
5453 {
5454 	float percent_left;
5455 	int	clip_h,w,h;
5456 
5457 	if ( Energy_bar.first_frame == -1 ){
5458 		return;
5459 	}
5460 
5461 	Assert(Player_ship);
5462 	if ( !(Ship_info[Player_ship->ship_info_index].flags[Ship::Info_Flags::Afterburner]) ) {
5463 		// Goober5000 - instead of drawing an empty burner gauge, don't draw the gauge at all
5464 		return;
5465 	} else {
5466 		percent_left = Player_ship->afterburner_fuel/Ship_info[Player_ship->ship_info_index].afterburner_fuel_capacity;
5467 	}
5468 
5469 	if ( percent_left > 1 ) {
5470 		percent_left = 1.0f;
5471 	}
5472 
5473 	clip_h = (int)std::lround((1.0f - percent_left) * Energy_h);
5474 
5475 	bm_get_info(Energy_bar.first_frame,&w,&h);
5476 
5477 	setGaugeColor();
5478 
5479 	if ( clip_h > 0) {
5480 		renderBitmapEx(Energy_bar.first_frame, position[0], position[1],w,clip_h,0,0);
5481 	}
5482 
5483 	if ( clip_h <= Energy_h ) {
5484 		renderBitmapEx(Energy_bar.first_frame+1, position[0], position[1] + clip_h,w,h-clip_h,0,clip_h);
5485 	}
5486 }
5487 
pageIn()5488 void HudGaugeAfterburner::pageIn()
5489 {
5490 	bm_page_in_aabitmap( Energy_bar.first_frame, Energy_bar.num_frames);
5491 }
5492 
HudGaugeWeaponEnergy()5493 HudGaugeWeaponEnergy::HudGaugeWeaponEnergy():
5494 HudGauge(HUD_OBJECT_WEAPON_ENERGY, HUD_WEAPONS_ENERGY, true, false, (VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY | VM_OTHER_SHIP), 255, 255, 255)
5495 {
5496 }
5497 
initTextOffsets(int x,int y)5498 void HudGaugeWeaponEnergy::initTextOffsets(int x, int y)
5499 {
5500 	Wenergy_text_offsets[0] = x;
5501 	Wenergy_text_offsets[1] = y;
5502 }
5503 
initEnergyHeight(int h)5504 void HudGaugeWeaponEnergy::initEnergyHeight(int h)
5505 {
5506 	Wenergy_h = h;
5507 }
5508 
initAlignments(HudAlignment text_align,HudAlignment armed_align)5509 void HudGaugeWeaponEnergy::initAlignments(HudAlignment text_align, HudAlignment armed_align)
5510 {
5511 	Text_alignment = text_align;
5512 	Armed_alignment = armed_align;
5513 }
5514 
initArmedOffsets(int x,int y,int h,bool show)5515 void HudGaugeWeaponEnergy::initArmedOffsets(int x, int y, int h, bool show)
5516 {
5517 	Armed_name_offsets[0] = x;
5518 	Armed_name_offsets[1] = y;
5519 	Show_armed = show;
5520 	Armed_name_h = h;
5521 }
5522 
initAlwaysShowText(bool show_text)5523 void HudGaugeWeaponEnergy::initAlwaysShowText(bool show_text)
5524 {
5525 	Always_show_text = show_text;
5526 }
5527 
initMoveText(bool move_text)5528 void HudGaugeWeaponEnergy::initMoveText(bool move_text)
5529 {
5530 	Moving_text = move_text;
5531 }
5532 
initShowBallistics(bool show_ballistics)5533 void HudGaugeWeaponEnergy::initShowBallistics(bool show_ballistics)
5534 {
5535 	Show_ballistic = show_ballistics;
5536 }
5537 
initBitmaps(char * fname)5538 void HudGaugeWeaponEnergy::initBitmaps(char *fname)
5539 {
5540 	Energy_bar.first_frame = bm_load_animation(fname, &Energy_bar.num_frames);
5541 	if ( Energy_bar.first_frame < 0 ) {
5542 		Warning(LOCATION,"Cannot load hud ani: %s\n", fname);
5543 	} else {
5544 		if (Energy_bar.num_frames != 4) {
5545 			error_display(0, "The animation '%s' has %d frames but the energy bar requires 4 frames in the animation! Ignoring animation...",
5546 						  fname, Energy_bar.num_frames);
5547 			// Free the bitmap slot and set the frame handle to -1 so that it's ignored when rendering this gauge
5548 			bm_release(Energy_bar.first_frame);
5549 
5550 			Energy_bar.first_frame = -1;
5551 			Energy_bar.num_frames = 0;
5552 		}
5553 	}
5554 }
5555 
pageIn()5556 void HudGaugeWeaponEnergy::pageIn()
5557 {
5558 	bm_page_in_aabitmap( Energy_bar.first_frame, Energy_bar.num_frames);
5559 }
5560 
render(float)5561 void HudGaugeWeaponEnergy::render(float  /*frametime*/)
5562 {
5563 	int x;
5564 	bool use_new_gauge = false;
5565 
5566 	// Goober5000 - only check for the new gauge in case of command line + a ballistic-capable ship
5567 	if (Cmdline_ballistic_gauge)
5568 	{
5569 		for (x = 0; x < Player_ship->weapons.num_primary_banks; ++x)
5570 		{
5571 			if (Weapon_info[Player_ship->weapons.primary_bank_weapons[x]].wi_flags[Weapon::Info_Flags::Ballistic])
5572 			{
5573 				use_new_gauge = true;
5574 				break;
5575 			}
5576 		}
5577 	}
5578 
5579 	if(use_new_gauge)
5580 	{
5581 		int currentx, currenty, line_height;
5582 		int y;
5583 		int max_w = 100;
5584 		float remaining;
5585 		currentx = position[0] + 10;
5586 		currenty = position[1];
5587 		line_height = gr_get_font_height() + 1;
5588 		if(gr_screen.max_w_unscaled == 640) {
5589 			max_w = 60;
5590 		}
5591 
5592 		//*****ENERGY GAUGE
5593 		if(Weapon_info[Player_ship->weapons.current_primary_bank].energy_consumed > 0.0f)
5594 			setGaugeColor(HUD_C_BRIGHT);
5595 
5596 		//Draw name
5597 		renderString(currentx, currenty, XSTR("Energy", 1616));
5598 		currenty += line_height;
5599 
5600 		//Draw background
5601 		setGaugeColor(HUD_C_DIM);
5602 		renderRect(currentx, currenty, max_w, 10);
5603 
5604 		//Draw gauge bar
5605 		setGaugeColor(HUD_C_NORMAL);
5606 		remaining = max_w * ((float)Player_ship->weapon_energy/(float)Ship_info[Player_ship->ship_info_index].max_weapon_reserve);
5607 		if(remaining > 0)
5608 		{
5609 			setGaugeColor(HUD_C_BRIGHT);
5610 			for(y = 0; y < 10; y++) {
5611 				renderGradientLine(currentx, currenty + y, currentx + fl2i(remaining), currenty + y);
5612 			}
5613 		}
5614 		currenty += 12;
5615 
5616 		char shortened_name[NAME_LENGTH];
5617 		//*****BALLISTIC GAUGES
5618 		for(x = 0; x < Player_ship->weapons.num_primary_banks; x++)
5619 		{
5620 			//Skip all pure-energy weapons
5621 			if(!(Weapon_info[Player_ship->weapons.primary_bank_weapons[x]].wi_flags[Weapon::Info_Flags::Ballistic]))
5622 				continue;
5623 
5624 			//Draw the weapon bright or normal depending if it's active or not.
5625 			if(x == Player_ship->weapons.current_primary_bank || (Player_ship->flags[Ship::Ship_Flags::Primary_linked])) {
5626 				setGaugeColor(HUD_C_BRIGHT);
5627 			} else {
5628 				setGaugeColor(HUD_C_NORMAL);
5629 			}
5630 			if(gr_screen.max_w_unscaled == 640) {
5631 				strcpy_s(shortened_name, Weapon_info[Player_ship->weapons.primary_bank_weapons[x]].get_display_name());
5632 				font::force_fit_string(shortened_name, NAME_LENGTH, 55);
5633 				renderString(currentx, currenty, shortened_name);
5634 			} else {
5635 				renderString(currentx, currenty, Weapon_info[Player_ship->weapons.primary_bank_weapons[x]].get_display_name());
5636 			}
5637 
5638 			//Next 'line'
5639 			currenty += line_height;
5640 
5641 			//Draw the background for the gauge
5642 			setGaugeColor(HUD_C_DIM);
5643 			renderRect(currentx, currenty, max_w, 10);
5644 
5645 			//Reset to normal brightness
5646 			setGaugeColor(HUD_C_NORMAL);
5647 
5648 			//Draw the bar graph
5649 			remaining = (max_w - 4) * ((float) Player_ship->weapons.primary_bank_ammo[x] / (float) Player_ship->weapons.primary_bank_start_ammo[x]);
5650 			if(remaining > 0) {
5651 				renderRect(currentx + 2, currenty + 2, fl2i(remaining), 6);
5652 			}
5653 			//Increment for next 'line'
5654 			currenty += 12;
5655 		}
5656 	}
5657 	else
5658 	{
5659 		float percent_left;
5660 		int ballistic_ammo = 0;
5661 		int max_ballistic_ammo = 0;
5662 		int	clip_h, w, h, i;
5663 		weapon_info *wip;
5664 		ship_weapon *sw;
5665 
5666 		if ( Energy_bar.first_frame == -1 ) {
5667 			return;
5668 		}
5669 
5670 		if ( Player_ship->weapons.num_primary_banks <= 0 ) {
5671 			return;
5672 		}
5673 
5674 		sw = &Player_ship->weapons;
5675 
5676 		// show ballistic ammunition in energy gauge if need be
5677 		if ( Show_ballistic ) {
5678 			if ( Player_ship->flags[Ship::Ship_Flags::Primary_linked] ) {
5679 
5680 				// go through all ballistic primaries and add up their ammunition totals and max capacities
5681 				for ( i = 0; i < sw->num_primary_banks; i++ ) {
5682 
5683 					// skip all pure-energy weapons
5684 					if( ! ( Weapon_info[sw->primary_bank_weapons[i]].wi_flags[Weapon::Info_Flags::Ballistic] ) ) {
5685 						continue;
5686 					}
5687 
5688 					ballistic_ammo += sw->primary_bank_ammo[i];
5689 					max_ballistic_ammo += sw->primary_bank_start_ammo[i];
5690 				}
5691 			} else {
5692 				ballistic_ammo = sw->primary_bank_ammo[sw->current_primary_bank];
5693 				max_ballistic_ammo = sw->primary_bank_start_ammo[sw->current_primary_bank];
5694 			}
5695 
5696 			if (max_ballistic_ammo > 0) {
5697 				percent_left = i2fl(ballistic_ammo) / i2fl(max_ballistic_ammo);
5698 			} else {
5699 				percent_left = 1.0f;
5700 			}
5701 		} else {
5702 			// also leave if no energy can be stored for weapons - Goober5000
5703 			if (!ship_has_energy_weapons(Player_ship))
5704 				return;
5705 
5706 			percent_left = Player_ship->weapon_energy/Ship_info[Player_ship->ship_info_index].max_weapon_reserve;
5707 			if ( percent_left > 1 )
5708 			{
5709 				percent_left = 1.0f;
5710 			}
5711 		}
5712 
5713 		clip_h = (int)std::lround((1.0f - percent_left) * Wenergy_h);
5714 
5715 		if ( percent_left <= 0.3 || Show_ballistic || Always_show_text ) {
5716 			char buf[10];
5717 			int delta_y = 0, delta_x = 0;
5718 
5719 			if ( percent_left < 0.1 ) {
5720 				gr_set_color_fast(&Color_bright_red);
5721 			} else {
5722 				setGaugeColor();
5723 			}
5724 
5725 			if ( Show_ballistic ) {
5726 				sprintf(buf, "%d", ballistic_ammo);
5727 			} else {
5728 				sprintf(buf, XSTR( "%d%%", 326), (int)std::lround(percent_left*100));
5729 			}
5730 
5731 			if ( Moving_text ) {
5732 				delta_y = clip_h;
5733 			}
5734 
5735 			hud_num_make_mono(buf, font_num);
5736 
5737 			if ( Text_alignment == HudAlignment::RIGHT ) {
5738 				gr_get_string_size(&w, &h, buf);
5739 				delta_x = -w;
5740 			}
5741 
5742 			renderString(position[0] + Wenergy_text_offsets[0] + delta_x, position[1] + Wenergy_text_offsets[1] + delta_y, buf);
5743 		}
5744 
5745 		setGaugeColor();
5746 
5747 		// list currently armed primary banks if we have to
5748 		if ( Show_armed ) {
5749 			if ( Player_ship->flags[Ship::Ship_Flags::Primary_linked] ) {
5750 				// show all primary banks
5751 				for ( i = 0; i < Player_ship->weapons.num_primary_banks; i++ ) {
5752 					wip = &Weapon_info[sw->primary_bank_weapons[i]];
5753 					auto weapon_name = wip->get_display_name();
5754 
5755 					if ( Armed_alignment == HudAlignment::RIGHT ) {
5756 						gr_get_string_size(&w, &h, weapon_name);
5757 					} else {
5758 						w = 0;
5759 					}
5760 
5761 					renderString(position[0] + Armed_name_offsets[0] - w, position[1] + Armed_name_offsets[1] + Armed_name_h * i, weapon_name);
5762 				}
5763 			} else {
5764 				// just show the current armed bank
5765 				i = Player_ship->weapons.current_primary_bank;
5766 				wip = &Weapon_info[sw->primary_bank_weapons[i]];
5767 				auto weapon_name = wip->get_display_name();
5768 
5769 				if ( Armed_alignment == HudAlignment::RIGHT ) {
5770 					gr_get_string_size(&w, &h, weapon_name);
5771 				} else {
5772 					w = 0;
5773 				}
5774 
5775 				renderString(position[0] + Armed_name_offsets[0] - w, position[1] + Armed_name_offsets[1], weapon_name);
5776 			}
5777 		}
5778 
5779 		for ( i = 0; i < sw->num_primary_banks; i++ )
5780 		{
5781 			if ( !timestamp_elapsed(Weapon_flash_info.flash_duration[i]) )
5782 			{
5783 				if ( Weapon_flash_info.is_bright & (1<<i) )
5784 				{
5785 					// hud_set_bright_color();
5786 					setGaugeColor(HUD_C_BRIGHT);
5787 					break;
5788 				}
5789 			}
5790 		}
5791 
5792 		bm_get_info(Energy_bar.first_frame+2,&w,&h);
5793 
5794 		if ( clip_h > 0 ) {
5795 			renderBitmapEx(Energy_bar.first_frame+2, position[0], position[1], w,clip_h,0,0);
5796 		}
5797 
5798 		if ( clip_h <= Wenergy_h ) {
5799 			renderBitmapEx(Energy_bar.first_frame+3, position[0], position[1] + clip_h, w,h-clip_h,0,clip_h);
5800 		}
5801 
5802 		// hud_set_default_color();
5803 	}
5804 }
5805 
HudGaugeWeapons()5806 HudGaugeWeapons::HudGaugeWeapons():
5807 HudGauge(HUD_OBJECT_WEAPONS, HUD_WEAPONS_GAUGE, false, false, VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY | VM_OTHER_SHIP, 255, 255, 255)
5808 {
5809 }
5810 
initLinkIcon()5811 void HudGaugeWeapons::initLinkIcon()
5812 {
5813 	ubyte sc = lcl_get_font_index(font_num);
5814 	// default to a '>' if the font has no special chars
5815 	// seems about the closest normal char to the triangle
5816 	if (sc == 0) {
5817 		Weapon_link_icon = ubyte ('>');
5818 	} else {
5819 		Weapon_link_icon = sc + 2;
5820 	}
5821 }
5822 
initTopOffsetX(int x,int x_b)5823 void HudGaugeWeapons::initTopOffsetX(int x, int x_b)
5824 {
5825 	top_offset_x[0] = x;
5826 	top_offset_x[1] = x_b;
5827 }
5828 
initHeaderOffsets(int x,int y,int x_b,int y_b)5829 void HudGaugeWeapons::initHeaderOffsets(int x, int y, int x_b, int y_b)
5830 {
5831 	Weapon_header_offsets[0][0] = x;
5832 	Weapon_header_offsets[0][1] = y;
5833 	Weapon_header_offsets[1][0] = x_b;
5834 	Weapon_header_offsets[1][1] = y_b;
5835 }
5836 
initFrameOffsetX(int x,int x_b)5837 void HudGaugeWeapons::initFrameOffsetX(int x, int x_b)
5838 {
5839 	frame_offset_x[0] = x;
5840 	frame_offset_x[1] = x_b;
5841 }
5842 
initPrimaryWeaponOffsets(int link_x,int name_x,int ammo_x)5843 void HudGaugeWeapons::initPrimaryWeaponOffsets(int link_x, int name_x, int ammo_x)
5844 {
5845 	Weapon_plink_offset_x = link_x;
5846 	Weapon_pname_offset_x = name_x;
5847 	Weapon_pammo_offset_x = ammo_x;
5848 }
5849 
initSecondaryWeaponOffsets(int ammo_x,int name_x,int reload_x,int linked_x,int unlinked_x)5850 void HudGaugeWeapons::initSecondaryWeaponOffsets(int ammo_x, int name_x, int reload_x, int linked_x, int unlinked_x)
5851 {
5852 	Weapon_sammo_offset_x = ammo_x;
5853 	Weapon_sname_offset_x = name_x;
5854 	Weapon_sreload_offset_x = reload_x;
5855 	Weapon_slinked_offset_x = linked_x;
5856 	Weapon_sunlinked_offset_x = unlinked_x;
5857 }
5858 
initStartNameOffsetsY(int p_y,int s_y)5859 void HudGaugeWeapons::initStartNameOffsetsY(int p_y, int s_y)
5860 {
5861 	pname_start_offset_y = p_y;
5862 	sname_start_offset_y = s_y;
5863 }
5864 
initPrimaryHeights(int top_h,int text_h)5865 void HudGaugeWeapons::initPrimaryHeights(int top_h, int text_h)
5866 {
5867 	top_primary_h = top_h;
5868 	primary_text_h = text_h;
5869 }
5870 
initSecondaryHeights(int top_h,int text_h)5871 void HudGaugeWeapons::initSecondaryHeights(int top_h, int text_h)
5872 {
5873 	top_secondary_h = top_h;
5874 	secondary_text_h = text_h;
5875 }
5876 
initBitmapsPrimaryTop(char * fname,char * fname_ballistic)5877 void HudGaugeWeapons::initBitmapsPrimaryTop(char *fname, char *fname_ballistic)
5878 {
5879 	// load the graphics for the top portion of the weapons gauge
5880 	primary_top[0].first_frame = bm_load_animation(fname, &primary_top[0].num_frames);
5881 	if(primary_top[0].first_frame < 0) {
5882 		Warning(LOCATION,"Cannot load hud ani: %s\n", fname);
5883 	}
5884 
5885 	primary_top[1].first_frame = bm_load_animation(fname_ballistic, &primary_top[1].num_frames);
5886 	if(primary_top[1].first_frame < 0) {
5887 		primary_top[1].first_frame = primary_top[0].first_frame;
5888 		primary_top[1].num_frames = primary_top[0].num_frames;
5889 	}
5890 }
5891 
initBitmapsPrimaryMiddle(char * fname,char * fname_ballistic)5892 void HudGaugeWeapons::initBitmapsPrimaryMiddle(char *fname, char *fname_ballistic)
5893 {
5894 	// load the graphics for the middle portion of the primary weapons listing
5895 	primary_middle[0].first_frame = bm_load_animation(fname, &primary_middle[0].num_frames);
5896 	if(primary_middle[0].first_frame < 0) {
5897 		Warning(LOCATION,"Cannot load hud ani: %s\n", fname);
5898 	}
5899 
5900 	primary_middle[1].first_frame = bm_load_animation(fname_ballistic, &primary_middle[1].num_frames);
5901 	if(primary_middle[1].first_frame < 0) {
5902 		primary_middle[1].first_frame = primary_middle[0].first_frame;
5903 		primary_middle[1].num_frames = primary_middle[0].num_frames;
5904 	}
5905 }
5906 
initBitmapsPrimaryLast(char * fname,char * fname_ballistic)5907 void HudGaugeWeapons::initBitmapsPrimaryLast(char *fname, char *fname_ballistic)
5908 {
5909 	// load the graphics for the bottom portion of the primary weapons listing if there is one.
5910 	// Don't bother the user if there isn't one since retail doesn't use this.
5911 	primary_last[0].first_frame = bm_load_animation(fname, &primary_last[0].num_frames);
5912 
5913 	primary_last[1].first_frame = bm_load_animation(fname_ballistic, &primary_last[1].num_frames);
5914 	if(primary_last[1].first_frame < 0) {
5915 		primary_last[1].first_frame = primary_last[0].first_frame;
5916 		primary_last[1].num_frames = primary_last[0].num_frames;
5917 	}
5918 }
5919 
initBitmapsSecondaryTop(char * fname,char * fname_ballistic)5920 void HudGaugeWeapons::initBitmapsSecondaryTop(char *fname, char *fname_ballistic)
5921 {
5922 	// top portion of the secondary weapons gauge
5923 	secondary_top[0].first_frame = bm_load_animation(fname, &secondary_top[0].num_frames);
5924 	if(secondary_top[0].first_frame < 0) {
5925 		Warning(LOCATION,"Cannot load hud ani: %s\n", fname);
5926 	}
5927 
5928 	secondary_top[1].first_frame = bm_load_animation(fname_ballistic, &secondary_top[1].num_frames);
5929 	if(secondary_top[1].first_frame < 0) {
5930 		secondary_top[1].first_frame = secondary_top[0].first_frame;
5931 		secondary_top[1].num_frames = secondary_top[0].num_frames;
5932 	}
5933 }
5934 
initBitmapsSecondaryMiddle(char * fname,char * fname_ballistic)5935 void HudGaugeWeapons::initBitmapsSecondaryMiddle(char *fname, char *fname_ballistic)
5936 {
5937 	// middle portion of the secondary weapons gauge
5938 	secondary_middle[0].first_frame = bm_load_animation(fname, &secondary_middle[0].num_frames);
5939 	if(secondary_middle[0].first_frame < 0) {
5940 		Warning(LOCATION,"Cannot load hud ani: %s\n", fname);
5941 	}
5942 
5943 	secondary_middle[1].first_frame = bm_load_animation(fname_ballistic, &secondary_middle[1].num_frames);
5944 	if(secondary_middle[1].first_frame < 0) {
5945 		secondary_middle[1].first_frame = secondary_middle[0].first_frame;
5946 		secondary_middle[1].num_frames = secondary_middle[0].num_frames;
5947 	}
5948 }
5949 
initBitmapsSecondaryBottom(char * fname,char * fname_ballistic)5950 void HudGaugeWeapons::initBitmapsSecondaryBottom(char *fname, char *fname_ballistic)
5951 {
5952 	// bottom portion of the entire weapons gauge
5953 	secondary_bottom[0].first_frame = bm_load_animation(fname, &secondary_bottom[0].num_frames);
5954 	if(secondary_bottom[0].first_frame < 0) {
5955 		Warning(LOCATION,"Cannot load hud ani: %s\n", fname);
5956 	}
5957 
5958 	secondary_bottom[1].first_frame = bm_load_animation(fname_ballistic, &secondary_bottom[1].num_frames);
5959 	if(secondary_bottom[1].first_frame < 0) {
5960 		secondary_bottom[1].first_frame = secondary_bottom[0].first_frame;
5961 		secondary_bottom[1].num_frames = secondary_bottom[0].num_frames;
5962 	}
5963 }
5964 
pageIn()5965 void HudGaugeWeapons::pageIn()
5966 {
5967 	if(primary_top[0].first_frame > -1) {
5968 		bm_page_in_aabitmap(primary_top[0].first_frame,primary_top[0].num_frames);
5969 	}
5970 
5971 	if(primary_top[1].first_frame > -1) {
5972 		bm_page_in_aabitmap(primary_top[1].first_frame,primary_top[1].num_frames);
5973 	}
5974 
5975 	if(primary_middle[0].first_frame > -1) {
5976 		bm_page_in_aabitmap(primary_middle[0].first_frame,primary_middle[0].num_frames);
5977 	}
5978 
5979 	if(primary_middle[1].first_frame > -1) {
5980 		bm_page_in_aabitmap(primary_middle[1].first_frame, primary_middle[1].num_frames);
5981 	}
5982 
5983 	if(primary_last[1].first_frame > -1) {
5984 		bm_page_in_aabitmap(primary_last[1].first_frame, primary_last[1].num_frames);
5985 	}
5986 
5987 	if(secondary_top[0].first_frame > -1) {
5988 		bm_page_in_aabitmap(secondary_top[0].first_frame, secondary_top[0].num_frames);
5989 	}
5990 
5991 	if(secondary_top[1].first_frame > -1) {
5992 		bm_page_in_aabitmap(secondary_top[1].first_frame, secondary_top[1].num_frames);
5993 	}
5994 
5995 	if(secondary_middle[0].first_frame > -1) {
5996 		bm_page_in_aabitmap(secondary_middle[0].first_frame, secondary_middle[0].num_frames);
5997 	}
5998 
5999 	if(secondary_middle[1].first_frame >-1) {
6000 		bm_page_in_aabitmap(secondary_middle[0].first_frame, secondary_middle[0].num_frames);
6001 	}
6002 
6003 	if(secondary_bottom[0].first_frame > -1) {
6004 		bm_page_in_aabitmap(secondary_bottom[0].first_frame, secondary_bottom[0].num_frames);
6005 	}
6006 
6007 	if(secondary_bottom[1].first_frame > -1) {
6008 		bm_page_in_aabitmap(secondary_bottom[0].first_frame, secondary_bottom[0].num_frames);
6009 	}
6010 }
6011 
render(float)6012 void HudGaugeWeapons::render(float  /*frametime*/)
6013 {
6014 	ship_weapon	*sw;
6015 	int			np, ns;		// np == num primary, ns == num secondary
6016 
6017 	if(Player_obj->type == OBJ_OBSERVER)
6018 		return;
6019 
6020 	Assert(Player_obj->type == OBJ_SHIP);
6021 	Assert(Player_obj->instance >= 0 && Player_obj->instance < MAX_SHIPS);
6022 
6023 	sw = &Ships[Player_obj->instance].weapons;
6024 	np = sw->num_primary_banks;
6025 	ns = sw->num_secondary_banks;
6026 
6027 	setGaugeColor();
6028 
6029 	// draw top of primary display
6030 	renderBitmap(primary_top[ballistic_hud_index].first_frame, position[0] + top_offset_x[ballistic_hud_index], position[1]);
6031 
6032 	// render the header of this gauge
6033 	renderString(position[0] + Weapon_header_offsets[ballistic_hud_index][0], position[1] + Weapon_header_offsets[ballistic_hud_index][1], EG_WEAPON_TITLE, XSTR( "weapons", 328));
6034 
6035 	const char *weapon_name;
6036 	char	ammo_str[32];
6037 	int		i, w, h;
6038 	int y = position[1] + top_primary_h;
6039 	int name_y = position[1] + pname_start_offset_y;
6040 
6041 	// render primaries
6042 	for(i = 0; i < np; i++) {
6043 		setGaugeColor();
6044 
6045 		// choose which background to draw for additional primaries.
6046 		// Note, we don't draw a background for the first primary.
6047 		// It is assumed that the top primary wep frame already has this rendered.
6048 		if(i == 1) {
6049 			// used to draw the second primary weapon background
6050 			renderBitmap(primary_middle[ballistic_hud_index].first_frame, position[0] + frame_offset_x[ballistic_hud_index], y);
6051 		} else if(i != 0) {
6052 			// used to draw the the third, fourth, fifth, etc...
6053 			if(primary_last[ballistic_hud_index].first_frame != -1)
6054 				renderBitmap(primary_last[ballistic_hud_index].first_frame, position[0] + frame_offset_x[ballistic_hud_index], y);
6055 		}
6056 
6057 		weapon_name = Weapon_info[sw->primary_bank_weapons[i]].get_display_name();
6058 
6059 		// maybe modify name here to fit
6060 
6061 		// do we need to flash the text?
6062 		if (HudGauge::maybeFlashSexp() == i ) {
6063 			setGaugeColor(HUD_C_BRIGHT);
6064 		} else {
6065 			maybeFlashWeapon(i);
6066 		}
6067 
6068 		// indicate if this is linked or currently armed
6069 		if ( ((sw->current_primary_bank == i) && !(Player_ship->flags[Ship::Ship_Flags::Primary_linked])) || ((Player_ship->flags[Ship::Ship_Flags::Primary_linked]) && !(Weapon_info[sw->primary_bank_weapons[i]].wi_flags[Weapon::Info_Flags::Nolink]))) {
6070 			renderPrintf(position[0] + Weapon_plink_offset_x, name_y, EG_NULL, "%c", Weapon_link_icon);
6071 		}
6072 
6073 		// either render this primary's image or its name
6074 		if(Weapon_info[sw->primary_bank_weapons[0]].hud_image_index != -1) {
6075 			renderBitmap(Weapon_info[sw->primary_bank_weapons[i]].hud_image_index, position[0] + Weapon_pname_offset_x, name_y);
6076 		} else {
6077 			renderPrintf(position[0] + Weapon_pname_offset_x, name_y, EG_WEAPON_P2, "%s", weapon_name);
6078 		}
6079 
6080 		// if this is a ballistic primary with ammo, render the ammo count
6081 		if (Weapon_info[sw->primary_bank_weapons[i]].wi_flags[Weapon::Info_Flags::Ballistic]) {
6082 			// print out the ammo right justified
6083 			sprintf(ammo_str, "%d", sw->primary_bank_ammo[i]);
6084 
6085 			hud_num_make_mono(ammo_str, font_num);
6086 			gr_get_string_size(&w, &h, ammo_str);
6087 
6088 			renderString(position[0] + Weapon_pammo_offset_x - w, name_y, EG_NULL, ammo_str);
6089 		}
6090 
6091 		if(i != 0) {
6092 			y += primary_text_h;
6093 		}
6094 		name_y += primary_text_h;
6095 	}
6096 
6097 	weapon_info	*wip;
6098 
6099 	if ( HudGauge::maybeFlashSexp() == i ) {
6100 		setGaugeColor(HUD_C_BRIGHT);
6101 	}
6102 
6103 	renderBitmap(secondary_top[ballistic_hud_index].first_frame, position[0] + frame_offset_x[ballistic_hud_index], y);
6104 	name_y = y + sname_start_offset_y;
6105 	y += top_secondary_h;
6106 
6107 	for(i = 0; i < ns; i++)
6108 	{
6109 		setGaugeColor();
6110 		wip = &Weapon_info[sw->secondary_bank_weapons[i]];
6111 
6112 		if(i!=0) {
6113 			renderBitmap(secondary_middle[ballistic_hud_index].first_frame, position[0] + frame_offset_x[ballistic_hud_index], y);
6114 		}
6115 
6116 		maybeFlashWeapon(np+i);
6117 
6118 		if (wip->has_display_name()) {
6119 			// Do not apply the cluster bomb hack if we have an alternate name to make translating that name possible
6120 			weapon_name = wip->get_display_name();
6121 		} else {
6122 			// HACK - make Cluster Bomb fit on the HUD.
6123 			if(!stricmp(wip->name,"cluster bomb")){
6124 				weapon_name = NOX("Cluster");
6125 			} else {
6126 				weapon_name = wip->get_display_name();
6127 			}
6128 		}
6129 
6130 		if ( sw->current_secondary_bank == i ) {
6131 			// show that this is the current secondary armed
6132 			renderPrintf(position[0] + Weapon_sunlinked_offset_x, name_y, EG_NULL, "%c", Weapon_link_icon);
6133 
6134 			// indicate if this is linked
6135 			// don't draw the link indicator if the fire can't be fired link.
6136 			// the link flag is ignored rather than cleared so the player can cycle past a no-doublefire weapon without the setting being cleared
6137 			if ( Player_ship->flags[Ship::Ship_Flags::Secondary_dual_fire] && !wip->wi_flags[Weapon::Info_Flags::No_doublefire] &&
6138 					!The_mission.ai_profile->flags[AI::Profile_Flags::Disable_player_secondary_doublefire] ) {
6139 				renderPrintf(position[0] + Weapon_slinked_offset_x, name_y, EG_NULL, "%c", Weapon_link_icon);
6140 			}
6141 
6142 			// show secondary weapon's image or print its name
6143 			if(wip->hud_image_index != -1) {
6144 				renderBitmap(wip->hud_image_index, position[0] + Weapon_sname_offset_x, name_y);
6145 			} else {
6146 				renderString(position[0] + Weapon_sname_offset_x, name_y, i ? EG_WEAPON_S1 : EG_WEAPON_S2, weapon_name);
6147 			}
6148 
6149 			// show the cooldown time
6150 			if ( (sw->secondary_bank_ammo[i] > 0) && (sw->current_secondary_bank >= 0) ) {
6151 				int ms_till_fire = timestamp_until(sw->next_secondary_fire_stamp[sw->current_secondary_bank]);
6152 				if ( (ms_till_fire >= 500) && ((wip->fire_wait >= 1 ) || (ms_till_fire > wip->fire_wait*1000)) ) {
6153 					renderPrintf(position[0] + Weapon_sreload_offset_x, name_y, EG_NULL, "%d", (int)std::lround(ms_till_fire/1000.0f));
6154 				}
6155 			}
6156 		} else {
6157 			// just print the weapon's name since this isn't armed
6158 			renderString(position[0] + Weapon_sname_offset_x, name_y, i ? EG_WEAPON_S1 : EG_WEAPON_S2, weapon_name);
6159 		}
6160 
6161 		int ammo=sw->secondary_bank_ammo[i];
6162 
6163 		// print out the ammo right justified
6164 		sprintf(ammo_str, "%d", ammo);
6165 		hud_num_make_mono(ammo_str, font_num);
6166 		gr_get_string_size(&w, &h, ammo_str);
6167 
6168 		renderString(position[0] + Weapon_sammo_offset_x - w, name_y, EG_NULL, ammo_str);
6169 
6170 		if(i != 0) {
6171 			y += secondary_text_h;
6172 		}
6173 		name_y += secondary_text_h;
6174 	}
6175 
6176 	// a bit lonely here with no secondaries so just print "<none>"
6177 	if(ns==0)
6178 	{
6179 		renderString(position[0] + Weapon_pname_offset_x, name_y, EG_WEAPON_S1, XSTR( "<none>", 329));
6180 	}
6181 
6182 	y -= 0;
6183 	// finish drawing the background
6184 	renderBitmap(secondary_bottom[ballistic_hud_index].first_frame, position[0] + frame_offset_x[ballistic_hud_index], y);
6185 }
6186 
hud_update_weapon_flash()6187 void hud_update_weapon_flash()
6188 {
6189 	ship_weapon	*sw;
6190 	int num_weapons;
6191 
6192 	sw = &Ships[Player_obj->instance].weapons;
6193 	num_weapons = sw->num_primary_banks + sw->num_secondary_banks;
6194 
6195 	if ( num_weapons > MAX_WEAPON_FLASH_LINES ) {
6196 		Int3();	// Get Alan
6197 		return;
6198 	}
6199 
6200 	for(int i = 0; i < num_weapons; i++) {
6201 		if ( !timestamp_elapsed(Weapon_flash_info.flash_duration[i]) ) {
6202 			if ( timestamp_elapsed(Weapon_flash_info.flash_next[i]) ) {
6203 				Weapon_flash_info.flash_next[i] = timestamp(TBOX_FLASH_INTERVAL);
6204 				Weapon_flash_info.is_bright ^= (1<<i);
6205 			}
6206 		}
6207 	}
6208 }
6209 
maybeFlashWeapon(int index)6210 void HudGaugeWeapons::maybeFlashWeapon(int index)
6211 {
6212 	if ( index >= MAX_WEAPON_FLASH_LINES ) {
6213 		Int3();	// Get Alan
6214 		return;
6215 	}
6216 
6217 	// hud_set_default_color();
6218 	setGaugeColor();
6219 	if ( !timestamp_elapsed(Weapon_flash_info.flash_duration[index]) ) {
6220 		if ( Weapon_flash_info.is_bright & (1<<index) ) {
6221 			setGaugeColor(HUD_C_BRIGHT);
6222 			// hud_set_bright_color();
6223 		} else {
6224 			setGaugeColor(HUD_C_DIM);
6225 			// hud_set_dim_color();
6226 		}
6227 	}
6228 }
6229 
hud_target_add_display_list(object * objp,const vertex * target_point,const vec3d * target_pos,int correction,const color * bracket_clr,const char * name,int flags)6230 void hud_target_add_display_list(object *objp, const vertex *target_point, const vec3d *target_pos, int correction, const color *bracket_clr, const char* name, int flags)
6231 {
6232 	target_display_info element;
6233 
6234 	element.objp = objp;
6235 	element.target_point = *target_point;
6236 	element.target_pos = *target_pos;
6237 	element.correction = correction;
6238 	element.flags = flags;
6239 
6240 	if(bracket_clr) {
6241 		element.bracket_clr = *bracket_clr;
6242 	} else {
6243 		// no color given, so this will tell the target display gauges to use IFF colors.
6244 		gr_init_color(&element.bracket_clr, 0, 0, 0);
6245 	}
6246 
6247 	if(name) {
6248 		strcpy_s(element.name, name);
6249 	} else {
6250 		strcpy_s(element.name, "");
6251 	}
6252 
6253 	target_display_list.push_back(element);
6254 }
6255 
hud_target_clear_display_list()6256 void hud_target_clear_display_list()
6257 {
6258 	target_display_list.clear();
6259 }
6260 
HudGaugeOffscreen()6261 HudGaugeOffscreen::HudGaugeOffscreen():
6262 HudGauge(HUD_OBJECT_OFFSCREEN, HUD_OFFSCREEN_INDICATOR, false, true, VM_DEAD_VIEW | VM_OTHER_SHIP, 255, 255, 255)
6263 {
6264 }
6265 
initMaxTriSeperation(float length)6266 void HudGaugeOffscreen::initMaxTriSeperation(float length)
6267 {
6268 	Max_offscreen_tri_seperation = length;
6269 }
6270 
initMaxFrontSeperation(float length)6271 void HudGaugeOffscreen::initMaxFrontSeperation(float length)
6272 {
6273 	Max_front_seperation = length;
6274 }
6275 
initTriBase(float length)6276 void HudGaugeOffscreen::initTriBase(float length)
6277 {
6278 	Offscreen_tri_base = length;
6279 }
6280 
initTriHeight(float length)6281 void HudGaugeOffscreen::initTriHeight(float length)
6282 {
6283 	Offscreen_tri_height = length;
6284 }
6285 
pageIn()6286 void HudGaugeOffscreen::pageIn()
6287 {
6288 }
6289 
render(float)6290 void HudGaugeOffscreen::render(float  /*frametime*/)
6291 {
6292 	// don't show offscreen indicator if we're warping out.
6293 	if ( Player->control_mode != PCM_NORMAL ) {
6294 		return;
6295 	}
6296 
6297 	for(size_t i = 0; i < target_display_list.size(); i++) {
6298 		if(target_display_list[i].target_point.codes != 0) {
6299 			float dist = 0.0f;
6300 			vec2d coords;
6301 			float half_triangle_sep;
6302 			int dir;
6303 
6304 			if(target_display_list[i].objp) {
6305 				dist = hud_find_target_distance( target_display_list[i].objp, Player_obj );
6306 			} else {
6307 				// if we don't have a corresponding object, use given position to figure out distance
6308 				dist = vm_vec_dist_quick(&Player_obj->pos, &target_display_list[i].target_pos);
6309 			}
6310 
6311 			if( target_display_list[i].bracket_clr.red && target_display_list[i].bracket_clr.green &&
6312 				target_display_list[i].bracket_clr.blue ) {
6313 				gr_set_color_fast(&target_display_list[i].bracket_clr);
6314 			} else {
6315 				// use IFF colors if none defined.
6316 				if(target_display_list[i].objp) {
6317 					hud_set_iff_color(target_display_list[i].objp, 1);
6318 				} else {
6319 					// no object so this must mean it's a nav point. but for some odd reason someone forgot to include a color.
6320 					gr_set_color_fast(&target_display_list[i].bracket_clr);
6321 				}
6322 			}
6323 
6324 			calculatePosition(&target_display_list[i].target_point, &target_display_list[i].target_pos, &coords, &dir, &half_triangle_sep);
6325 			renderOffscreenIndicator(&coords, dir, dist, half_triangle_sep, true);
6326 		}
6327 	}
6328 }
6329 
calculatePosition(vertex * target_point,vec3d * tpos,vec2d * outcoords,int * dir,float * half_triangle_sep)6330 void HudGaugeOffscreen::calculatePosition(vertex* target_point, vec3d *tpos, vec2d *outcoords, int *dir, float *half_triangle_sep)
6331 {
6332 	float xpos,ypos;
6333 	vec3d targ_to_player;
6334 	float dist_behind;
6335 	float triangle_sep;
6336 	float half_gauge_length;
6337 
6338 	bool in_frame = g3_in_frame() > 0;
6339 	if(!in_frame)
6340 		g3_start_frame(0);
6341 	gr_set_screen_scale(base_w, base_h);
6342 
6343 	// calculate the dot product between the players forward vector and the vector connecting
6344 	// the player to the target. Normalize targ_to_player since we want the dot product
6345 	// to range between 0 -> 1.
6346 	vm_vec_sub(&targ_to_player, &Player_obj->pos, tpos);
6347 	vm_vec_normalize(&targ_to_player);
6348 	dist_behind = vm_vec_dot(&Player_obj->orient.vec.fvec, &targ_to_player);
6349 
6350 	if (dist_behind < 0) {	// still in front of player, but not in view
6351 		dist_behind = dist_behind + 1.0f;
6352 		if (dist_behind > 0.2 ){
6353 			triangle_sep = ( dist_behind ) * Max_front_seperation;
6354 		} else {
6355 			triangle_sep = 0.0f;
6356 		}
6357 	} else {
6358 		triangle_sep = dist_behind * Max_offscreen_tri_seperation + Max_offscreen_tri_seperation;
6359 	}
6360 
6361 	if ( triangle_sep > Max_offscreen_tri_seperation + Max_front_seperation){
6362 		triangle_sep = Max_offscreen_tri_seperation + Max_front_seperation;
6363 	}
6364 
6365 	// calculate these values only once, since it will be used in several places
6366 	*half_triangle_sep = 0.5f * triangle_sep;
6367 	half_gauge_length = *half_triangle_sep + Offscreen_tri_base;
6368 
6369 	// We need to find the screen (x,y) for where to draw the offscreen indicator
6370 	//
6371 	// The best way I've found is to draw a line from the eye_pos to the target, and
6372 	// then use clip_line() to find the screen (x,y) for where the line hits the edge
6373 	// of the screen.
6374 	//
6375 	// The weird thing about clip_line() is that is flips around the two verticies,
6376 	// so I use eye_vertex->sx and eye_vertex->sy for the off-screen indicator (x,y)
6377 	//
6378 	vertex *eye_vertex = NULL;
6379 	vertex real_eye_vertex;
6380 	eye_vertex = &real_eye_vertex;	// this is needed since clip line takes a **vertex
6381 	vec3d eye_pos;
6382 	vm_vec_add( &eye_pos, &Eye_position, &View_matrix.vec.fvec);
6383 	g3_rotate_vertex(eye_vertex, &eye_pos);
6384 
6385 	ubyte codes_or;
6386 	codes_or = (ubyte)(target_point->codes | eye_vertex->codes);
6387 	clip_line(&target_point,&eye_vertex,codes_or,0);
6388 
6389 	if (!(target_point->flags&PF_PROJECTED))
6390 		g3_project_vertex(target_point);
6391 
6392 	if (!(eye_vertex->flags&PF_PROJECTED))
6393 		g3_project_vertex(eye_vertex);
6394 
6395 	if (eye_vertex->flags&PF_OVERFLOW) {
6396 		Int3();			//	This is unlikely to happen, but can if a clip goes through the player's eye.
6397 		Player_ai->target_objnum = -1;
6398 		if (!in_frame)
6399 			g3_end_frame();
6400 		return;
6401 	}
6402 
6403 	if (target_point->flags & PF_TEMP_POINT)
6404 		free_temp_point(target_point);
6405 
6406 	if (eye_vertex->flags & PF_TEMP_POINT)
6407 		free_temp_point(eye_vertex);
6408 
6409 	xpos = eye_vertex->screen.xyw.x;
6410 	ypos = eye_vertex->screen.xyw.y;
6411 
6412 	// we need it unsized here and it will be fixed when things are acutally drawn
6413 	gr_unsize_screen_posf(&xpos, &ypos);
6414 
6415 	xpos = (xpos<1) ? 0 : xpos;
6416 	ypos = (ypos<1) ? 0 : ypos;
6417 
6418 	if ( xpos >= gr_screen.clip_right_unscaled) {
6419 		xpos = i2fl(gr_screen.clip_right_unscaled);
6420 		*dir = 0;
6421 
6422 		if ( ypos < (half_gauge_length - gr_screen.clip_top_unscaled) )
6423 			ypos = half_gauge_length;
6424 
6425 		if ( ypos > (gr_screen.clip_bottom_unscaled - half_gauge_length) )
6426 			ypos = gr_screen.clip_bottom_unscaled - half_gauge_length;
6427 
6428 	} else if ( xpos <= gr_screen.clip_left_unscaled ) {
6429 		xpos = i2fl(gr_screen.clip_left_unscaled);
6430 		*dir = 1;
6431 
6432 		if ( ypos < (half_gauge_length - gr_screen.clip_top_unscaled) )
6433 			ypos = half_gauge_length;
6434 
6435 		if ( ypos > (gr_screen.clip_bottom_unscaled - half_gauge_length) )
6436 			ypos = gr_screen.clip_bottom_unscaled - half_gauge_length;
6437 
6438 	} else if ( ypos <= gr_screen.clip_top_unscaled ) {
6439 		ypos = i2fl(gr_screen.clip_top_unscaled);
6440 		*dir = 2;
6441 
6442 		if ( xpos < ( half_gauge_length - gr_screen.clip_left_unscaled) )
6443 			xpos = half_gauge_length;
6444 
6445 		if ( xpos > (gr_screen.clip_right_unscaled - half_gauge_length) )
6446 			xpos = gr_screen.clip_right_unscaled - half_gauge_length;
6447 
6448 	} else if ( ypos >= gr_screen.clip_bottom_unscaled ) {
6449 		ypos = i2fl(gr_screen.clip_bottom_unscaled);
6450 		*dir = 3;
6451 
6452 		if ( xpos < ( half_gauge_length - gr_screen.clip_left_unscaled) )
6453 			xpos = half_gauge_length;
6454 
6455 		if ( xpos > (gr_screen.clip_right_unscaled - half_gauge_length) )
6456 			xpos = gr_screen.clip_right_unscaled - half_gauge_length;
6457 
6458 	} else {
6459 		Int3();
6460 		if (!in_frame)
6461 			g3_end_frame();
6462 		return;
6463 	}
6464 
6465 	// The offscreen target triangles are drawn according the the diagram below
6466 	//
6467 	//
6468 	//
6469 	//              x3                x3
6470 	//            /  |                |  \.
6471 	//          /    |                |    \.
6472 	//        x1____x2                x2____x1
6473 	//               |                |
6474 	//        .......|................|............(xpos,ypos)
6475 	//               |                |
6476 	//        x4____x5                x5____x4
6477 	//         \     |                |     /
6478 	//           \   |                |   /
6479 	//              x6                x6
6480 	//
6481 	//
6482 
6483 	xpos = (float)floor(xpos);
6484 	ypos = (float)floor(ypos);
6485 
6486 	if (outcoords != NULL) {
6487 		outcoords->x = xpos;
6488 		outcoords->y = ypos;
6489 	}
6490 
6491 	gr_reset_screen_scale();
6492 
6493 	if(!in_frame)
6494 		g3_end_frame();
6495 }
6496 
renderOffscreenIndicator(vec2d * coords,int dir,float distance,float half_triangle_sep,bool draw_solid)6497 void HudGaugeOffscreen::renderOffscreenIndicator(vec2d *coords, int dir, float distance, float half_triangle_sep, bool draw_solid)
6498 {
6499 	float xpos, ypos;
6500 	float displayed_distance;
6501 	char buf[32];
6502 	int w = 0, h = 0;
6503 
6504 	// points to draw triangles
6505 	float x1=0.0f;
6506 	float y1=0.0f;
6507 	float x2=0.0f;
6508 	float y2=0.0f;
6509 	float x3=0.0f;
6510 	float y3=0.0f;
6511 	float x4=0.0f;
6512 	float y4=0.0f;
6513 	float x5=0.0f;
6514 	float y5=0.0f;
6515 	float x6=0.0f;
6516 	float y6=0.0f;
6517 
6518 	// scale by distance modifier from hud_guages.tbl for display purposes
6519 	displayed_distance = distance * Hud_unit_multiplier;
6520 
6521 	bool in_frame = g3_in_frame() > 0;
6522 	if(!in_frame)
6523 		g3_start_frame(0);
6524 
6525 	gr_set_screen_scale(base_w, base_h);
6526 
6527 	if (displayed_distance > 0.0f) {
6528 		sprintf(buf, "%d", (int)std::lround(displayed_distance));
6529 		hud_num_make_mono(buf, font_num);
6530 		gr_get_string_size(&w, &h, buf);
6531 	} else {
6532 		buf[0] = 0;
6533 	}
6534 
6535 	xpos = coords->x;
6536 	ypos = coords->y;
6537 
6538 	if (dir == 0) {
6539 		x1 = x4 = (xpos+2);
6540 
6541 		x2 = x3 = x5 = x6 = x1 - Offscreen_tri_height;
6542 		y1 = y2 = ypos - half_triangle_sep;
6543 		y3 = y2 - Offscreen_tri_base;
6544 
6545 		y4 = y5 = ypos + half_triangle_sep;
6546 		y6 = y5 + Offscreen_tri_base;
6547 
6548 		if ( buf[0] ) {
6549 			gr_string( fl2i(xpos - w - 10), (int)std::lround(ypos - h/2.0f), buf);
6550 		}
6551 	} else if (dir == 1) {
6552 		x1 = x4 = (xpos-1);
6553 
6554 		x2 = x3 = x5 = x6 = x1 + Offscreen_tri_height;
6555 		y1 = y2 = ypos - half_triangle_sep;
6556 		y3 = y2 - Offscreen_tri_base;
6557 
6558 		y4 = y5 = ypos + half_triangle_sep;
6559 		y6 = y5 + Offscreen_tri_base;
6560 
6561 		if ( buf[0] ) {
6562 			gr_string(fl2i(xpos + 10), (int)std::lround(ypos - h/2.0f), buf);
6563 		}
6564 	} else if (dir == 2) {
6565 		y1 = y4 = (ypos-1);
6566 
6567 		y2 = y3 = y5 = y6 = y1 + Offscreen_tri_height;
6568 		x1 = x2 = xpos - half_triangle_sep;
6569 		x3 = x2 - Offscreen_tri_base;
6570 
6571 		x4 = x5 = xpos + half_triangle_sep;
6572 		x6 = x5 + Offscreen_tri_base;
6573 
6574 		if ( buf[0] ) {
6575 			gr_string((int)std::lround(xpos - w/2.0f), fl2i(ypos+10), buf);
6576 		}
6577 	} else if (dir == 3) {
6578 		y1 = y4 = (ypos+2);
6579 
6580 		y2 = y3 = y5 = y6 = y1 - Offscreen_tri_height;
6581 		x1 = x2 = xpos - half_triangle_sep;
6582 		x3 = x2 - Offscreen_tri_base;
6583 
6584 		x4 = x5 = xpos + half_triangle_sep;
6585 		x6 = x5 + Offscreen_tri_base;
6586 
6587 		if ( buf[0] ) {
6588 			gr_string((int)std::lround(xpos - w/2.0f), fl2i(ypos-h-10), buf);
6589 		}
6590 	}
6591 
6592 	if (draw_solid) {
6593 		hud_tri(x3,y3,x2,y2,x1,y1);
6594 		hud_tri(x4,y4,x5,y5,x6,y6);
6595 	} else {
6596 		hud_tri_empty(x3,y3,x2,y2,x1,y1);
6597 		hud_tri_empty(x4,y4,x5,y5,x6,y6);
6598 	}
6599 
6600 	if (dir == 0 || dir == 3){
6601 		gr_line(fl2i(x2),fl2i(y2),fl2i(x5),fl2i(y5));
6602 	} else if (dir == 1) {
6603 		gr_line(fl2i(x2-1),fl2i(y2),fl2i(x5-1),fl2i(y5));
6604 	} else {
6605 		gr_line(fl2i(x2),fl2i(y2-1),fl2i(x5),fl2i(y5-1));
6606 	}
6607 
6608 	gr_reset_screen_scale();
6609 
6610 	if(!in_frame)
6611 		g3_end_frame();
6612 }
6613 
HudGaugeWarheadCount()6614 HudGaugeWarheadCount::HudGaugeWarheadCount():
6615 HudGauge(HUD_OBJECT_WARHEAD_COUNT, HUD_WEAPONS_GAUGE, false, false, VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY, 255, 255, 255)
6616 {
6617 }
6618 
initBitmap(char * fname)6619 void HudGaugeWarheadCount::initBitmap(char *fname)
6620 {
6621 	Warhead.first_frame = bm_load_animation(fname, &Warhead.num_frames);
6622 
6623 	if ( Warhead.first_frame < 0 ) {
6624 		Warning(LOCATION,"Cannot load hud ani: %s\n", fname);
6625 	}
6626 }
6627 
initNameOffsets(int x,int y)6628 void HudGaugeWarheadCount::initNameOffsets(int x, int y)
6629 {
6630 	Warhead_name_offsets[0] = x;
6631 	Warhead_name_offsets[1] = y;
6632 }
6633 
initCountOffsets(int x,int y)6634 void HudGaugeWarheadCount::initCountOffsets(int x, int y)
6635 {
6636 	Warhead_count_offsets[0] = x;
6637 	Warhead_count_offsets[1] = y;
6638 }
6639 
initCountSizes(int w,int h)6640 void HudGaugeWarheadCount::initCountSizes(int w, int h)
6641 {
6642 	Warhead_count_size[0] = w;
6643 	Warhead_count_size[1] = h;
6644 }
6645 
initMaxSymbols(int count)6646 void HudGaugeWarheadCount::initMaxSymbols(int count)
6647 {
6648 	Max_symbols = count;
6649 }
6650 
initMaxColumns(int count)6651 void HudGaugeWarheadCount::initMaxColumns(int count)
6652 {
6653 	Max_columns = count;
6654 }
6655 
initTextAlign(HudAlignment align)6656 void HudGaugeWarheadCount::initTextAlign(HudAlignment align)
6657 {
6658 	Text_align = align;
6659 }
6660 
pageIn()6661 void HudGaugeWarheadCount::pageIn()
6662 {
6663 	bm_page_in_aabitmap(Warhead.first_frame, Warhead.num_frames);
6664 }
6665 
render(float)6666 void HudGaugeWarheadCount::render(float  /*frametime*/)
6667 {
6668 	if(Player_obj->type == OBJ_OBSERVER) {
6669 		return;
6670 	}
6671 
6672 	Assert(Player_obj->type == OBJ_SHIP);
6673 	Assert(Player_obj->instance >= 0 && Player_obj->instance < MAX_SHIPS);
6674 
6675 	ship_weapon	*sw = &Ships[Player_obj->instance].weapons;
6676 
6677 	// don't bother displaying anything if we have no secondaries
6678 	if ( sw->num_secondary_banks <= 0 ) {
6679 		return;
6680 	}
6681 
6682 	int wep_num = sw->current_secondary_bank;
6683 	weapon_info *wip = &Weapon_info[sw->secondary_bank_weapons[wep_num]];
6684 	int ammo = sw->secondary_bank_ammo[wep_num];
6685 
6686 	// don't bother displaying anything if we have no ammo.
6687 	if ( ammo <= 0 ) {
6688 		return;
6689 	}
6690 
6691 	auto weapon_name = wip->get_display_name();
6692 
6693 	setGaugeColor();
6694 
6695 	// display the weapon name
6696 	if ( Text_align == HudAlignment::RIGHT ) {
6697 		int w, h;
6698 
6699 		gr_get_string_size(&w, &h, weapon_name);
6700 		renderString(position[0] + Warhead_name_offsets[0] - w, position[1] + Warhead_name_offsets[1], weapon_name);
6701 	} else if ( Text_align == HudAlignment::LEFT ) {
6702 		renderString(position[0] + Warhead_name_offsets[0], position[1] + Warhead_name_offsets[1], weapon_name);
6703 	}
6704 
6705 	setGaugeColor(HUD_C_BRIGHT);
6706 
6707 	// if ammo is greater than the icon display limit, just show a numeric
6708 	if ( ammo > Max_symbols ) {
6709 		char ammo_str[32];
6710 
6711 		sprintf(ammo_str, "%d", ammo);
6712 		hud_num_make_mono(ammo_str, font_num);
6713 
6714 		if ( Text_align == HudAlignment::RIGHT ) {
6715 			int w, h;
6716 
6717 			gr_get_string_size(&w, &h, ammo_str);
6718 			renderString(position[0] + Warhead_count_offsets[0] - w, position[1] + Warhead_count_offsets[1], ammo_str);
6719 		} else if ( Text_align == HudAlignment::LEFT ) {
6720 			renderString(position[0] + Warhead_count_offsets[0], position[1] + Warhead_count_offsets[1], ammo_str);
6721 		}
6722 
6723 		return;
6724 	}
6725 
6726 	int delta_x = 0, delta_y = 0;
6727 	if ( Text_align == HudAlignment::RIGHT ) {
6728 		delta_x = -Warhead_count_size[0];
6729 	} else if ( Text_align == HudAlignment::LEFT ) {
6730 		delta_x = Warhead_count_size[0];
6731 	}
6732 
6733 	int i, column;
6734 	for ( i = 0; i < ammo; i++ ) {
6735 		if ( Max_columns > 0 ) {
6736 			delta_y = Warhead_count_size[1] * (i / Max_columns);
6737 			column = i % Max_columns;
6738 		} else {
6739 			column = i;
6740 		}
6741 
6742 		renderBitmap(Warhead.first_frame, position[0] + Warhead_count_offsets[0] + column * delta_x, position[1] + Warhead_count_offsets[1] + delta_y);
6743 	}
6744 }
6745 
HudGaugeWeaponList(int _gauge_object)6746 HudGaugeWeaponList::HudGaugeWeaponList(int _gauge_object):
6747 HudGauge(_gauge_object, HUD_WEAPONS_GAUGE, false, false, VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY, 255, 255, 255)
6748 {
6749 
6750 }
6751 
initLinkIcon()6752 void HudGaugeWeaponList::initLinkIcon() {
6753 	ubyte sc = lcl_get_font_index(font_num);
6754 	// default to a '>' if the font has no special chars
6755 	// seems about the closest normal char to the triangle
6756 	if (sc == 0) {
6757 		Weapon_link_icon = ubyte ('>');
6758 	} else {
6759 		Weapon_link_icon = sc + 2;
6760 	}
6761 }
6762 
initBitmaps(char * fname_first,char * fname_entry,char * fname_last)6763 void HudGaugeWeaponList::initBitmaps(char *fname_first, char *fname_entry, char *fname_last)
6764 {
6765 	_background_first.first_frame = bm_load_animation(fname_first, &_background_first.num_frames);
6766 	if(_background_first.first_frame < 0) {
6767 		Warning(LOCATION,"Cannot load hud ani: %s\n", fname_first);
6768 	}
6769 
6770 	_background_entry.first_frame = bm_load_animation(fname_entry, &_background_entry.num_frames);
6771 	if(_background_entry.first_frame < 0) {
6772 		Warning(LOCATION,"Cannot load hud ani: %s\n", fname_entry);
6773 	}
6774 
6775 	_background_last.first_frame = bm_load_animation(fname_last, &_background_last.num_frames);
6776 	if(_background_last.first_frame < 0) {
6777 		Warning(LOCATION,"Cannot load hud ani: %s\n", fname_last);
6778 	}
6779 }
6780 
initBgFirstOffsetX(int x)6781 void HudGaugeWeaponList::initBgFirstOffsetX(int x)
6782 {
6783 	_bg_first_offset_x = x;
6784 }
6785 
initBgEntryOffsetX(int x)6786 void HudGaugeWeaponList::initBgEntryOffsetX(int x)
6787 {
6788 	_bg_entry_offset_x = x;
6789 }
6790 
initBgLastOffsetX(int x)6791 void HudGaugeWeaponList::initBgLastOffsetX(int x)
6792 {
6793 	_bg_last_offset_x = x;
6794 }
6795 
initBgLastOffsetY(int y)6796 void HudGaugeWeaponList::initBgLastOffsetY(int y)
6797 {
6798 	_bg_last_offset_y = y;
6799 }
6800 
initBgFirstHeight(int h)6801 void HudGaugeWeaponList::initBgFirstHeight(int h)
6802 {
6803 	_background_first_h = h;
6804 }
6805 
initBgEntryHeight(int h)6806 void HudGaugeWeaponList::initBgEntryHeight(int h)
6807 {
6808 	_background_entry_h = h;
6809 }
6810 
initHeaderText(char * text)6811 void HudGaugeWeaponList::initHeaderText(char *text)
6812 {
6813 	strcpy_s(header_text, text);
6814 }
6815 
initHeaderOffsets(int x,int y)6816 void HudGaugeWeaponList::initHeaderOffsets(int x, int y)
6817 {
6818 	_header_offsets[0] = x;
6819 	_header_offsets[1] = y;
6820 }
6821 
initEntryStartY(int y)6822 void HudGaugeWeaponList::initEntryStartY(int y)
6823 {
6824 	_entry_start_y = y;
6825 }
6826 
initEntryHeight(int h)6827 void HudGaugeWeaponList::initEntryHeight(int h)
6828 {
6829 	_entry_h = h;
6830 }
6831 
pageIn()6832 void HudGaugeWeaponList::pageIn()
6833 {
6834 	if ( _background_first.first_frame >= 0 ) {
6835 		bm_page_in_aabitmap(_background_first.first_frame, _background_first.num_frames);
6836 	}
6837 
6838 	if ( _background_entry.first_frame >= 0 ) {
6839 		bm_page_in_aabitmap(_background_entry.first_frame, _background_entry.num_frames);
6840 	}
6841 
6842 	if ( _background_last.first_frame >= 0 ) {
6843 		bm_page_in_aabitmap(_background_last.first_frame, _background_last.num_frames);
6844 	}
6845 }
6846 
maybeFlashWeapon(int index)6847 void HudGaugeWeaponList::maybeFlashWeapon(int index)
6848 {
6849 	if ( index >= MAX_WEAPON_FLASH_LINES ) {
6850 		Int3();	// Get Alan
6851 		return;
6852 	}
6853 
6854 	// hud_set_default_color();
6855 	setGaugeColor();
6856 	if ( !timestamp_elapsed(Weapon_flash_info.flash_duration[index]) ) {
6857 		if ( Weapon_flash_info.is_bright & (1<<index) ) {
6858 			setGaugeColor(HUD_C_BRIGHT);
6859 			// hud_set_bright_color();
6860 		} else {
6861 			setGaugeColor(HUD_C_DIM);
6862 			// hud_set_dim_color();
6863 		}
6864 	}
6865 }
6866 
render(float)6867 void HudGaugeWeaponList::render(float  /*frametime*/)
6868 {
6869 
6870 }
6871 
HudGaugePrimaryWeapons()6872 HudGaugePrimaryWeapons::HudGaugePrimaryWeapons():
6873 HudGaugeWeaponList(HUD_OBJECT_PRIMARY_WEAPONS)
6874 {
6875 
6876 }
6877 
initPrimaryLinkOffsetX(int x)6878 void HudGaugePrimaryWeapons::initPrimaryLinkOffsetX(int x)
6879 {
6880 	_plink_offset_x = x;
6881 }
6882 
initPrimaryNameOffsetX(int x)6883 void HudGaugePrimaryWeapons::initPrimaryNameOffsetX(int x)
6884 {
6885 	_pname_offset_x = x;
6886 }
6887 
initPrimaryAmmoOffsetX(int x)6888 void HudGaugePrimaryWeapons::initPrimaryAmmoOffsetX(int x)
6889 {
6890 	_pammo_offset_x = x;
6891 }
6892 
render(float)6893 void HudGaugePrimaryWeapons::render(float  /*frametime*/)
6894 {
6895 	ship_weapon	*sw;
6896 
6897 	int		num_primaries;		// np == num primary
6898 
6899 	if(Player_obj->type == OBJ_OBSERVER)
6900 		return;
6901 
6902 	Assert(Player_obj->type == OBJ_SHIP);
6903 	Assert(Player_obj->instance >= 0 && Player_obj->instance < MAX_SHIPS);
6904 
6905 	sw = &Ships[Player_obj->instance].weapons;
6906 
6907 	num_primaries = sw->num_primary_banks;
6908 
6909 	setGaugeColor();
6910 
6911 	renderBitmap(_background_first.first_frame, position[0], position[1]);
6912 
6913 	// render the header of this gauge
6914 	renderString(position[0] + _header_offsets[0], position[1] + _header_offsets[1], EG_WEAPON_TITLE, header_text);
6915 
6916 	char ammo_str[32];
6917 	int i, w, h;
6918 	int bg_y_offset = _background_first_h;
6919 	int text_y_offset = _entry_start_y;
6920 
6921 	for ( i = 0; i < num_primaries; ++i ) {
6922 		setGaugeColor();
6923 
6924 		renderBitmap(_background_entry.first_frame, position[0], position[1] + bg_y_offset);
6925 
6926 		auto weapon_name = Weapon_info[sw->primary_bank_weapons[i]].get_display_name();
6927 
6928 		if (HudGauge::maybeFlashSexp() == i ) {
6929 			setGaugeColor(HUD_C_BRIGHT);
6930 		} else {
6931 			maybeFlashWeapon(i);
6932 		}
6933 
6934 		// indicate if this is linked or currently armed
6935 		if ( (sw->current_primary_bank == i) || (Player_ship->flags[Ship::Ship_Flags::Primary_linked]) ) {
6936 			renderPrintf(position[0] + _plink_offset_x, position[1] + text_y_offset, EG_NULL, "%c", Weapon_link_icon);
6937 		}
6938 
6939 		// either render this primary's image or its name
6940 		if(Weapon_info[sw->primary_bank_weapons[0]].hud_image_index != -1) {
6941 			renderBitmap(Weapon_info[sw->primary_bank_weapons[i]].hud_image_index, position[0] + _pname_offset_x, text_y_offset);
6942 		} else {
6943 			renderPrintf(position[0] + _pname_offset_x, position[1] + text_y_offset, EG_WEAPON_P2, "%s", weapon_name);
6944 		}
6945 
6946 		// if this is a ballistic primary with ammo, render the ammo count
6947 		if (Weapon_info[sw->primary_bank_weapons[i]].wi_flags[Weapon::Info_Flags::Ballistic]) {
6948 			// print out the ammo right justified
6949 			sprintf(ammo_str, "%d", sw->primary_bank_ammo[i]);
6950 
6951 			hud_num_make_mono(ammo_str, font_num);
6952 			gr_get_string_size(&w, &h, ammo_str);
6953 
6954 			renderString(position[0] + _pammo_offset_x - w, position[1] + text_y_offset, EG_NULL, ammo_str);
6955 		}
6956 
6957 		text_y_offset += _entry_h;
6958 		bg_y_offset += _background_entry_h;
6959 	}
6960 
6961 	if ( num_primaries == 0 ) {
6962 		renderBitmap(_background_entry.first_frame, position[0], position[1] + bg_y_offset);
6963 		renderString(position[0] + _pname_offset_x, position[1] + text_y_offset, EG_WEAPON_P1, XSTR( "<none>", 329));
6964 
6965 		bg_y_offset += _background_entry_h;
6966 	}
6967 
6968 	renderBitmap(_background_last.first_frame, position[0], position[1] + bg_y_offset + _bg_last_offset_y);
6969 }
6970 
HudGaugeSecondaryWeapons()6971 HudGaugeSecondaryWeapons::HudGaugeSecondaryWeapons():
6972 HudGaugeWeaponList(HUD_OBJECT_SECONDARY_WEAPONS)
6973 {
6974 
6975 }
6976 
initSecondaryAmmoOffsetX(int x)6977 void HudGaugeSecondaryWeapons::initSecondaryAmmoOffsetX(int x)
6978 {
6979 	_sammo_offset_x = x;
6980 }
6981 
initSecondaryNameOffsetX(int x)6982 void HudGaugeSecondaryWeapons::initSecondaryNameOffsetX(int x)
6983 {
6984 	_sname_offset_x = x;
6985 }
6986 
initSecondaryReloadOffsetX(int x)6987 void HudGaugeSecondaryWeapons::initSecondaryReloadOffsetX(int x)
6988 {
6989 	_sreload_offset_x = x;
6990 }
6991 
initSecondaryLinkedOffsetX(int x)6992 void HudGaugeSecondaryWeapons::initSecondaryLinkedOffsetX(int x)
6993 {
6994 	_slinked_offset_x = x;
6995 }
6996 
initSecondaryUnlinkedOffsetX(int x)6997 void HudGaugeSecondaryWeapons::initSecondaryUnlinkedOffsetX(int x)
6998 {
6999 	_sunlinked_offset_x = x;
7000 }
7001 
render(float)7002 void HudGaugeSecondaryWeapons::render(float  /*frametime*/)
7003 {
7004 	ship_weapon	*sw;
7005 
7006 	int num_primaries, num_secondaries;
7007 
7008 	Assert(Player_obj->type == OBJ_SHIP);
7009 	Assert(Player_obj->instance >= 0 && Player_obj->instance < MAX_SHIPS);
7010 
7011 	sw = &Ships[Player_obj->instance].weapons;
7012 
7013 	num_primaries = sw->num_primary_banks;
7014 	num_secondaries = sw->num_secondary_banks;
7015 
7016 	setGaugeColor();
7017 
7018 	renderBitmap(_background_first.first_frame, position[0], position[1]);
7019 
7020 	// render the header of this gauge
7021 	renderString(position[0] + _header_offsets[0], position[1] + _header_offsets[1], EG_WEAPON_TITLE, header_text);
7022 
7023 	weapon_info	*wip;
7024 	char ammo_str[32];
7025 	int i, w, h;
7026 	int bg_y_offset = _background_first_h;
7027 	int text_y_offset = _entry_start_y;
7028 
7029 	for ( i = 0; i < num_secondaries; ++i ) {
7030 		setGaugeColor();
7031 		wip = &Weapon_info[sw->secondary_bank_weapons[i]];
7032 
7033 		renderBitmap(_background_entry.first_frame, position[0], position[1] + bg_y_offset);
7034 
7035 		maybeFlashWeapon(num_primaries+i);
7036 
7037 		auto weapon_name = wip->get_display_name();
7038 
7039 		if ( sw->current_secondary_bank == i ) {
7040 			// show that this is the current secondary armed
7041 			renderPrintf(position[0] + _sunlinked_offset_x, position[1] + text_y_offset, EG_NULL, "%c", Weapon_link_icon);
7042 
7043 			// indicate if this is linked
7044 			// don't draw the link indicator if the fire can't be fired link.
7045 			// the link flag is ignored rather than cleared so the player can cycle past a no-doublefire weapon without the setting being cleared
7046 			if ( Player_ship->flags[Ship::Ship_Flags::Secondary_dual_fire] && !wip->wi_flags[Weapon::Info_Flags::No_doublefire] &&
7047 					!The_mission.ai_profile->flags[AI::Profile_Flags::Disable_player_secondary_doublefire] ) {
7048 				renderPrintf(position[0] + _slinked_offset_x, position[1] + text_y_offset, EG_NULL, "%c", Weapon_link_icon);
7049 			}
7050 
7051 			// show secondary weapon's image or print its name
7052 			if(wip->hud_image_index != -1) {
7053 				renderBitmap(wip->hud_image_index, position[0] + _sname_offset_x, position[1] + text_y_offset);
7054 			} else {
7055 				renderString(position[0] + _sname_offset_x, position[1] + text_y_offset, i ? EG_WEAPON_S1 : EG_WEAPON_S2, weapon_name);
7056 			}
7057 
7058 			// show the cooldown time
7059 			if ( (sw->secondary_bank_ammo[i] > 0) && (sw->current_secondary_bank >= 0) ) {
7060 				int ms_till_fire = timestamp_until(sw->next_secondary_fire_stamp[sw->current_secondary_bank]);
7061 				if ( (ms_till_fire >= 500) && ((wip->fire_wait >= 1 ) || (ms_till_fire > wip->fire_wait*1000)) ) {
7062 					renderPrintf(position[0] + _sreload_offset_x, position[1] + text_y_offset, EG_NULL, "%d", (int)std::lround(ms_till_fire/1000.0f));
7063 				}
7064 			}
7065 		} else {
7066 			renderString(position[0] + _sname_offset_x, position[1] + text_y_offset, i ? EG_WEAPON_S1 : EG_WEAPON_S2, weapon_name);
7067 		}
7068 
7069 		int ammo = sw->secondary_bank_ammo[i];
7070 
7071 		// print out the ammo right justified
7072 		sprintf(ammo_str, "%d", ammo);
7073 		hud_num_make_mono(ammo_str, font_num);
7074 		gr_get_string_size(&w, &h, ammo_str);
7075 
7076 		renderString(position[0] + _sammo_offset_x - w, position[1] + text_y_offset, EG_NULL, ammo_str);
7077 
7078 		bg_y_offset += _background_entry_h;
7079 		text_y_offset += _entry_h;
7080 	}
7081 
7082 	if ( num_secondaries == 0 ) {
7083 		renderBitmap(_background_entry.first_frame, position[0], position[1] + bg_y_offset);
7084 		renderString(position[0] + _sname_offset_x, position[1] + text_y_offset, EG_WEAPON_S1, XSTR( "<none>", 329));
7085 
7086 		bg_y_offset += _background_entry_h;
7087 	}
7088 
7089 	// finish drawing the background
7090 	renderBitmap(_background_last.first_frame, position[0], position[1] + bg_y_offset + _bg_last_offset_y);
7091 }
7092 
HudGaugeHardpoints()7093 HudGaugeHardpoints::HudGaugeHardpoints():
7094 HudGauge(HUD_OBJECT_HARDPOINTS, HUD_WEAPONS_GAUGE, false, false, VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY, 255, 255, 255)
7095 {
7096 
7097 }
7098 
initSizes(int w,int h)7099 void HudGaugeHardpoints::initSizes(int w, int h)
7100 {
7101 	_size[0] = w;
7102 	_size[1] = h;
7103 }
7104 
initLineWidth(float w)7105 void HudGaugeHardpoints::initLineWidth(float w)
7106 {
7107 	_line_width = w;
7108 }
7109 
initViewDir(int dir)7110 void HudGaugeHardpoints::initViewDir(int dir)
7111 {
7112 	_view_direction = dir;
7113 }
7114 
initDrawOptions(bool primary_models,bool secondary_models)7115 void HudGaugeHardpoints::initDrawOptions(bool primary_models, bool secondary_models)
7116 {
7117 	draw_primary_models = primary_models;
7118 	draw_secondary_models = secondary_models;
7119 }
7120 
render(float)7121 void HudGaugeHardpoints::render(float  /*frametime*/)
7122 {
7123 	int			sx, sy;
7124 	ship			*sp;
7125 	ship_info	*sip;
7126 	object *objp = Player_obj;
7127 
7128 	sp = &Ships[objp->instance];
7129 	sip = &Ship_info[sp->ship_info_index];
7130 
7131 	sx = position[0];
7132 	sy = position[1];
7133 
7134 	bool g3_yourself = !g3_in_frame();
7135 	angles top_view = {-PI_2,0.0f,0.0f};
7136 	angles front_view = {PI_2*2.0f,PI_2*2.0f,0.0f};
7137 	matrix	object_orient;
7138 
7139 	switch ( _view_direction ) {
7140 		case TOP:
7141 			vm_angles_2_matrix(&object_orient, &top_view);
7142 			break;
7143 		case FRONT:
7144 			vm_angles_2_matrix(&object_orient, &front_view);
7145 			break;
7146 	}
7147 
7148 	gr_screen.clip_width = _size[0];
7149 	gr_screen.clip_height = _size[1];
7150 
7151 	//Fire it up
7152 	if(g3_yourself)
7153 		g3_start_frame(1);
7154 	hud_save_restore_camera_data(1);
7155 	setClip(sx, sy, _size[0], _size[1]);
7156 
7157 	g3_set_view_matrix( &sip->closeup_pos, &vmd_identity_matrix, sip->closeup_zoom*1.5f);
7158 
7159 	gr_set_proj_matrix(Proj_fov, gr_screen.clip_aspect, Min_draw_distance, Max_draw_distance);
7160 	gr_set_view_matrix(&Eye_position, &Eye_matrix);
7161 
7162 	setGaugeColor();
7163 
7164 	//We're ready to show stuff
7165 	model_render_params render_info;
7166 
7167 	gr_stencil_clear();
7168 
7169 	const int detail_level_lock = 1;
7170 
7171 	render_info.set_detail_level_lock(detail_level_lock);
7172 	render_info.set_flags(
7173 		MR_NO_LIGHTING | MR_AUTOCENTER | MR_NO_FOGGING | MR_NO_TEXTURING | MR_NO_CULL | MR_NO_ZBUFFER | MR_STENCIL_WRITE
7174 			| MR_NO_COLOR_WRITES);
7175 	render_info.set_object_number(OBJ_INDEX(objp));
7176 
7177 	model_render_immediate( &render_info, sip->model_num, &object_orient, &vmd_zero_vector);
7178 
7179 	render_info.set_color(gauge_color);
7180 	render_info.set_flags(MR_NO_LIGHTING | MR_AUTOCENTER | MR_NO_FOGGING | MR_NO_TEXTURING | MR_NO_ZBUFFER | MR_NO_CULL | MR_STENCIL_READ);
7181 	// Factor was chosen pretty arbitrarily to make outline feature work with existing data
7182 	render_info.set_outline_thickness(15.f * _line_width);
7183 
7184 	model_render_immediate(
7185 		&render_info,
7186 		sip->model_num,
7187 		&object_orient,
7188 		&vmd_zero_vector
7189 	);
7190 
7191 	gr_stencil_set(GR_STENCIL_NONE);
7192 
7193 	// draw weapon models
7194 	int i, k;
7195 	ship_weapon *swp = &sp->weapons;
7196 	vertex draw_point;
7197 	vec3d subobj_pos;
7198 
7199 	int render_flags = MR_NO_LIGHTING | MR_AUTOCENTER | MR_NO_FOGGING | MR_NO_TEXTURING | MR_NO_ZBUFFER;
7200 
7201 	setGaugeColor();
7202 	float alpha = gr_screen.current_color.alpha / 255.0f;
7203 
7204 	//secondary weapons
7205 	int num_secondaries_rendered = 0;
7206 	if ( draw_secondary_models ) {
7207 		auto ship_pm = model_get(sip->model_num);
7208 
7209 		for (i = 0; i < swp->num_secondary_banks; i++) {
7210 			auto wip = &Weapon_info[swp->secondary_bank_weapons[i]];
7211 
7212 			if (wip->external_model_num == -1 || !sip->draw_secondary_models[i])
7213 				continue;
7214 
7215 			auto bank = &ship_pm->missile_banks[i];
7216 
7217 			if (wip->wi_flags[Weapon::Info_Flags::External_weapon_lnch]) {
7218 				for(k = 0; k < bank->num_slots; k++) {
7219 					model_render_params weapon_render_info;
7220 
7221 					weapon_render_info.set_detail_level_lock(detail_level_lock);
7222 					weapon_render_info.set_flags(render_flags);
7223 
7224 					// We need to transform the position local to the model to be in "world" space relative to the rendered outline
7225 					vec3d world_position;
7226 					vm_vec_unrotate(&world_position, &bank->pnt[k], &object_orient);
7227 
7228 					// "Bank" the external model by the angle offset
7229 					angles angs = { 0.0f, bank->external_model_angle_offset[k], 0.0f };
7230 					matrix model_orient = object_orient;
7231 					vm_rotate_matrix_by_angles(&model_orient, &angs);
7232 
7233 					model_render_immediate(&weapon_render_info, wip->external_model_num, &model_orient, &bank->pnt[k]);
7234 				}
7235 			} else {
7236 				num_secondaries_rendered = 0;
7237 
7238 				for(k = 0; k < bank->num_slots; k++)
7239 				{
7240 					auto secondary_weapon_pos = bank->pnt[k];
7241 
7242 					if (num_secondaries_rendered >= sp->weapons.secondary_bank_ammo[i])
7243 						break;
7244 
7245 					if(sp->secondary_point_reload_pct.get(i, k) <= 0.0)
7246 						continue;
7247 
7248 					model_render_params weapon_render_info;
7249 
7250 					if ( swp->current_secondary_bank == i && ( swp->secondary_next_slot[i] == k || ( swp->secondary_next_slot[i]+1 == k && sp->flags[Ship::Ship_Flags::Secondary_dual_fire] ) ) ) {
7251 						weapon_render_info.set_color(Color_bright_blue);
7252 					} else {
7253 						weapon_render_info.set_color(Color_bright_white);
7254 					}
7255 
7256 					num_secondaries_rendered++;
7257 
7258 					vm_vec_scale_add2(&secondary_weapon_pos, &vmd_z_vector, -(1.0f-sp->secondary_point_reload_pct.get(i, k)) * model_get(wip->external_model_num)->rad);
7259 
7260 					weapon_render_info.set_detail_level_lock(detail_level_lock);
7261 					weapon_render_info.set_flags(render_flags);
7262 
7263 					// We need to transform the position local to the model to be in "world" space relative to the rendered outline
7264 					vec3d world_position;
7265 					vm_vec_unrotate(&world_position, &secondary_weapon_pos, &object_orient);
7266 
7267 					// "Bank" the external model by the angle offset
7268 					angles angs = { 0.0f, bank->external_model_angle_offset[k], 0.0f };
7269 					matrix model_orient = object_orient;
7270 					vm_rotate_matrix_by_angles(&model_orient, &angs);
7271 
7272 					model_render_immediate(&weapon_render_info, wip->external_model_num, &model_orient, &world_position);
7273 				}
7274 			}
7275 		}
7276 	}
7277 
7278 	resetClip();
7279 
7280 	setGaugeColor(HUD_C_BRIGHT);
7281 
7282 	//primary weapons
7283 	if ( draw_primary_models ) {
7284 		auto ship_pm = model_get(sip->model_num);
7285 
7286 		for ( i = 0; i < swp->num_primary_banks; i++ ) {
7287 			auto wip = &Weapon_info[swp->primary_bank_weapons[i]];
7288 			auto bank = &ship_pm->gun_banks[i];
7289 
7290 			for ( k = 0; k < bank->num_slots; k++ ) {
7291 				if ( wip->external_model_num < 0 || !sip->draw_primary_models[i] ) {
7292 					vm_vec_unrotate(&subobj_pos, &bank->pnt[k], &object_orient);
7293 					//vm_vec_sub(&subobj_pos, &Eye_position, &subobj_pos);
7294 					//g3_rotate_vertex(&draw_point, &bank->pnt[k]);
7295 
7296 					g3_rotate_vertex(&draw_point, &subobj_pos);
7297 					g3_project_vertex(&draw_point);
7298 
7299 					//resize(&width, &height);
7300 
7301 					//unsize(&xc, &yc);
7302 					//unsize(&draw_point.screen.xyw.x, &draw_point.screen.xyw.y);
7303 
7304 					renderCircle((int)draw_point.screen.xyw.x + position[0], (int)draw_point.screen.xyw.y + position[1], 10);
7305 					//renderCircle(xc, yc, 25);
7306 				} else {
7307 					model_render_params weapon_render_info;
7308 					weapon_render_info.set_detail_level_lock(detail_level_lock);
7309 					weapon_render_info.set_flags(render_flags);
7310 					weapon_render_info.set_alpha(alpha);
7311 
7312 					// We need to transform the position local to the model to be in "world" space relative to the rendered outline
7313 					vec3d world_position;
7314 					vm_vec_unrotate(&world_position, &bank->pnt[k], &object_orient);
7315 
7316 					// "Bank" the external model by the angle offset
7317 					angles angs = { 0.0f, bank->external_model_angle_offset[k], 0.0f };
7318 					matrix model_orient = object_orient;
7319 					vm_rotate_matrix_by_angles(&model_orient, &angs);
7320 
7321 					model_render_immediate(&weapon_render_info, wip->external_model_num, swp->primary_bank_external_model_instance[i], &model_orient, &world_position);
7322 				}
7323 			}
7324 		}
7325 	}
7326 
7327 	//We're done
7328 	gr_end_view_matrix();
7329 	gr_end_proj_matrix();
7330 	if(g3_yourself)
7331 		g3_end_frame();
7332 
7333 	hud_save_restore_camera_data(0);
7334 }
7335