2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
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
10 * $Logfile: /Freespace2/code/Hud/HUDlock.cpp $
15 * C module that controls missile locking
18 * Revision 1.6 2003/05/25 02:30:42 taylor
21 * Revision 1.5 2002/06/17 06:33:09 relnev
22 * ryan's struct patch for gcc 2.95
24 * Revision 1.4 2002/06/09 04:41:21 relnev
25 * added copyright header
27 * Revision 1.3 2002/05/31 07:24:28 relnev
30 * Revision 1.2 2002/05/07 03:16:45 theoddone33
31 * The Great Newline Fix
33 * Revision 1.1.1.1 2002/05/03 03:28:09 root
37 * 19 8/09/99 10:50a Jasenw
39 * 18 7/30/99 5:42p Jasenw
40 * changed coords to center HUD lock indicator.
42 * 17 7/28/99 2:49p Andsager
43 * Make hud target speed use vm_vec_mag (not vm_vec_mag_quick)
45 * 16 6/08/99 7:41p Dave
46 * Animated hud lockup icon a bit differently.
48 * 15 6/08/99 8:35a Jasenw
49 * new coords for new lock ani
51 * 14 6/07/99 4:20p Andsager
52 * Add HUD color for tagged object. Apply to target and radar.
54 * 13 6/07/99 1:42p Jasenw
56 * 12 6/04/99 10:58a Dave
57 * Updated hud lock gauge.
59 * 11 6/03/99 2:32p Jasenw
60 * changed coords for new HUD stuff
62 * 10 6/02/99 5:41p Andsager
63 * Reduce range of secondary weapons not fired from turrets in nebula.
64 * Reduce range of beams fired from turrrets in nebula
66 * 9 4/23/99 12:01p Johnson
69 * 8 2/25/99 4:19p Dave
70 * Added multiplayer_beta defines. Added cd_check define. Fixed a few
71 * release build warnings. Added more data to the squad war request and
74 * 7 1/07/99 9:07a Jasen
77 * 6 12/30/98 7:47a Jasen
78 * added coordinates for hi res
80 * 5 12/28/98 3:17p Dave
81 * Support for multiple hud bitmap filenames for hi-res mode.
83 * 4 12/21/98 5:02p Dave
84 * Modified all hud elements to be multi-resolution friendly.
86 * 3 11/05/98 5:55p Dave
87 * Big pass at reducing #includes
89 * 2 10/07/98 10:53a Dave
92 * 1 10/07/98 10:49a Dave
94 * 55 8/25/98 1:48p Dave
95 * First rev of EMP effect. Player side stuff basically done. Next comes
98 * 54 5/11/98 3:48p Lawrance
99 * Break lock when banks switch
101 * 53 5/10/98 7:05p Dave
102 * Fix endgame sequencing ESC key. Changed how host options warning popups
103 * are done. Fixed pause/message scrollback/options screen problems in mp.
104 * Make sure observer HUD doesn't try to lock weapons.
106 * 52 5/04/98 10:50p Lawrance
107 * Don't break lock when switching to secondary weapons of the same type
109 * 51 4/20/98 12:36a Mike
110 * Make team vs. team work when player is hostile. Several targeting
113 * 50 4/15/98 12:55a Lawrance
114 * Make shockwave damage 1/4 if bomb detonated by another weapon. Allow
115 * missiles to lock on bombs.
117 * 49 4/13/98 4:52p Allender
118 * remove AI_frametime and us flFrametime instead. Make lock warning work
119 * in multiplayer for aspect seeking missiles. Debris fixups
121 * 48 4/08/98 8:33p Lawrance
122 * Make player re-acquire lock when targeting subsystems on a locked ship.
124 * 47 4/03/98 10:29a Mike
125 * Make aspect seekers home on a ship even if the targeted subsystem is
126 * backfacing. Too confusing.
128 * 46 3/26/98 5:46p Lawrance
129 * call obj_team() instead of ship_team_from_obj()
131 * 45 3/26/98 5:26p John
132 * added new paging code. nonfunctional.
134 * 44 3/10/98 4:19p John
135 * Cleaned up graphics lib. Took out most unused gr functions. Made D3D
136 * & Glide have popups and print screen. Took out all >8bpp software
137 * support. Made Fred zbuffer. Made zbuffer allocate dynamically to
138 * support Fred. Made zbuffering key off of functions rather than one
141 * 43 3/10/98 11:16a Lawrance
142 * Fix potential bug with missles not locking on a subsystem that appeared
145 * 42 2/28/98 7:03p Lawrance
146 * Change player missile locking to use dot product, so we can use it in
149 * 41 2/22/98 4:17p John
150 * More string externalization classification... 190 left to go!
152 * 40 1/25/98 10:31p Lawrance
153 * Don't draw most hud gauges when viewing from another ship.
155 * 39 1/24/98 4:48p Lawrance
156 * Add 'locking_subsys_parent', needed to save/restore locking_subsys
159 * 38 1/23/98 6:25p Lawrance
160 * Change player missile locking to lock on subsystem points automatically
162 * 37 1/21/98 7:20p Lawrance
163 * Make subsystem locking only work with line-of-sight, cleaned up locking
164 * code, moved globals to player struct.
166 * 36 1/19/98 10:02p Lawrance
167 * Fix bug with locking on friendlies
169 * 35 12/30/97 4:28p Lawrance
170 * remove .ani extensions from filenames
172 * 34 12/10/97 9:57a Lawrance
173 * allow missile locks on all ships but friendly
175 * 33 11/17/97 6:37p Lawrance
176 * new gauges: extended target view, new lock triangles, support ship view
178 * 32 11/17/97 5:51p Lawrance
179 * fix bug that was sometimes causing lock to not occur
181 * 31 11/13/97 10:46p Lawrance
182 * ensure only attempt lock on HOSTILE
184 * 30 11/11/97 12:59a Lawrance
185 * fix couple of bugs with lock indicator not going away
187 * 29 11/08/97 11:07p Lawrance
188 * add in new lock indicator
190 * 28 10/28/97 9:02a Lawrance
191 * don't loop lock sound, some minor reformatting
193 * 27 10/08/97 5:07p Lawrance
194 * don't lock on friendly ships
196 * 26 9/07/97 10:02p Lawrance
197 * don't lock on a ship if player doesn't have any missiles
199 * 25 7/02/97 9:35a Hoffoss
200 * Changed all references to weapon variables in ships to 'weapons'
201 * structure variables in ships.
203 * 24 6/05/97 1:07a Lawrance
204 * changes to support sound interface
206 * 23 5/20/97 2:45p Mike
207 * Move current_target and a bunch of other stuff out of player struct.
209 * 22 4/24/97 10:55a Lawrance
210 * added Show_lock_cone to DCF
212 * 21 4/18/97 2:54p Lawrance
213 * sounds now have a default volume, when playing, pass a scaling factor
214 * not the actual volume
216 * 20 4/15/97 8:36a Lawrance
217 * fixed bug where lock indicator was lagging locked target
219 * 19 4/13/97 3:53p Lawrance
220 * separate out the non-rendering dependant portions of the HUD ( sounds,
221 * updating lock position, changing targets, etc) and put into
224 * 18 4/12/97 4:29p Lawrance
225 * get missle locking and offscreen indicator working properly with
226 * different sized screens
228 * 17 4/10/97 5:29p Lawrance
229 * hud rendering split up into hud_render_3d(), hud_render_2d() and
230 * hud_render_target_model()
232 * 16 4/09/97 4:34p Lawrance
233 * allow looped sounds to be cut off after they complete the full sample
236 * 15 3/25/97 8:17p Lawrance
237 * don't allow ASPECT homing missles to lock on debris
239 * 14 3/19/97 5:53p Lawrance
240 * integrating new Misc_sounds[] array (replaces old Game_sounds
243 * 13 3/10/97 8:53a Lawrance
244 * using hud_stop_looped_locking_sounds() in place of
245 * hud_stop_looped_sounds()
247 * 12 3/07/97 4:37p Mike
248 * Make rockeye missile home.
249 * Remove UNKNOWN and NEUTRAL teams.
251 * 11 3/04/97 2:27p Lawrance
252 * check to ensure player ship has missile banks
254 * 10 3/04/97 11:19a Lawrance
255 * supporting banked weapons
257 * 9 1/23/97 12:00p Lawrance
258 * made lock sound play at normal volume.
260 * 8 1/20/97 7:58p John
261 * Fixed some link errors with testcode.
263 * 7 1/13/97 5:36p Lawrance
264 * integrating new Game_sounds structure for general game sounds
266 * 6 1/06/97 10:43p Lawrance
267 * Changes to make save/restore functional
269 * 5 1/02/97 7:12p Lawrance
270 * adding hooks for more sounds
272 * 4 1/02/97 10:32a Lawrance
273 * fixed some bugs related to stopping looped sounds when targets die and
276 * 3 12/24/96 4:32p Lawrance
277 * some minor improvements
279 * 2 12/23/96 7:53p Lawrance
280 * missile locking working in new source files
287 #include "hudtarget.h"
288 #include "hudreticle.h"
294 #include "freespace.h"
295 #include "gamesequence.h"
300 #include "linklist.h"
305 static float Lock_start_dist;
306 static int Rotate_time_id = 1; // timer id for controlling how often to rotate triangles around lock indicator
308 int Missile_track_loop = -1;
309 int Missile_lock_loop = -1;
311 int Lock_target_box_width[GR_NUM_RESOLUTIONS] = {
315 int Lock_target_box_height[GR_NUM_RESOLUTIONS] = {
320 // the locked triangles (that orbit lock indicator) dimensions
321 float Lock_triangle_base[GR_NUM_RESOLUTIONS] = {
325 float Lock_triangle_height[GR_NUM_RESOLUTIONS] = {
330 int Lock_gauge_half_w[GR_NUM_RESOLUTIONS] = {
338 int Lock_gauge_half_h[GR_NUM_RESOLUTIONS] = {
343 // hud_frames Lock_gauge;
344 int Lock_gauge_loaded = 0;
346 int Lock_gauge_draw = 0;
347 int Lock_gauge_draw_stamp = -1;
348 #define LOCK_GAUGE_BLINK_RATE 5 // blinks/sec
350 int Lockspin_half_w[GR_NUM_RESOLUTIONS] = {
358 int Lockspin_half_h[GR_NUM_RESOLUTIONS] = {
368 char Lock_fname[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN] = {
373 char Lockspin_fname[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN] = {
378 void hud_lock_determine_lock_point(vector *lock_world_pos_out);
380 // hud_init_missile_lock() is called at the beginning of a mission
382 void hud_init_missile_lock()
384 Players[Player_num].lock_indicator_start_x = -1;
385 Players[Player_num].lock_indicator_start_y = -1;
386 Players[Player_num].lock_indicator_visible = 0;
387 Player_ai->current_target_is_locked = 0;
389 Player_ai->last_secondary_index = -1;
393 // Load in the frames need for the lead indicator
394 if (!Lock_gauge_loaded) {
396 Lock_gauge.first_frame = bm_load_animation(Lock_fname[gr_screen.res], &Lock_gauge.num_frames);
397 if ( Lock_gauge.first_frame < 0 ) {
398 Warning(LOCATION,"Cannot load hud ani: Lock_fname[gr_screen.res]\n");
401 hud_anim_init(&Lock_gauge, 0, 0, Lock_fname[gr_screen.res]);
402 hud_anim_load(&Lock_gauge);
404 hud_anim_init(&Lock_anim, 0, 0, Lockspin_fname[gr_screen.res]);
405 hud_anim_load(&Lock_anim);
407 Lock_gauge_loaded = 1;
409 Lock_gauge_draw_stamp = -1;
414 void hud_draw_diamond(int x, int y, int width, int height)
416 SDL_assert(height>0);
419 int x1,x2,x3,x4,y1,y2,y3,y4;
433 gr_line(x1,y1,x2,y2);
434 gr_line(x2,y2,x3,y3);
435 gr_line(x3,y3,x4,y4);
436 gr_line(x4,y4,x1,y1);
440 // hud_show_lock_indicator() will display the lock indicator for homing missiles
441 void hud_show_lock_indicator(float frametime)
443 int target_objnum, sx, sy;
446 if (!Players[Player_num].lock_indicator_visible){
450 target_objnum = Player_ai->target_objnum;
451 SDL_assert(target_objnum != -1);
452 targetp = &Objects[target_objnum];
454 // check to see if there are any missile to fire.. we don't want to show the
455 // lock indicator if there are missiles to fire.
456 if ( !ship_secondary_bank_has_ammo(Player_obj->instance) ) {
460 hud_set_iff_color(targetp);
461 // 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));
463 if (Player_ai->current_target_is_locked) {
464 sx = Player->current_target_sx;
465 sy = Player->current_target_sy;
466 // show the rotating triangles if target is locked
467 hud_draw_lock_triangles(sx, sy, frametime);
469 sx = Players[Player_num].lock_indicator_x;
470 sy = Players[Player_num].lock_indicator_y;
473 // show locked indicator
475 if ( Lock_gauge.first_frame >= 0 ) {
476 gr_set_bitmap(Lock_gauge.first_frame);
477 gr_aabitmap(sx - Lock_gauge_half_w[gr_screen.res], sy - Lock_gauge_half_h[gr_screen.res]);
479 hud_draw_diamond(sx, sy, Lock_target_box_width[gr_screen.res], Lock_target_box_height[gr_screen.res]);
482 Lock_gauge.sx = sx - Lock_gauge_half_w[gr_screen.res];
483 Lock_gauge.sy = sy - Lock_gauge_half_h[gr_screen.res];
484 if(Player_ai->current_target_is_locked){
485 Lock_gauge.time_elapsed = 0.0f;
486 hud_anim_render(&Lock_gauge, 0.0f, 1);
488 hud_anim_render(&Lock_gauge, frametime, 1);
492 // Reset data used for player lock indicator
493 void hud_lock_reset(float lock_time_scale)
498 swp = &Player_ship->weapons;
499 wip = &Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]];
501 Player_ai->current_target_is_locked = 0;
502 Players[Player_num].lock_indicator_visible = 0;
503 Player->target_in_lock_cone = 0;
504 Player->lock_time_to_target = i2fl(wip->min_lock_time*lock_time_scale);
505 Player->current_target_sx = -1;
506 Player->current_target_sy = -1;
507 Player->locking_subsys=NULL;
508 Player->locking_on_center=0;
509 Player->locking_subsys_parent=-1;
510 hud_stop_looped_locking_sounds();
512 Lock_gauge_draw_stamp = -1;
515 // reset the lock anim time elapsed
516 Lock_anim.time_elapsed = 0.0f;
519 // Determine if the locking code has a point to track
520 int hud_lock_has_homing_point()
522 if ( Player_ai->targeted_subsys || Player->locking_subsys || Player->locking_on_center ) {
528 int Nebula_sec_range = 0;
529 DCF_BOOL(nebula_sec_range, Nebula_sec_range)
531 int hud_lock_world_pos_in_range(vector *target_world_pos, vector *vec_to_target)
533 float dist_to_target, weapon_range;
537 int target_in_range=1;
539 swp = &Player_ship->weapons;
540 wip = &Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]];
542 vm_vec_sub(vec_to_target, target_world_pos, &Player_obj->pos);
543 dist_to_target = vm_vec_mag(vec_to_target);
545 // calculate the range of the weapon, and only display the lead target indicator when
546 // if the weapon can actually hit the target
547 weapon_range = wip->max_speed * wip->lifetime;
549 // reduce firing range in nebula
550 if ((The_mission.flags & MISSION_FLAG_FULLNEB) && Nebula_sec_range) {
551 weapon_range *= 0.8f;
554 if (dist_to_target > weapon_range) {
558 return target_in_range;
561 // Determine if point to lock on is in range
562 int hud_lock_target_in_range()
564 vector target_world_pos, vec_to_target;
567 if ( !hud_lock_has_homing_point() ) {
571 targetp = &Objects[Player_ai->target_objnum];
573 if ( Player_ai->targeted_subsys != NULL ) {
574 vm_vec_unrotate(&target_world_pos, &Player_ai->targeted_subsys->system_info->pnt, &targetp->orient);
575 vm_vec_add2(&target_world_pos, &targetp->pos);
577 if ( Player->locking_subsys ) {
578 vm_vec_unrotate(&target_world_pos, &Player->locking_subsys->system_info->pnt, &targetp->orient);
579 vm_vec_add2(&target_world_pos, &targetp->pos);
581 SDL_assert(Player->locking_on_center);
582 target_world_pos = targetp->pos;
586 return hud_lock_world_pos_in_range(&target_world_pos, &vec_to_target);
593 target_team = obj_team(&Objects[Player_ai->target_objnum]);
595 if ( Player_ship->weapons.num_secondary_banks <= 0 ) {
599 if ( Player_ship->weapons.current_secondary_bank < 0 ) {
603 // check to see if there are any missile to fire.. we don't want to show the
604 // lock indicator if there are no missiles to fire.
605 if ( !ship_secondary_bank_has_ammo(Player_obj->instance) ) {
609 // if the target is friendly, don't lock!
610 if ( hud_team_matches_filter(Player_ship->team, target_team)) {
611 // if we're in multiplayer dogfight, ignore this
612 if(!((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_DOGFIGHT))){
620 // determine if the subsystem to lock on to has a direct line of sight
621 int hud_lock_on_subsys_ok()
628 SDL_assert(Player_ai->target_objnum >= 0);
629 target_objp = &Objects[Player_ai->target_objnum];
631 subsys = Player_ai->targeted_subsys;
636 vm_vec_unrotate(&subobj_pos, &subsys->system_info->pnt, &target_objp->orient);
637 vm_vec_add2(&subobj_pos, &target_objp->pos);
639 if ( Player->subsys_in_view < 0 ) {
640 in_sight = ship_subsystem_in_sight(target_objp, subsys, &View_position, &subobj_pos);
642 in_sight = Player->subsys_in_view;
648 // Determine if locking point is in the locking cone
649 void hud_lock_check_if_target_in_lock_cone(vector *lock_world_pos)
652 vector vec_to_target;
654 dist = vm_vec_normalized_dir(&vec_to_target, lock_world_pos, &Player_obj->pos);
655 dot = vm_vec_dot(&Player_obj->orient.v.fvec, &vec_to_target);
658 Player->target_in_lock_cone = 1;
660 Player->target_in_lock_cone = 0;
665 // return 1 if current secondary weapon is different than previous secondary weapon
666 int hud_lock_secondary_weapon_changed(ship_weapon *swp)
669 if ( swp->current_secondary_bank != Player_ai->last_secondary_index ) {
675 int last_wi_index = -1;
676 int current_wi_index = -1;
679 // do a quick out if same bank is selected
680 if ( swp->current_secondary_bank == Player_ai->last_secondary_index ) {
684 // bank has changed, but it still may be the same weapon type
685 if ( swp->current_secondary_bank >= 0 ) {
686 current_wi_index = swp->secondary_bank_weapons[swp->current_secondary_bank];
689 if ( Player_ai->last_secondary_index >= 0 ) {
690 last_wi_index = swp->secondary_bank_weapons[Player_ai->last_secondary_index];
693 if ( current_wi_index != last_wi_index ) {
702 // hud_update_lock_indicator() will manage the non-rendering dependant part of
704 void hud_update_lock_indicator(float frametime)
708 vector lock_world_pos;
710 // if i'm a multiplayer observer, bail here
711 if((Game_mode & GM_MULTIPLAYER) && ((Net_player->flags & NETINFO_FLAG_OBSERVER) || (Player_obj->type == OBJ_OBSERVER)) ){
715 SDL_assert(Player_ai->target_objnum != -1);
717 // be sure to unset this flag, then possibly set later in this function so that
718 // threat indicators work properly.
719 Player_ai->ai_flags &= ~AIF_SEEK_LOCK;
721 if ( hud_abort_lock() ) {
726 // if there is an EMP effect active, never update lock
727 if(emp_active_local()){
732 swp = &Player_ship->weapons;
733 wip = &Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]];
735 Lock_start_dist = wip->min_lock_time * wip->lock_pixels_per_sec;
737 // if secondary weapons change, reset the lock
738 if ( hud_lock_secondary_weapon_changed(swp) ) {
742 Player_ai->last_secondary_index = swp->current_secondary_bank;
744 if ( !(wip->wi_flags & WIF_HOMING_ASPECT) ) {
749 // Allow locking on ships and bombs (only targeted weapon allowed is a bomb, so don't bother checking flags)
750 if ( (Objects[Player_ai->target_objnum].type != OBJ_SHIP) && (Objects[Player_ai->target_objnum].type != OBJ_WEAPON) ) {
755 hud_lock_determine_lock_point(&lock_world_pos);
757 if ( !hud_lock_has_homing_point() ) {
758 Player->target_in_lock_cone=0;
761 hud_lock_check_if_target_in_lock_cone(&lock_world_pos);
763 // check if the target is within range of the current secondary weapon. If it is not,
764 // a lock will not be detected
765 if ( !hud_lock_target_in_range() ) {
766 Player->target_in_lock_cone = 0;
769 // If locking on a subsystem, and not in sight... can't lock
770 // Changed by MK on 4/3/98. It was confusing me that my hornets would not lock on my target.
771 // It will now be confusing that they lock, but don't home on your subsystem, but I think that's preferable.
772 // Often you really care about destroying the target, not just the subsystem.
773 /*if ( Player_ai->targeted_subsys ) {
774 if ( !hud_lock_on_subsys_ok() ) {
775 Player->target_in_lock_cone=0;
779 if ( !Player->target_in_lock_cone ) {
780 Player->locking_on_center=0;
781 Player->locking_subsys_parent=-1;
782 Player->locking_subsys=NULL;
785 hud_calculate_lock_position(frametime);
787 if (!Players[Player_num].lock_indicator_visible)
790 if (Player_ai->current_target_is_locked) {
791 if ( Missile_track_loop > -1 ) {
792 snd_chg_loop_status(Missile_track_loop, 0);
793 Missile_track_loop = -1;
794 Missile_lock_loop = snd_play(&Snds[SND_MISSILE_LOCK]);
798 Player_ai->ai_flags |= AIF_SEEK_LOCK; // set this flag so multiplayer's properly track lock on other ships
799 if ( Missile_lock_loop != -1 && snd_is_playing(Missile_lock_loop) ) {
800 snd_stop(Missile_lock_loop);
801 Missile_lock_loop = -1;
806 // hud_draw_lock_triangles() will draw the 4 rotating triangles around a lock indicator
807 // (This is done when a lock has been acquired)
808 #define ROTATE_DELAY 40
809 void hud_draw_lock_triangles_old(int center_x, int center_y, int radius)
811 static float ang = 0.0f;
813 float end_ang = ang + 2*PI;
814 float x3,y3,x4,y4,xpos,ypos;
816 if ( timestamp_elapsed(Rotate_time_id) ) {
817 Rotate_time_id = timestamp(ROTATE_DELAY);
821 for (; ang <= end_ang; ang += PI/2.0f) {
823 // draw the orbiting triangles
825 //ang = atan2(target_point.y,target_point.x);
826 xpos = center_x + (float)cos(ang)*(radius + Lock_triangle_height[gr_screen.res] + 2);
827 ypos = center_y - (float)sin(ang)*(radius + Lock_triangle_height[gr_screen.res] + 2);
829 x3 = xpos - Lock_triangle_base[gr_screen.res] * (float)sin(-ang);
830 y3 = ypos + Lock_triangle_base[gr_screen.res] * (float)cos(-ang);
831 x4 = xpos + Lock_triangle_base[gr_screen.res] * (float)sin(-ang);
832 y4 = ypos - Lock_triangle_base[gr_screen.res] * (float)cos(-ang);
834 xpos = xpos - Lock_triangle_base[gr_screen.res] * (float)cos(ang);
835 ypos = ypos + Lock_triangle_base[gr_screen.res] * (float)sin(ang);
837 hud_tri(x3, y3, xpos, ypos, x4, y4);
841 // draw a frame of the rotating lock triangles animation
842 void hud_draw_lock_triangles(int center_x, int center_y, float frametime)
844 if ( Lock_anim.first_frame == -1 ) {
845 hud_draw_lock_triangles_old(center_x, center_y, Lock_target_box_width[gr_screen.res]/2);
848 Lock_anim.sx = center_x - Lockspin_half_w[gr_screen.res];
849 Lock_anim.sy = center_y - Lockspin_half_h[gr_screen.res];
851 // if its still animating
852 if(Lock_anim.time_elapsed < Lock_anim.total_time){
855 hud_anim_render(&Lock_anim, frametime, 1, 1, 0);
857 hud_anim_render(&Lock_anim, frametime, 1, 0, 1);
860 // if the timestamp is unset or expired
861 if((Lock_gauge_draw_stamp < 0) || timestamp_elapsed(Lock_gauge_draw_stamp)){
863 Lock_gauge_draw_stamp = timestamp(1000 / (2 * LOCK_GAUGE_BLINK_RATE));
865 // switch between draw and dont-draw
866 Lock_gauge_draw = !Lock_gauge_draw;
869 // maybe draw the anim
870 Lock_gauge.time_elapsed = 0.0f;
874 hud_anim_render(&Lock_anim, frametime, 1, 1, 0);
876 hud_anim_render(&Lock_anim, frametime, 1, 0, 1);
883 // hud_calculate_lock_position() will determine where on the screen to draw the lock
884 // indicator, and will determine when a lock has occurred. If the lock indicator is not
885 // on the screen yet, hud_calculate_lock_start_pos() is called to pick a starting location
886 void hud_calculate_lock_position(float frametime)
891 static float pixels_moved_while_locking;
892 static float pixels_moved_while_degrading;
893 static int Need_new_start_pos = 0;
895 static double accumulated_x_pixels, accumulated_y_pixels;
898 static float last_dist_to_target;
900 static int catching_up;
902 static int maintain_lock_count = 0;
904 static float catch_up_distance = 0.0f;
906 double hypotenuse, delta_x, delta_y;
908 swp = &Player_ship->weapons;
909 wip = &Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]];
911 if (Player->target_in_lock_cone) {
912 if (!Players[Player_num].lock_indicator_visible) {
913 hud_calculate_lock_start_pos();
914 last_dist_to_target = 0.0f;
916 Players[Player_num].lock_indicator_x = Players[Player_num].lock_indicator_start_x;
917 Players[Player_num].lock_indicator_y = Players[Player_num].lock_indicator_start_y;
918 Players[Player_num].lock_indicator_visible = 1;
920 Players[Player_num].lock_time_to_target = i2fl(wip->min_lock_time);
924 Need_new_start_pos = 1;
926 if (Player_ai->current_target_is_locked) {
927 Players[Player_num].lock_indicator_x = Player->current_target_sx;
928 Players[Player_num].lock_indicator_y = Player->current_target_sy;
932 delta_x = Players[Player_num].lock_indicator_x - Player->current_target_sx;
933 delta_y = Players[Player_num].lock_indicator_y - Player->current_target_sy;
935 if (!delta_y && !delta_x) {
939 hypotenuse = _hypot(delta_y, delta_x);
942 Players[Player_num].lock_dist_to_target = (float)hypotenuse;
944 if (last_dist_to_target == 0) {
945 last_dist_to_target = Players[Player_num].lock_dist_to_target;
948 //nprintf(("Alan","dist to target: %.2f\n",Players[Player_num].lock_dist_to_target));
949 //nprintf(("Alan","last to target: %.2f\n\n",last_dist_to_target));
952 //nprintf(("Alan","IN CATCH UP MODE catch_up_dist is %.2f\n",catch_up_distance));
953 if ( Players[Player_num].lock_dist_to_target < catch_up_distance )
957 //nprintf(("Alan","IN NORMAL MODE\n"));
958 if ( (Players[Player_num].lock_dist_to_target - last_dist_to_target) > 2.0f ) {
960 catch_up_distance = last_dist_to_target + wip->catchup_pixel_penalty;
964 last_dist_to_target = Players[Player_num].lock_dist_to_target;
967 Players[Player_num].lock_time_to_target -= frametime;
968 if (Players[Player_num].lock_time_to_target < 0.0f)
969 Players[Player_num].lock_time_to_target = 0.0f;
972 float lock_pixels_per_sec;
973 if (Players[Player_num].lock_time_to_target > 0) {
974 lock_pixels_per_sec = Players[Player_num].lock_dist_to_target / Players[Player_num].lock_time_to_target;
976 lock_pixels_per_sec = i2fl(wip->lock_pixels_per_sec);
979 if (lock_pixels_per_sec > wip->lock_pixels_per_sec) {
980 lock_pixels_per_sec = i2fl(wip->lock_pixels_per_sec);
984 pixels_moved_while_locking = wip->catchup_pixels_per_sec * frametime;
986 pixels_moved_while_locking = lock_pixels_per_sec * frametime;
990 accumulated_x_pixels += pixels_moved_while_locking * delta_x/hypotenuse;
994 accumulated_y_pixels += pixels_moved_while_locking * delta_y/hypotenuse;
997 if (fl_abs(accumulated_x_pixels) > 1.0f) {
998 modf(accumulated_x_pixels, &int_portion);
1000 Players[Player_num].lock_indicator_x -= (int)int_portion;
1002 if ( fl_abs(Players[Player_num].lock_indicator_x - Player->current_target_sx) < fl_abs(int_portion) )
1003 Players[Player_num].lock_indicator_x = Player->current_target_sx;
1005 accumulated_x_pixels -= int_portion;
1008 if (fl_abs(accumulated_y_pixels) > 1.0f) {
1009 modf(accumulated_y_pixels, &int_portion);
1011 Players[Player_num].lock_indicator_y -= (int)int_portion;
1013 if ( fl_abs(Players[Player_num].lock_indicator_y - Player->current_target_sy) < fl_abs(int_portion) )
1014 Players[Player_num].lock_indicator_y = Player->current_target_sy;
1016 accumulated_y_pixels -= int_portion;
1019 if ( Missile_track_loop == -1 ) {
1020 Missile_track_loop = snd_play_looping( &Snds[SND_MISSILE_TRACKING], 0.0f);
1023 if (!Players[Player_num].lock_time_to_target) {
1024 if ( (Players[Player_num].lock_indicator_x == Player->current_target_sx) && (Players[Player_num].lock_indicator_y == Player->current_target_sy) ) {
1025 if (maintain_lock_count++ > 1) {
1026 Player_ai->current_target_is_locked = 1;
1029 maintain_lock_count = 0;
1035 if ( Missile_track_loop > -1 ) {
1036 snd_chg_loop_status(Missile_track_loop, 0);
1037 Missile_track_loop = -1;
1040 Player_ai->current_target_is_locked = 0;
1042 if (!Players[Player_num].lock_indicator_visible) {
1047 last_dist_to_target = 0.0f;
1049 if (Need_new_start_pos) {
1050 hud_calculate_lock_start_pos();
1051 Need_new_start_pos = 0;
1052 accumulated_x_pixels = 0.0f;
1053 accumulated_y_pixels = 0.0f;
1056 delta_x = Players[Player_num].lock_indicator_x - Players[Player_num].lock_indicator_start_x;
1057 delta_y = Players[Player_num].lock_indicator_y - Players[Player_num].lock_indicator_start_y;
1059 if (!delta_y && !delta_x) {
1063 hypotenuse = _hypot(delta_y, delta_x);
1066 Players[Player_num].lock_time_to_target += frametime;
1068 if (Players[Player_num].lock_time_to_target > wip->min_lock_time)
1069 Players[Player_num].lock_time_to_target = i2fl(wip->min_lock_time);
1071 pixels_moved_while_degrading = 2.0f * wip->lock_pixels_per_sec * frametime;
1074 accumulated_x_pixels += pixels_moved_while_degrading * delta_x/hypotenuse;
1077 accumulated_y_pixels += pixels_moved_while_degrading * delta_y/hypotenuse;
1079 if (fl_abs(accumulated_x_pixels) > 1.0f) {
1080 modf(accumulated_x_pixels, &int_portion);
1082 Players[Player_num].lock_indicator_x -= (int)int_portion;
1084 if ( fl_abs(Players[Player_num].lock_indicator_x - Players[Player_num].lock_indicator_start_x) < fl_abs(int_portion) )
1085 Players[Player_num].lock_indicator_x = Players[Player_num].lock_indicator_start_x;
1087 accumulated_x_pixels -= int_portion;
1090 if (fl_abs(accumulated_y_pixels) > 1.0f) {
1091 modf(accumulated_y_pixels, &int_portion);
1093 Players[Player_num].lock_indicator_y -= (int)int_portion;
1095 if ( fl_abs(Players[Player_num].lock_indicator_y - Players[Player_num].lock_indicator_start_y) < fl_abs(int_portion) )
1096 Players[Player_num].lock_indicator_y = Players[Player_num].lock_indicator_start_y;
1098 accumulated_y_pixels -= int_portion;
1101 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) ) {
1102 Players[Player_num].lock_indicator_visible = 0;
1107 // hud_calculate_lock_start_pos() will determine where to draw the starting location of the lock
1108 // indicator. It does this by picking a location that is Lock_start_dist pixels away from the current
1109 // target (in 2D). This is accomplished by finding the endpoint of a line that passes through the
1110 // origin, and connects the target and lock indicator postion (and has a magnitude of Lock_start_dist)
1111 void hud_calculate_lock_start_pos()
1116 double target_mag, target_x, target_y;
1118 delta_x = Player->current_target_sx - SCREEN_CENTER_X;
1119 delta_y = Player->current_target_sy - SCREEN_CENTER_Y;
1121 if (!delta_x && !delta_y) {
1122 Players[Player_num].lock_indicator_start_x = fl2i(SCREEN_CENTER_X + Lock_start_dist);
1123 Players[Player_num].lock_indicator_start_y = fl2i(SCREEN_CENTER_Y);
1127 hypotenuse = _hypot(delta_y, delta_x);
1129 if (hypotenuse >= Lock_start_dist) {
1130 Players[Player_num].lock_indicator_start_x = fl2i(SCREEN_CENTER_X);
1131 Players[Player_num].lock_indicator_start_y = fl2i(SCREEN_CENTER_Y);
1135 target_mag = Lock_start_dist - hypotenuse;
1136 target_x = target_mag * (delta_x / hypotenuse);
1137 target_y = target_mag * (delta_y / hypotenuse);
1139 Players[Player_num].lock_indicator_start_x = fl2i(SCREEN_CENTER_X - target_x);
1140 Players[Player_num].lock_indicator_start_y = fl2i(SCREEN_CENTER_Y - target_y);
1142 if (Players[Player_num].lock_indicator_start_x > gr_screen.clip_right)
1143 Players[Player_num].lock_indicator_start_x = gr_screen.clip_right;
1145 if (Players[Player_num].lock_indicator_start_y > gr_screen.clip_bottom)
1146 Players[Player_num].lock_indicator_start_y = gr_screen.clip_bottom;
1148 if (Players[Player_num].lock_indicator_start_x < gr_screen.clip_left)
1149 Players[Player_num].lock_indicator_start_x = gr_screen.clip_left;
1151 if (Players[Player_num].lock_indicator_start_y < gr_screen.clip_top)
1152 Players[Player_num].lock_indicator_start_y = gr_screen.clip_top;
1155 // hud_stop_looped_locking_sounds() will terminate any hud related looping sounds that are playing
1156 void hud_stop_looped_locking_sounds()
1158 if ( Missile_track_loop > -1 ) {
1159 snd_stop(Missile_track_loop);
1160 Missile_track_loop = -1;
1164 // Get a new world pos for the locking point
1165 void hud_lock_update_lock_pos(object *target_objp, vector *lock_world_pos)
1167 if ( Player_ai->targeted_subsys ) {
1168 get_subsystem_world_pos(target_objp, Player_ai->targeted_subsys, lock_world_pos);
1172 if ( Player->locking_on_center ) {
1173 *lock_world_pos = target_objp->pos;
1175 SDL_assert(Player->locking_subsys);
1176 get_subsystem_world_pos(target_objp, Player->locking_subsys, lock_world_pos);
1180 // Try and find a new locking point
1181 void hud_lock_get_new_lock_pos(object *target_objp, vector *lock_world_pos)
1183 ship *target_shipp=NULL;
1184 int lock_in_range=0;
1185 float best_lock_dot=-1.0f, lock_dot=-1.0f;
1187 vector subsys_world_pos, vec_to_lock;
1189 if ( target_objp->type == OBJ_SHIP ) {
1190 target_shipp = &Ships[target_objp->instance];
1193 // if a large ship, lock to pos closest to center and within range
1194 if ( (target_shipp) && (Ship_info[target_shipp->ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) ) {
1195 // check all the subsystems and the center of the ship
1197 // assume best lock pos is the center of the ship
1198 *lock_world_pos=target_objp->pos;
1199 Player->locking_on_center=1;
1200 Player->locking_subsys=NULL;
1201 Player->locking_subsys_parent=-1;
1202 lock_in_range = hud_lock_world_pos_in_range(lock_world_pos, &vec_to_lock);
1203 vm_vec_normalize(&vec_to_lock);
1204 if ( lock_in_range ) {
1205 best_lock_dot=vm_vec_dot(&Player_obj->orient.v.fvec, &vec_to_lock);
1207 // take center if reasonable dot
1208 if ( best_lock_dot > 0.95 ) {
1212 // iterate through subsystems to see if we can get a better choice
1213 ss = GET_FIRST(&target_shipp->subsys_list);
1214 while ( ss != END_OF_LIST( &target_shipp->subsys_list ) ) {
1216 // get world pos of subsystem
1217 get_subsystem_world_pos(target_objp, ss, &subsys_world_pos);
1219 if ( hud_lock_world_pos_in_range(&subsys_world_pos, &vec_to_lock) ) {
1220 vm_vec_normalize(&vec_to_lock);
1221 lock_dot=vm_vec_dot(&Player_obj->orient.v.fvec, &vec_to_lock);
1222 if ( lock_dot > best_lock_dot ) {
1223 best_lock_dot=lock_dot;
1224 Player->locking_on_center=0;
1225 Player->locking_subsys=ss;
1226 Player->locking_subsys_parent=Player_ai->target_objnum;
1227 *lock_world_pos=subsys_world_pos;
1230 ss = GET_NEXT( ss );
1233 // if small ship (or weapon), just go for the center
1234 *lock_world_pos = target_objp->pos;
1235 Player->locking_on_center=1;
1236 Player->locking_subsys=NULL;
1237 Player->locking_subsys_parent=-1;
1241 // Decide which point lock should be homing on
1242 void hud_lock_determine_lock_point(vector *lock_world_pos_out)
1244 vector lock_world_pos;
1246 object *target_objp;
1248 SDL_assert(Player_ai->target_objnum >= 0);
1249 target_objp = &Objects[Player_ai->target_objnum];
1251 Player->current_target_sx = -1;
1252 Player->current_target_sx = -1;
1254 // If subsystem is targeted, we must try to lock on that
1255 if ( Player_ai->targeted_subsys ) {
1256 hud_lock_update_lock_pos(target_objp, &lock_world_pos);
1257 Player->locking_on_center=0;
1258 Player->locking_subsys=NULL;
1259 Player->locking_subsys_parent=-1;
1261 // See if we already have a successful locked point
1262 if ( hud_lock_has_homing_point() ) {
1263 hud_lock_update_lock_pos(target_objp, &lock_world_pos);
1265 hud_lock_get_new_lock_pos(target_objp, &lock_world_pos);
1269 *lock_world_pos_out=lock_world_pos;
1271 g3_rotate_vertex(&lock_point,&lock_world_pos);
1272 g3_project_vertex(&lock_point);
1274 if (!(lock_point.flags & PF_OVERFLOW)) { // make sure point projected
1275 Player->current_target_sx = (int)lock_point.sx;
1276 Player->current_target_sy = (int)lock_point.sy;
1280 void hudlock_page_in()
1282 bm_page_in_aabitmap( Lock_gauge.first_frame, Lock_gauge.num_frames );