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