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 "ai/ai.h"
14 #include "debugconsole/console.h"
15 #include "gamesequence/gamesequence.h"
16 #include "gamesnd/gamesnd.h"
17 #include "globalincs/linklist.h"
18 #include "hud/hudlock.h"
19 #include "iff_defs/iff_defs.h"
20 #include "io/timer.h"
21 #include "mission/missionparse.h"
22 #include "network/multi.h"
23 #include "object/object.h"
24 #include "playerman/player.h"
25 #include "render/3d.h"
26 #include "ship/ship.h"
27 #include "weapon/emp.h"
28 #include "weapon/weapon.h"
29 
30 
31 // Used for aspect locks. -MageKing17
32 #define VIRTUAL_FRAME_HALF_WIDTH	320.0f
33 #define VIRTUAL_FRAME_HALF_HEIGHT	240.0f
34 
35 
36 vec3d lock_world_pos;
37 
38 static float Lock_start_dist;
39 
40 sound_handle Missile_track_loop = sound_handle::invalid();
41 sound_handle Missile_lock_loop  = sound_handle::invalid();
42 
43 int Lock_target_box_width[GR_NUM_RESOLUTIONS] = {
44 	19,
45 	30
46 };
47 int Lock_target_box_height[GR_NUM_RESOLUTIONS] = {
48 	19,
49 	30
50 };
51 
52 // the locked triangles (that orbit lock indicator) dimensions
53 float Lock_triangle_base[GR_NUM_RESOLUTIONS] = {
54 	4.0f,
55 	6.5f
56 };
57 float Lock_triangle_height[GR_NUM_RESOLUTIONS] = {
58 	4.0f,
59 	6.5f
60 };
61 
62 int Lock_gauge_half_w[NUM_HUD_RETICLE_STYLES][GR_NUM_RESOLUTIONS] = {
63 	{ 15, 24 },
64 	{ 17, 28 }
65 };
66 int Lock_gauge_half_h[GR_NUM_RESOLUTIONS] = {
67 	15,
68 	25
69 };
70 
71 #define LOCK_GAUGE_BLINK_RATE			5			// blinks/sec
72 
73 int Lockspin_half_w[NUM_HUD_RETICLE_STYLES][GR_NUM_RESOLUTIONS] = {
74 	{ 16, 26 },
75 	{ 31, 50 }
76 };
77 int Lockspin_half_h[NUM_HUD_RETICLE_STYLES][GR_NUM_RESOLUTIONS] = {
78 	{ 16, 26 },
79 	{ 32, 52 }
80 };
81 
82 char Lock_fname[NUM_HUD_RETICLE_STYLES][GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN] =
83 {
84 	{ "lock1_fs1", "2_lock1_fs1" },
85 	{ "lock1", "2_lock1" }
86 };
87 
88 char Lockspin_fname[NUM_HUD_RETICLE_STYLES][GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN] =
89 {
90 	{ "lockspin_fs1", "2_lockspin_fs1" },
91 	{ "lockspin", "2_lockspin" }
92 };
93 
94 void hud_lock_determine_lock_point(vec3d *lock_world_pos_out);
95 void hud_lock_determine_lock_point(lock_info *current_lock);
96 
97 // hud_init_missile_lock() is called at the beginning of a mission
98 //
hud_init_missile_lock()99 void hud_init_missile_lock()
100 {
101 	Players[Player_num].lock_indicator_start_x = -1;
102 	Players[Player_num].lock_indicator_start_y = -1;
103 	Players[Player_num].lock_indicator_visible = 0;
104 	Player_ai->current_target_is_locked = 0;
105 
106 	Player_ai->last_secondary_index = -1;
107 }
108 
HudGaugeLock()109 HudGaugeLock::HudGaugeLock():
110 HudGauge(HUD_OBJECT_LOCK, HUD_LEAD_INDICATOR, false, false, VM_DEAD_VIEW, 255, 255, 255)
111 {
112 }
113 
initGaugeHalfSize(int w,int h)114 void HudGaugeLock::initGaugeHalfSize(int w, int h)
115 {
116 	Lock_gauge_half_w = w;
117 	Lock_gauge_half_h = h;
118 }
119 
initSpinHalfSize(int w,int h)120 void HudGaugeLock::initSpinHalfSize(int w, int h)
121 {
122 	Lockspin_half_w = w;
123 	Lockspin_half_h = h;
124 }
125 
initTriHeight(float h)126 void HudGaugeLock::initTriHeight(float h)
127 {
128 	Lock_triangle_height = h;
129 }
130 
initTriBase(float length)131 void HudGaugeLock::initTriBase(float length)
132 {
133 	Lock_triangle_base = length;
134 }
135 
initTargetBoxSize(int w,int h)136 void HudGaugeLock::initTargetBoxSize(int w, int h)
137 {
138 	Lock_target_box_width = w;
139 	Lock_target_box_height = h;
140 }
141 
initLoopLockedAnim(bool loop)142 void HudGaugeLock::initLoopLockedAnim(bool loop)
143 {
144 	loop_locked_anim = loop;
145 }
146 
initBlinkLockedAnim(bool blink)147 void HudGaugeLock::initBlinkLockedAnim(bool blink)
148 {
149 	blink_locked_anim = blink;
150 }
151 
initBitmaps(char * lock_gauge_fname,char * lock_anim_fname)152 void HudGaugeLock::initBitmaps(char *lock_gauge_fname, char *lock_anim_fname)
153 {
154 	hud_anim_init(&Lock_gauge, 0, 0, lock_gauge_fname);
155 	hud_anim_load(&Lock_gauge);
156 
157 	hud_anim_init(&Lock_anim, 0, 0, lock_anim_fname);
158 	hud_anim_load(&Lock_anim);
159 }
160 
initialize()161 void HudGaugeLock::initialize()
162 {
163 	Lock_gauge_draw_stamp = -1;
164 	Lock_gauge_draw = 0;
165 	Rotate_time_id = 1;
166 	Last_lock_status = false;
167 
168 	Lock_anim.time_elapsed = 0.0f;
169 	Lock_gauge.time_elapsed = 0.0f;
170 
171 	HudGauge::initialize();
172 }
173 
174 // hud_show_lock_indicator() will display the lock indicator for homing missiles.
175 // lock_point_pos should be the world coordinates of the target being locked. Assuming all the
176 // necessary locking calculations are done for this frame, this function will compute
177 // where the indicator should be relative to the player's viewpoint and will render accordingly.
renderOld(float frametime)178 void HudGaugeLock::renderOld(float frametime)
179 {
180 	int			target_objnum, sx, sy;
181 	object		*targetp;
182 	vertex lock_point;
183 
184 	bool locked = Player_ai->current_target_is_locked ? true : false;
185 	bool reset_timers = false;
186 
187 	if ( locked != Last_lock_status ) {
188 		// check if player lock status has changed since the last frame.
189 		reset_timers = true;
190 		Last_lock_status = locked;
191 	}
192 
193 	if (Player_ai->target_objnum == -1) {
194 		return;
195 	}
196 
197 	if (Player->target_is_dying) {
198 		return;
199 	}
200 
201 	if (!Players[Player_num].lock_indicator_visible){
202 		return;
203 	}
204 
205 	target_objnum = Player_ai->target_objnum;
206 	Assert(target_objnum != -1);
207 	targetp = &Objects[target_objnum];
208 
209 	// check to see if there are any missile to fire.. we don't want to show the
210 	// lock indicator if there are missiles to fire.
211 	if ( !ship_secondary_bank_has_ammo(Player_obj->instance) ) {
212 		return;
213 	}
214 
215 	bool in_frame = g3_in_frame() > 0;
216 	if(!in_frame)
217 		g3_start_frame(0);
218 	gr_set_screen_scale(base_w, base_h);
219 
220 	// Get the target's current position on the screen. If he's not on there,
221 	// we're not going to draw the lock indicator even if he's in front
222 	// of our ship, so bail out.
223 	g3_rotate_vertex(&lock_point, &lock_world_pos);
224 	g3_project_vertex(&lock_point);
225 	if (lock_point.codes & PF_OVERFLOW) {
226 		gr_reset_screen_scale();
227 
228 		if(!in_frame)
229 			g3_end_frame();
230 
231 		return;
232 	}
233 
234 	hud_set_iff_color(targetp);
235 //	nprintf(("Alan","lockx: %d, locky: %d TargetX: %d, TargetY: %d\n", Players[Player_num].lock_indicator_x, Players[Player_num].lock_indicator_y, Player->current_target_sx, Player->current_target_sy));
236 
237 	// We have the coordinates of the lock indicator relative to the target in our "virtual frame"
238 	// so, we calculate where it should be drawn based on the player's viewpoint.
239 	if (Player_ai->current_target_is_locked) {
240 		sx = fl2i(lock_point.screen.xyw.x);
241 		sy = fl2i(lock_point.screen.xyw.y);
242 		gr_unsize_screen_pos(&sx, &sy);
243 
244 		// show the rotating triangles if target is locked
245 		renderLockTriangles(sx, sy, frametime);
246 
247 		if ( reset_timers ) {
248 			Lock_gauge.time_elapsed = 0.0f;
249 		}
250 	} else {
251 		const float scaling_factor = (gr_screen.clip_center_x < gr_screen.clip_center_y) ? (gr_screen.clip_center_x / VIRTUAL_FRAME_HALF_WIDTH) : (gr_screen.clip_center_y / VIRTUAL_FRAME_HALF_HEIGHT);
252 		sx = fl2i(lock_point.screen.xyw.x) - fl2i(i2fl(Player->current_target_sx - Players[Player_num].lock_indicator_x) * scaling_factor);
253 		sy = fl2i(lock_point.screen.xyw.y) - fl2i(i2fl(Player->current_target_sy - Players[Player_num].lock_indicator_y) * scaling_factor);
254 		gr_unsize_screen_pos(&sx, &sy);
255 
256 		if ( reset_timers ) {
257 			Lock_gauge_draw_stamp = -1;
258 			Lock_gauge_draw = 0;
259 			Lock_anim.time_elapsed = 0.0f;
260 		}
261 	}
262 
263 	// show locked indicator
264 	Lock_gauge.sx = sx - Lock_gauge_half_w;
265 	Lock_gauge.sy = sy - Lock_gauge_half_h;
266 	if (Player_ai->current_target_is_locked) {
267 		hud_anim_render(&Lock_gauge, 0.0f, 1);
268 	} else {
269 		hud_anim_render(&Lock_gauge, frametime, 1);
270 	}
271 
272 	gr_reset_screen_scale();
273 	if(!in_frame)
274 		g3_end_frame();
275 }
276 
277 // hud_show_lock_indicator() will display the lock indicator for homing missiles.
278 // lock_point_pos should be the world coordinates of the target being locked. Assuming all the
279 // necessary locking calculations are done for this frame, this function will compute
280 // where the indicator should be relative to the player's viewpoint and will render accordingly.
render(float frametime)281 void HudGaugeLock::render(float frametime)
282 {
283 	size_t i;
284 	lock_info *current_lock;
285 
286 	vertex lock_point;
287 	int sx, sy;
288 
289 	// check to see if there are any missile to fire.. we don't want to show the
290 	// lock indicator if there are missiles to fire.
291 	if ( !ship_secondary_bank_has_ammo(Player_obj->instance) ) {
292 		return;
293 	}
294 
295 	bool in_frame = g3_in_frame() > 0;
296 	if(!in_frame)
297 		g3_start_frame(0);
298 	gr_set_screen_scale(base_w, base_h);
299 
300 	// go through all present lock indicators
301 	for ( i = 0; i < Player_ship->missile_locks.size(); ++i ) {
302 		current_lock = &Player_ship->missile_locks[i];
303 
304 		if ( !current_lock->indicator_visible ) {
305 			continue;
306 		}
307 
308 		// Get the target's current position on the screen. If he's not on there,
309 		// we're not going to draw the lock indicator even if he's in front
310 		// of our ship, so bail out.
311 		g3_rotate_vertex(&lock_point, &current_lock->world_pos);
312 		g3_project_vertex(&lock_point);
313 
314 		if (lock_point.codes & PF_OVERFLOW) {
315 			continue;
316 		}
317 
318 		hud_set_iff_color(current_lock->obj);
319 
320 		// We have the coordinates of the lock indicator relative to the target in our "virtual frame"
321 		// so, we calculate where it should be drawn based on the player's viewpoint.
322 		if ( current_lock->locked ) {
323 			sx = fl2i(lock_point.screen.xyw.x);
324 			sy = fl2i(lock_point.screen.xyw.y);
325 			gr_unsize_screen_pos(&sx, &sy);
326 
327 			// show the rotating triangles if target is locked
328 			renderLockTrianglesNew(sx, sy, frametime, current_lock);
329 		} else {
330 			const float scaling_factor = (gr_screen.clip_center_x < gr_screen.clip_center_y)
331 											 ? (gr_screen.clip_center_x / VIRTUAL_FRAME_HALF_WIDTH)
332 											 : (gr_screen.clip_center_y / VIRTUAL_FRAME_HALF_HEIGHT);
333 			sx = fl2i(lock_point.screen.xyw.x) - fl2i(i2fl(current_lock->current_target_sx - current_lock->indicator_x) * scaling_factor);
334 			sy = fl2i(lock_point.screen.xyw.y) - fl2i(i2fl(current_lock->current_target_sy - current_lock->indicator_y) * scaling_factor);
335 			gr_unsize_screen_pos(&sx, &sy);
336 		}
337 
338 		Lock_gauge.sx = sx - Lock_gauge_half_w;
339 		Lock_gauge.sy = sy - Lock_gauge_half_h;
340 		if( current_lock->locked ){
341 			current_lock->lock_gauge_time_elapsed = 0.0f;
342 			Lock_gauge.time_elapsed = current_lock->lock_gauge_time_elapsed;
343 			hud_anim_render(&Lock_gauge, 0.0f, 1);
344 		} else {
345 			// manually track the animation time, since we may have more than one lock
346 			current_lock->lock_gauge_time_elapsed += frametime;
347 			if (current_lock->lock_gauge_time_elapsed > Lock_gauge.total_time) {
348 				current_lock->lock_gauge_time_elapsed = 0.0f;
349 			}
350 			Lock_gauge.time_elapsed = current_lock->lock_gauge_time_elapsed;
351 
352 			hud_anim_render(&Lock_gauge, 0.0f, 1);
353 		}
354 	}
355 
356 	gr_reset_screen_scale();
357 	if(!in_frame)
358 		g3_end_frame();
359 }
360 
361 // Reset data used for player lock indicator
hud_lock_reset(float lock_time_scale)362 void hud_lock_reset(float lock_time_scale)
363 {
364 	weapon_info	*wip;
365 	ship_weapon	*swp;
366 
367 	swp = &Player_ship->weapons;
368 
369 	if ((swp->current_secondary_bank >= 0) && (swp->secondary_bank_weapons[swp->current_secondary_bank] >= 0)) {
370 		Assert(swp->current_secondary_bank < MAX_SHIP_SECONDARY_BANKS);
371 		Assert(swp->secondary_bank_weapons[swp->current_secondary_bank] < weapon_info_size());
372 		wip = &Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]];
373 		Player->lock_time_to_target = i2fl(wip->min_lock_time*lock_time_scale);
374 	} else {
375 		Player->lock_time_to_target = 0.0f;
376 	}
377 
378 	Player_ai->current_target_is_locked = 0;
379 	Players[Player_num].lock_indicator_visible = 0;
380 	Player->target_in_lock_cone = 0;
381 	Player->current_target_sx = -1;
382 	Player->current_target_sy = -1;
383 	Player->locking_subsys=NULL;
384 	Player->locking_on_center=0;
385 	Player->locking_subsys_parent=-1;
386 	hud_stop_looped_locking_sounds();
387 
388 	// reset the lock anim time elapsed
389 //	Lock_anim.time_elapsed = 0.0f;
390 
391 	for ( auto & missile_locks : Player_ship->missile_locks) {
392 		ship_clear_lock(&missile_locks);
393 	}
394 }
395 
396 // Determine if the locking code has a point to track
hud_lock_has_homing_point()397 int hud_lock_has_homing_point()
398 {
399 	if ( Player_ai->targeted_subsys || Player->locking_subsys || Player->locking_on_center ) {
400 		return 1;
401 	}
402 	return 0;
403 }
404 
405 int Nebula_sec_range = 0;
406 DCF_BOOL(nebula_sec_range, Nebula_sec_range);
407 
408 // Determine if point to lock on is in range
hud_lock_target_in_range()409 int hud_lock_target_in_range()
410 {
411 	vec3d		target_world_pos;
412 	object		*targetp;
413 
414 	if ( !hud_lock_has_homing_point() ) {
415 		return 0;
416 	}
417 
418 	targetp = &Objects[Player_ai->target_objnum];
419 
420 	if ( Player_ai->targeted_subsys != NULL ) {
421 		vm_vec_unrotate(&target_world_pos, &Player_ai->targeted_subsys->system_info->pnt, &targetp->orient);
422 		vm_vec_add2(&target_world_pos, &targetp->pos);
423 	} else {
424 		if ( Player->locking_subsys ) {
425 			vm_vec_unrotate(&target_world_pos, &Player->locking_subsys->system_info->pnt, &targetp->orient);
426 			vm_vec_add2(&target_world_pos, &targetp->pos);
427 		} else {
428 			Assert(Player->locking_on_center);
429 			target_world_pos = targetp->pos;
430 		}
431 	}
432 	ship_weapon* swp = &Player_ship->weapons;
433 	weapon_info* wip = &Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]];
434 
435 	return weapon_secondary_world_pos_in_range(Player_obj, wip, &target_world_pos);
436 }
437 
hud_abort_lock()438 int hud_abort_lock()
439 {
440 	int target_team;
441 
442 	weapon_info	*wip;
443 	ship_weapon	*swp;
444 
445 	if ( Player_ship->weapons.num_secondary_banks <= 0 ) {
446 		return 1;
447 	}
448 
449 	if ( Player_ship->weapons.current_secondary_bank < 0 ) {
450 		return 1;
451 	}
452 
453 	// check to see if there are any missile to fire.. we don't want to show the
454 	// lock indicator if there are no missiles to fire.
455 	if ( !ship_secondary_bank_has_ammo(Player_obj->instance) ) {
456 		return 1;
457 	}
458 
459 	if ( Player_ship->flags[Ship::Ship_Flags::No_secondary_lockon] ) {
460 		return 1;
461 	}
462 
463 	swp = &Player_ship->weapons;
464 	wip = &Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]];
465 
466 	// if we're on the same team and the team doesn't attack itself, then don't lock!
467 	if ( (Player_ai->target_objnum >= 0) ) {
468 		target_team = obj_team(&Objects[Player_ai->target_objnum]);
469 
470 		if ( ( Player_ship->team == target_team) && ( !iff_x_attacks_y(Player_ship->team, target_team) )
471 			&& !weapon_has_iff_restrictions(wip)) {
472 			// if we're in multiplayer dogfight, ignore this
473 			// remember to check if we're firing a missile that doesn't require a current target
474 			if(!MULTI_DOGFIGHT || wip->target_restrict == LR_ANY_TARGETS) {
475 				return 1;
476 			}
477 		}
478 	}
479 
480 	// Reset locks on launch if this weapon allows it and if we've recently fired.
481 	if ( wip->launch_reset_locks && !timestamp_elapsed(swp->next_secondary_fire_stamp[swp->current_secondary_bank]) ) {
482 		return 1;
483 	}
484 
485 	return 0;
486 }
487 
488 // determine if the subsystem to lock on to has a direct line of sight
hud_lock_on_subsys_ok()489 int hud_lock_on_subsys_ok()
490 {
491 	ship_subsys		*subsys;
492 	vec3d			subobj_pos;
493 	object			*target_objp;
494 	int				in_sight=0;
495 
496 	Assert(Player_ai->target_objnum >= 0);
497 	target_objp	= &Objects[Player_ai->target_objnum];
498 
499 	subsys = Player_ai->targeted_subsys;
500 	if ( !subsys ) {
501 		return 0;
502 	}
503 
504 	vm_vec_unrotate(&subobj_pos, &subsys->system_info->pnt, &target_objp->orient);
505 	vm_vec_add2(&subobj_pos, &target_objp->pos);
506 
507 	if ( Player->subsys_in_view < 0 ) {
508 		in_sight = ship_subsystem_in_sight(target_objp, subsys, &View_position, &subobj_pos);
509 	} else {
510 		in_sight = Player->subsys_in_view;
511 	}
512 
513 	return in_sight;
514 }
515 
516 // Determine if locking point is in the locking cone
hud_lock_check_if_target_in_lock_cone()517 void hud_lock_check_if_target_in_lock_cone()
518 {
519 	float	dot;
520 	vec3d	vec_to_target;
521 
522 	vm_vec_normalized_dir(&vec_to_target, &lock_world_pos, &Player_obj->pos);
523 	dot = vm_vec_dot(&Player_obj->orient.vec.fvec, &vec_to_target);
524 
525 	if ( dot > 0.85) {
526 		Player->target_in_lock_cone = 1;
527 	} else {
528 		Player->target_in_lock_cone = 0;
529 	}
530 }
531 
hud_lock_check_if_target_in_lock_cone(lock_info * current_lock,weapon_info * wip)532 void hud_lock_check_if_target_in_lock_cone(lock_info *current_lock, weapon_info *wip)
533 {
534 	float	dot;
535 	vec3d	vec_to_target;
536 
537 	vm_vec_normalized_dir(&vec_to_target, &current_lock->world_pos, &Player_obj->pos);
538 	dot = vm_vec_dot(&Player_obj->orient.vec.fvec, &vec_to_target);
539 
540 	current_lock->target_in_lock_cone = dot > wip->lock_fov;
541 }
542 
543 // return 1 if current secondary weapon is different than previous secondary weapon
hud_lock_secondary_weapon_changed(ship_weapon * swp)544 int hud_lock_secondary_weapon_changed(ship_weapon *swp)
545 {
546 
547 	if ( swp->current_secondary_bank != Player_ai->last_secondary_index ) {
548 		return 1;
549 	}
550 
551 	return 0;
552 /*
553 	int last_wi_index = -1;
554 	int current_wi_index = -1;
555 
556 
557 	// do a quick out if same bank is selected
558 	if ( swp->current_secondary_bank == Player_ai->last_secondary_index ) {
559 		return 0;
560 	}
561 
562 	// bank has changed, but it still may be the same weapon type
563 	if ( swp->current_secondary_bank >= 0 ) {
564 		current_wi_index = swp->secondary_bank_weapons[swp->current_secondary_bank];
565 	}
566 
567 	if ( Player_ai->last_secondary_index >= 0 ) {
568 		last_wi_index = swp->secondary_bank_weapons[Player_ai->last_secondary_index];
569 	}
570 
571 	if ( current_wi_index != last_wi_index ) {
572 		return 1;
573 	}
574 
575 	return 0;
576 */
577 
578 }
579 
580 // hud_do_lock_indicator() manages missle locking, both the non-rendering calculations and the 2D HUD rendering
hud_do_lock_indicator(float frametime)581 void hud_do_lock_indicator(float frametime)
582 {
583 	ship_weapon *swp;
584 	weapon_info	*wip;
585 
586 	// if i'm a multiplayer observer, bail here
587 	if((Game_mode & GM_MULTIPLAYER) && ((Net_player->flags & NETINFO_FLAG_OBSERVER) || (Player_obj->type == OBJ_OBSERVER)) ){
588 		return;
589 	}
590 
591 	Assert(Player_ai->target_objnum >= 0);
592 
593 	// be sure to unset this flag, then possibly set later in this function so that
594 	// threat indicators work properly.
595 	Player_ai->ai_flags.remove(AI::AI_Flags::Seek_lock);
596 
597 	if ( hud_abort_lock() ) {
598 		hud_lock_reset();
599 		return;
600 	}
601 
602 	// if there is an EMP effect active, never update lock
603 	if(emp_active_local()){
604 		hud_lock_reset();
605 		return;
606 	}
607 
608 	swp = &Player_ship->weapons;
609 	wip = &Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]];
610 
611 	Lock_start_dist = wip->min_lock_time * wip->lock_pixels_per_sec;
612 
613 	// if secondary weapons change, reset the lock
614 	if ( hud_lock_secondary_weapon_changed(swp) ) {
615 		hud_lock_reset();
616 	}
617 
618 	Player_ai->last_secondary_index = swp->current_secondary_bank;
619 
620 	object *tobjp = &Objects[Player_ai->target_objnum];
621 	vec3d dir_to_target;
622 	vm_vec_normalized_dir(&dir_to_target, &tobjp->pos, &Player_obj->pos);
623 
624 	if ( !(wip->is_locked_homing()) ) {
625 		hud_lock_reset();
626 		return;
627 	}
628 
629 	// Allow locking on ships and bombs (only targeted weapon allowed is a bomb, so don't bother checking flags)
630 	if ( (Objects[Player_ai->target_objnum].type != OBJ_SHIP) && (Objects[Player_ai->target_objnum].type != OBJ_WEAPON) ) {
631 		hud_lock_reset();
632 		return;
633 	}
634 
635 	// Javelins must lock on engines if locking on a ship and those must be in sight
636 	if (wip->wi_flags[Weapon::Info_Flags::Homing_javelin] &&
637 		tobjp->type == OBJ_SHIP &&
638 		Player->locking_subsys != NULL) {
639 			vec3d subobj_pos;
640 			vm_vec_unrotate(&subobj_pos, &Player->locking_subsys->system_info->pnt, &tobjp->orient);
641 			vm_vec_add2(&subobj_pos, &tobjp->pos);
642 			int target_subsys_in_sight = ship_subsystem_in_sight(tobjp, Player->locking_subsys, &Player_obj->pos, &subobj_pos);
643 
644 			if (!target_subsys_in_sight || Player->locking_subsys->system_info->type != SUBSYSTEM_ENGINE) {
645 				Player->locking_subsys =
646 					ship_get_closest_subsys_in_sight(&Ships[tobjp->instance], SUBSYSTEM_ENGINE, &Player_obj->pos);
647 			}
648 	}
649 
650 	if (wip->wi_flags[Weapon::Info_Flags::Homing_javelin] &&
651 		tobjp->type == OBJ_SHIP &&
652 		Player->locking_subsys == NULL) {
653 			Player->locking_subsys =
654 				ship_get_closest_subsys_in_sight(&Ships[tobjp->instance], SUBSYSTEM_ENGINE, &Player_obj->pos);
655 
656 			if (Player->locking_subsys == NULL) {
657 				hud_lock_reset();
658 				return;
659 			}
660 	}
661 
662 	hud_lock_determine_lock_point(&lock_world_pos);
663 
664 	if ( !hud_lock_has_homing_point() ) {
665 		Player->target_in_lock_cone=0;
666 	}
667 
668 	hud_lock_check_if_target_in_lock_cone();
669 
670 	// check if the target is within range of the current secondary weapon.  If it is not,
671 	// a lock will not be detected
672 	if ( !hud_lock_target_in_range() ) {
673 		Player->target_in_lock_cone = 0;
674 	}
675 
676 	// If locking on a subsystem, and not in sight... can't lock
677 	//	Changed by MK on 4/3/98.  It was confusing me that my hornets would not lock on my target.
678 	//	It will now be confusing that they lock, but don't home on your subsystem, but I think that's preferable.
679 	//	Often you really care about destroying the target, not just the subsystem.
680 	/*if ( Player_ai->targeted_subsys ) {
681 		if ( !hud_lock_on_subsys_ok() ) {
682 			Player->target_in_lock_cone=0;
683 		}
684 	}*/
685 
686 	if ( !Player->target_in_lock_cone ) {
687 		Player->locking_on_center=0;
688 		Player->locking_subsys_parent=-1;
689 		Player->locking_subsys=NULL;
690 	}
691 
692 	hud_calculate_lock_position(frametime);
693 
694 	if (!Players[Player_num].lock_indicator_visible)
695 		return;
696 
697 	if (Player_ai->current_target_is_locked) {
698 		if (Missile_track_loop.isValid()) {
699 			snd_stop(Missile_track_loop);
700 			Missile_track_loop = sound_handle::invalid();
701 
702 			if (wip->hud_locked_snd.isValid())
703 			{
704 				Missile_lock_loop = snd_play(gamesnd_get_game_sound(wip->hud_locked_snd));
705 			}
706 			else
707 			{
708 				Missile_lock_loop = snd_play(gamesnd_get_game_sound(ship_get_sound(Player_obj, GameSounds::MISSILE_LOCK)));
709 			}
710 		}
711 	}
712 	else {
713 		Player_ai->ai_flags.set(AI::AI_Flags::Seek_lock);		// set this flag so multiplayer's properly track lock on other ships
714 		if (Missile_lock_loop.isValid() && snd_is_playing(Missile_lock_loop)) {
715 			snd_stop(Missile_lock_loop);
716 			Missile_lock_loop = sound_handle::invalid();
717 		}
718 	}
719 }
720 
hud_lock_acquire_current_target(object * target_objp,ship_subsys * target_subsys)721 void hud_lock_acquire_current_target(object *target_objp, ship_subsys *target_subsys)
722 {
723 	ship			*target_shipp=nullptr;
724 	int			lock_in_range=0;
725 	float			best_lock_dot=-1.0f, lock_dot=-1.0f;
726 	ship_subsys	*ss;
727 	vec3d		subsys_world_pos, vec_to_lock;
728 
729 	if ( target_objp->type == OBJ_SHIP ) {
730 		target_shipp = &Ships[target_objp->instance];
731 	}
732 
733 	ship_weapon* swp = &Player_ship->weapons;
734 	weapon_info* wip = &Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]];
735 
736 	// Reset target subsys in case it isn't needed
737 	if (target_subsys != nullptr) target_subsys = nullptr;
738 
739 	// if a large ship, lock to pos closest to center and within range
740 	if ( (target_shipp) && (Ship_info[target_shipp->ship_info_index].is_big_or_huge()) ) {
741 		// check all the subsystems and the center of the ship
742 
743 		// assume best lock pos is the center of the ship
744 		lock_in_range = weapon_secondary_world_pos_in_range(Player_obj, wip, &target_objp->pos);
745 		vm_vec_normalized_dir(&vec_to_lock, &target_objp->pos, &Player_obj->pos);
746 		if ( lock_in_range ) {
747 			best_lock_dot=vm_vec_dot(&Player_obj->orient.vec.fvec, &vec_to_lock);
748 		}
749 		// take center if reasonable dot
750 		if ( best_lock_dot > 0.95 ) {
751 			return;
752 		}
753 
754 		// iterate through subsystems to see if we can get a better choice
755 		ss = GET_FIRST(&target_shipp->subsys_list);
756 		while ( ss != END_OF_LIST( &target_shipp->subsys_list ) ) {
757 
758 			// get world pos of subsystem
759 			get_subsystem_world_pos(target_objp, ss, &subsys_world_pos);
760 
761 			if ( weapon_secondary_world_pos_in_range(Player_obj, wip, &subsys_world_pos) ) {
762 				vm_vec_normalized_dir(&vec_to_lock, &subsys_world_pos, &Player_obj->pos);
763 				lock_dot=vm_vec_dot(&Player_obj->orient.vec.fvec, &vec_to_lock);
764 				if ( lock_dot > best_lock_dot ) {
765 					best_lock_dot=lock_dot;
766 					target_subsys = ss;
767 				}
768 			}
769 			ss = GET_NEXT( ss );
770 		}
771 	}
772 
773 }
774 
hud_lock_acquire_uncaged_subsystem(weapon_info * wip,lock_info * lock,float * best_dot,int * least_num_locks)775 void hud_lock_acquire_uncaged_subsystem(weapon_info *wip, lock_info *lock, float *best_dot, int *least_num_locks)
776 {
777 	Assert( lock->obj->type == OBJ_SHIP );
778 
779 	ship *sp = &Ships[lock->obj->instance];
780 
781 	float ss_dot;
782 
783 	int current_num_locks = 0;
784 
785 	if ( Ship_info[sp->ship_info_index].is_big_or_huge() ) {
786 		for (ship_subsys* ss = GET_FIRST(&sp->subsys_list); ss != END_OF_LIST(&sp->subsys_list); ss = GET_NEXT(ss) ) {
787 
788 			if (!weapon_multilock_can_lock_on_subsys(Player_obj, lock->obj, ss, wip, &ss_dot))
789 				continue;
790 
791 			// check for existing locks
792 			current_num_locks = 0;
793 			bool actively_locking = false;
794 
795 			for ( auto & missile_lock : Player_ship->missile_locks) {
796 				if (missile_lock.obj != nullptr && OBJ_INDEX(missile_lock.obj) == OBJ_INDEX(lock->obj) ) {
797 					if (missile_lock.subsys != nullptr && missile_lock.subsys == ss ) {
798 						if ( !missile_lock.locked ) {
799 							// we're already currently locking on this subsystem so let's not throw another aspect lock on it.
800 							actively_locking = true;
801 							continue;
802 						}
803 
804 						++current_num_locks;
805 					}
806 				}
807 			}
808 
809 			if ( !actively_locking
810 				&& current_num_locks < wip->max_seekers_per_target
811 				&& current_num_locks <= *least_num_locks
812 				&& ss_dot > *best_dot ) {
813 					lock->subsys = ss;
814 					*best_dot = ss_dot;
815 					*least_num_locks = current_num_locks;
816 			}
817 		}
818 	}
819 }
820 
hud_lock_acquire_uncaged_target(lock_info * current_lock,weapon_info * wip)821 void hud_lock_acquire_uncaged_target(lock_info *current_lock, weapon_info *wip)
822 {
823 	object *A;
824 	float dot;
825 
826 	size_t i = 0;
827 
828 	object* best_obj = nullptr;
829 	ship_subsys *best_subsys = nullptr;
830 
831 	float best_dot = 0.0f;
832 
833 	int current_num_locks = 0;
834 	int least_num_locks = INT_MAX;
835 	bool actively_locking = false;
836 
837 	for ( A = GET_FIRST(&obj_used_list); A !=END_OF_LIST(&obj_used_list); A = GET_NEXT(A) ) {
838 		ship* sp = &Ships[A->instance];
839 
840 		if (!weapon_multilock_can_lock_on_target(Player_obj, A, wip, &dot))
841 			continue;
842 
843 		bool in_range = weapon_secondary_world_pos_in_range(Player_obj, wip, &A->pos);
844 
845 		if ( Ship_info[sp->ship_info_index].is_big_or_huge() ) {
846 			lock_info temp_lock;
847 
848 			temp_lock.obj = A;
849 			temp_lock.subsys = nullptr;
850 
851 			float ss_dot = 0.0f;
852 			int ss_num_locks = INT_MAX;
853 
854 			hud_lock_acquire_uncaged_subsystem(wip, &temp_lock, &ss_dot, &ss_num_locks);
855 
856 			if ( temp_lock.subsys != nullptr && ss_num_locks < wip->max_seekers_per_target && ss_num_locks <= least_num_locks && ss_dot > best_dot ) {
857 				best_subsys = temp_lock.subsys;
858 				best_obj = A;
859 				best_dot = ss_dot;
860 				least_num_locks = ss_num_locks;
861 			}
862 		} else {
863 			if ( !in_range ) {
864 				continue;
865 			}
866 
867 			if ( dot < wip->lock_fov ) {
868 				continue;
869 			}
870 
871 			current_num_locks = 0;
872 			actively_locking = false;
873 
874 			for ( i = 0; i < Player_ship->missile_locks.size(); ++i ) {
875 				if ( Player_ship->missile_locks[i].obj != nullptr && OBJ_INDEX(Player_ship->missile_locks[i].obj) == OBJ_INDEX(A) && Player_ship->missile_locks[i].subsys == nullptr) {
876 					if ( !Player_ship->missile_locks[i].locked ) {
877 						// we're already currently locking on this subsystem so let's not throw another aspect lock on it.
878 						actively_locking = true;
879 						continue;
880 					}
881 
882 					current_num_locks++;
883 				}
884 			}
885 
886 			if ( !actively_locking
887 				&& current_num_locks < wip->max_seekers_per_target
888 				&& current_num_locks <= least_num_locks
889 				&& dot > best_dot ) {
890 					best_subsys = nullptr;
891 					best_obj = A;
892 					best_dot = dot;
893 					least_num_locks = current_num_locks;
894 			}
895 		}
896 	}
897 
898 	current_lock->obj = best_obj;
899 	current_lock->subsys = best_subsys;
900 }
901 
hud_lock_determine_lock_target(lock_info * lock_slot,weapon_info * wip)902 void hud_lock_determine_lock_target(lock_info *lock_slot, weapon_info *wip)
903 {
904 	if ( lock_slot->obj != nullptr) {
905 		return;
906 	}
907 
908 	if ( wip->target_restrict == LR_ANY_TARGETS ) {
909 		// if this weapon is uncaged, grab the best possible target within the player's lock cone and weapon distance
910 		vec3d vec_to_target;
911 		vec3d target_pos;
912 		object *objp;
913 		float dot;
914 
915 		if ( lock_slot->obj != nullptr) {
916 
917 			// lock slot occupied; do a check to see if this is a valid lock
918 			objp = lock_slot->obj;
919 
920 			if ( lock_slot->subsys ) {
921 				vm_vec_unrotate(&target_pos, &Player->locking_subsys->system_info->pnt, &objp->orient);
922 				vm_vec_add2(&target_pos, &objp->pos);
923 			} else {
924 				target_pos = objp->pos;
925 			}
926 
927 			vm_vec_normalized_dir(&vec_to_target, &target_pos, &Eye_position);
928 			dot = vm_vec_dot(&Player_obj->orient.vec.fvec, &vec_to_target);
929 
930 			if ( !weapon_secondary_world_pos_in_range(Player_obj, wip, &lock_slot->obj->pos) || dot < wip->lock_fov) {
931 				// set this lock slot to empty
932 				ship_clear_lock(lock_slot);
933 				hud_lock_acquire_uncaged_target(lock_slot, wip);
934 			}
935 		} else {
936 			// not using an else because the previous block may have
937 			// invalidated the target that was previously in this lock slot
938 			ship_clear_lock(lock_slot);
939 			hud_lock_acquire_uncaged_target(lock_slot, wip);
940 		}
941 	} else if ( wip->target_restrict == LR_CURRENT_TARGET_SUBSYS ) {
942 		if ( lock_slot->obj != nullptr) {
943 			return;
944 		}
945 
946 		if ( Player_ai->target_objnum < 0 ) {
947 			ship_clear_lock(lock_slot);
948 			return;
949 		}
950 
951 		lock_slot->obj = &Objects[Player_ai->target_objnum];
952 
953 		// Allow locking on ships and bombs (only targeted weapon allowed is a bomb, so don't bother checking flags)
954 		if ( lock_slot->obj->type != OBJ_SHIP && lock_slot->obj->type != OBJ_WEAPON ) {
955 			ship_clear_lock(lock_slot);
956 			return;
957 		}
958 
959 		if ( !weapon_target_satisfies_lock_restrictions(wip, lock_slot->obj) ) {
960 			ship_clear_lock(lock_slot);
961 			return;
962 		}
963 
964 		if ( lock_slot->obj->type == OBJ_SHIP && Ship_info[Ships[lock_slot->obj->instance].ship_info_index].is_big_or_huge() ) {
965 			float dot = 0.0f;
966 			int num_locks = INT_MAX;
967 			lock_info temp_lock;
968 
969 			temp_lock.obj = lock_slot->obj;
970 			temp_lock.subsys = nullptr;
971 
972 			hud_lock_acquire_uncaged_subsystem(wip, &temp_lock, &dot, &num_locks);
973 
974 			ship_clear_lock(lock_slot);
975 
976 			if ( temp_lock.subsys != nullptr) {
977 				lock_slot->obj = temp_lock.obj;
978 				lock_slot->subsys = temp_lock.subsys;
979 			}
980 		} else {
981 			// If subsystem is targeted, we must try to lock on that
982 			if ( Player_ai->targeted_subsys && !(wip->wi_flags[Weapon::Info_Flags::Homing_javelin]) ) {
983 				lock_slot->subsys = Player_ai->targeted_subsys;
984 			} else if ( wip->wi_flags[Weapon::Info_Flags::Homing_javelin] && lock_slot->obj->type == OBJ_SHIP) {
985 				if ( Player_ai->targeted_subsys ) {
986 					vec3d subobj_pos;
987 
988 					vm_vec_unrotate(&subobj_pos, &Player->locking_subsys->system_info->pnt, &lock_slot->obj->orient);
989 					vm_vec_add2(&subobj_pos, &lock_slot->obj->pos);
990 
991 					int target_subsys_in_sight = ship_subsystem_in_sight(lock_slot->obj, Player_ai->targeted_subsys, &Player_obj->pos, &subobj_pos);
992 
993 					if ( !target_subsys_in_sight || Player->locking_subsys->system_info->type != SUBSYSTEM_ENGINE ) {
994 						lock_slot->subsys = ship_get_closest_subsys_in_sight(&Ships[lock_slot->obj->instance], SUBSYSTEM_ENGINE, &Player_obj->pos);
995 					}
996 				} else {
997 					lock_slot->subsys = ship_get_closest_subsys_in_sight(&Ships[lock_slot->obj->instance], SUBSYSTEM_ENGINE, &Player_obj->pos);
998 
999 					if (lock_slot->subsys == nullptr) {
1000 						lock_slot->obj = nullptr;
1001 						return;
1002 					}
1003 				}
1004 			} else {
1005 				hud_lock_acquire_current_target(lock_slot->obj, lock_slot->subsys);
1006 			}
1007 		}
1008 	} else {
1009 		if ( lock_slot->obj != nullptr) {
1010 			return;
1011 		}
1012 
1013 		if ( Player_ai->target_objnum < 0 ) {
1014 			ship_clear_lock(lock_slot);
1015 			return;
1016 		}
1017 
1018 		// if caged, we're locking on the current target
1019 		lock_slot->obj = &Objects[Player_ai->target_objnum];
1020 
1021 		// Allow locking on ships and bombs (only targeted weapon allowed is a bomb, so don't bother checking flags)
1022 		if ( lock_slot->obj->type != OBJ_SHIP && lock_slot->obj->type != OBJ_WEAPON ) {
1023 			ship_clear_lock(lock_slot);
1024 			return;
1025 		}
1026 
1027 		if ( !weapon_target_satisfies_lock_restrictions(wip, lock_slot->obj) ) {
1028 			ship_clear_lock(lock_slot);
1029 			return;
1030 		}
1031 
1032 		// If subsystem is targeted, we must try to lock on that
1033 		if ( Player_ai->targeted_subsys && !(wip->wi_flags[Weapon::Info_Flags::Homing_javelin]) ) {
1034 			lock_slot->subsys = Player_ai->targeted_subsys;
1035 		} else if ( wip->wi_flags[Weapon::Info_Flags::Homing_javelin] && lock_slot->obj->type == OBJ_SHIP) {
1036 			if ( Player_ai->targeted_subsys ) {
1037 				vec3d subobj_pos;
1038 
1039 				vm_vec_unrotate(&subobj_pos, &Player_ai->targeted_subsys->system_info->pnt, &lock_slot->obj->orient);
1040 				vm_vec_add2(&subobj_pos, &lock_slot->obj->pos);
1041 
1042 				int target_subsys_in_sight = ship_subsystem_in_sight(lock_slot->obj, Player_ai->targeted_subsys, &Player_obj->pos, &subobj_pos);
1043 
1044 				if ( !target_subsys_in_sight || Player_ai->targeted_subsys->system_info->type != SUBSYSTEM_ENGINE ) {
1045 					lock_slot->subsys = ship_get_closest_subsys_in_sight(&Ships[lock_slot->obj->instance], SUBSYSTEM_ENGINE, &Player_obj->pos);
1046 				}
1047 			} else {
1048 				lock_slot->subsys = ship_get_closest_subsys_in_sight(&Ships[lock_slot->obj->instance], SUBSYSTEM_ENGINE, &Player_obj->pos);
1049 
1050 				if (lock_slot->subsys == nullptr) {
1051 					lock_slot->obj = nullptr;
1052 					return;
1053 				}
1054 			}
1055 		} else {
1056 			hud_lock_acquire_current_target(lock_slot->obj, lock_slot->subsys);
1057 		}
1058 	}
1059 }
1060 
1061 // Decide which point lock should be homing on
hud_lock_determine_lock_point(lock_info * current_lock)1062 void hud_lock_determine_lock_point(lock_info *current_lock)
1063 {
1064 	vec3d vec_to_lock_pos;
1065 	vec3d lock_local_pos;
1066 
1067 	Assert(current_lock->obj != nullptr);
1068 
1069 	current_lock->current_target_sx = -1;
1070 	current_lock->current_target_sy = -1;
1071 
1072 	if ( current_lock->subsys ) {
1073 		get_subsystem_world_pos(current_lock->obj, current_lock->subsys, &current_lock->world_pos);
1074 	} else {
1075 		current_lock->world_pos = current_lock->obj->pos;
1076 	}
1077 
1078 	vm_vec_sub(&vec_to_lock_pos,&current_lock->world_pos,&Player_obj->pos);
1079 	vm_vec_rotate(&lock_local_pos,&vec_to_lock_pos,&Player_obj->orient);
1080 
1081 	if ( lock_local_pos.xyz.z > 0.0f ) {
1082 		// Get the location of our target in the "virtual frame" where the locking computation will be done
1083 		float w = 1.0f / lock_local_pos.xyz.z;
1084 		// Let's force our "virtual frame" to be 640x480. -MageKing17
1085 		float sx = gr_screen.clip_center_x + (lock_local_pos.xyz.x * VIRTUAL_FRAME_HALF_WIDTH * w);
1086 		float sy = gr_screen.clip_center_y - (lock_local_pos.xyz.y * VIRTUAL_FRAME_HALF_HEIGHT * w);
1087 
1088 		current_lock->current_target_sx = (int)sx;
1089 		current_lock->current_target_sy = (int)sy;
1090 	}
1091 }
1092 
hud_calculate_lock_start_pos(lock_info * current_lock)1093 void hud_calculate_lock_start_pos(lock_info *current_lock)
1094 {
1095 	double hypotenuse;
1096 	double delta_y;
1097 	double delta_x;
1098 	double target_mag, target_x, target_y;
1099 
1100 	delta_x = static_cast<double>(current_lock->current_target_sx) - gr_screen.clip_center_x;
1101 	delta_y = static_cast<double>(current_lock->current_target_sy) - gr_screen.clip_center_y;
1102 
1103 	if ( (delta_x == 0.0) && (delta_y == 0.0) ) {
1104 		current_lock->indicator_start_x = fl2i(gr_screen.clip_center_x + Lock_start_dist);
1105 		current_lock->indicator_start_y = fl2i(gr_screen.clip_center_y);
1106 		return;
1107 	}
1108 
1109 	hypotenuse = _hypot(delta_y, delta_x);
1110 
1111 	if (hypotenuse >= Lock_start_dist) {
1112 		current_lock->indicator_start_x = fl2i(gr_screen.clip_center_x);
1113 		current_lock->indicator_start_y = fl2i(gr_screen.clip_center_y);
1114 		return;
1115 	}
1116 
1117 	target_mag = Lock_start_dist - hypotenuse;
1118 	target_x = target_mag * (delta_x / hypotenuse);
1119 	target_y = target_mag * (delta_y / hypotenuse);
1120 
1121 	current_lock->indicator_start_x = fl2i(gr_screen.clip_center_x - target_x);
1122 	current_lock->indicator_start_y = fl2i(gr_screen.clip_center_y - target_y);
1123 
1124 	CLAMP(current_lock->indicator_start_x, gr_screen.clip_left, gr_screen.clip_right);
1125 	CLAMP(current_lock->indicator_start_y, gr_screen.clip_top, gr_screen.clip_bottom);
1126 }
1127 
hud_calculate_lock_slot_time(lock_info * current_lock,weapon_info * wip,float frametime)1128 void hud_calculate_lock_slot_time(lock_info *current_lock, weapon_info *wip, float frametime)
1129 {
1130 	if ( current_lock->target_in_lock_cone ) {
1131 		if ( !current_lock->indicator_visible ) {
1132 			hud_calculate_lock_start_pos(current_lock);
1133 			current_lock->last_dist_to_target = 0;
1134 
1135 			current_lock->indicator_x = current_lock->indicator_start_x;
1136 			current_lock->indicator_y = current_lock->indicator_start_y;
1137 			current_lock->indicator_visible = true;
1138 
1139 			current_lock->accumulated_x_pixels = 0.0f;
1140 			current_lock->accumulated_y_pixels = 0.0f;
1141 
1142 			current_lock->time_to_lock = i2fl(wip->min_lock_time);
1143 			current_lock->catching_up = 0;
1144 		}
1145 
1146 		current_lock->need_new_start_pos = true;
1147 
1148 		if ( current_lock->locked ) {
1149 			current_lock->indicator_x = current_lock->current_target_sx;
1150 			current_lock->indicator_y = current_lock->current_target_sy;
1151 			return;
1152 		}
1153 
1154 		current_lock->time_to_lock -= frametime;
1155 	} else {
1156 		current_lock->time_to_lock += frametime;
1157 	}
1158 }
1159 
hud_calculate_lock_slot_position(lock_info * current_lock,float frametime)1160 void hud_calculate_lock_slot_position(lock_info *current_lock, float frametime)
1161 {
1162 	ship_weapon *swp;
1163 	weapon_info	*wip;
1164 
1165 	float pixels_moved_while_locking;
1166 	float pixels_moved_while_degrading;
1167 	//static int Need_new_start_pos = 0;
1168 
1169 	//static double accumulated_x_pixels, accumulated_y_pixels;
1170 	double int_portion;
1171 
1172 	//static float last_dist_to_target;
1173 
1174 	//static int catching_up;
1175 
1176 	//static int maintain_lock_count = 0;
1177 
1178 	//static float catch_up_distance = 0.0f;
1179 
1180 	double hypotenuse, delta_x, delta_y;
1181 
1182 	swp = &Player_ship->weapons;
1183 	wip = &Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]];
1184 
1185 	if ( current_lock->target_in_lock_cone ) {
1186 		if ( !current_lock->indicator_visible ) {
1187 			hud_calculate_lock_start_pos(current_lock);
1188 			current_lock->last_dist_to_target = 0.0f;
1189 
1190 			current_lock->indicator_x = current_lock->indicator_start_x;
1191 			current_lock->indicator_y = current_lock->indicator_start_y;
1192 			current_lock->indicator_visible = true;
1193 
1194 			current_lock->time_to_lock = i2fl(wip->min_lock_time);
1195 			current_lock->catching_up = 0;
1196 			current_lock->maintain_lock_count = 0;
1197 		}
1198 
1199 		current_lock->need_new_start_pos = true;
1200 
1201 		if ( current_lock->locked ) {
1202 			current_lock->indicator_x = current_lock->current_target_sx;
1203 			current_lock->indicator_y = current_lock->current_target_sy;
1204 			return;
1205 		}
1206 
1207 		delta_x = (double)(current_lock->indicator_x - current_lock->current_target_sx);
1208 		delta_y = (double)(current_lock->indicator_y - current_lock->current_target_sy);
1209 
1210 		if (!delta_y && !delta_x) {
1211 			hypotenuse = 0;
1212 		}
1213 		else {
1214 			hypotenuse = (float)_hypot((double)delta_y, (double)delta_x);
1215 		}
1216 
1217 		current_lock->dist_to_lock = (float)hypotenuse;
1218 
1219 		if ( current_lock->last_dist_to_target == 0) {
1220 			current_lock->last_dist_to_target = current_lock->dist_to_lock;
1221 		}
1222 
1223 		//nprintf(("Alan","dist to target: %.2f\n",Players[Player_num].lock_dist_to_target));
1224 		//nprintf(("Alan","last to target: %.2f\n\n",last_dist_to_target));
1225 
1226 		if ( current_lock->catching_up ) {
1227 			//nprintf(("Alan","IN CATCH UP MODE  catch_up_dist is %.2f\n",catch_up_distance));
1228 			if ( current_lock->dist_to_lock < current_lock->catch_up_distance )
1229 				current_lock->catching_up = 0;
1230 		}
1231 		else {
1232 			//nprintf(("Alan","IN NORMAL MODE\n"));
1233 			if ( (current_lock->dist_to_lock - current_lock->last_dist_to_target) > 2.0f ) {
1234 				current_lock->catching_up = 1;
1235 				current_lock->catch_up_distance = current_lock->last_dist_to_target + wip->catchup_pixel_penalty;
1236 			}
1237 		}
1238 
1239 		current_lock->last_dist_to_target = current_lock->dist_to_lock;
1240 
1241 		if (!current_lock->catching_up) {
1242 			current_lock->time_to_lock -= frametime;
1243 			if ( current_lock->time_to_lock < 0.0f )
1244 				current_lock->time_to_lock = 0.0f;
1245 		}
1246 
1247 		float lock_pixels_per_sec;
1248 		if (current_lock->time_to_lock > 0) {
1249 			lock_pixels_per_sec = current_lock->dist_to_lock / current_lock->time_to_lock;
1250 		} else {
1251 			lock_pixels_per_sec = i2fl(wip->lock_pixels_per_sec);
1252 		}
1253 
1254 		if (lock_pixels_per_sec > wip->lock_pixels_per_sec) {
1255 			lock_pixels_per_sec = i2fl(wip->lock_pixels_per_sec);
1256 		}
1257 
1258 		if ( current_lock->catching_up ) {
1259 			pixels_moved_while_locking = wip->catchup_pixels_per_sec * frametime;
1260 		} else {
1261 			pixels_moved_while_locking = lock_pixels_per_sec * frametime;
1262 		}
1263 
1264 		if ((delta_x != 0) && (hypotenuse != 0)) {
1265 			current_lock->accumulated_x_pixels += pixels_moved_while_locking * delta_x/hypotenuse;
1266 		}
1267 
1268 		if ((delta_y != 0) && (hypotenuse != 0)) {
1269 			current_lock->accumulated_y_pixels += pixels_moved_while_locking * delta_y/hypotenuse;
1270 		}
1271 
1272 		if (fl_abs((float)current_lock->accumulated_x_pixels) > 1.0f) {
1273 			modf(current_lock->accumulated_x_pixels, &int_portion);
1274 
1275 			current_lock->indicator_x -= (int)int_portion;
1276 
1277 			if ( fl_abs((float)current_lock->indicator_x - (float)current_lock->current_target_sx) < fl_abs((float)int_portion) )
1278 				current_lock->indicator_x = current_lock->current_target_sx;
1279 
1280 			current_lock->accumulated_x_pixels -= int_portion;
1281 		}
1282 
1283 		if (fl_abs((float)current_lock->accumulated_y_pixels) > 1.0f) {
1284 			modf(current_lock->accumulated_y_pixels, &int_portion);
1285 
1286 			current_lock->indicator_y -= (int)int_portion;
1287 
1288 			if ( fl_abs((float)current_lock->indicator_y - (float)current_lock->current_target_sy) < fl_abs((float)int_portion) )
1289 				current_lock->indicator_y = current_lock->current_target_sy;
1290 
1291 			current_lock->accumulated_y_pixels -= int_portion;
1292 		}
1293 
1294 		if (!current_lock->time_to_lock) {
1295 			if ( (current_lock->indicator_x == current_lock->current_target_sx) && (current_lock->indicator_y == current_lock->current_target_sy) ) {
1296 				if (current_lock->maintain_lock_count++ > 1) {
1297 					current_lock->locked = true;
1298 				}
1299 			} else {
1300 				current_lock->maintain_lock_count = 0;
1301 			}
1302 		}
1303 
1304 	} else {
1305 		current_lock->locked = false;
1306 
1307 		if (!current_lock->indicator_visible) {
1308 			return;
1309 		}
1310 
1311 		current_lock->catching_up = 0;
1312 		current_lock->last_dist_to_target = 0.0f;
1313 
1314 		if (current_lock->need_new_start_pos) {
1315 			hud_calculate_lock_start_pos(current_lock);
1316 			current_lock->need_new_start_pos = false;
1317 			current_lock->accumulated_x_pixels = 0.0f;
1318 			current_lock->accumulated_y_pixels = 0.0f;
1319 			current_lock->maintain_lock_count = 0;
1320 		}
1321 
1322 		delta_x = i2fl(current_lock->indicator_x - current_lock->indicator_start_x);
1323 		delta_y = i2fl(current_lock->indicator_y - current_lock->indicator_start_y);
1324 
1325 		if (!delta_y && !delta_x) {
1326 			hypotenuse = 0;
1327 		}
1328 		else {
1329 			hypotenuse = _hypot(delta_y, delta_x);
1330 		}
1331 
1332 		current_lock->time_to_lock += frametime;
1333 
1334 		if (current_lock->time_to_lock > wip->min_lock_time)
1335 			current_lock->time_to_lock = i2fl(wip->min_lock_time);
1336 
1337 		pixels_moved_while_degrading = 2.0f * wip->lock_pixels_per_sec * frametime;
1338 
1339 		if ((delta_x != 0) && (hypotenuse != 0))
1340 			current_lock->accumulated_x_pixels += pixels_moved_while_degrading * delta_x/hypotenuse;
1341 
1342 		if ((delta_y != 0) && (hypotenuse != 0))
1343 			current_lock->accumulated_y_pixels += pixels_moved_while_degrading * delta_y/hypotenuse;
1344 
1345 		if (fl_abs((float)current_lock->accumulated_x_pixels) > 1.0f) {
1346 			modf(current_lock->accumulated_x_pixels, &int_portion);
1347 
1348 			current_lock->indicator_x -= (int)int_portion;
1349 
1350 			if ( fl_abs((float)current_lock->indicator_x - (float)current_lock->indicator_start_x) < fl_abs((float)int_portion) )
1351 				current_lock->indicator_x = current_lock->indicator_start_x;
1352 
1353 			current_lock->accumulated_x_pixels -= int_portion;
1354 		}
1355 
1356 		if (fl_abs((float)current_lock->accumulated_y_pixels) > 1.0f) {
1357 			modf(current_lock->accumulated_y_pixels, &int_portion);
1358 
1359 			current_lock->indicator_y -= (int)int_portion;
1360 
1361 			if ( fl_abs((float)current_lock->indicator_y - (float)current_lock->indicator_start_y) < fl_abs((float)int_portion) )
1362 				current_lock->indicator_y = current_lock->indicator_start_y;
1363 
1364 			current_lock->accumulated_y_pixels -= int_portion;
1365 		}
1366 
1367 		if ( (current_lock->indicator_x == current_lock->indicator_start_x) && (current_lock->indicator_y == current_lock->indicator_start_y) ) {
1368 			current_lock->indicator_visible = false;
1369 		}
1370 	}
1371 }
1372 
1373 // Determine if point to lock on is in range
hud_lock_target_in_range(lock_info * lock_slot)1374 int hud_lock_target_in_range(lock_info *lock_slot)
1375 {
1376 	vec3d		target_world_pos;
1377 
1378 	if ( lock_slot == nullptr || lock_slot->obj == nullptr) {
1379 		return 0;
1380 	}
1381 
1382 	if ( lock_slot->subsys != nullptr) {
1383 		vm_vec_unrotate(&target_world_pos, &lock_slot->subsys->system_info->pnt, &lock_slot->obj->orient);
1384 		vm_vec_add2(&target_world_pos, &lock_slot->obj->pos);
1385 	} else {
1386 		if ( Player->locking_subsys ) {
1387 			vm_vec_unrotate(&target_world_pos, &lock_slot->subsys->system_info->pnt, &lock_slot->obj->orient);
1388 			vm_vec_add2(&target_world_pos, &lock_slot->obj->pos);
1389 		} else {
1390 			target_world_pos = lock_slot->obj->pos;
1391 		}
1392 	}
1393 	ship_weapon* swp = &Player_ship->weapons;
1394 
1395 	return weapon_secondary_world_pos_in_range(Player_obj, &Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]], &target_world_pos);
1396 }
1397 
hud_do_lock_indicators(float frametime)1398 void hud_do_lock_indicators(float frametime)
1399 {
1400 	ship_weapon *swp;
1401 	weapon_info	*wip;
1402 
1403 	// if i'm a multiplayer observer, bail here
1404  	if((Game_mode & GM_MULTIPLAYER) && ((Net_player->flags & NETINFO_FLAG_OBSERVER) || (Player_obj->type == OBJ_OBSERVER)) ){
1405 		return;
1406 	}
1407 
1408 	// do not continue locking if the game is paused (viewer pause still has hud on)
1409 	if (gameseq_get_state() == GS_STATE_GAME_PAUSED) {
1410 		return;
1411 	}
1412 
1413 	// be sure to unset this flag, then possibly set later in this function so that
1414 	// threat indicators work properly.
1415 	Player_ai->ai_flags.remove(AI::AI_Flags::Seek_lock);
1416 
1417 	if ( hud_abort_lock() ) {
1418 		hud_lock_reset();
1419 		return;
1420 	}
1421 
1422 	// if there is an EMP effect active, never update lock
1423 	if(emp_active_local()){
1424 		hud_lock_reset();
1425 		return;
1426 	}
1427 
1428 	swp = &Player_ship->weapons;
1429 	wip = &Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]];
1430 
1431 	if ( !(wip->is_locked_homing()) ) {
1432 		hud_lock_reset();
1433 		return;
1434 	}
1435 
1436 	Lock_start_dist = wip->min_lock_time * wip->lock_pixels_per_sec;
1437 
1438 	// if secondary weapons change, reset the lock
1439 	if ( hud_lock_secondary_weapon_changed(swp) ) {
1440 		hud_lock_reset();
1441 	}
1442 
1443 	Player_ai->last_secondary_index = swp->current_secondary_bank;
1444 
1445 	int max_target_locks = weapon_get_max_missile_seekers(wip);
1446 
1447 	// make sure ship has enough lock slots
1448 	if ( (int)Player_ship->missile_locks.size() < max_target_locks) {
1449 		lock_info new_slots;
1450 		ship_clear_lock(&new_slots);
1451 
1452 		Player_ship->missile_locks.resize(max_target_locks, new_slots);
1453 	}
1454 
1455 	lock_info *lock_slot;
1456 	int num_active_seekers = 0;
1457 	bool play_tracking_sound = false;
1458 	bool player_has_lock = false;
1459 
1460 	// go through all lock slots in play and do missile locks
1461 	for ( int i = 0; i < max_target_locks; ++i ) {
1462 		// need a check to see if we've exhausted our alloted simultaneous locks. if so, just check if already locked targets are valid.
1463 		lock_slot = &Player_ship->missile_locks[i];
1464 
1465 		hud_lock_determine_lock_target(lock_slot, wip);
1466 
1467 		if ( lock_slot->obj == nullptr) {
1468 			// reset this lock and continue
1469 			ship_clear_lock(lock_slot);
1470 			continue;
1471 		}
1472 
1473 		if ( lock_slot->obj->flags[Object::Object_Flags::Should_be_dead] ) {
1474 			ship_clear_lock(lock_slot);
1475 			continue;
1476 		}
1477 
1478 		if ( num_active_seekers >= wip->max_seeking ) {
1479 			ship_clear_lock(lock_slot);
1480 			continue;
1481 		}
1482 
1483 		if ( lock_slot->obj->type == OBJ_SHIP && Ships[lock_slot->obj->instance].flags[Ship::Ship_Flags::Dying] ) {
1484 			ship_clear_lock(lock_slot);
1485 			continue;
1486 		}
1487 
1488 		if ( !wip->wi_flags[Weapon::Info_Flags::Multilock_target_dead_subsys] && lock_slot->subsys != nullptr && lock_slot->subsys->current_hits <= 0.0f) {
1489 			ship_clear_lock(lock_slot);
1490 			continue;
1491 		}
1492 
1493 		hud_lock_determine_lock_point(lock_slot);
1494 
1495 		hud_lock_check_if_target_in_lock_cone(lock_slot, wip);
1496 
1497 		if ( !lock_slot->indicator_visible && !lock_slot->target_in_lock_cone ) {
1498 			ship_clear_lock(lock_slot);
1499 			continue;
1500 		}
1501 
1502 		// we can probably move this check into hud_lock_determine_lock_target
1503 		if ( !hud_lock_target_in_range(lock_slot) ) {
1504 			lock_slot->target_in_lock_cone = false;
1505 		}
1506 
1507 		if ( !lock_slot->locked && wip->trigger_lock && !(swp->flags[Ship::Weapon_Flags::Trigger_Lock]) ) {
1508 			// only reset locks that are not locked if this is a trigger dependent weapon
1509 			// and player isn't holding down trigger
1510 			ship_clear_lock(lock_slot);
1511 			continue;
1512 		}
1513 
1514 		/*if ( wip->acquire_method == WLOCK_TIMER ) {
1515 			hud_calculate_lock_slot_time(lock_slot, wip, frametime);
1516 		} else {
1517 			hud_calculate_lock_slot_position(lock_slot, frametime);
1518 		}*/
1519 
1520 		bool current_lock_status = lock_slot->locked;
1521 
1522 		hud_calculate_lock_slot_position(lock_slot, frametime);
1523 
1524 		if ( !lock_slot->locked ) {
1525 			num_active_seekers++;
1526 		} else {
1527 			player_has_lock = true;
1528 		}
1529 
1530 		if ( !current_lock_status && lock_slot->locked ) {
1531 			if (Missile_track_loop.isValid()) {
1532 				snd_stop(Missile_track_loop);
1533 				Missile_track_loop = sound_handle::invalid();
1534 
1535 				if (wip->hud_locked_snd.isValid())
1536 				{
1537 					Missile_lock_loop = snd_play(gamesnd_get_game_sound(wip->hud_locked_snd));
1538 				}
1539 				else
1540 				{
1541 					Missile_lock_loop = snd_play(gamesnd_get_game_sound(ship_get_sound(Player_obj, GameSounds::MISSILE_LOCK)));
1542 				}
1543 			}
1544 
1545 			lock_slot->lock_anim_time_elapsed = 0.0f;
1546 		} else if ( !lock_slot->locked ) {
1547 			if (Missile_lock_loop.isValid() && snd_is_playing(Missile_lock_loop)) {
1548 				snd_stop(Missile_lock_loop);
1549 				Missile_lock_loop = sound_handle::invalid();
1550 			}
1551 		}
1552 
1553 		// if there's at least one lock_slot current locking, play the looping sound
1554 		if ( lock_slot->indicator_visible && !lock_slot->locked ) {
1555 			play_tracking_sound = true;
1556 		}
1557 	}
1558 
1559 	if (player_has_lock) {
1560 		Player_ai->ai_flags.remove(AI::AI_Flags::Seek_lock);		// set this flag so multiplayer's properly track lock on other ships
1561 		Player_ai->current_target_is_locked = 1;
1562 	} else {
1563 		Player_ai->ai_flags.set(AI::AI_Flags::Seek_lock);		// set this flag so multiplayer's properly track lock on other ships
1564 		Player_ai->current_target_is_locked = 0;
1565 	}
1566 
1567 	if ( play_tracking_sound ) {
1568 		if ( !Missile_track_loop.isValid() ) {
1569 			Missile_track_loop = snd_play_looping(gamesnd_get_game_sound(ship_get_sound(Player_obj, GameSounds::MISSILE_TRACKING)), 0.0f, -1, -1);
1570 		}
1571 	} else {
1572 		if ( Missile_track_loop.isValid() )	{
1573 			snd_stop(Missile_track_loop);
1574 			Missile_track_loop = sound_handle::invalid();
1575 		}
1576 	}
1577 }
1578 
1579 // hud_draw_lock_triangles() will draw the 4 rotating triangles around a lock indicator
1580 // (This is done when a lock has been acquired)
1581 #define ROTATE_DELAY 40
renderLockTrianglesOld(int center_x,int center_y,int radius)1582 void HudGaugeLock::renderLockTrianglesOld(int center_x, int center_y, int radius)
1583 {
1584 	static float ang = 0.0f;
1585 
1586 	float end_ang = ang + PI2;
1587 	float x3,y3,x4,y4,xpos,ypos;
1588 
1589 	if ( timestamp_elapsed(Rotate_time_id) ) {
1590 		Rotate_time_id = timestamp(ROTATE_DELAY);
1591 		ang += PI/12;
1592 	}
1593 
1594 	for (; ang <= end_ang; ang += PI_2) {
1595 
1596 		// draw the orbiting triangles
1597 
1598 		//ang = atan2(target_point.y,target_point.x);
1599 		xpos = center_x + (float)cos(ang)*(radius + Lock_triangle_height + 2);
1600 		ypos = center_y - (float)sin(ang)*(radius + Lock_triangle_height + 2);
1601 
1602 		x3 = xpos - Lock_triangle_base * (float)sin(-ang);
1603 		y3 = ypos + Lock_triangle_base * (float)cos(-ang);
1604 		x4 = xpos + Lock_triangle_base * (float)sin(-ang);
1605 		y4 = ypos - Lock_triangle_base * (float)cos(-ang);
1606 
1607 		xpos = xpos - Lock_triangle_base * (float)cos(ang);
1608 		ypos = ypos + Lock_triangle_base * (float)sin(ang);
1609 
1610 		hud_tri(x3, y3, xpos, ypos, x4, y4);
1611 	} // end for
1612 }
1613 
1614 // draw a frame of the rotating lock triangles animation
renderLockTriangles(int center_x,int center_y,float frametime)1615 void HudGaugeLock::renderLockTriangles(int center_x, int center_y, float frametime)
1616 {
1617 	if ( Lock_anim.first_frame == -1 ) {
1618 		renderLockTrianglesOld(center_x, center_y, Lock_target_box_width/2);
1619 	} else {
1620 		// render the anim
1621 		Lock_anim.sx = center_x - Lockspin_half_w;
1622 		Lock_anim.sy = center_y - Lockspin_half_h;
1623 
1624 		// if it's still animating
1625 		if(Lock_anim.time_elapsed < Lock_anim.total_time){
1626 			if(loop_locked_anim) {
1627 				hud_anim_render(&Lock_anim, frametime, 1, 1, 0);
1628 			} else {
1629 				hud_anim_render(&Lock_anim, frametime, 1, 0, 1);
1630 			}
1631 		} else {
1632 			if(blink_locked_anim) {
1633 				// if the timestamp is unset or expired
1634 				if((Lock_gauge_draw_stamp < 0) || timestamp_elapsed(Lock_gauge_draw_stamp)){
1635 					// reset timestamp
1636 					Lock_gauge_draw_stamp = timestamp(1000 / (2 * LOCK_GAUGE_BLINK_RATE));
1637 
1638 					// switch between draw and don't-draw
1639 					Lock_gauge_draw = !Lock_gauge_draw;
1640 				}
1641 			}
1642 
1643 			// maybe draw the anim
1644 			Lock_gauge.time_elapsed = 0.0f;
1645 			if(Lock_gauge_draw || !blink_locked_anim){
1646 				if(loop_locked_anim) {
1647 					hud_anim_render(&Lock_anim, frametime, 1, 1, 0);
1648 				} else {
1649 					hud_anim_render(&Lock_anim, frametime, 1, 0, 1);
1650 				}
1651 			}
1652 		}
1653 	}
1654 }
1655 
renderLockTrianglesNew(int center_x,int center_y,float frametime,lock_info * slot)1656 void HudGaugeLock::renderLockTrianglesNew(int center_x, int center_y, float frametime, lock_info *slot)
1657 {
1658 	if ( Lock_anim.first_frame == -1 ) {
1659 		renderLockTrianglesOld(center_x, center_y, Lock_target_box_width/2);
1660 	} else {
1661 		// render the anim
1662 		Lock_anim.sx = center_x - Lockspin_half_w;
1663 		Lock_anim.sy = center_y - Lockspin_half_h;
1664 
1665 		// if it's still animating
1666 		if(slot->lock_anim_time_elapsed < Lock_anim.total_time){
1667 			// manually track the animation time, since we may have more than one lock
1668 			slot->lock_anim_time_elapsed += frametime;
1669 			Lock_anim.time_elapsed = slot->lock_anim_time_elapsed;
1670 
1671 			if(loop_locked_anim) {
1672 				hud_anim_render(&Lock_anim, 0.0f, 1, 1, 0);
1673 			} else {
1674 				hud_anim_render(&Lock_anim, 0.0f, 1, 0, 1);
1675 			}
1676 		} else {
1677 			if(blink_locked_anim) {
1678 				// if the timestamp is unset or expired
1679 				if((Lock_gauge_draw_stamp < 0) || timestamp_elapsed(Lock_gauge_draw_stamp)){
1680 					// reset timestamp
1681 					Lock_gauge_draw_stamp = timestamp(1000 / (2 * LOCK_GAUGE_BLINK_RATE));
1682 
1683 					// switch between draw and don't-draw
1684 					Lock_gauge_draw = !Lock_gauge_draw;
1685 				}
1686 			}
1687 
1688 			// maybe draw the anim
1689 			slot->lock_gauge_time_elapsed = 0.0f;
1690 			if(Lock_gauge_draw || !blink_locked_anim){
1691 				// manually track the animation time, since we may have more than one lock
1692 				slot->lock_anim_time_elapsed += frametime;
1693 				Lock_anim.time_elapsed = slot->lock_anim_time_elapsed;
1694 
1695 				if(loop_locked_anim) {
1696 					hud_anim_render(&Lock_anim, 0.0f, 1, 1, 0);
1697 				} else {
1698 					hud_anim_render(&Lock_anim, 0.0f, 1, 0, 1);
1699 				}
1700 			}
1701 		}
1702 	}
1703 }
1704 
1705 // hud_calculate_lock_position()  will determine where on the screen to draw the lock
1706 // indicator, and will determine when a lock has occurred.  If the lock indicator is not
1707 // on the screen yet, hud_calculate_lock_start_pos() is called to pick a starting location
hud_calculate_lock_position(float frametime)1708 void hud_calculate_lock_position(float frametime)
1709 {
1710 	ship_weapon *swp;
1711 	weapon_info	*wip;
1712 
1713 	static float pixels_moved_while_locking;
1714 	static float pixels_moved_while_degrading;
1715 	static int Need_new_start_pos = 0;
1716 
1717 	static double accumulated_x_pixels, accumulated_y_pixels;
1718 	double int_portion;
1719 
1720 	static float last_dist_to_target;
1721 
1722 	static int catching_up;
1723 
1724 	static int maintain_lock_count = 0;
1725 
1726 	static float catch_up_distance = 0.0f;
1727 
1728 	double hypotenuse, delta_x, delta_y;
1729 
1730 	swp = &Player_ship->weapons;
1731 	wip = &Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]];
1732 
1733 	if (Player->target_in_lock_cone) {
1734 		if (!Players[Player_num].lock_indicator_visible) {
1735 			hud_calculate_lock_start_pos();
1736 			last_dist_to_target = 0.0f;
1737 
1738 			Players[Player_num].lock_indicator_x = Players[Player_num].lock_indicator_start_x;
1739 			Players[Player_num].lock_indicator_y = Players[Player_num].lock_indicator_start_y;
1740 			Players[Player_num].lock_indicator_visible = 1;
1741 
1742 			Players[Player_num].lock_time_to_target = i2fl(wip->min_lock_time);
1743 			catching_up = 0;
1744 		}
1745 
1746 		Need_new_start_pos = 1;
1747 
1748 		if (Player_ai->current_target_is_locked) {
1749 			Players[Player_num].lock_indicator_x = Player->current_target_sx;
1750 			Players[Player_num].lock_indicator_y = Player->current_target_sy;
1751 			return;
1752 		}
1753 
1754 		delta_x = Players[Player_num].lock_indicator_x - Player->current_target_sx;
1755 		delta_y = Players[Player_num].lock_indicator_y - Player->current_target_sy;
1756 
1757 		if (!delta_y && !delta_x) {
1758 			hypotenuse = 0;
1759 		}
1760 		else {
1761 			hypotenuse = _hypot(delta_y, delta_x);
1762 		}
1763 
1764 		Players[Player_num].lock_dist_to_target = (float)hypotenuse;
1765 
1766 		if (last_dist_to_target == 0) {
1767 			last_dist_to_target = Players[Player_num].lock_dist_to_target;
1768 		}
1769 
1770 		//nprintf(("Alan","dist to target: %.2f\n",Players[Player_num].lock_dist_to_target));
1771 		//nprintf(("Alan","last to target: %.2f\n\n",last_dist_to_target));
1772 
1773 		if (catching_up) {
1774 			//nprintf(("Alan","IN CATCH UP MODE  catch_up_dist is %.2f\n",catch_up_distance));
1775 			if ( Players[Player_num].lock_dist_to_target < catch_up_distance )
1776 				catching_up = 0;
1777 		}
1778 		else {
1779 			//nprintf(("Alan","IN NORMAL MODE\n"));
1780 			if ( (Players[Player_num].lock_dist_to_target - last_dist_to_target) > 2.0f ) {
1781 				catching_up = 1;
1782 				catch_up_distance = last_dist_to_target + wip->catchup_pixel_penalty;
1783 			}
1784 		}
1785 
1786 		last_dist_to_target = Players[Player_num].lock_dist_to_target;
1787 
1788 		if (!catching_up) {
1789 			Players[Player_num].lock_time_to_target -= frametime;
1790 			if (Players[Player_num].lock_time_to_target < 0.0f)
1791 				Players[Player_num].lock_time_to_target = 0.0f;
1792 		}
1793 
1794 		float lock_pixels_per_sec;
1795 		if (Players[Player_num].lock_time_to_target > 0) {
1796 			lock_pixels_per_sec = Players[Player_num].lock_dist_to_target / Players[Player_num].lock_time_to_target;
1797 		} else {
1798 			lock_pixels_per_sec = i2fl(wip->lock_pixels_per_sec);
1799 		}
1800 
1801 		if (lock_pixels_per_sec > wip->lock_pixels_per_sec) {
1802 			lock_pixels_per_sec = i2fl(wip->lock_pixels_per_sec);
1803 		}
1804 
1805 		if (catching_up) {
1806 			pixels_moved_while_locking = wip->catchup_pixels_per_sec * frametime;
1807 		} else {
1808 			pixels_moved_while_locking = lock_pixels_per_sec * frametime;
1809 		}
1810 
1811 		if ((delta_x != 0) && (hypotenuse != 0)) {
1812 			accumulated_x_pixels += pixels_moved_while_locking * delta_x/hypotenuse;
1813 		}
1814 
1815 		if ((delta_y != 0) && (hypotenuse != 0)) {
1816 			accumulated_y_pixels += pixels_moved_while_locking * delta_y/hypotenuse;
1817 		}
1818 
1819 		if (fl_abs((float)accumulated_x_pixels) > 1.0f) {
1820 			modf(accumulated_x_pixels, &int_portion);
1821 
1822 			Players[Player_num].lock_indicator_x -= (int)int_portion;
1823 
1824 			if ( fl_abs((float)Players[Player_num].lock_indicator_x - (float)Player->current_target_sx) < fl_abs((float)int_portion) )
1825 				Players[Player_num].lock_indicator_x = Player->current_target_sx;
1826 
1827 			accumulated_x_pixels -= int_portion;
1828 		}
1829 
1830 		if (fl_abs((float)accumulated_y_pixels) > 1.0f) {
1831 			modf(accumulated_y_pixels, &int_portion);
1832 
1833 			Players[Player_num].lock_indicator_y -= (int)int_portion;
1834 
1835 			if ( fl_abs((float)Players[Player_num].lock_indicator_y - (float)Player->current_target_sy) < fl_abs((float)int_portion) )
1836 				Players[Player_num].lock_indicator_y = Player->current_target_sy;
1837 
1838 			accumulated_y_pixels -= int_portion;
1839 		}
1840 
1841 		if (!Missile_track_loop.isValid()) {
1842 			if (wip->hud_tracking_snd.isValid())
1843 			{
1844 				Missile_track_loop = snd_play_looping( gamesnd_get_game_sound(wip->hud_tracking_snd), 0.0f , -1, -1);
1845 			}
1846 			else
1847 			{
1848 				Missile_track_loop = snd_play_looping( gamesnd_get_game_sound(ship_get_sound(Player_obj, GameSounds::MISSILE_TRACKING)), 0.0f , -1, -1);
1849 			}
1850 		}
1851 
1852 		if (!Players[Player_num].lock_time_to_target) {
1853 			if ( (Players[Player_num].lock_indicator_x == Player->current_target_sx) && (Players[Player_num].lock_indicator_y == Player->current_target_sy) ) {
1854 				if (maintain_lock_count++ > 1) {
1855 					Player_ai->current_target_is_locked = 1;
1856 				}
1857 			} else {
1858 				maintain_lock_count = 0;
1859 			}
1860 		}
1861 
1862 	} else {
1863 
1864 		if (Missile_track_loop.isValid()) {
1865 			snd_stop(Missile_track_loop);
1866 			Missile_track_loop = sound_handle::invalid();
1867 		}
1868 
1869 		Player_ai->current_target_is_locked = 0;
1870 
1871 		if (!Players[Player_num].lock_indicator_visible) {
1872 			return;
1873 		}
1874 
1875 		catching_up = 0;
1876 		last_dist_to_target = 0.0f;
1877 
1878 		if (Need_new_start_pos) {
1879 			hud_calculate_lock_start_pos();
1880 			Need_new_start_pos = 0;
1881 			accumulated_x_pixels = 0.0f;
1882 			accumulated_y_pixels = 0.0f;
1883 		}
1884 
1885 		delta_x = Players[Player_num].lock_indicator_x - Players[Player_num].lock_indicator_start_x;
1886 		delta_y = Players[Player_num].lock_indicator_y - Players[Player_num].lock_indicator_start_y;
1887 
1888 		if (!delta_y && !delta_x) {
1889 			hypotenuse = 0;
1890 		}
1891 		else {
1892 			hypotenuse = _hypot(delta_y, delta_x);
1893 		}
1894 
1895 		Players[Player_num].lock_time_to_target += frametime;
1896 
1897 		if (Players[Player_num].lock_time_to_target > wip->min_lock_time)
1898 			Players[Player_num].lock_time_to_target = i2fl(wip->min_lock_time);
1899 
1900 		pixels_moved_while_degrading = 2.0f * wip->lock_pixels_per_sec * frametime;
1901 
1902 		if ((delta_x != 0) && (hypotenuse != 0))
1903 			accumulated_x_pixels += pixels_moved_while_degrading * delta_x/hypotenuse;
1904 
1905 		if ((delta_y != 0) && (hypotenuse != 0))
1906 			accumulated_y_pixels += pixels_moved_while_degrading * delta_y/hypotenuse;
1907 
1908 		if (fl_abs((float)accumulated_x_pixels) > 1.0f) {
1909 			modf(accumulated_x_pixels, &int_portion);
1910 
1911 			Players[Player_num].lock_indicator_x -= (int)int_portion;
1912 
1913 			if ( fl_abs((float)Players[Player_num].lock_indicator_x - (float)Players[Player_num].lock_indicator_start_x) < fl_abs((float)int_portion) )
1914 				Players[Player_num].lock_indicator_x = Players[Player_num].lock_indicator_start_x;
1915 
1916 			accumulated_x_pixels -= int_portion;
1917 		}
1918 
1919 		if (fl_abs((float)accumulated_y_pixels) > 1.0f) {
1920 			modf(accumulated_y_pixels, &int_portion);
1921 
1922 			Players[Player_num].lock_indicator_y -= (int)int_portion;
1923 
1924 			if ( fl_abs((float)Players[Player_num].lock_indicator_y - (float)Players[Player_num].lock_indicator_start_y) < fl_abs((float)int_portion) )
1925 				Players[Player_num].lock_indicator_y = Players[Player_num].lock_indicator_start_y;
1926 
1927 			accumulated_y_pixels -= int_portion;
1928 		}
1929 
1930 		if ( (Players[Player_num].lock_indicator_x == Players[Player_num].lock_indicator_start_x) && (Players[Player_num].lock_indicator_y == Players[Player_num].lock_indicator_start_y) ) {
1931 			Players[Player_num].lock_indicator_visible = 0;
1932 		}
1933 	}
1934 }
1935 
1936 // hud_calculate_lock_start_pos() will determine where to draw the starting location of the lock
1937 // indicator.  It does this by picking a location that is Lock_start_dist pixels away from the current
1938 // target (in 2D).  This is accomplished by finding the endpoint of a line that passes through the
1939 // origin, and connects the target and lock indicator postion (and has a magnitude of Lock_start_dist)
hud_calculate_lock_start_pos()1940 void hud_calculate_lock_start_pos()
1941 {
1942 	double hypotenuse;
1943 	double delta_y;
1944 	double delta_x;
1945 	double target_mag, target_x, target_y;
1946 
1947 	delta_x = Player->current_target_sx - gr_screen.clip_center_x;
1948 	delta_y = Player->current_target_sy - gr_screen.clip_center_y;
1949 
1950 	if ( (delta_x == 0.0) && (delta_y == 0.0) ) {
1951 		Players[Player_num].lock_indicator_start_x = fl2i(gr_screen.clip_center_x + Lock_start_dist);
1952 		Players[Player_num].lock_indicator_start_y = fl2i(gr_screen.clip_center_y);
1953 		return;
1954 	}
1955 
1956 	hypotenuse = _hypot(delta_y, delta_x);
1957 
1958 	if (hypotenuse >= Lock_start_dist) {
1959 		Players[Player_num].lock_indicator_start_x = fl2i(gr_screen.clip_center_x);
1960 		Players[Player_num].lock_indicator_start_y = fl2i(gr_screen.clip_center_y);
1961 		return;
1962 	}
1963 
1964 	target_mag = Lock_start_dist - hypotenuse;
1965 	target_x = target_mag * (delta_x / hypotenuse);
1966 	target_y = target_mag * (delta_y / hypotenuse);
1967 
1968 	Players[Player_num].lock_indicator_start_x = fl2i(gr_screen.clip_center_x - target_x);
1969 	Players[Player_num].lock_indicator_start_y = fl2i(gr_screen.clip_center_y - target_y);
1970 
1971 	CLAMP(Players[Player_num].lock_indicator_start_x, gr_screen.clip_left, gr_screen.clip_right);
1972 	CLAMP(Players[Player_num].lock_indicator_start_y, gr_screen.clip_top, gr_screen.clip_bottom);
1973 }
1974 
1975 // hud_stop_looped_locking_sounds() will terminate any hud related looping sounds that are playing
hud_stop_looped_locking_sounds()1976 void hud_stop_looped_locking_sounds()
1977 {
1978 	if (Missile_track_loop.isValid()) {
1979 		snd_stop(Missile_track_loop);
1980 		Missile_track_loop = sound_handle::invalid();
1981 	}
1982 }
1983 
1984 // Get a new world pos for the locking point
hud_lock_update_lock_pos(object * target_objp)1985 void hud_lock_update_lock_pos(object *target_objp)
1986 {
1987 	ship_weapon *swp = &Player_ship->weapons;
1988 	weapon_info *wip = &Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]];
1989 
1990 	if ( Player_ai->targeted_subsys && !(wip->wi_flags[Weapon::Info_Flags::Homing_javelin]) ) {
1991 		get_subsystem_world_pos(target_objp, Player_ai->targeted_subsys, &lock_world_pos);
1992 		return;
1993 	}
1994 
1995 	if ( Player->locking_on_center) {
1996 		lock_world_pos = target_objp->pos;
1997 	} else {
1998 		Assert(Player->locking_subsys);
1999 		get_subsystem_world_pos(target_objp, Player->locking_subsys, &lock_world_pos);
2000 	}
2001 }
2002 
2003 // Try and find a new locking point
hud_lock_get_new_lock_pos(object * target_objp)2004 void hud_lock_get_new_lock_pos(object *target_objp)
2005 {
2006 	ship			*target_shipp=NULL;
2007 	int			lock_in_range=0;
2008 	float			best_lock_dot=-1.0f, lock_dot=-1.0f;
2009 	ship_subsys	*ss;
2010 	vec3d		subsys_world_pos, vec_to_lock;
2011 	ship_weapon *swp;
2012 	weapon_info *wip;
2013 
2014 	if ( target_objp->type == OBJ_SHIP ) {
2015 		target_shipp = &Ships[target_objp->instance];
2016 	}
2017 
2018 	swp = &Player_ship->weapons;
2019 	wip = &Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]];
2020 
2021 	// if a large ship, lock to pos closest to center and within range
2022 	if ( (target_shipp) && (Ship_info[target_shipp->ship_info_index].is_big_or_huge()) &&
2023 		 !(wip->wi_flags[Weapon::Info_Flags::Homing_javelin]) ) {
2024 		// check all the subsystems and the center of the ship
2025 
2026 		// assume best lock pos is the center of the ship
2027 		lock_world_pos = target_objp->pos;
2028 		Player->locking_on_center=1;
2029 		Player->locking_subsys=NULL;
2030 		Player->locking_subsys_parent=-1;
2031 		lock_in_range = weapon_secondary_world_pos_in_range(Player_obj, wip, &lock_world_pos);
2032 		vm_vec_normalized_dir(&vec_to_lock, &lock_world_pos, &Player_obj->pos);
2033 		if ( lock_in_range ) {
2034 			best_lock_dot=vm_vec_dot(&Player_obj->orient.vec.fvec, &vec_to_lock);
2035 		}
2036 		// take center if reasonable dot
2037 		if ( best_lock_dot > 0.95 ) {
2038 			return;
2039 		}
2040 
2041 		// iterate through subsystems to see if we can get a better choice
2042 		ss = GET_FIRST(&target_shipp->subsys_list);
2043 		while ( ss != END_OF_LIST( &target_shipp->subsys_list ) ) {
2044 
2045 			// get world pos of subsystem
2046 			get_subsystem_world_pos(target_objp, ss, &subsys_world_pos);
2047 
2048 			if ( weapon_secondary_world_pos_in_range(Player_obj, wip, &subsys_world_pos) ) {
2049 				vm_vec_normalized_dir(&vec_to_lock, &subsys_world_pos, &Player_obj->pos);
2050 				lock_dot=vm_vec_dot(&Player_obj->orient.vec.fvec, &vec_to_lock);
2051 				if ( lock_dot > best_lock_dot ) {
2052 					best_lock_dot=lock_dot;
2053 					Player->locking_on_center=0;
2054 					Player->locking_subsys=ss;
2055 					Player->locking_subsys_parent=Player_ai->target_objnum;
2056 					lock_world_pos = subsys_world_pos;
2057 				}
2058 			}
2059 			ss = GET_NEXT( ss );
2060 		}
2061 	} else if ( (target_shipp) && (wip->wi_flags[Weapon::Info_Flags::Homing_javelin])) {
2062 		Player->locking_subsys = ship_get_closest_subsys_in_sight(target_shipp, SUBSYSTEM_ENGINE, &Player_obj->pos);
2063 		if (Player->locking_subsys != NULL) {
2064 			get_subsystem_world_pos(target_objp, Player->locking_subsys, &lock_world_pos);
2065 			Player->locking_on_center=0;
2066 			Player->locking_subsys_parent=Player_ai->target_objnum;
2067 		} else {
2068 			hud_lock_reset();
2069 			return;
2070 		}
2071 	} else {
2072 		// if small ship (or weapon), just go for the center
2073 		lock_world_pos = target_objp->pos;
2074 		Player->locking_on_center=1;
2075 		Player->locking_subsys=NULL;
2076 		Player->locking_subsys_parent=-1;
2077 	}
2078 }
2079 
2080 // Decide which point lock should be homing on
hud_lock_determine_lock_point(vec3d * lock_world_pos_out)2081 void hud_lock_determine_lock_point(vec3d *lock_world_pos_out)
2082 {
2083 	object		*target_objp;
2084 	ship_weapon	*swp;
2085 	weapon_info	*wip;
2086 
2087 	vec3d vec_to_lock_pos;
2088 	vec3d lock_local_pos;
2089 
2090 	Assert(Player_ai->target_objnum >= 0);
2091 	target_objp = &Objects[Player_ai->target_objnum];
2092 
2093 	Player->current_target_sx = -1;
2094 	Player->current_target_sy = -1;
2095 
2096 	swp = &Player_ship->weapons;
2097 	wip = &Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]];
2098 
2099 	// If subsystem is targeted, we must try to lock on that
2100 	if ( Player_ai->targeted_subsys && !(wip->wi_flags[Weapon::Info_Flags::Homing_javelin]) ) {
2101 		hud_lock_update_lock_pos(target_objp);
2102 		Player->locking_on_center=0;
2103 		Player->locking_subsys=Player_ai->targeted_subsys;
2104 		Player->locking_subsys_parent=Player_ai->target_objnum;
2105 	} else if ( (wip->wi_flags[Weapon::Info_Flags::Homing_javelin]) && (target_objp->type == OBJ_SHIP)) {
2106 		if (!Player->locking_subsys ||
2107 			Player->locking_subsys->system_info->type != SUBSYSTEM_ENGINE) {
2108 				Player->locking_subsys = ship_get_closest_subsys_in_sight(&Ships[target_objp->instance], SUBSYSTEM_ENGINE, &Player_obj->pos);
2109 		}
2110 		if (Player->locking_subsys != NULL) {
2111 			get_subsystem_world_pos(target_objp, Player->locking_subsys, &lock_world_pos);
2112 			Player->locking_on_center=0;
2113 			Player->locking_subsys_parent=Player_ai->target_objnum;
2114 		} else {
2115 			hud_lock_reset();
2116 			return;
2117 		}
2118 	} else {
2119 		// See if we already have a successful locked point
2120 		if ( hud_lock_has_homing_point() ) {
2121 			hud_lock_update_lock_pos(target_objp);
2122 		} else {
2123 			hud_lock_get_new_lock_pos(target_objp);
2124 		}
2125 	}
2126 
2127 	*lock_world_pos_out=lock_world_pos;
2128 
2129 	vm_vec_sub(&vec_to_lock_pos,&lock_world_pos,&Player_obj->pos);
2130 	vm_vec_rotate(&lock_local_pos,&vec_to_lock_pos,&Player_obj->orient);
2131 
2132 	if ( lock_local_pos.xyz.z > 0.0f ) {
2133 		// Get the location of our target in the "virtual frame" where the locking computation will be done
2134 		float w = 1.0f / lock_local_pos.xyz.z;
2135 		// Let's force our "virtual frame" to be 640x480. -MageKing17
2136 		float sx = gr_screen.clip_center_x + (lock_local_pos.xyz.x * VIRTUAL_FRAME_HALF_WIDTH * w);
2137 		float sy = gr_screen.clip_center_y - (lock_local_pos.xyz.y * VIRTUAL_FRAME_HALF_HEIGHT * w);
2138 
2139 		Player->current_target_sx = (int)sx;
2140 		Player->current_target_sy = (int)sy;
2141 	}
2142 }
2143 
pageIn()2144 void HudGaugeLock::pageIn()
2145 {
2146 	bm_page_in_aabitmap( Lock_gauge.first_frame, Lock_gauge.num_frames );
2147 	bm_page_in_aabitmap( Lock_anim.first_frame, Lock_anim.num_frames );
2148 }
2149