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, ¤t_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, ¤t_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, ¤t_lock->world_pos);
1074 } else {
1075 current_lock->world_pos = current_lock->obj->pos;
1076 }
1077
1078 vm_vec_sub(&vec_to_lock_pos,¤t_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