]> icculus.org git repositories - taylor/freespace2.git/blob - src/hud/hudlock.cpp
warning
[taylor/freespace2.git] / src / hud / hudlock.cpp
1 /*
2  * $Logfile: /Freespace2/code/Hud/HUDlock.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  *
7  * C module that controls missile locking
8  *
9  * $Log$
10  * Revision 1.3  2002/05/31 07:24:28  relnev
11  * warning
12  *
13  * Revision 1.2  2002/05/07 03:16:45  theoddone33
14  * The Great Newline Fix
15  *
16  * Revision 1.1.1.1  2002/05/03 03:28:09  root
17  * Initial import.
18  *
19  * 
20  * 19    8/09/99 10:50a Jasenw
21  * 
22  * 18    7/30/99 5:42p Jasenw
23  * changed coords to center HUD lock indicator.
24  * 
25  * 17    7/28/99 2:49p Andsager
26  * Make hud target speed use vm_vec_mag (not vm_vec_mag_quick)
27  * 
28  * 16    6/08/99 7:41p Dave
29  * Animated hud lockup icon a bit differently.
30  * 
31  * 15    6/08/99 8:35a Jasenw
32  * new coords for new lock ani
33  * 
34  * 14    6/07/99 4:20p Andsager
35  * Add HUD color for tagged object.  Apply to target and radar.
36  * 
37  * 13    6/07/99 1:42p Jasenw
38  * 
39  * 12    6/04/99 10:58a Dave
40  * Updated hud lock gauge.
41  * 
42  * 11    6/03/99 2:32p Jasenw
43  * changed coords for new HUD stuff
44  * 
45  * 10    6/02/99 5:41p Andsager
46  * Reduce range of secondary weapons not fired from turrets in nebula.
47  * Reduce range of beams fired from turrrets in nebula
48  * 
49  * 9     4/23/99 12:01p Johnson
50  * Added SIF_HUGE_SHIP
51  * 
52  * 8     2/25/99 4:19p Dave
53  * Added multiplayer_beta defines. Added cd_check define. Fixed a few
54  * release build warnings. Added more data to the squad war request and
55  * response packets.
56  * 
57  * 7     1/07/99 9:07a Jasen
58  * HUD coords
59  * 
60  * 6     12/30/98 7:47a Jasen
61  * added coordinates for hi res
62  * 
63  * 5     12/28/98 3:17p Dave
64  * Support for multiple hud bitmap filenames for hi-res mode.
65  * 
66  * 4     12/21/98 5:02p Dave
67  * Modified all hud elements to be multi-resolution friendly.
68  * 
69  * 3     11/05/98 5:55p Dave
70  * Big pass at reducing #includes
71  * 
72  * 2     10/07/98 10:53a Dave
73  * Initial checkin.
74  * 
75  * 1     10/07/98 10:49a Dave
76  * 
77  * 55    8/25/98 1:48p Dave
78  * First rev of EMP effect. Player side stuff basically done. Next comes
79  * AI code.
80  * 
81  * 54    5/11/98 3:48p Lawrance
82  * Break lock when banks switch
83  * 
84  * 53    5/10/98 7:05p Dave
85  * Fix endgame sequencing ESC key. Changed how host options warning popups
86  * are done. Fixed pause/message scrollback/options screen problems in mp.
87  * Make sure observer HUD doesn't try to lock weapons.
88  * 
89  * 52    5/04/98 10:50p Lawrance
90  * Don't break lock when switching to secondary weapons of the same type
91  * 
92  * 51    4/20/98 12:36a Mike
93  * Make team vs. team work when player is hostile.  Several targeting
94  * problems.
95  * 
96  * 50    4/15/98 12:55a Lawrance
97  * Make shockwave damage 1/4 if bomb detonated by another weapon.  Allow
98  * missiles to lock on bombs.
99  * 
100  * 49    4/13/98 4:52p Allender
101  * remove AI_frametime and us flFrametime instead.  Make lock warning work
102  * in multiplayer for aspect seeking missiles.  Debris fixups
103  * 
104  * 48    4/08/98 8:33p Lawrance
105  * Make player re-acquire lock when targeting subsystems on a locked ship.
106  * 
107  * 47    4/03/98 10:29a Mike
108  * Make aspect seekers home on a ship even if the targeted subsystem is
109  * backfacing.  Too confusing.
110  * 
111  * 46    3/26/98 5:46p Lawrance
112  * call obj_team() instead of ship_team_from_obj()
113  * 
114  * 45    3/26/98 5:26p John
115  * added new paging code. nonfunctional.
116  * 
117  * 44    3/10/98 4:19p John
118  * Cleaned up graphics lib.  Took out most unused gr functions.   Made D3D
119  * & Glide have popups and print screen.  Took out all >8bpp software
120  * support.  Made Fred zbuffer.  Made zbuffer allocate dynamically to
121  * support Fred.  Made zbuffering key off of functions rather than one
122  * global variable.
123  * 
124  * 43    3/10/98 11:16a Lawrance
125  * Fix potential bug with missles not locking on a subsystem that appeared
126  * in view
127  * 
128  * 42    2/28/98 7:03p Lawrance
129  * Change player missile locking to use dot product, so we can use it in
130  * the external views
131  * 
132  * 41    2/22/98 4:17p John
133  * More string externalization classification... 190 left to go!
134  * 
135  * 40    1/25/98 10:31p Lawrance
136  * Don't draw most hud gauges when viewing from another ship.
137  * 
138  * 39    1/24/98 4:48p Lawrance
139  * Add 'locking_subsys_parent', needed to save/restore locking_subsys
140  * pointer.
141  * 
142  * 38    1/23/98 6:25p Lawrance
143  * Change player missile locking to lock on subsystem points automatically
144  * 
145  * 37    1/21/98 7:20p Lawrance
146  * Make subsystem locking only work with line-of-sight, cleaned up locking
147  * code, moved globals to player struct.
148  * 
149  * 36    1/19/98 10:02p Lawrance
150  * Fix bug with locking on friendlies
151  * 
152  * 35    12/30/97 4:28p Lawrance
153  * remove .ani extensions from filenames
154  * 
155  * 34    12/10/97 9:57a Lawrance
156  * allow missile locks on all ships but friendly
157  * 
158  * 33    11/17/97 6:37p Lawrance
159  * new gauges: extended target view, new lock triangles, support ship view
160  * 
161  * 32    11/17/97 5:51p Lawrance
162  * fix bug that was sometimes causing lock to not occur
163  * 
164  * 31    11/13/97 10:46p Lawrance
165  * ensure only attempt lock on HOSTILE
166  * 
167  * 30    11/11/97 12:59a Lawrance
168  * fix couple of bugs with lock indicator not going away
169  * 
170  * 29    11/08/97 11:07p Lawrance
171  * add in new lock indicator
172  * 
173  * 28    10/28/97 9:02a Lawrance
174  * don't loop lock sound, some minor reformatting
175  * 
176  * 27    10/08/97 5:07p Lawrance
177  * don't lock on friendly ships
178  * 
179  * 26    9/07/97 10:02p Lawrance
180  * don't lock on a ship if player doesn't have any missiles
181  * 
182  * 25    7/02/97 9:35a Hoffoss
183  * Changed all references to weapon variables in ships to 'weapons'
184  * structure variables in ships.
185  * 
186  * 24    6/05/97 1:07a Lawrance
187  * changes to support sound interface
188  * 
189  * 23    5/20/97 2:45p Mike
190  * Move current_target and a bunch of other stuff out of player struct.
191  * 
192  * 22    4/24/97 10:55a Lawrance
193  * added Show_lock_cone to DCF
194  * 
195  * 21    4/18/97 2:54p Lawrance
196  * sounds now have a default volume, when playing, pass a scaling factor
197  * not the actual volume
198  * 
199  * 20    4/15/97 8:36a Lawrance
200  * fixed bug where lock indicator was lagging locked target
201  * 
202  * 19    4/13/97 3:53p Lawrance
203  * separate out the non-rendering dependant portions of the HUD ( sounds,
204  * updating lock position, changing targets, etc) and put into
205  * hud_update_frame()
206  * 
207  * 18    4/12/97 4:29p Lawrance
208  * get missle locking and offscreen indicator working properly with
209  * different sized screens
210  * 
211  * 17    4/10/97 5:29p Lawrance
212  * hud rendering split up into hud_render_3d(), hud_render_2d() and
213  * hud_render_target_model()
214  * 
215  * 16    4/09/97 4:34p Lawrance
216  * allow looped sounds to be cut off after they complete the full sample
217  * duration
218  * 
219  * 15    3/25/97 8:17p Lawrance
220  * don't allow ASPECT homing missles to lock on debris
221  * 
222  * 14    3/19/97 5:53p Lawrance
223  * integrating new Misc_sounds[] array (replaces old Game_sounds
224  * structure)
225  * 
226  * 13    3/10/97 8:53a Lawrance
227  * using hud_stop_looped_locking_sounds() in place of
228  * hud_stop_looped_sounds()
229  * 
230  * 12    3/07/97 4:37p Mike
231  * Make rockeye missile home.
232  * Remove UNKNOWN and NEUTRAL teams.
233  * 
234  * 11    3/04/97 2:27p Lawrance
235  * check to ensure player ship has missile banks
236  * 
237  * 10    3/04/97 11:19a Lawrance
238  * supporting banked weapons
239  * 
240  * 9     1/23/97 12:00p Lawrance
241  * made lock sound play at normal volume.
242  * 
243  * 8     1/20/97 7:58p John
244  * Fixed some link errors with testcode.
245  * 
246  * 7     1/13/97 5:36p Lawrance
247  * integrating new Game_sounds structure for general game sounds
248  * 
249  * 6     1/06/97 10:43p Lawrance
250  * Changes to make save/restore functional
251  * 
252  * 5     1/02/97 7:12p Lawrance
253  * adding hooks for more sounds
254  * 
255  * 4     1/02/97 10:32a Lawrance
256  * fixed some bugs related to stopping looped sounds when targets die and
257  * going to menus
258  * 
259  * 3     12/24/96 4:32p Lawrance
260  * some minor improvements
261  * 
262  * 2     12/23/96 7:53p Lawrance
263  * missile locking working in new source files
264  *
265  * $NoKeywords: $
266  */
267
268 #include "hud.h"
269 #include "hudlock.h"
270 #include "hudtarget.h"
271 #include "hudreticle.h"
272 #include "player.h"
273 #include "ship.h"
274 #include "weapon.h"
275 #include "sound.h"
276 #include "timer.h"
277 #include "freespace.h"
278 #include "gamesequence.h"
279 #include "gamesnd.h"
280 #include "ai.h"
281 #include "bmpman.h"
282 #include "3d.h"
283 #include "linklist.h"
284 #include "multi.h"
285 #include "emp.h"
286
287
288 static float Lock_start_dist;
289 static int Rotate_time_id = 1;  // timer id for controlling how often to rotate triangles around lock indicator
290
291 int Missile_track_loop = -1;
292 int Missile_lock_loop = -1;
293
294 int Lock_target_box_width[GR_NUM_RESOLUTIONS] = {
295         19,
296         30
297 };
298 int Lock_target_box_height[GR_NUM_RESOLUTIONS] = {
299         19,
300         30
301 };
302
303 // the locked triangles (that orbit lock indicator) dimensions
304 float Lock_triangle_base[GR_NUM_RESOLUTIONS] = {
305         4.0f,
306         6.5f
307 };
308 float Lock_triangle_height[GR_NUM_RESOLUTIONS] = {
309         4.0f,
310         6.5f
311 };
312
313 int Lock_gauge_half_w[GR_NUM_RESOLUTIONS] = {
314         17,
315         28
316 };
317 int Lock_gauge_half_h[GR_NUM_RESOLUTIONS] = {
318         15, 
319         25
320 };
321
322 // hud_frames Lock_gauge;
323 int Lock_gauge_loaded = 0;
324 hud_anim Lock_gauge;
325 int Lock_gauge_draw = 0;
326 int Lock_gauge_draw_stamp = -1;
327 #define LOCK_GAUGE_BLINK_RATE                   5                       // blinks/sec
328
329 int Lockspin_half_w[GR_NUM_RESOLUTIONS] = {
330         31,
331         50
332 };
333 int Lockspin_half_h[GR_NUM_RESOLUTIONS] = {
334         32, 
335         52
336 };
337 hud_anim        Lock_anim;
338
339 char Lock_fname[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN] = {
340         "lock1",
341         "2_lock1"
342 };
343
344 char Lockspin_fname[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN] = {
345         "lockspin",
346         "2_lockspin"
347 };
348
349 void hud_lock_determine_lock_point(vector *lock_world_pos_out);
350
351 // hud_init_missile_lock() is called at the beginning of a mission
352 //
353 void hud_init_missile_lock()
354 {
355         Players[Player_num].lock_indicator_start_x = -1;
356         Players[Player_num].lock_indicator_start_y = -1;
357         Players[Player_num].lock_indicator_visible = 0;
358         Player_ai->current_target_is_locked = 0;
359
360         Player_ai->last_secondary_index = -1;
361
362         Rotate_time_id = 1;
363
364         // Load in the frames need for the lead indicator
365         if (!Lock_gauge_loaded) {
366                 /*
367                 Lock_gauge.first_frame = bm_load_animation(Lock_fname[gr_screen.res], &Lock_gauge.num_frames);
368                 if ( Lock_gauge.first_frame < 0 ) {
369                         Warning(LOCATION,"Cannot load hud ani: Lock_fname[gr_screen.res]\n");
370                 }
371                 */
372                 hud_anim_init(&Lock_gauge, 0, 0, Lock_fname[gr_screen.res]);
373                 hud_anim_load(&Lock_gauge);
374
375                 hud_anim_init(&Lock_anim, 0, 0, Lockspin_fname[gr_screen.res]);
376                 hud_anim_load(&Lock_anim);
377
378                 Lock_gauge_loaded = 1;
379                 
380                 Lock_gauge_draw_stamp = -1;
381                 Lock_gauge_draw = 0;
382         }
383 }
384
385 void hud_draw_diamond(int x, int y, int width, int height)
386 {
387         Assert(height>0);
388         Assert(width>0);
389
390         int x1,x2,x3,x4,y1,y2,y3,y4;
391
392         x1=x;
393         y1=y-height/2;
394
395         x2=x+width/2;
396         y2=y;
397
398         x3=x;
399         y3=y+height/2;
400
401         x4=x-width/2;
402         y4=y;
403
404         gr_line(x1,y1,x2,y2);
405         gr_line(x2,y2,x3,y3);
406         gr_line(x3,y3,x4,y4);
407         gr_line(x4,y4,x1,y1);
408 }
409
410
411 // hud_show_lock_indicator() will display the lock indicator for homing missiles
412 void hud_show_lock_indicator(float frametime)
413 {
414         int                     target_objnum, sx, sy;
415         object          *targetp;
416
417         if (!Players[Player_num].lock_indicator_visible){
418                 return;
419         }
420
421         target_objnum = Player_ai->target_objnum;
422         Assert(target_objnum != -1);
423         targetp = &Objects[target_objnum];
424
425         // check to see if there are any missile to fire.. we don't want to show the 
426         // lock indicator if there are missiles to fire.
427         if ( !ship_secondary_bank_has_ammo(Player_obj->instance) ) {
428                 return;
429         }
430         
431         hud_set_iff_color(targetp);
432 //      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));
433
434         if (Player_ai->current_target_is_locked) {
435                 sx = Player->current_target_sx;
436                 sy = Player->current_target_sy;
437                 // show the rotating triangles if target is locked
438                 hud_draw_lock_triangles(sx, sy, frametime);
439         } else {
440                 sx = Players[Player_num].lock_indicator_x;
441                 sy = Players[Player_num].lock_indicator_y;
442         }
443
444         // show locked indicator
445         /*
446         if ( Lock_gauge.first_frame >= 0 ) {
447                 gr_set_bitmap(Lock_gauge.first_frame);
448                 gr_aabitmap(sx - Lock_gauge_half_w[gr_screen.res], sy - Lock_gauge_half_h[gr_screen.res]);
449         } else {
450                 hud_draw_diamond(sx, sy, Lock_target_box_width[gr_screen.res], Lock_target_box_height[gr_screen.res]);
451         }
452         */
453         Lock_gauge.sx = sx - Lock_gauge_half_w[gr_screen.res];
454         Lock_gauge.sy = sy - Lock_gauge_half_h[gr_screen.res];
455         if(Player_ai->current_target_is_locked){
456                 Lock_gauge.time_elapsed = 0.0f;                 
457                 hud_anim_render(&Lock_gauge, 0.0f, 1);          
458         } else {
459                 hud_anim_render(&Lock_gauge, frametime, 1);
460         }
461 }
462
463 // Reset data used for player lock indicator
464 void hud_lock_reset(float lock_time_scale)
465 {
466         weapon_info     *wip;
467         ship_weapon     *swp;
468
469         swp = &Player_ship->weapons;
470         wip = &Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]];
471
472         Player_ai->current_target_is_locked = 0;
473         Players[Player_num].lock_indicator_visible = 0;
474         Player->target_in_lock_cone = 0;
475         Player->lock_time_to_target = i2fl(wip->min_lock_time*lock_time_scale);
476         Player->current_target_sx = -1;
477         Player->current_target_sy = -1;
478         Player->locking_subsys=NULL;
479         Player->locking_on_center=0;
480         Player->locking_subsys_parent=-1;
481         hud_stop_looped_locking_sounds();
482
483         Lock_gauge_draw_stamp = -1;
484         Lock_gauge_draw = 0;
485
486         // reset the lock anim time elapsed
487         Lock_anim.time_elapsed = 0.0f;
488 }
489
490 // Determine if the locking code has a point to track
491 int hud_lock_has_homing_point()
492 {
493         if ( Player_ai->targeted_subsys || Player->locking_subsys || Player->locking_on_center ) {
494                 return 1;
495         }
496         return 0;
497 }
498
499 int Nebula_sec_range = 0;
500 DCF_BOOL(nebula_sec_range, Nebula_sec_range)
501
502 int hud_lock_world_pos_in_range(vector *target_world_pos, vector *vec_to_target)
503 {
504         float                   dist_to_target, weapon_range;
505         weapon_info     *wip;
506         ship_weapon     *swp;
507
508         int target_in_range=1;
509
510         swp = &Player_ship->weapons;
511         wip = &Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]];
512
513         vm_vec_sub(vec_to_target, target_world_pos, &Player_obj->pos);
514         dist_to_target = vm_vec_mag(vec_to_target);
515
516         // calculate the range of the weapon, and only display the lead target indicator when
517         // if the weapon can actually hit the target
518         weapon_range = wip->max_speed * wip->lifetime;
519
520         // reduce firing range in nebula
521         if ((The_mission.flags & MISSION_FLAG_FULLNEB) && Nebula_sec_range) {
522                 weapon_range *= 0.8f;
523         }
524
525         if (dist_to_target > weapon_range) {
526                 target_in_range=0;
527         }
528
529         return target_in_range;
530 }
531
532 // Determine if point to lock on is in range
533 int hud_lock_target_in_range()
534 {
535         vector          target_world_pos, vec_to_target;
536         object          *targetp;
537
538         if ( !hud_lock_has_homing_point() ) {
539                 return 0;
540         }
541
542         targetp = &Objects[Player_ai->target_objnum];
543
544         if ( Player_ai->targeted_subsys != NULL ) {
545                 vm_vec_unrotate(&target_world_pos, &Player_ai->targeted_subsys->system_info->pnt, &targetp->orient);
546                 vm_vec_add2(&target_world_pos, &targetp->pos);
547         } else {
548                 if ( Player->locking_subsys ) {
549                         vm_vec_unrotate(&target_world_pos, &Player->locking_subsys->system_info->pnt, &targetp->orient);
550                         vm_vec_add2(&target_world_pos, &targetp->pos);
551                 } else {
552                         Assert(Player->locking_on_center);
553                         target_world_pos = targetp->pos;
554                 }
555         }
556
557         return hud_lock_world_pos_in_range(&target_world_pos, &vec_to_target);
558 }
559
560 int hud_abort_lock()
561 {
562         int target_team;
563
564         target_team = obj_team(&Objects[Player_ai->target_objnum]);
565
566         if ( Player_ship->weapons.num_secondary_banks <= 0 ) {
567                 return 1;
568         }
569
570         if ( Player_ship->weapons.current_secondary_bank < 0 ) {
571                 return 1;
572         }
573
574         // check to see if there are any missile to fire.. we don't want to show the 
575         // lock indicator if there are no missiles to fire.
576         if ( !ship_secondary_bank_has_ammo(Player_obj->instance) ) {
577                 return 1;
578         }
579
580         // if the target is friendly, don't lock!
581         if ( hud_team_matches_filter(Player_ship->team, target_team)) {
582                 // if we're in multiplayer dogfight, ignore this
583                 if(!((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_DOGFIGHT))){
584                         return 1;
585                 }
586         }
587
588         return 0;
589 }
590
591 // determine if the subsystem to lock on to has a direct line of sight
592 int hud_lock_on_subsys_ok()
593 {
594         ship_subsys             *subsys;
595         vector                  subobj_pos;
596         object                  *target_objp;
597         int                             in_sight=0;
598         
599         Assert(Player_ai->target_objnum >= 0);
600         target_objp     = &Objects[Player_ai->target_objnum];
601
602         subsys = Player_ai->targeted_subsys;
603         if ( !subsys ) {
604                 return 0;
605         }
606
607         vm_vec_unrotate(&subobj_pos, &subsys->system_info->pnt, &target_objp->orient);
608         vm_vec_add2(&subobj_pos, &target_objp->pos);
609
610         if ( Player->subsys_in_view < 0 ) {
611                 in_sight = ship_subsystem_in_sight(target_objp, subsys, &View_position, &subobj_pos);
612         } else {
613                 in_sight = Player->subsys_in_view;
614         }
615
616         return in_sight;
617 }
618
619 // Determine if locking point is in the locking cone
620 void hud_lock_check_if_target_in_lock_cone(vector *lock_world_pos)
621 {
622         float           dist, dot;
623         vector  vec_to_target;
624
625         dist = vm_vec_normalized_dir(&vec_to_target, lock_world_pos, &Player_obj->pos);
626         dot = vm_vec_dot(&Player_obj->orient.fvec, &vec_to_target);
627
628         if ( dot > 0.85) {
629                 Player->target_in_lock_cone = 1;
630         } else {
631                 Player->target_in_lock_cone = 0;
632         }
633
634 }
635
636 // return 1 if current secondary weapon is different than previous secondary weapon
637 int hud_lock_secondary_weapon_changed(ship_weapon *swp)
638 {
639
640         if ( swp->current_secondary_bank != Player_ai->last_secondary_index ) {
641                 return 1;
642         }
643
644         return 0;
645 /*
646         int last_wi_index = -1;
647         int current_wi_index = -1;
648
649
650         // do a quick out if same bank is selected
651         if ( swp->current_secondary_bank == Player_ai->last_secondary_index ) {
652                 return 0;
653         }
654
655         // bank has changed, but it still may be the same weapon type
656         if ( swp->current_secondary_bank >= 0 ) {
657                 current_wi_index = swp->secondary_bank_weapons[swp->current_secondary_bank];
658         }
659
660         if ( Player_ai->last_secondary_index >= 0 ) {
661                 last_wi_index = swp->secondary_bank_weapons[Player_ai->last_secondary_index];
662         }
663
664         if ( current_wi_index != last_wi_index ) {
665                 return 1;
666         }
667
668         return 0;
669 */
670
671 }
672
673 // hud_update_lock_indicator() will manage the non-rendering dependant part of
674 // missle locking
675 void hud_update_lock_indicator(float frametime)
676 {
677         ship_weapon *swp;
678         weapon_info     *wip;
679         vector          lock_world_pos;
680
681         // if i'm a multiplayer observer, bail here
682         if((Game_mode & GM_MULTIPLAYER) && ((Net_player->flags & NETINFO_FLAG_OBSERVER) || (Player_obj->type == OBJ_OBSERVER)) ){
683                 return;
684         }
685
686         Assert(Player_ai->target_objnum != -1);
687
688         // be sure to unset this flag, then possibly set later in this function so that
689         // threat indicators work properly.
690         Player_ai->ai_flags &= ~AIF_SEEK_LOCK;
691
692         if ( hud_abort_lock() ) {
693                 hud_lock_reset();
694                 return;
695         }
696
697         // if there is an EMP effect active, never update lock
698         if(emp_active_local()){
699                 hud_lock_reset();
700                 return;
701         }
702
703         swp = &Player_ship->weapons;
704         wip = &Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]];
705
706         Lock_start_dist = wip->min_lock_time * wip->lock_pixels_per_sec;
707
708         // if secondary weapons change, reset the lock
709         if ( hud_lock_secondary_weapon_changed(swp) ) {
710                 hud_lock_reset();
711         }
712                 
713         Player_ai->last_secondary_index = swp->current_secondary_bank;
714
715         if ( !(wip->wi_flags & WIF_HOMING_ASPECT) ) {
716                 hud_lock_reset();
717                 return;         
718         }
719
720         // Allow locking on ships and bombs (only targeted weapon allowed is a bomb, so don't bother checking flags)
721         if ( (Objects[Player_ai->target_objnum].type != OBJ_SHIP) && (Objects[Player_ai->target_objnum].type != OBJ_WEAPON) ) { 
722                 hud_lock_reset();
723                 return;
724         }
725
726         hud_lock_determine_lock_point(&lock_world_pos);
727
728         if ( !hud_lock_has_homing_point() ) {
729                 Player->target_in_lock_cone=0;
730         }
731
732         hud_lock_check_if_target_in_lock_cone(&lock_world_pos);
733
734         // check if the target is within range of the current secondary weapon.  If it is not,
735         // a lock will not be detected
736         if ( !hud_lock_target_in_range() ) {
737                 Player->target_in_lock_cone = 0;
738         }
739
740         // If locking on a subsystem, and not in sight... can't lock
741         //      Changed by MK on 4/3/98.  It was confusing me that my hornets would not lock on my target.
742         //      It will now be confusing that they lock, but don't home on your subsystem, but I think that's preferable.
743         //      Often you really care about destroying the target, not just the subsystem.
744         /*if ( Player_ai->targeted_subsys ) {
745                 if ( !hud_lock_on_subsys_ok() ) {
746                         Player->target_in_lock_cone=0;
747                 }
748         }*/
749
750         if ( !Player->target_in_lock_cone ) {
751                 Player->locking_on_center=0;
752                 Player->locking_subsys_parent=-1;
753                 Player->locking_subsys=NULL;
754         }
755                 
756         hud_calculate_lock_position(frametime);
757
758         if (!Players[Player_num].lock_indicator_visible)
759                 return;
760
761         if (Player_ai->current_target_is_locked) {
762                 if ( Missile_track_loop > -1 )  {
763                         snd_chg_loop_status(Missile_track_loop, 0);
764                         Missile_track_loop = -1;
765                         Missile_lock_loop = snd_play(&Snds[SND_MISSILE_LOCK]);
766                 }
767         }
768         else {
769                 Player_ai->ai_flags |= AIF_SEEK_LOCK;           // set this flag so multiplayer's properly track lock on other ships
770                 if ( Missile_lock_loop != -1 && snd_is_playing(Missile_lock_loop) ) {
771                         snd_stop(Missile_lock_loop);
772                         Missile_lock_loop = -1;
773                 }
774         }
775 }
776
777 // hud_draw_lock_triangles() will draw the 4 rotating triangles around a lock indicator
778 // (This is done when a lock has been acquired)
779 #define ROTATE_DELAY 40
780 void hud_draw_lock_triangles_old(int center_x, int center_y, int radius)
781 {
782         static float ang = 0.0f;
783
784         float end_ang = ang + 2*PI;
785         float x3,y3,x4,y4,xpos,ypos;
786
787         if ( timestamp_elapsed(Rotate_time_id) ) {
788                 Rotate_time_id = timestamp(ROTATE_DELAY);
789                 ang += PI/12;
790         }
791
792         for (; ang <= end_ang; ang += PI/2.0f) {
793
794                 // draw the orbiting triangles
795
796                 //ang = atan2(target_point.y,target_point.x);
797                 xpos = center_x + (float)cos(ang)*(radius + Lock_triangle_height[gr_screen.res] + 2);
798                 ypos = center_y - (float)sin(ang)*(radius + Lock_triangle_height[gr_screen.res] + 2);
799                         
800                 x3 = xpos - Lock_triangle_base[gr_screen.res] * (float)sin(-ang);
801                 y3 = ypos + Lock_triangle_base[gr_screen.res] * (float)cos(-ang);
802                 x4 = xpos + Lock_triangle_base[gr_screen.res] * (float)sin(-ang);
803                 y4 = ypos - Lock_triangle_base[gr_screen.res] * (float)cos(-ang);
804
805                 xpos = xpos - Lock_triangle_base[gr_screen.res] * (float)cos(ang);
806                 ypos = ypos + Lock_triangle_base[gr_screen.res] * (float)sin(ang);
807
808                 hud_tri(x3, y3, xpos, ypos, x4, y4);
809         } // end for
810 }
811
812 // draw a frame of the rotating lock triangles animation
813 void hud_draw_lock_triangles(int center_x, int center_y, float frametime)
814 {
815         if ( Lock_anim.first_frame == -1 ) {
816                 hud_draw_lock_triangles_old(center_x, center_y, Lock_target_box_width[gr_screen.res]/2);
817         } else {
818                 // render the anim
819                 Lock_anim.sx = center_x - Lockspin_half_w[gr_screen.res];
820                 Lock_anim.sy = center_y - Lockspin_half_h[gr_screen.res];
821
822                 // if its still animating
823                 if(Lock_anim.time_elapsed < Lock_anim.total_time){
824                         hud_anim_render(&Lock_anim, frametime, 1, 0, 1);
825                 } else {
826                         // if the timestamp is unset or expired
827                         if((Lock_gauge_draw_stamp < 0) || timestamp_elapsed(Lock_gauge_draw_stamp)){
828                                 // reset timestamp
829                                 Lock_gauge_draw_stamp = timestamp(1000 / (2 * LOCK_GAUGE_BLINK_RATE));
830
831                                 // switch between draw and dont-draw
832                                 Lock_gauge_draw = !Lock_gauge_draw;
833                         }
834
835                         // maybe draw the anim
836                         Lock_gauge.time_elapsed = 0.0f;                 
837                         if(Lock_gauge_draw){
838                                 hud_anim_render(&Lock_anim, frametime, 1, 0, 1);
839                         }                       
840                 }               
841         }
842 }
843
844 // hud_calculate_lock_position()  will determine where on the screen to draw the lock 
845 // indicator, and will determine when a lock has occurred.  If the lock indicator is not
846 // on the screen yet, hud_calculate_lock_start_pos() is called to pick a starting location
847 void hud_calculate_lock_position(float frametime)
848 {
849         ship_weapon *swp;
850         weapon_info     *wip;
851
852         static float pixels_moved_while_locking;
853         static float pixels_moved_while_degrading;
854         static int Need_new_start_pos = 0;
855
856         static double accumulated_x_pixels, accumulated_y_pixels;
857         double int_portion;
858
859         static float last_dist_to_target;
860         
861         static int catching_up;
862
863         static int maintain_lock_count = 0;
864
865         static float catch_up_distance = 0.0f;
866
867         double hypotenuse, delta_x, delta_y;
868
869         swp = &Player_ship->weapons;
870         wip = &Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]];
871
872         if (Player->target_in_lock_cone) {
873                 if (!Players[Player_num].lock_indicator_visible) {
874                         hud_calculate_lock_start_pos();
875                         last_dist_to_target = 0.0f;
876
877                         Players[Player_num].lock_indicator_x = Players[Player_num].lock_indicator_start_x;
878                         Players[Player_num].lock_indicator_y = Players[Player_num].lock_indicator_start_y;
879                         Players[Player_num].lock_indicator_visible = 1;
880
881                         Players[Player_num].lock_time_to_target = i2fl(wip->min_lock_time);
882                         catching_up = 0;
883                 }
884
885                 Need_new_start_pos = 1;
886
887                 if (Player_ai->current_target_is_locked) {
888                         Players[Player_num].lock_indicator_x = Player->current_target_sx;
889                         Players[Player_num].lock_indicator_y = Player->current_target_sy;
890                         return;
891                 }
892
893                 delta_x = Players[Player_num].lock_indicator_x - Player->current_target_sx;
894                 delta_y = Players[Player_num].lock_indicator_y - Player->current_target_sy;
895
896                 if (!delta_y && !delta_x) {
897                         hypotenuse = 0.0f;
898                 }
899                 else {
900                         hypotenuse = _hypot(delta_y, delta_x);
901                 }
902
903                 Players[Player_num].lock_dist_to_target = (float)hypotenuse;
904
905                 if (last_dist_to_target == 0) {
906                         last_dist_to_target = Players[Player_num].lock_dist_to_target;
907                 }
908
909                 //nprintf(("Alan","dist to target: %.2f\n",Players[Player_num].lock_dist_to_target));
910                 //nprintf(("Alan","last to target: %.2f\n\n",last_dist_to_target));
911
912                 if (catching_up) {
913                         //nprintf(("Alan","IN CATCH UP MODE  catch_up_dist is %.2f\n",catch_up_distance));      
914                         if ( Players[Player_num].lock_dist_to_target < catch_up_distance )
915                                 catching_up = 0;
916                 }
917                 else {
918                         //nprintf(("Alan","IN NORMAL MODE\n"));
919                         if ( (Players[Player_num].lock_dist_to_target - last_dist_to_target) > 2.0f ) {
920                                 catching_up = 1;
921                                 catch_up_distance = last_dist_to_target + wip->catchup_pixel_penalty;
922                         }
923                 }
924
925                 last_dist_to_target = Players[Player_num].lock_dist_to_target;
926
927                 if (!catching_up) {
928                         Players[Player_num].lock_time_to_target -= frametime;
929                         if (Players[Player_num].lock_time_to_target < 0.0f)
930                                 Players[Player_num].lock_time_to_target = 0.0f;
931                 }
932
933                 float lock_pixels_per_sec;
934                 if (Players[Player_num].lock_time_to_target > 0) {
935                         lock_pixels_per_sec = Players[Player_num].lock_dist_to_target / Players[Player_num].lock_time_to_target;
936                 } else {
937                         lock_pixels_per_sec = i2fl(wip->lock_pixels_per_sec);
938                 }
939
940                 if (lock_pixels_per_sec > wip->lock_pixels_per_sec) {
941                         lock_pixels_per_sec = i2fl(wip->lock_pixels_per_sec);
942                 }
943                 
944                 if (catching_up) {
945                         pixels_moved_while_locking = wip->catchup_pixels_per_sec * frametime;
946                 } else {
947                         pixels_moved_while_locking = lock_pixels_per_sec * frametime;
948                 }
949                 
950                 if (delta_x != 0) {
951                         accumulated_x_pixels += pixels_moved_while_locking * delta_x/hypotenuse; 
952                 }
953
954                 if (delta_y != 0) {
955                         accumulated_y_pixels += pixels_moved_while_locking * delta_y/hypotenuse; 
956                 }
957
958                 if (fl_abs(accumulated_x_pixels) > 1.0f) {
959                         modf(accumulated_x_pixels, &int_portion);
960
961                         Players[Player_num].lock_indicator_x -= (int)int_portion;
962
963                         if ( fl_abs(Players[Player_num].lock_indicator_x - Player->current_target_sx) < fl_abs(int_portion) )
964                                 Players[Player_num].lock_indicator_x = Player->current_target_sx;
965
966                         accumulated_x_pixels -= int_portion;
967                 }
968
969                 if (fl_abs(accumulated_y_pixels) > 1.0f) {
970                         modf(accumulated_y_pixels, &int_portion);
971
972                         Players[Player_num].lock_indicator_y -= (int)int_portion;
973
974                         if ( fl_abs(Players[Player_num].lock_indicator_y - Player->current_target_sy) < fl_abs(int_portion) )
975                                 Players[Player_num].lock_indicator_y = Player->current_target_sy;
976
977                         accumulated_y_pixels -= int_portion;
978                 }
979
980                 if ( Missile_track_loop == -1 ) {       
981                         Missile_track_loop = snd_play_looping( &Snds[SND_MISSILE_TRACKING], 0.0f , -1, -1);
982                 }
983
984                 if (!Players[Player_num].lock_time_to_target) {
985                         if ( (Players[Player_num].lock_indicator_x == Player->current_target_sx) && (Players[Player_num].lock_indicator_y == Player->current_target_sy) ) {
986                                 if (maintain_lock_count++ > 1) {
987                                         Player_ai->current_target_is_locked = 1;
988                                 }
989                         } else {
990                                 maintain_lock_count = 0;
991                         }
992                 }
993
994         } else {
995
996                 if ( Missile_track_loop > -1 )  {
997                         snd_chg_loop_status(Missile_track_loop, 0);
998                         Missile_track_loop = -1;
999                 }
1000
1001                 Player_ai->current_target_is_locked = 0;
1002
1003                 if (!Players[Player_num].lock_indicator_visible) {
1004                         return;
1005                 }
1006
1007                 catching_up = 0;
1008                 last_dist_to_target = 0.0f;
1009
1010                 if (Need_new_start_pos) {
1011                         hud_calculate_lock_start_pos();
1012                         Need_new_start_pos = 0;
1013                         accumulated_x_pixels = 0.0f;
1014                         accumulated_y_pixels = 0.0f;
1015                 }
1016
1017                 delta_x = Players[Player_num].lock_indicator_x - Players[Player_num].lock_indicator_start_x;
1018                 delta_y = Players[Player_num].lock_indicator_y - Players[Player_num].lock_indicator_start_y;
1019
1020                 if (!delta_y && !delta_x) {
1021                         hypotenuse = 0.0f;
1022                 }
1023                 else {
1024                         hypotenuse = _hypot(delta_y, delta_x);
1025                 }
1026
1027                 Players[Player_num].lock_time_to_target += frametime;
1028
1029                 if (Players[Player_num].lock_time_to_target > wip->min_lock_time)
1030                         Players[Player_num].lock_time_to_target = i2fl(wip->min_lock_time);
1031
1032                 pixels_moved_while_degrading = 2.0f * wip->lock_pixels_per_sec * frametime;
1033
1034                 if (delta_x != 0)
1035                         accumulated_x_pixels += pixels_moved_while_degrading * delta_x/hypotenuse; 
1036
1037                 if (delta_y != 0)
1038                         accumulated_y_pixels += pixels_moved_while_degrading * delta_y/hypotenuse; 
1039
1040                 if (fl_abs(accumulated_x_pixels) > 1.0f) {
1041                         modf(accumulated_x_pixels, &int_portion);
1042
1043                         Players[Player_num].lock_indicator_x -= (int)int_portion;
1044
1045                         if ( fl_abs(Players[Player_num].lock_indicator_x - Players[Player_num].lock_indicator_start_x) < fl_abs(int_portion) )
1046                                 Players[Player_num].lock_indicator_x = Players[Player_num].lock_indicator_start_x;
1047
1048                         accumulated_x_pixels -= int_portion;
1049                 }
1050
1051                 if (fl_abs(accumulated_y_pixels) > 1.0f) {
1052                         modf(accumulated_y_pixels, &int_portion);
1053
1054                         Players[Player_num].lock_indicator_y -= (int)int_portion;
1055
1056                         if ( fl_abs(Players[Player_num].lock_indicator_y - Players[Player_num].lock_indicator_start_y) < fl_abs(int_portion) )
1057                                 Players[Player_num].lock_indicator_y = Players[Player_num].lock_indicator_start_y;
1058
1059                         accumulated_y_pixels -= int_portion;
1060                 }
1061
1062                 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) ) {
1063                         Players[Player_num].lock_indicator_visible = 0;
1064                 }
1065         }
1066 }
1067
1068 // hud_calculate_lock_start_pos() will determine where to draw the starting location of the lock
1069 // indicator.  It does this by picking a location that is Lock_start_dist pixels away from the current
1070 // target (in 2D).  This is accomplished by finding the endpoint of a line that passes through the 
1071 // origin, and connects the target and lock indicator postion (and has a magnitude of Lock_start_dist)
1072 void hud_calculate_lock_start_pos()
1073 {
1074         double hypotenuse;
1075         double delta_y;
1076         double delta_x;
1077         double target_mag, target_x, target_y;
1078
1079         delta_x = Player->current_target_sx - SCREEN_CENTER_X;
1080         delta_y = Player->current_target_sy - SCREEN_CENTER_Y;
1081
1082         if (!delta_x && !delta_y) {
1083                 Players[Player_num].lock_indicator_start_x = fl2i(SCREEN_CENTER_X + Lock_start_dist);
1084                 Players[Player_num].lock_indicator_start_y = fl2i(SCREEN_CENTER_Y);
1085                 return;
1086         }
1087
1088         hypotenuse = _hypot(delta_y, delta_x);
1089
1090         if (hypotenuse >= Lock_start_dist) {
1091                 Players[Player_num].lock_indicator_start_x = fl2i(SCREEN_CENTER_X);
1092                 Players[Player_num].lock_indicator_start_y = fl2i(SCREEN_CENTER_Y);
1093                 return;
1094         }
1095
1096         target_mag = Lock_start_dist - hypotenuse;
1097         target_x = target_mag * (delta_x / hypotenuse);
1098         target_y = target_mag * (delta_y / hypotenuse);
1099
1100         Players[Player_num].lock_indicator_start_x = fl2i(SCREEN_CENTER_X - target_x);
1101         Players[Player_num].lock_indicator_start_y = fl2i(SCREEN_CENTER_Y - target_y);
1102
1103         if (Players[Player_num].lock_indicator_start_x > gr_screen.clip_right)
1104                 Players[Player_num].lock_indicator_start_x = gr_screen.clip_right;
1105
1106         if (Players[Player_num].lock_indicator_start_y > gr_screen.clip_bottom)
1107                 Players[Player_num].lock_indicator_start_y = gr_screen.clip_bottom;
1108
1109         if (Players[Player_num].lock_indicator_start_x < gr_screen.clip_left)
1110                 Players[Player_num].lock_indicator_start_x = gr_screen.clip_left;
1111
1112         if (Players[Player_num].lock_indicator_start_y < gr_screen.clip_top)
1113                 Players[Player_num].lock_indicator_start_y = gr_screen.clip_top;
1114 }
1115
1116 // hud_stop_looped_locking_sounds() will terminate any hud related looping sounds that are playing
1117 void hud_stop_looped_locking_sounds()
1118 {
1119         if ( Missile_track_loop > -1 )  {
1120                 snd_stop(Missile_track_loop);
1121                 Missile_track_loop = -1;
1122         }
1123 }
1124
1125 // Get a new world pos for the locking point
1126 void hud_lock_update_lock_pos(object *target_objp, vector *lock_world_pos)
1127 {
1128         if ( Player_ai->targeted_subsys ) {
1129                 get_subsystem_world_pos(target_objp, Player_ai->targeted_subsys, lock_world_pos);
1130                 return;
1131         }
1132
1133         if ( Player->locking_on_center ) {
1134                 *lock_world_pos = target_objp->pos;
1135         } else {
1136                 Assert(Player->locking_subsys);
1137                 get_subsystem_world_pos(target_objp, Player->locking_subsys, lock_world_pos);
1138         }
1139 }
1140
1141 // Try and find a new locking point
1142 void hud_lock_get_new_lock_pos(object *target_objp, vector *lock_world_pos)
1143 {
1144         ship                    *target_shipp=NULL;
1145         int                     lock_in_range=0;
1146         float                   best_lock_dot=-1.0f, lock_dot=-1.0f;
1147         ship_subsys     *ss;
1148         vector          subsys_world_pos, vec_to_lock;
1149
1150         if ( target_objp->type == OBJ_SHIP ) {
1151                 target_shipp = &Ships[target_objp->instance];
1152         }
1153
1154         // if a large ship, lock to pos closest to center and within range
1155         if ( (target_shipp) && (Ship_info[target_shipp->ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) ) {
1156                 // check all the subsystems and the center of the ship
1157                 
1158                 // assume best lock pos is the center of the ship
1159                 *lock_world_pos=target_objp->pos;
1160                 Player->locking_on_center=1;
1161                 Player->locking_subsys=NULL;
1162                 Player->locking_subsys_parent=-1;
1163                 lock_in_range = hud_lock_world_pos_in_range(lock_world_pos, &vec_to_lock);
1164                 vm_vec_normalize(&vec_to_lock);
1165                 if ( lock_in_range ) {
1166                         best_lock_dot=vm_vec_dot(&Player_obj->orient.fvec, &vec_to_lock);
1167                 } 
1168                 // take center if reasonable dot
1169                 if ( best_lock_dot > 0.95 ) {
1170                         return;
1171                 }
1172
1173                 // iterate through subsystems to see if we can get a better choice
1174                 ss = GET_FIRST(&target_shipp->subsys_list);
1175                 while ( ss != END_OF_LIST( &target_shipp->subsys_list ) ) {
1176
1177                         // get world pos of subsystem
1178                         get_subsystem_world_pos(target_objp, ss, &subsys_world_pos);
1179
1180                         if ( hud_lock_world_pos_in_range(&subsys_world_pos, &vec_to_lock) ) {
1181                                 vm_vec_normalize(&vec_to_lock);
1182                                 lock_dot=vm_vec_dot(&Player_obj->orient.fvec, &vec_to_lock);
1183                                 if ( lock_dot > best_lock_dot ) {
1184                                         best_lock_dot=lock_dot;
1185                                         Player->locking_on_center=0;
1186                                         Player->locking_subsys=ss;
1187                                         Player->locking_subsys_parent=Player_ai->target_objnum;
1188                                         *lock_world_pos=subsys_world_pos;
1189                                 }
1190                         }
1191                         ss = GET_NEXT( ss );
1192                 }
1193         } else {
1194                 // if small ship (or weapon), just go for the center
1195                 *lock_world_pos = target_objp->pos;
1196                 Player->locking_on_center=1;
1197                 Player->locking_subsys=NULL;
1198                 Player->locking_subsys_parent=-1;
1199         }
1200 }
1201
1202 // Decide which point lock should be homing on
1203 void hud_lock_determine_lock_point(vector *lock_world_pos_out)
1204 {
1205         vector  lock_world_pos;
1206         vertex  lock_point;
1207         object  *target_objp;
1208
1209         Assert(Player_ai->target_objnum >= 0);
1210         target_objp = &Objects[Player_ai->target_objnum];
1211
1212         Player->current_target_sx = -1;
1213         Player->current_target_sx = -1;
1214
1215         // If subsystem is targeted, we must try to lock on that
1216         if ( Player_ai->targeted_subsys ) {
1217                 hud_lock_update_lock_pos(target_objp, &lock_world_pos);
1218                 Player->locking_on_center=0;
1219                 Player->locking_subsys=NULL;
1220                 Player->locking_subsys_parent=-1;
1221         } else {
1222                 // See if we already have a successful locked point
1223                 if ( hud_lock_has_homing_point() ) {
1224                         hud_lock_update_lock_pos(target_objp, &lock_world_pos);
1225                 } else {
1226                         hud_lock_get_new_lock_pos(target_objp, &lock_world_pos);
1227                 }
1228         }
1229
1230         *lock_world_pos_out=lock_world_pos;
1231
1232         g3_rotate_vertex(&lock_point,&lock_world_pos);
1233         g3_project_vertex(&lock_point);
1234
1235         if (!(lock_point.flags & PF_OVERFLOW)) {  // make sure point projected
1236                 Player->current_target_sx = (int)lock_point.sx;
1237                 Player->current_target_sy = (int)lock_point.sy;
1238         }
1239 }
1240
1241 void hudlock_page_in()
1242 {
1243         bm_page_in_aabitmap( Lock_gauge.first_frame, Lock_gauge.num_frames );
1244 }