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