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