]> icculus.org git repositories - taylor/freespace2.git/blob - src/hud/hudtarget.cpp
removed some old stuff
[taylor/freespace2.git] / src / hud / hudtarget.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/HUDtarget.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  *
15  * C module to provide HUD targeting functions
16  *
17  * $Log$
18  * Revision 1.4  2002/06/17 06:33:09  relnev
19  * ryan's struct patch for gcc 2.95
20  *
21  * Revision 1.3  2002/06/09 04:41:21  relnev
22  * added copyright header
23  *
24  * Revision 1.2  2002/05/07 03:16:45  theoddone33
25  * The Great Newline Fix
26  *
27  * Revision 1.1.1.1  2002/05/03 03:28:09  root
28  * Initial import.
29  *
30  * 
31  * 43    11/01/99 11:22a Jefff
32  * some weapon name translations
33  * 
34  * 42    9/15/99 1:59p Mikek
35  * Fix bug in targeting due to Alt-Y (target last ship sending
36  * transmission).  Was just bogus code in the call to
37  * hud_restore_subsystem_target().pm.
38  * 
39  * 41    9/08/99 11:42p Andsager
40  * Remove maximum target distance check when finding enemy target
41  * 
42  * 40    9/07/99 11:26p Andsager
43  * Fix "r" targeting key, making evaluate_ship_as_closest_target() and
44  * hud_target_live_turret() consider if turret is targeting player
45  * 
46  * 39    9/01/99 12:58a Andsager
47  * Make all targeting keys able to choose turret on BIG|HUGE ships
48  * 
49  * 38    8/24/99 2:55p Andsager
50  * Add new prioritized turret selection code.
51  * 
52  * 37    8/18/99 10:59p Andsager
53  * Enable "b" key to target bombers.
54  * 
55  * 36    8/17/99 8:32p Jefff
56  * fixes to auto-speed/target so it looks much better in the nebula
57  * 
58  * 35    8/17/99 7:15p Jefff
59  * auto-target & auto-speed text drawn in code
60  * 
61  * 34    8/04/99 9:54a Andsager
62  * Auto target turrets on big ships.
63  * 
64  * 33    8/02/99 4:03p Andsager
65  * target closest favors beams and flak
66  * 
67  * 32    8/01/99 12:39p Dave
68  * Added HUD contrast control key (for nebula).
69  * 
70  * 31    7/30/99 11:10a Andsager
71  * Modify hud_target_closest() to consider turrets against all ships, not
72  * just player
73  * 
74  * 30    7/15/99 5:41p Andsager
75  * Clean up demo build
76  * 
77  * 29    7/15/99 9:20a Andsager
78  * FS2_DEMO initial checkin
79  * 
80  * 28    7/09/99 12:00a Andsager
81  * Added target box with distance for remote detonate weapons
82  * 
83  * 27    7/07/99 4:31p Andsager
84  * Make closest_attacking_hostile guage respect stealth.  Kill a bunch of
85  * goto's.
86  * 
87  * 26    7/01/99 11:44a Dave
88  * Updated object sound system to allow multiple obj sounds per ship.
89  * Added hit-by-beam sound. Added killed by beam sound.
90  * 
91  * 25    6/17/99 9:04a Andsager
92  * Fix hack
93  * 
94  * 24    6/16/99 5:32p Andsager
95  * HACK temp fix
96  * 
97  * 23    6/15/99 9:24a Andsager
98  * Make hotkeys work with stealth
99  * 
100  * 22    6/10/99 3:43p Dave
101  * Do a better job of syncing text colors to HUD gauges.
102  * 
103  * 21    6/09/99 2:55p Andsager
104  * Allow multiple asteroid subtypes (of large, medium, small) and follow
105  * family.
106  * 
107  * 20    6/08/99 8:35a Jasenw
108  * new coords for new lead indicator
109  * 
110  * 19    6/07/99 4:20p Andsager
111  * Add HUD color for tagged object.  Apply to target and radar.
112  * 
113  * 18    6/02/99 3:23p Andsager
114  * Make AI aware of team visibility.  Allow player targeting with team
115  * visibility info.  Make stealth ships not targetable by AI in nebula
116  * unless tagged.
117  * 
118  * 17    5/24/99 9:00a Andsager
119  * modify target next/prev live turret only when turret has a weapon
120  * 
121  * 16    4/20/99 6:39p Dave
122  * Almost done with artillery targeting. Added support for downloading
123  * images on the PXO screen.
124  * 
125  * 15    2/25/99 4:19p Dave
126  * Added multiplayer_beta defines. Added cd_check define. Fixed a few
127  * release build warnings. Added more data to the squad war request and
128  * response packets.
129  * 
130  * 14    1/27/99 3:02p Anoop
131  * Fixed 640 HUD afterburner and weapon energy gauge.
132  * 
133  * 13    1/25/99 5:03a Dave
134  * First run of stealth, AWACS and TAG missile support. New mission type
135  * :)
136  * 
137  * 12    1/07/99 10:07a Jasen
138  * coords
139  * 
140  * 11    1/07/99 9:06a Jasen
141  * coords
142  * 
143  * 10    1/06/99 2:24p Dave
144  * Stubs and release build fixes.
145  * 
146  * 9     12/29/98 7:21p Dave
147  * Put in a bunch of missing hi-res coord globalizations.
148  * 
149  * 8     12/29/98 2:29p Jasen
150  * added new coords for some 1024 HUD stuff.
151  * 
152  * 7     12/28/98 3:17p Dave
153  * Support for multiple hud bitmap filenames for hi-res mode.
154  * 
155  * 5     12/21/98 5:03p Dave
156  * Modified all hud elements to be multi-resolution friendly.
157  * 
158  * 4     11/05/98 4:18p Dave
159  * First run nebula support. Beefed up localization a bit. Removed all
160  * conditional compiles for foreign versions. Modified mission file
161  * format.
162  * 
163  * 3     10/13/98 9:28a Dave
164  * Started neatening up freespace.h. Many variables renamed and
165  * reorganized. Added AlphaColors.[h,cpp]
166  * 
167  * 2     10/07/98 10:53a Dave
168  * Initial checkin.
169  * 
170  * 1     10/07/98 10:49a Dave
171  * 
172  * 407   9/21/98 9:27a Dave
173  * Special case code to draw Cluster Bomb as Cluster on the HUD.
174  * 
175  * 406   8/28/98 3:28p Dave
176  * EMP effect done. AI effects may need some tweaking as required.
177  * 
178  * 405   8/25/98 1:48p Dave
179  * First rev of EMP effect. Player side stuff basically done. Next comes
180  * AI code.
181  * 
182  * 404   6/12/98 4:52p Hoffoss
183  * Added support for special characters in in forgeign languages.
184  * 
185  * 403   6/09/98 5:17p Lawrance
186  * French/German localization
187  * 
188  * 402   6/09/98 10:31a Hoffoss
189  * Created index numbers for all xstr() references.  Any new xstr() stuff
190  * added from here on out should be added to the end if the list.  The
191  * current list count can be found in FreeSpace.cpp (search for
192  * XSTR_SIZE).
193  * 
194  * 401   5/27/98 1:24p Allender
195  * make targeting dots work (as well as other targeting features) properly
196  * in multiplayer.  Don't query for CD when entering debrief in
197  * multiplayer
198  * 
199  * 400   5/27/98 1:20p Mike
200  * Fix bug in target nearest ship attacking target.
201  * 
202  */
203
204 #include "hud.h"
205 #include "hudlock.h"
206 #include "hudtarget.h"
207 #include "hudreticle.h"
208 #include "object.h"
209 #include "ship.h"
210 #include "2d.h"
211 #include "3d.h"
212 #include "3dinternal.h"
213 #include "line.h"
214 #include "linklist.h"
215 #include "model.h"
216 #include "math.h"
217 #include "weapon.h"
218 #include "player.h"
219 #include "freespace.h"  // for flFrametime
220 #include "ai.h"
221 #include "timer.h"
222 #include "sound.h"
223 #include "missionparse.h"
224 #include "player.h"     // for MAX_PLAYERS
225 #include "hudets.h"
226 #include "hudbrackets.h"
227 #include "gamesnd.h"
228 #include "eventmusic.h"
229 #include "debris.h"
230 #include "missionmessage.h"
231 #include "key.h"
232 #include "ai.h"
233 #include "hudtargetbox.h"
234 #include "bmpman.h"
235 #include "subsysdamage.h"
236 #include "hudshield.h"
237 #include "missionhotkey.h"
238 #include "asteroid.h"
239 #include "jumpnode.h"
240 #include "multi.h"
241 #include "emp.h"
242 #include "alphacolors.h"
243 #include "localize.h"
244 #include "awacs.h"
245 #include "hudartillery.h"
246
247 // If any of these bits in the ship->flags are set, ignore this ship when targetting
248 int TARGET_SHIP_IGNORE_FLAGS = (SF_EXPLODED|SF_DEPART_WARP|SF_DYING|SF_ARRIVING_STAGE_1|SF_HIDDEN_FROM_SENSORS);
249
250 // Global values for the target bracket width and height, used for debugging
251 int Hud_target_w, Hud_target_h;
252
253 // offscreen triangle that point the the off-screen target
254 float Offscreen_tri_base[GR_NUM_RESOLUTIONS] = {
255         6.0f,
256         9.5f
257 };
258 float Offscreen_tri_height[GR_NUM_RESOLUTIONS] = {
259         7.0f,
260         11.0f
261 };
262 float Max_offscreen_tri_seperation[GR_NUM_RESOLUTIONS] = {
263         10.0f,
264         16.0f
265 };
266 float Max_front_seperation[GR_NUM_RESOLUTIONS] = {
267         10.0f,
268         16.0f
269 };
270
271 // The following variables are global to this file, and do not need to be persistent from frame-to-frame
272 // This means the variables are not player-specific
273 static int Target_in_reticle = 0;
274
275 extern object obj_used_list;            // dummy node in linked list of active objects
276 extern char *Cargo_names[];
277
278 // shader is used to shade the target box 
279 shader Training_msg_glass;
280
281 // the target triangle (that orbits the reticle) dimensions
282 float Target_triangle_base[GR_NUM_RESOLUTIONS] = {
283         6.0f,
284         9.5f
285 };
286 float Target_triangle_height[GR_NUM_RESOLUTIONS] = {
287         7.0f,
288         11.0f
289 };
290
291 // stuff for hotkey targeting lists
292 htarget_list htarget_items[MAX_HOTKEY_TARGET_ITEMS];
293 htarget_list htarget_free_list;
294
295 // coordinates and widths used to render the HUD afterburner energy gauge
296 int Aburn_coords[GR_NUM_RESOLUTIONS][4] = {
297         { // GR_640
298                 171, 265, 60, 60
299         },
300         { // GR_1024
301                 274, 424, 86, 96
302         }
303 };
304
305 // coordinates and widths used to render the HUD weapons energy gauge
306 int Wenergy_coords[GR_NUM_RESOLUTIONS][4] = {
307         { // GR_640
308                 416, 265, 60, 60
309         },
310         { // GR_1024
311                 666, 424, 86, 96
312         }
313 };
314
315 #define MIN_DISTANCE_TO_CONSIDER_THREAT 1500    // min distance to show hostile warning triangle
316
317 //////////////////////////////////////////////////////////////////////////
318 // lists for target in reticle cycling
319 //////////////////////////////////////////////////////////////////////////
320 #define RL_USED         (1<<0)
321 #define RL_USE_DOT      (1<<1)  // use dot product result, not distance
322
323 typedef struct _reticle_list {
324         _reticle_list   *next, *prev;
325         object                  *objp;
326         float                           dist, dot;
327         int                             flags;
328 } reticle_list;
329
330 #define                 RESET_TARGET_IN_RETICLE 750
331 int                             Reticle_save_timestamp;
332 reticle_list    Reticle_cur_list;
333 reticle_list    Reticle_save_list;
334 #define                 MAX_RETICLE_TARGETS     50
335 reticle_list    Reticle_list[MAX_RETICLE_TARGETS];
336
337 //////////////////////////////////////////////////////////////////////////
338 // used for closest target cycling
339 //////////////////////////////////////////////////////////////////////////
340 #define TL_RESET                        1500
341 #define TURRET_RESET    1000
342 static int Tl_hostile_reset_timestamp;
343 static int Tl_friendly_reset_timestamp;
344 static int Target_next_uninspected_object_timestamp;
345 static int Target_newest_ship_timestamp;
346 static int Target_next_turret_timestamp;
347
348 // animation frames for the hud targeting gauges
349 // frames:      0       =>              out of range lead
350 //                              1       =>              in range lead
351 float Lead_indicator_half[GR_NUM_RESOLUTIONS][2] = {
352         { // GR_640
353                 8.0f,           // half-width
354                 8.0f                    // half-height
355         },
356         { // GR_1024
357                 13.0f,          // half-width
358                 13.0f                   // half-height
359         }
360 };
361 hud_frames Lead_indicator_gauge;
362 int Lead_indicator_gauge_loaded = 0;
363 char Lead_fname[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN] = {
364         "lead1",
365         "2_lead1"
366 };
367
368 // animation frames for the afterburner gauge and the weapon energy gauge
369 // frames:      0       =>              afterburner dark
370 //                              1       =>              afterburner light
371 //                              2       =>              gun energy dark
372 //                              3       =>              gun energy light
373 hud_frames Energy_bar_gauges;
374 int Energy_bar_gauges_loaded = 0;
375 char Energy_fname[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN] = {
376         "energy2",
377         "2_energy2"
378 };
379 int Weapon_energy_text_coords[GR_NUM_RESOLUTIONS][2] = {
380         { // GR_640
381                 439, 318
382         },
383         { // GR_1024
384                 708, 509
385         }
386 };
387
388 // animation frames for the countermeasures gauge
389 // frames:      0       =>              background
390 hud_frames Cmeasure_gauge;
391 int Cmeasure_gauge_loaded = 0;
392 int Cm_coords[GR_NUM_RESOLUTIONS][2] = {
393         { // GR_640
394                 497, 343
395         },
396         { // GR_1024
397                 880, 602
398         }
399 };
400 int Cm_text_coords[GR_NUM_RESOLUTIONS][2] = {
401         { // GR_640
402                 533, 347
403         },
404         { // GR_1024
405                 916, 606
406         }
407 };
408 int Cm_text_val_coords[GR_NUM_RESOLUTIONS][2] = {
409         { // GR_640
410                 506, 347
411         },
412         { // GR_1024
413                 889, 606
414         }
415 };
416 char Cm_fname[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN] = {
417         "countermeasure1",
418         "countermeasure1"
419 };
420
421 // animation frames for the auto-target and auto-match_speed icons
422 // frames:      0       =>              auto-target off
423 //                              1       =>              auto-target on
424 //                              2       =>              auto-match-speed on 
425 //                              3       =>              auto-match-speed off
426 hud_frames Toggle_gauge;
427 int Toggle_gauge_loaded = 0;
428 int Toggle_target_gauge_coords[GR_NUM_RESOLUTIONS][2] = {
429         { // GR_640
430                 577, 380
431         },
432         { // GR_1024
433                 960, 648
434         }
435 };
436 int Toggle_speed_gauge_coords[GR_NUM_RESOLUTIONS][2] = {
437         { // GR_640
438                 577, 404
439         },
440         { // GR_1024
441                 960, 672
442         }
443 };
444 char Toggle_fname[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN] = {
445         "toggle1",
446         "toggle1"
447 };
448
449 #define TOGGLE_TEXT_AUTOT               0
450 #define TOGGLE_TEXT_TARGET              1
451 #define TOGGLE_TEXT_AUTOS               2
452 #define TOGGLE_TEXT_SPEED               3
453 static int Hud_toggle_coords[GR_NUM_RESOLUTIONS][4][2] = {
454         {               // GR_640
455                 { 590, 382 },
456                 { 584, 390 },
457                 { 590, 406 },
458                 { 587, 414 }
459         },
460         {               // GR_1024
461                 { 973, 650 },
462                 { 967, 658 },
463                 { 973, 674 },
464                 { 970, 682 }
465         }
466 };
467
468 static int Toggle_text_alpha = 255;
469
470
471 // animation files for the weapons gauge
472 #define NUM_WEAPON_GAUGES       5
473 hud_frames Weapon_gauges[NUM_WEAPON_GAUGES];
474 int Weapon_gauges_loaded = 0;
475 // for primaries
476 int Weapon_gauge_primary_coords[GR_NUM_RESOLUTIONS][3][2] = {
477         { // GR_640
478                 // based on the # of primaries
479                 {509, 273},                             // top of weapon gauge, first frame, always
480                 {497, 293},                             // for the first primary
481                 {497, 305}                              // for the second primary
482         },
483         { // GR_1024
484                 // based on the # of primaries
485                 {892, 525},                             // top of weapon gauge, first frame, always
486                 {880, 545},                             // for the first primary
487                 {880, 557}                              // for the second primary
488         }
489 };
490 int Weapon_gauge_secondary_coords[GR_NUM_RESOLUTIONS][5][2] = {
491         { // GR_640
492                 // based on the # of secondaries
493                 {497, 318},                             // bottom of gauge, 0 secondaries
494                 {497, 318},                             // bottom of gauge, 1 secondaries
495                 {497, 317},                             // middle of gauge, 2 secondaries AND middle of gauge, 3 secondaries
496                 {497, 326},                             // bottom of gauge, 2 secondaries AND middle of gauge, 3 secondaries
497                 {497, 335}                              // bottom of gauge, 3 secondaries
498         },
499         { // GR_1024
500                 // based on the # of secondaries
501                 {880, 570},                             // bottom of gauge, 0 secondaries
502                 {880, 570},                             // bottom of gauge, 1 secondaries
503                 {880, 569},                             // middle of gauge, 2 secondaries AND middle of gauge, 3 secondaries
504                 {880, 578},                             // bottom of gauge, 2 secondaries AND middle of gauge, 3 secondaries
505                 {880, 587}                              // bottom of gauge, 3 secondaries
506         }
507 };
508 int Weapon_title_coords[GR_NUM_RESOLUTIONS][2] = {
509         { // GR_640
510                 518, 275
511         },
512         { // GR_1024
513                 901, 527
514         }
515 };
516 int Weapon_plink_coords[GR_NUM_RESOLUTIONS][2][2] = {
517         { // GR_640
518                 {530, 285},                             // fire-linked thingie, for the first primary
519                 {530, 295}                              // fire-linked thingie, for the second primary
520         },
521         { // GR_1024
522                 {913, 537},                             // fire-linked thingie, for the first primary
523                 {913, 547}                              // fire-linked thingie, for the second primary
524         }
525 };
526 int Weapon_pname_coords[GR_NUM_RESOLUTIONS][2][2] = {
527         { // GR_640
528                 {536, 285},                             // weapon name, first primary
529                 {536, 295}                              // weapon name, second primary
530         },
531         { // GR_1024
532                 {919, 537},                             // weapon name, first primary
533                 {919, 547}                              // weapon name, second primary
534         }
535 };
536 int Weapon_slinked_x[GR_NUM_RESOLUTIONS] = {
537         525,                                                    // where to draw the second thingie if this weapon is fire-linked
538         908
539 };
540 int Weapon_sunlinked_x[GR_NUM_RESOLUTIONS] = {
541         530,                                                    // where to draw the first thingie if this weapon is selected at all (fire-linked or not)
542         913
543 };
544 int Weapon_secondary_y[GR_NUM_RESOLUTIONS][3] = {
545         { // GR_640
546                 309,                                            // y location of where to draw text for the first secondary
547                 318,                                            // y location of where to draw text for the second secondary
548                 327                                             // y location of where to draw text for the third secondary
549         },
550         { // GR_1024
551                 561,                                            // y location of where to draw text for the third secondary
552                 570,                                            // y location of where to draw text for the third secondary
553                 579                                             // y location of where to draw text for the third secondary
554         }
555 };
556 int Weapon_secondary_name_x[GR_NUM_RESOLUTIONS] = {
557         536,                                                    // x location of where to draw weapon name
558         919
559 };
560 int Weapon_secondary_ammo_x[GR_NUM_RESOLUTIONS] = {
561         525,                                                    // x location of where to draw weapon ammo count
562         908
563 };
564 int Weapon_secondary_reload_x[GR_NUM_RESOLUTIONS] = {
565         615,                                                    // x location of where to draw the weapon reload time
566         998
567 };
568 char *Weapon_gauge_fnames[GR_NUM_RESOLUTIONS][NUM_WEAPON_GAUGES] = 
569 {
570 //XSTR:OFF
571         { // GR_640
572                 "weapons1",
573                 "weapons2",
574                 "weapons3",
575                 "weapons4",
576                 "weapons5"
577         }, 
578         { // GR_1024
579                 "weapons1",
580                 "weapons2",
581                 "weapons3",
582                 "weapons4",
583                 "weapons5"
584         }
585 //XSTR:ON
586 };
587
588 // Flash the line for a weapon.  This normally occurs when the player tries to fire that
589 // weapon, but the firing fails (due to lack of energy or damaged weapons subsystem).
590 #define MAX_WEAPON_FLASH_LINES 7                // 3 primary and 4 secondary
591 typedef struct weapon_flash
592 {
593         int flash_duration[MAX_WEAPON_FLASH_LINES];
594         int flash_next[MAX_WEAPON_FLASH_LINES];
595         int is_bright;
596 } weapon_flash;
597 weapon_flash Weapon_flash_info;
598
599 // Data used for the proximity warning
600 typedef struct homing_beep_info
601 {
602         int     snd_handle;                             // sound handle for last played beep
603         fix     last_time_played;               //      time beep was last played
604         int     min_cycle_time;         // time (in ms) for fastest cycling of the sound
605         int     max_cycle_time;         // time (in ms) for slowest cycling of the sound
606         float min_cycle_dist;           // distance at which fastest cycling occurs 
607         float max_cycle_dist;           // distance at which slowest cycling occurs
608         float   precalced_interp;               // a precalculated value used in a linear interpretation
609 } homing_beep_info;
610
611 homing_beep_info Homing_beep = { -1, 0, 150, 1000, 30.0f, 1500.0f };
612
613 // Set at the start of a mission, used to decide how to draw the separation for the warning missile indicators
614 float Min_warning_missile_dist;
615 float   Max_warning_missile_dist;
616
617 void hud_maybe_flash_weapon(int index);                                   
618
619 // if a given object should be ignored because of AWACS effects
620 int hud_target_invalid_awacs(object *objp)
621 {
622         // if objp is ship object, first check if can be targeted with team info
623         if (objp->type == OBJ_SHIP) {
624                 if (Player_ship != NULL) {
625                         if (ship_is_visible_by_team(objp->instance, Player_ship->team)) {
626                                 return 0;
627                         }
628                 }
629         }
630
631         // check for invalid status
632         if((Player_ship != NULL) && (awacs_get_level(objp, Player_ship) < 1.0f)){
633                 return 1;
634         }
635
636         // valid
637         return 0;
638 }
639
640 ship_subsys *advance_subsys(ship_subsys *cur, int next_flag)
641 {
642         if (next_flag) {
643                 return GET_NEXT(cur);
644         } else {
645                 return GET_LAST(cur);
646         }
647 }
648
649 // select a sorted turret subsystem on a ship if no other subsys has been selected
650 void hud_maybe_set_sorted_turret_subsys(ship *shipp)
651 {
652         Assert((Player_ai->target_objnum >= 0) && (Player_ai->target_objnum < MAX_OBJECTS));
653         if (!((Player_ai->target_objnum >= 0) && (Player_ai->target_objnum < MAX_OBJECTS))) {
654                 return;
655         }
656         Assert(Objects[Player_ai->target_objnum].type == OBJ_SHIP);
657         if (Objects[Player_ai->target_objnum].type != OBJ_SHIP) {
658                 return;
659         }
660
661         if (Ship_info[shipp->ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) {
662                 if (shipp->last_targeted_subobject[Player_num] == NULL) {
663                         hud_target_live_turret(1, 1);
664                 }
665         }
666
667 }
668
669 // -----------------------------------------------------------------------
670 //      clear out the linked list of targets in the reticle
671 void hud_reticle_clear_list(reticle_list *rlist)
672 {
673         reticle_list *cur;
674         for ( cur = GET_FIRST(rlist); cur != END_OF_LIST(rlist); cur = GET_NEXT(cur) ) {
675                 cur->flags = 0;
676         }
677         list_init(rlist);
678 }
679
680 // --------------------------------------------------------------------------------------
681 //      hud_reticle_list_init()
682 void hud_reticle_list_init()
683 {
684         int i;
685
686         for ( i = 0; i < MAX_RETICLE_TARGETS; i++ ) {
687                 Reticle_list[i].flags = 0;
688         }
689
690         Reticle_save_timestamp = 1;
691         list_init(&Reticle_save_list);
692         list_init(&Reticle_cur_list);
693 }
694
695 // --------------------------------------------------------------------------------------
696 //      hud_check_reticle_list()
697 //
698 //
699 void    hud_check_reticle_list()
700 {
701         reticle_list    *rl, *temp;
702
703         // cull dying objects from reticle list
704         rl = GET_FIRST(&Reticle_cur_list);
705         while( rl !=END_OF_LIST(&Reticle_cur_list) )    {
706                 temp = GET_NEXT(rl);
707                 if ( rl->objp->flags & OF_SHOULD_BE_DEAD ) {
708                         list_remove( &Reticle_cur_list, rl );
709                         rl->flags = 0;
710                 }
711                 rl = temp;
712         }
713
714         if ( timestamp_elapsed(Reticle_save_timestamp) ) {
715                 hud_reticle_clear_list(&Reticle_save_list);
716                 Reticle_save_timestamp = timestamp(RESET_TARGET_IN_RETICLE);
717         }
718 }
719
720 // --------------------------------------------------------------------------------------
721 //      hud_reticle_list_find_free()
722 //
723 //
724 int hud_reticle_list_find_free()
725 {
726         int i;
727
728         // find a free reticle_list element
729         for ( i = 0; i < MAX_RETICLE_TARGETS; i++ ) {
730                 if ( !(Reticle_list[i].flags & RL_USED) ) {
731                         break;
732                 }
733         }
734
735         if ( i == MAX_RETICLE_TARGETS ) {
736 //              nprintf(("Warning","Warning ==> Ran out of reticle target elements...\n"));
737                 return -1;
738         }
739
740         return i;
741 }
742
743 // --------------------------------------------------------------------------------------
744 //      hud_stuff_reticle_list()
745 //
746 //
747 #define RETICLE_DEFAULT_DIST            100000.0f
748 #define RETICLE_DEFAULT_DOT             1.0f
749 void hud_stuff_reticle_list(reticle_list *rl, object *objp, float measure, int dot_flag)
750 {
751         if ( dot_flag ) {
752                 rl->dot = measure;
753                 rl->dist = RETICLE_DEFAULT_DIST;
754                 rl->flags |= RL_USE_DOT;
755         }
756         else {
757                 rl->dist = measure;
758                 rl->dot = RETICLE_DEFAULT_DOT;          
759         }
760         rl->objp = objp;
761 }
762
763 // --------------------------------------------------------------------------------------
764 //      hud_reticle_list_update()
765 //
766 //      Update Reticle_cur_list with an object that lies in the reticle
767 //
768 //      parmeters:      objp            =>              object pointer to target
769 //                                      measure =>              distance or dot product, depending on dot_flag
770 //                                      dot_flag        =>              if 0, measure is distance, if 1 measure is dot
771 //
772 void hud_reticle_list_update(object *objp, float measure, int dot_flag)
773 {
774         reticle_list    *rl, *new_rl;
775         int                             i;
776
777         for ( rl = GET_FIRST(&Reticle_cur_list); rl != END_OF_LIST(&Reticle_cur_list); rl = GET_NEXT(rl) ) {
778                 if ( rl->objp == objp )
779                         return;
780         }
781
782         i = hud_reticle_list_find_free();
783         if ( i == -1 )
784                 return;
785
786         new_rl = &Reticle_list[i];
787         new_rl->flags |= RL_USED;
788         hud_stuff_reticle_list(new_rl, objp, measure, dot_flag);
789
790         int was_inserted = 0;
791         
792         if ( EMPTY(&Reticle_cur_list) ) {
793                 list_insert(&Reticle_cur_list, new_rl);
794                 was_inserted = 1;
795         }
796         else {
797                 for ( rl = GET_FIRST(&Reticle_cur_list); rl != END_OF_LIST(&Reticle_cur_list); rl = GET_NEXT(rl) ) {
798                         if ( !dot_flag ) {      
799                                 // compare based on distance
800                                 if ( measure < rl->dist ) {
801                                         list_insert_before(rl, new_rl);
802                                         was_inserted = 1;
803                                         break;
804                                 }
805                         }                       
806                         else {
807                                 // compare based on dot
808                                 if ( measure > rl->dot ) {
809                                         list_insert_before(rl, new_rl);
810                                         was_inserted = 1;
811                                         break;
812                                 }
813                         }               
814                 }       // end for
815         }
816
817         if ( !was_inserted ) {
818                 list_append(&Reticle_cur_list, new_rl);
819         }
820 }
821
822 // --------------------------------------------------------------------------------------
823 //      hud_reticle_pick_target()
824 //
825 //      Pick a target from Reticle_cur_list, based on what is in Reticle_save_list
826 //
827 //
828 object *hud_reticle_pick_target()
829 {
830         reticle_list    *cur_rl, *save_rl, *new_rl;
831         object                  *return_objp;
832         int                             in_save_list, i;
833
834         return_objp = NULL;
835
836         // As a first step, see if both ships and debris are in the list.  If so, cull the debris.
837         int debris_in_list = 0;
838         int ship_in_list = 0;
839         for ( cur_rl = GET_FIRST(&Reticle_cur_list); cur_rl != END_OF_LIST(&Reticle_cur_list); cur_rl = GET_NEXT(cur_rl) ) {
840                 if ( (cur_rl->objp->type == OBJ_SHIP) || (cur_rl->objp->type == OBJ_JUMP_NODE) ) {
841                         ship_in_list = 1;
842                         continue;
843                 }
844
845                 if ( cur_rl->objp->type == OBJ_WEAPON ) {
846                         if ( Weapon_info[Weapons[cur_rl->objp->instance].weapon_info_index].subtype == WP_MISSILE ) {
847                                 ship_in_list = 1;
848                                 continue;
849                         }
850                 }
851                         
852                 if ( (cur_rl->objp->type == OBJ_DEBRIS) || (cur_rl->objp->type == OBJ_ASTEROID) ) {
853                         debris_in_list = 1;
854                         continue;
855                 }
856         }
857
858         if ( ship_in_list && debris_in_list ) {
859                 // cull debris
860                 reticle_list    *rl, *next;
861                 
862                 rl = GET_FIRST(&Reticle_cur_list);
863                 while ( rl != &Reticle_cur_list ) {
864                         next = rl->next;
865                         if ( (rl->objp->type == OBJ_DEBRIS) || (rl->objp->type == OBJ_ASTEROID) ){
866                                 list_remove(&Reticle_cur_list,rl);
867                                 rl->flags = 0;
868                         }
869                         rl = next;
870                 }
871         }
872         
873         for ( cur_rl = GET_FIRST(&Reticle_cur_list); cur_rl != END_OF_LIST(&Reticle_cur_list); cur_rl = GET_NEXT(cur_rl) ) {
874                 in_save_list = 0;
875                 for ( save_rl = GET_FIRST(&Reticle_save_list); save_rl != END_OF_LIST(&Reticle_save_list); save_rl = GET_NEXT(save_rl) ) {
876                         if ( cur_rl->objp == save_rl->objp ) {
877                                 in_save_list = 1;
878                                 break;
879                         }
880                 }
881
882                 if ( !in_save_list ) {
883                         return_objp = cur_rl->objp;
884                         i = hud_reticle_list_find_free();
885                         if ( i == -1 )
886                                 break;
887
888                         new_rl = &Reticle_list[i];
889                         new_rl->flags |= RL_USED;
890                         if ( cur_rl->flags & RL_USE_DOT ) {
891                                 hud_stuff_reticle_list(new_rl, cur_rl->objp, cur_rl->dot, 1);
892                         }
893                         else {
894                                 hud_stuff_reticle_list(new_rl, cur_rl->objp, cur_rl->dist, 0);
895                         }
896
897                         list_append(&Reticle_save_list, new_rl);
898                         break;
899                 }
900         }       // end for
901
902         if ( return_objp == NULL && !EMPTY(&Reticle_cur_list) ) {
903                         i = hud_reticle_list_find_free();
904                         if ( i == -1 ) 
905                                 return NULL;
906                         new_rl = &Reticle_list[i];
907                         cur_rl = GET_FIRST(&Reticle_cur_list);
908                         *new_rl = *cur_rl;
909                         return_objp = cur_rl->objp;
910                         hud_reticle_clear_list(&Reticle_save_list);
911                         list_append(&Reticle_save_list, new_rl);
912         }
913
914         return return_objp;
915 }
916
917 // hud_target_hotkey_add_remove takes as it's parameter which hotkey (1-0) to add/remove the current
918 // target from.  This functio behaves like the Shift-<selection> does in Windows -- using shift # will toggle
919 // the current target in and out of the selection set.
920 void hud_target_hotkey_add_remove( int k, object *ctarget, int how_to_add )
921 {
922         htarget_list *hitem, *plist;
923
924         // don't do anything if a standalone multiplayer server
925         if ( MULTIPLAYER_STANDALONE )
926                 return;
927
928         if ( k < 0 || k > 7 ) {
929                 nprintf(("Warning", "Bogus hotkey %d sent to hud_target_hotkey_add_remove\n"));
930                 return;
931         }
932
933         plist = &(Players[Player_num].keyed_targets[k]);
934         
935         // we must operate only on ships
936         if ( ctarget->type != OBJ_SHIP )
937                 return;
938
939         // don't allow player into hotkey set
940         if ( ctarget == Player_obj )
941                 return;
942
943         // don't put dying or departing
944         if ( Ships[ctarget->instance].flags & (SF_DYING|SF_DEPARTING) )
945                 return;
946
947         // don't add mission file added hotkey assignments if there are player added assignments 
948         // already in the list
949         if ( (how_to_add == HOTKEY_MISSION_FILE_ADDED) && NOT_EMPTY(plist) ) {
950                 for ( hitem = GET_FIRST(plist); hitem != END_OF_LIST(plist); hitem = GET_NEXT(hitem) ) {
951                         if ( hitem->how_added == HOTKEY_USER_ADDED )
952                                 return;
953                 }
954         }
955
956         // determine if the current target is currently in the set or not
957         for ( hitem = GET_FIRST(plist); hitem != END_OF_LIST(plist); hitem = GET_NEXT(hitem) ) {
958                 if ( hitem->objp == ctarget )
959                         break;
960         }
961
962         // if hitem == end of the list, then the target should be added, else it should be removed
963         if ( hitem == END_OF_LIST(plist) ) {
964                 if ( EMPTY(&htarget_free_list) ) {
965                         Int3();                 // get Allender -- no more free hotkey target items
966                         return;
967                 }
968
969                 nprintf(("network", "Hotkey: Adding %s\n", Ships[ctarget->instance].ship_name));
970                 hitem = GET_FIRST( &htarget_free_list );
971                 list_remove( &htarget_free_list, hitem );
972                 list_append( plist, hitem );
973                 hitem->objp = ctarget;
974                 hitem->how_added = how_to_add;
975         } else {
976                 nprintf(("network", "Hotkey: Removing %s\n", Ships[ctarget->instance].ship_name));
977                 list_remove( plist, hitem );
978                 list_append( &htarget_free_list, hitem );
979                 hitem->objp = NULL;                                                                     // for safety
980         }
981 }
982
983 // the following function clears the hotkey set given by parameter passed in
984 void hud_target_hotkey_clear( int k )
985 {
986         htarget_list *hitem, *plist, *temp;
987
988         plist = &(Players[Player_num].keyed_targets[k]);
989         hitem = GET_FIRST(plist);
990         while ( hitem != END_OF_LIST(plist) ) {
991                 temp = GET_NEXT(hitem);
992                 list_remove( plist, hitem );
993                 list_append( &htarget_free_list, hitem );
994                 hitem->objp = NULL;
995                 hitem = temp;
996         }
997         if ( Players[Player_num].current_hotkey_set == k )              // clear this variable if we removed the bindings
998                 Players[Player_num].current_hotkey_set = -1;
999 }
1000
1001 // the next function sets the current selected set to be N.  If there is just one ship in the selection
1002 // set, this ship will become the new target.  If there is more than one ship in the selection set,
1003 // then the current_target will remain what it was.
1004 void hud_target_hotkey_select( int k )
1005 {
1006         int visible_count = 0;
1007         htarget_list *hitem, *plist, *target, *next_target, *first_target;
1008         int target_objnum;
1009
1010         plist = &(Players[Player_num].keyed_targets[k]);
1011         
1012         if ( EMPTY( plist ) )                   // no items in list, then do nothing
1013                 return;
1014
1015         // a simple walk of the list to get the count
1016         for ( hitem = GET_FIRST(plist); hitem != END_OF_LIST(plist); hitem = GET_NEXT(hitem) ){
1017                 if (awacs_get_level(hitem->objp, Player_ship, 1) > 1) {
1018                         visible_count++;
1019                 }
1020         }
1021
1022         // no visible ships in list
1023         if (visible_count == 0) {
1024                 return;
1025         }
1026
1027         // set the current target to be the "next" ship in the list.  Scan the list to see if our
1028         // current target is in the set.  If so, target the next ship in the list, otherwise target
1029         // the first
1030         // set  first_target - first visible item in list
1031         //                      target - item in list that is the player's currently selected target
1032         //                      next_target -   next visible item in list following target
1033         target_objnum = Player_ai->target_objnum;
1034         target = NULL;
1035         next_target = NULL;
1036         first_target = NULL;
1037         for ( hitem = GET_FIRST(plist); hitem != END_OF_LIST(plist); hitem = GET_NEXT(hitem) ) {
1038                 
1039                 if (awacs_get_level(hitem->objp, Player_ship, 1) > 1) {
1040                         // get the first valid target
1041                         if (first_target == NULL) {
1042                                 first_target = hitem;
1043                         }
1044
1045                         // get the next target in the list following the player currently selected target
1046                         if (target != NULL) {
1047                                 next_target = hitem;
1048                                 break;
1049                         }
1050                 }
1051
1052                 // mark the player currently selected target
1053                 if ( OBJ_INDEX(hitem->objp) == target_objnum ) {
1054                         target = hitem;
1055                 }
1056         }
1057
1058         // if current target is not in list, then target and next_target will be NULL
1059         // so we use the first found target
1060         if (target == NULL) {
1061                 Assert(first_target != NULL);
1062                 if (first_target != NULL) {
1063                         target = first_target;
1064                         next_target = first_target;
1065                 } else {
1066                         // this should not happen
1067                         return;
1068                 }
1069         }
1070         
1071         // update target if more than 1 is visible
1072         if (visible_count > 1) {
1073                 // next already found (after current target in list)
1074                 if (next_target != NULL) {
1075                         target = next_target;
1076                 } else {
1077
1078                 // next is before current target, so search from start of list
1079                         for ( hitem = GET_FIRST(plist); hitem != END_OF_LIST(plist); hitem = GET_NEXT(hitem) ) {
1080                                 if (awacs_get_level(hitem->objp, Player_ship, 1) > 1) {
1081                                         target = hitem;
1082                                         break;
1083                                 }
1084                         }
1085                 }
1086         }
1087
1088         Assert( target != END_OF_LIST(plist) );
1089
1090         if ( Player_obj != target->objp ){
1091                 set_target_objnum( Player_ai, OBJ_INDEX(target->objp) );
1092         }
1093
1094         Players[Player_num].current_hotkey_set = k;
1095 }
1096
1097 // hud_init_targeting_colors() will initalize the shader and gradient objects used
1098 // on the HUD
1099 //
1100
1101 color HUD_color_homing_indicator;
1102
1103 void hud_make_shader(shader *sh, int r, int g, int b, float dimmer = 1000.0f)
1104 {
1105         float rf,gf,bf,cf;
1106
1107         // The m matrix converts all colors to shades of green
1108         float tmp = 0.0015625f * i2fl(HUD_color_alpha+1.0f) / 16.0f;
1109
1110         rf = tmp*r;
1111         gf = tmp*r;
1112         bf = tmp*r;
1113         cf = (i2fl(r) / dimmer)*(i2fl(HUD_color_alpha) / 15.0f);
1114
1115         gr_create_shader( sh, rf, gf, bf, cf );
1116 }
1117
1118 void hud_init_targeting_colors()
1119 {
1120         gr_init_color( &HUD_color_homing_indicator, 0x7f, 0x7f, 0 );    // yellow
1121
1122         hud_make_shader(&Training_msg_glass, 61, 61, 85, 500.0f);
1123
1124         hud_init_brackets();
1125 }
1126
1127 void hud_keyed_targets_clear()
1128 {
1129         int i;
1130
1131         // clear out the keyed target list
1132         for (i = 0; i < MAX_KEYED_TARGETS; i++ )
1133                 list_init( &(Players[Player_num].keyed_targets[i]) );
1134         Players[Player_num].current_hotkey_set = -1;
1135
1136         // place all of the hoykey target items back onto the free list
1137         list_init( &htarget_free_list );
1138         for ( i = 0; i < MAX_HOTKEY_TARGET_ITEMS; i++ )
1139                 list_append( &htarget_free_list, &htarget_items[i] );
1140 }
1141
1142 // Init data used for the weapons display on the HUD
1143 void hud_weapons_init()
1144 {
1145         int i;
1146
1147         Weapon_flash_info.is_bright = 0;
1148         for ( i = 0; i < MAX_WEAPON_FLASH_LINES; i++ ) {
1149                 Weapon_flash_info.flash_duration[i] = 1;
1150                 Weapon_flash_info.flash_next[i] = 1;
1151         }
1152
1153         if ( !Weapon_gauges_loaded ) {
1154                 for ( i = 0; i < NUM_WEAPON_GAUGES; i++ ) {
1155                         Weapon_gauges[i].first_frame = bm_load_animation(Weapon_gauge_fnames[gr_screen.res][i], &Weapon_gauges[i].num_frames);
1156                         if ( Weapon_gauges[i].first_frame < 0 ) {
1157                                 Warning(LOCATION,"Cannot load hud ani: %s\n",Weapon_gauge_fnames[gr_screen.res][i]);
1158                         }
1159                 }
1160                 Weapon_gauges_loaded = 1;
1161         }
1162 }
1163
1164 // init data used to play the homing "proximity warning" sound
1165 void hud_init_homing_beep()
1166 {
1167         Homing_beep.snd_handle = -1;
1168         Homing_beep.last_time_played  = 0;
1169         Homing_beep.precalced_interp = (Homing_beep.max_cycle_dist-Homing_beep.min_cycle_dist) / (Homing_beep.max_cycle_time - Homing_beep.min_cycle_time );
1170 }
1171
1172 // hud_init_targeting() will set the current target to point to the dummy node
1173 // in the object used list
1174 //
1175 void hud_init_targeting()
1176 {
1177         Assert(Player_ai != NULL);
1178
1179         // make sure there is no current target
1180         set_target_objnum( Player_ai, -1 );
1181         Player_ai->last_target = -1;
1182         Player_ai->last_subsys_target = NULL;
1183         Player_ai->last_dist = 0.0f;
1184         Player_ai->last_speed = 0.0f;
1185
1186         hud_keyed_targets_clear();
1187         hud_init_missile_lock();
1188         hud_init_artillery();
1189         
1190         // Init the lists that hold targets in reticle (to allow cycling of targets in reticle)
1191         hud_reticle_list_init();
1192         hud_init_homing_beep();
1193
1194         // Load in the frames need for the lead indicator
1195         if (!Lead_indicator_gauge_loaded) {
1196                 Lead_indicator_gauge.first_frame = bm_load_animation(Lead_fname[gr_screen.res], &Lead_indicator_gauge.num_frames);
1197                 if ( Lead_indicator_gauge.first_frame < 0 ) {
1198                         Warning(LOCATION,"Cannot load hud ani: %s\n", Lead_fname[gr_screen.res]);
1199                 }
1200                 Lead_indicator_gauge_loaded = 1;
1201         }
1202
1203         if (!Energy_bar_gauges_loaded) {
1204                 Energy_bar_gauges.first_frame = bm_load_animation(Energy_fname[gr_screen.res], &Energy_bar_gauges.num_frames);
1205                 if ( Energy_bar_gauges.first_frame < 0 ) {
1206                         Warning(LOCATION,"Cannot load hud ani: %s\n", Energy_fname[gr_screen.res]);
1207                 }
1208                 Energy_bar_gauges_loaded = 1;
1209         }
1210
1211         if (!Toggle_gauge_loaded) {
1212                 Toggle_gauge.first_frame = bm_load_animation(Toggle_fname[gr_screen.res], &Toggle_gauge.num_frames);
1213                 if ( Toggle_gauge.first_frame < 0 ) {
1214                         Warning(LOCATION,"Cannot load hud ani: %s\n", Toggle_fname[gr_screen.res]);
1215                 }
1216                 Toggle_gauge_loaded = 1;
1217         }
1218
1219         if (!Cmeasure_gauge_loaded) {
1220                 Cmeasure_gauge.first_frame = bm_load_animation(Cm_fname[gr_screen.res], &Cmeasure_gauge.num_frames);
1221                 if ( Cmeasure_gauge.first_frame < 0 ) {
1222                         Warning(LOCATION,"Cannot load hud ani: %s\n", Cm_fname[gr_screen.res]);
1223                 }
1224                 Cmeasure_gauge_loaded = 1;
1225         }
1226
1227
1228         hud_weapons_init();
1229
1230         Min_warning_missile_dist = 2.5f*Player_obj->radius;
1231         Max_warning_missile_dist = 1500.0f;
1232
1233         Tl_hostile_reset_timestamp = timestamp(0);
1234         Tl_friendly_reset_timestamp = timestamp(0);
1235         Target_next_uninspected_object_timestamp = timestamp(0);
1236         Target_newest_ship_timestamp = timestamp(0);
1237         Target_next_turret_timestamp = timestamp(0);
1238
1239         if(The_mission.flags & MISSION_FLAG_FULLNEB) {
1240                 Toggle_text_alpha = 127; 
1241         } else {
1242                 Toggle_text_alpha = 160;
1243         }
1244
1245
1246 }
1247
1248 //      Target the next or previous subobject on the currently selected ship, based on next_flag.
1249 void hud_target_subobject_common(int next_flag)
1250 {
1251         if (Player_ai->target_objnum == -1) {
1252                 HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "No target selected.", 322));
1253                 snd_play( &Snds[SND_TARGET_FAIL] );
1254                 return;
1255         }
1256
1257         if (Objects[Player_ai->target_objnum].type != OBJ_SHIP) {
1258                 snd_play( &Snds[SND_TARGET_FAIL]);
1259                 return;
1260         }
1261
1262         ship_subsys     *start, *start2, *A;
1263         ship_subsys     *subsys_to_target=NULL;
1264         ship                    *target_shipp;
1265
1266         target_shipp = &Ships[Objects[Player_ai->target_objnum].instance];
1267
1268         if (!Player_ai->targeted_subsys) {
1269                 start = GET_FIRST(&target_shipp->subsys_list);
1270         } else {
1271                 start = Player_ai->targeted_subsys;
1272         }
1273
1274         start2 = advance_subsys(start, next_flag);
1275
1276         for ( A = start2; A != start; A = advance_subsys(A, next_flag) ) {
1277
1278                 if ( A == &target_shipp->subsys_list ) {
1279                         continue;
1280                 }
1281
1282                 // ignore turrets
1283                 if ( A->system_info->type == SUBSYSTEM_TURRET ) {
1284                         continue;
1285                 }
1286
1287                 subsys_to_target = A;
1288                 break;
1289
1290         } // end for
1291
1292         if ( subsys_to_target == NULL ) {
1293                 snd_play( &Snds[SND_TARGET_FAIL]);
1294         } else {
1295                 set_targeted_subsys(Player_ai, subsys_to_target, Player_ai->target_objnum);
1296                 target_shipp->last_targeted_subobject[Player_num] =  Player_ai->targeted_subsys;
1297         }       
1298 }
1299
1300 object *advance_fb(object *objp, int next_flag)
1301 {
1302         if (next_flag)
1303                 return GET_NEXT(objp);
1304         else
1305                 return GET_LAST(objp);
1306 }
1307
1308 //      Target the previous subobject on the currently selected ship.
1309 //
1310
1311 void hud_target_prev_subobject()
1312 {
1313         hud_target_subobject_common(0);
1314 }
1315
1316 void hud_target_next_subobject()
1317 {
1318         hud_target_subobject_common(1);
1319 }
1320
1321 // hud_target_next() will set the Players[Player_num].current_target to the next target in the object
1322 // used list whose team matches the team parameter.  The player is NOT included in the target list.
1323 //
1324 //      parameters:             team    => team of ship to target next.  Default value is -1, if team doesn't matter.
1325 //
1326
1327 void hud_target_common(int team, int next_flag)
1328 {
1329         object  *A, *start, *start2;
1330         ship            *shipp;
1331         int             is_ship, target_found = FALSE;  
1332
1333         if (Player_ai->target_objnum == -1)
1334                 start = &obj_used_list;
1335         else
1336                 start = &Objects[Player_ai->target_objnum];
1337
1338         start2 = advance_fb(start, next_flag);
1339
1340         for ( A = start2; A != start; A = advance_fb(A, next_flag) ) {
1341                 is_ship=0;
1342
1343                 if ( A == &obj_used_list ) {
1344                         continue;
1345                 }
1346
1347                 if (A == Player_obj || ( A->type != OBJ_SHIP && A->type != OBJ_WEAPON && A->type != OBJ_JUMP_NODE) ){
1348                         continue;
1349                 }
1350
1351                 if(hud_target_invalid_awacs(A)){
1352                         continue;
1353                 }
1354
1355                 if ( A->type == OBJ_WEAPON ) {
1356                         if ( !(Weapon_info[Weapons[A->instance].weapon_info_index].wi_flags & WIF_BOMB) ){
1357                                 continue;
1358                         }
1359                 }
1360
1361                 if ( A->type == OBJ_SHIP ) {
1362                         if ( Ships[A->instance].flags & TARGET_SHIP_IGNORE_FLAGS ){
1363                                 continue;
1364                         }
1365                         is_ship=1;
1366                 }
1367
1368                 if ( vm_vec_same( &A->pos, &Eye_position ) ) {
1369                         continue;
1370                 }
1371
1372                 if ( is_ship ) {
1373                         shipp = &Ships[A->instance];    // get a pointer to the ship information
1374
1375                         if ( !hud_team_matches_filter(team, shipp->team) ) {
1376                                 // if we're in multiplayer dogfight, ignore this
1377                                 if(!((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_DOGFIGHT))){
1378                                         continue;
1379                                 }
1380                         }
1381
1382                         if ( A == Player_obj || (shipp->flags & TARGET_SHIP_IGNORE_FLAGS) ){
1383                                 continue;
1384                         }
1385
1386                         // if we've reached here, it is a valid next target
1387                         if ( Player_ai->target_objnum != A-Objects ) {
1388                                 target_found = TRUE;
1389                                 set_target_objnum( Player_ai, OBJ_INDEX(A) );
1390                                 // if ship is BIG|HUGE and last subsys is NULL, get turret
1391                                 hud_maybe_set_sorted_turret_subsys(shipp);
1392                                 hud_restore_subsystem_target(shipp);
1393                         }
1394                 } else {
1395                         target_found = TRUE;
1396                         set_target_objnum( Player_ai, OBJ_INDEX(A) );
1397                 }
1398
1399                 break;
1400         }
1401
1402         if ( target_found == FALSE ) {
1403                 snd_play( &Snds[SND_TARGET_FAIL] );
1404         }
1405 }
1406
1407 void hud_target_next(int team)
1408 {
1409         hud_target_common(team, 1);
1410 }
1411
1412 void hud_target_prev(int team)
1413 {
1414         hud_target_common(team, 0);
1415 }
1416
1417 // -------------------------------------------------------------------
1418 // advance_missile_obj()
1419 //
1420 missile_obj *advance_missile_obj(missile_obj *mo, int next_flag)
1421 {
1422         if (next_flag){
1423                 return GET_NEXT(mo);
1424         } 
1425         
1426         return GET_LAST(mo);
1427 }
1428
1429 ship_obj *advance_ship(ship_obj *so, int next_flag)
1430 {
1431         if (next_flag){
1432                 return GET_NEXT(so);
1433         } 
1434         
1435         return GET_LAST(so);
1436 }
1437
1438 ship_obj *get_ship_obj_ptr_from_index(int index);
1439 // -------------------------------------------------------------------
1440 // hud_target_missile()
1441 //
1442 // Target the closest locked missile that is locked on locked_obj
1443 //
1444 //      input:  source_obj      =>              pointer to object that fired weapon
1445 //                              next_flag       =>              0 -> previous 1 -> next
1446 //                                                                      
1447 // NOTE: this function is only allows targeting bombs
1448 void hud_target_missile(object *source_obj, int next_flag)
1449 {
1450         missile_obj     *end, *start, *mo;
1451         object          *A, *target_objp;
1452         ai_info         *aip;
1453         weapon          *wp;
1454         weapon_info     *wip;
1455         int                     target_found = 0;       
1456
1457         if ( source_obj->type != OBJ_SHIP )
1458                 return;
1459
1460         Assert( Ships[source_obj->instance].ai_index != -1 );
1461         aip = &Ai_info[Ships[source_obj->instance].ai_index];
1462         
1463         end = &Missile_obj_list;
1464         if (aip->target_objnum != -1) {
1465                 target_objp = &Objects[aip->target_objnum];
1466                 if ( target_objp->type == OBJ_WEAPON && Weapon_info[Weapons[target_objp->instance].weapon_info_index].subtype == WP_MISSILE )   {       // must be a missile
1467                         end = missile_obj_return_address(Weapons[target_objp->instance].missile_list_index);
1468                 }
1469         }
1470
1471         start = advance_missile_obj(end, next_flag);
1472
1473         for ( mo = start; mo != end; mo = advance_missile_obj(mo, next_flag) ) {
1474                 if ( mo == &Missile_obj_list ){
1475                         continue;
1476                 }
1477
1478                 Assert(mo->objnum >= 0 && mo->objnum < MAX_OBJECTS);
1479                 A = &Objects[mo->objnum];
1480
1481                 Assert(A->type == OBJ_WEAPON);
1482                 Assert((A->instance >= 0) && (A->instance < MAX_WEAPONS));
1483                 wp = &Weapons[A->instance];
1484                 wip = &Weapon_info[wp->weapon_info_index];
1485
1486                 // only allow targeting of bombs
1487                 if ( !(wip->wi_flags & WIF_BOMB) ) {
1488                         continue;
1489                 }
1490
1491                 // only allow targeting of hostile bombs
1492                 if ( (obj_team(A) == Player_ship->team) && (Player_ship->team != TEAM_TRAITOR) ) {
1493                         continue;
1494                 }
1495
1496                 if(hud_target_invalid_awacs(A)){
1497                         continue;
1498                 }
1499
1500                 // if we've reached here, got a new target
1501                 target_found = TRUE;
1502                 set_target_objnum( aip, OBJ_INDEX(A) );
1503                 break;
1504         }       // end for
1505
1506         if ( !target_found ) {
1507         // if no bomb is found, search for bombers
1508                 ship_obj *start, *so;
1509
1510                 //extern ship_obj *Ship_objs;
1511                 if ( (aip->target_objnum != -1) && (Objects[aip->target_objnum].type == OBJ_SHIP) && (Ship_info[Ships[Objects[aip->target_objnum].instance].ship_info_index].flags & SIF_BOMBER) ) {
1512                         int index = Ships[Objects[aip->target_objnum].instance].ship_list_index;
1513                         start = get_ship_obj_ptr_from_index(index);
1514                 } else {
1515                         start = GET_FIRST(&Ship_obj_list);
1516                 }
1517
1518                 for (so=advance_ship(start, next_flag); so!=start; so=advance_ship(so, next_flag)) {
1519                         object *ship_obj = &Objects[so->objnum];
1520
1521                         // don't look at header
1522                         if (so == &Ship_obj_list) {
1523                                 continue;
1524                         }
1525
1526                         // only allow targeting of hostile bombs
1527                         if ( (obj_team(ship_obj) == Player_ship->team) && (Player_ship->team != TEAM_TRAITOR) ) {
1528                                 continue;
1529                         }
1530
1531                         if(hud_target_invalid_awacs(ship_obj)){
1532                                 continue;
1533                         }
1534
1535                         // check if ship type is bomber
1536                         if ( !(Ship_info[Ships[ship_obj->instance].ship_info_index].flags & SIF_BOMBER) ) {
1537                                 continue;
1538                         }
1539
1540                         // check if ignore
1541                         if ( Ships[ship_obj->instance].flags & TARGET_SHIP_IGNORE_FLAGS ){
1542                                 continue;
1543                         }
1544
1545                         // found a good one
1546                         target_found = TRUE;
1547                         set_target_objnum( aip, OBJ_INDEX(ship_obj) );
1548                         break;
1549                 }
1550         }
1551
1552         if ( !target_found ) {
1553                 snd_play( &Snds[SND_TARGET_FAIL], 0.0f );
1554         }               
1555 }
1556
1557 // Return !0 if shipp can be scanned, otherwise return 0
1558 int hud_target_ship_can_be_scanned(ship *shipp)
1559 {
1560         ship_info *sip;
1561
1562         sip = &Ship_info[shipp->ship_info_index];
1563
1564         // ignore cargo that has already been scanned
1565         if ( shipp->flags & SF_CARGO_REVEALED ) {
1566                 return 0;
1567         }
1568
1569         // allow ships with scannable flag set
1570         if ( shipp->flags & SF_SCANNABLE ) {
1571                 return 1;
1572         }
1573
1574         // ignore ships that don't carry cargo
1575         if ( !(sip->flags & (SIF_CARGO|SIF_FREIGHTER)) ) {
1576                 return 0;
1577         }
1578
1579         return 1;
1580 }
1581
1582 // target the next/prev uninspected cargo container
1583 void hud_target_uninspected_cargo(int next_flag)
1584 {
1585         object  *A, *start, *start2;
1586         ship            *shipp;
1587         int             target_found = 0;       
1588
1589         if (Player_ai->target_objnum == -1) {
1590                 start = &obj_used_list;
1591         }  else {
1592                 start = &Objects[Player_ai->target_objnum];
1593         }
1594
1595         start2 = advance_fb(start, next_flag);
1596
1597         for ( A = start2; A != start; A = advance_fb(A, next_flag) ) {
1598                 if ( A == &obj_used_list ) {
1599                         continue;
1600                 }
1601
1602                 if (A == Player_obj || (A->type != OBJ_SHIP) ) {
1603                         continue;
1604                 }
1605
1606                 shipp = &Ships[A->instance];    // get a pointer to the ship information
1607
1608                 if ( shipp->flags & TARGET_SHIP_IGNORE_FLAGS ) {
1609                         continue;
1610                 }
1611
1612                 // ignore all non-cargo carrying craft
1613                 if ( !hud_target_ship_can_be_scanned(shipp) ) {
1614                         continue;
1615                 }
1616
1617                 if(hud_target_invalid_awacs(A)){
1618                         continue;
1619                 }
1620
1621                 // if we've reached here, it is a valid next target
1622                 if ( Player_ai->target_objnum != OBJ_INDEX(A) ) {
1623                         target_found = TRUE;
1624                         set_target_objnum( Player_ai, OBJ_INDEX(A) );
1625                 }
1626         }
1627
1628         if ( target_found == FALSE ) {
1629                 snd_play( &Snds[SND_TARGET_FAIL]);
1630         }
1631 }
1632
1633 // target the newest ship in the area
1634 void hud_target_newest_ship()
1635 {
1636         object  *A, *player_target_objp;
1637         object  *newest_obj=NULL;
1638         ship            *shipp;
1639         ship_obj        *so;
1640         uint            current_target_arrived_time = 0xffffffff, newest_time = 0;
1641
1642         if ( Player_ai->target_objnum >= 0 ) {
1643                 player_target_objp = &Objects[Player_ai->target_objnum];
1644                 if ( player_target_objp->type == OBJ_SHIP ) {
1645                         current_target_arrived_time = Ships[player_target_objp->instance].create_time;
1646                 }
1647         } else {
1648                 player_target_objp = NULL;
1649         }
1650
1651         // If no target is selected, then simply target the closest uninspected cargo
1652         if ( Player_ai->target_objnum == -1 || timestamp_elapsed(Target_newest_ship_timestamp) ) {
1653                 current_target_arrived_time = 0xffffffff;
1654         }
1655
1656         Target_newest_ship_timestamp = timestamp(TL_RESET);
1657
1658         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
1659                 A = &Objects[so->objnum];
1660                 shipp = &Ships[A->instance];    // get a pointer to the ship information
1661
1662                 if ( (A == Player_obj) || (shipp->flags & TARGET_SHIP_IGNORE_FLAGS) )
1663                         continue;
1664
1665                 // ignore navbuoys
1666                 if ( Ship_info[shipp->ship_info_index].flags & SIF_NAVBUOY ) {
1667                         continue;
1668                 }
1669
1670                 if ( A == player_target_objp ) {
1671                         continue;
1672                 }
1673
1674                 if(hud_target_invalid_awacs(A)){
1675                         continue;
1676                 }
1677
1678                 if ( (shipp->create_time >= newest_time) && (shipp->create_time <= current_target_arrived_time) ) {
1679                         newest_time = shipp->create_time;
1680                         newest_obj = A;
1681                 }
1682         }
1683
1684         if (newest_obj) {
1685                 set_target_objnum( Player_ai, OBJ_INDEX(newest_obj) );
1686                 // if BIG|HUGE and no selected subsystem, get sorted turret
1687                 hud_maybe_set_sorted_turret_subsys(&Ships[newest_obj->instance]);
1688                 hud_restore_subsystem_target(&Ships[newest_obj->instance]);
1689         }
1690         else {
1691                 snd_play( &Snds[SND_TARGET_FAIL]);
1692         }
1693 }
1694
1695 #define TYPE_NONE                                               0
1696 #define TYPE_FACING_BEAM                        1
1697 #define TYPE_FACING_FLAK                        2
1698 #define TYPE_FACING_MISSILE             3
1699 #define TYPE_FACING_LASER                       4
1700 #define TYPE_NONFACING_BEAM             5
1701 #define TYPE_NONFACING_FLAK             6
1702 #define TYPE_NONFACING_MISSILE  7
1703 #define TYPE_NONFACING_LASER            8
1704 #define TYPE_NONFACING_INC                      4
1705
1706 typedef struct eval_next_turret {
1707         ship_subsys *ss;
1708         int type;
1709         float dist;
1710 } eval_next_turret;
1711
1712 int sort_turret_func(const void *e1, const void *e2)
1713 {
1714         eval_next_turret *p1 = (eval_next_turret*)e1;
1715         eval_next_turret *p2 = (eval_next_turret*)e2;
1716
1717         Assert(p1->type != TYPE_NONE);
1718         Assert(p2->type != TYPE_NONE);
1719
1720         if (p1->type != p2->type) {
1721                 return (p1->type - p2->type);
1722         } else {
1723                 float delta_dist = p1->dist - p2->dist;
1724                 if (delta_dist < 0) {
1725                         return -1;
1726                 } else if (delta_dist > 0) {
1727                         return 1;
1728                 } else {
1729                         return 0;
1730                 }
1731         }
1732 }
1733
1734 // target the next/prev live turret on the current target
1735 // auto_advance from hud_update_closest_turret
1736 void hud_target_live_turret(int next_flag, int auto_advance, int only_player_target)
1737 {
1738         ship_subsys     *A;
1739         ship_subsys     *live_turret=NULL;
1740         ship                    *target_shipp;
1741         object          *objp;
1742         eval_next_turret ent[MAX_MODEL_SUBSYSTEMS];
1743         int num_live_turrets = 0;
1744
1745         // make sure we're targeting a ship
1746         if (Player_ai->target_objnum == -1 && !auto_advance) {
1747                 snd_play(&Snds[SND_TARGET_FAIL]);
1748                 return;
1749         }
1750         
1751         // only targeting subsystems on ship
1752         if ((Objects[Player_ai->target_objnum].type != OBJ_SHIP) && (!auto_advance)) {
1753                 snd_play( &Snds[SND_TARGET_FAIL]);
1754                 return;
1755         }
1756
1757         // set some pointers
1758         objp = &Objects[Player_ai->target_objnum];
1759         target_shipp = &Ships[objp->instance];
1760
1761         // set timestamp
1762         int timestamp_val = 0;
1763         if (!auto_advance) {
1764                 timestamp_val = Target_next_turret_timestamp;
1765                 Target_next_turret_timestamp = timestamp(TURRET_RESET);
1766         }
1767
1768         // If no target is selected, then simply target the closest (or facing) turret
1769         int last_subsys_turret = FALSE;
1770         if (Player_ai->targeted_subsys != NULL) {
1771                 if (Player_ai->targeted_subsys->system_info->type == SUBSYSTEM_TURRET) {
1772                         if (Player_ai->targeted_subsys->system_info->turret_weapon_type >= 0) {
1773                                 last_subsys_turret = TRUE;
1774                         }
1775                 }
1776         }
1777
1778         // do we want the closest turret (or the one our ship is pointing at)
1779         int get_closest_turret = (auto_advance || !last_subsys_turret || timestamp_elapsed(timestamp_val));
1780
1781         // initialize eval struct
1782         memset(ent,0, sizeof(ent));
1783         int use_straigh_ahead_turret = FALSE;
1784
1785         // go through list of turrets
1786         for (A=GET_FIRST(&target_shipp->subsys_list); A!=END_OF_LIST(&target_shipp->subsys_list); A=GET_NEXT(A))  {
1787                 // get a turret
1788                 if (A->system_info->type == SUBSYSTEM_TURRET) {
1789                         // check turret has hit points and has a weapon
1790                         if ( (A->current_hits > 0) && (A->system_info->turret_weapon_type >= 0) ) {
1791                                 if ( !only_player_target || (A->turret_enemy_objnum == OBJ_INDEX(Player_obj)) ) {
1792                                         vector gsubpos, vec_to_subsys;
1793                                         float distance, dot;
1794                                         // get world pos of subsystem and its distance
1795                                         get_subsystem_world_pos(objp, A, &gsubpos);
1796                                         distance = vm_vec_normalized_dir(&vec_to_subsys, &gsubpos, &View_position);
1797
1798                                         // check if facing and in view
1799                                         int facing = ship_subsystem_in_sight(objp, A, &View_position, &gsubpos, 0);
1800                                         
1801                                         if (!auto_advance && get_closest_turret && !only_player_target) {
1802                                                 // if within 3 degrees and not previous subsys, use subsys in front
1803                                                 dot = vm_vec_dotprod(&vec_to_subsys, &Player_obj->orient.v.fvec);
1804                                                 if ((dot > 0.9986) && facing) {
1805                                                         use_straigh_ahead_turret = TRUE;
1806                                                         break;
1807                                                 }
1808                                         }
1809
1810                                         // set weapon_type to allow sort of ent on type
1811                                         if (Weapon_info[A->system_info->turret_weapon_type].wi_flags & WIF_BEAM) {
1812                                                 ent[num_live_turrets].type = TYPE_FACING_BEAM;
1813                                         } else  if (Weapon_info[A->system_info->turret_weapon_type].wi_flags & WIF_FLAK) {
1814                                                 ent[num_live_turrets].type = TYPE_FACING_FLAK;
1815                                         } else {
1816                                                 if (Weapon_info[A->system_info->turret_weapon_type].subtype == WP_MISSILE) {
1817                                                         ent[num_live_turrets].type = TYPE_FACING_MISSILE;
1818                                                 } else if (Weapon_info[A->system_info->turret_weapon_type].subtype == WP_LASER) {
1819                                                         ent[num_live_turrets].type = TYPE_FACING_LASER;
1820                                                 } else {
1821                                                         Int3();
1822                                                         ent[num_live_turrets].type = TYPE_FACING_LASER;
1823                                                 }
1824                                         }
1825
1826                                         // fill out ent struct
1827                                         ent[num_live_turrets].ss = A;
1828                                         ent[num_live_turrets].dist = distance;
1829                                         if (!facing) {
1830                                                 ent[num_live_turrets].type += TYPE_NONFACING_INC;
1831                                         }
1832                                         num_live_turrets++;
1833                                 }
1834                         }
1835                 }
1836         }
1837
1838         // sort the list if we're not using turret straigh ahead of us
1839         if (!use_straigh_ahead_turret) {
1840                 qsort(ent, num_live_turrets, sizeof(eval_next_turret), sort_turret_func);
1841         }
1842
1843         if (use_straigh_ahead_turret) {
1844         // use the straight ahead turret
1845                 live_turret = A;
1846         } else {
1847         // check if we have a currently targeted turret and find its position after the sort
1848                 int i, start_index, next_index;
1849                 if (get_closest_turret) {
1850                         start_index = 0;
1851                 } else {
1852                         start_index = -1;
1853                         for (i=0; i<num_live_turrets; i++) {
1854                                 if (ent[i].ss == Player_ai->targeted_subsys) {
1855                                         start_index = i;
1856                                         break;
1857                                 }
1858                         }
1859                         // check that we started with a turret
1860                         if (start_index == -1) {
1861                                 start_index = 0;
1862                         }
1863                 }
1864
1865                 // set next live turret
1866                 if (num_live_turrets == 0) {
1867                         // no live turrets
1868                         live_turret = NULL;
1869                 } else if (num_live_turrets == 1 || get_closest_turret) {
1870                         // only 1 live turret, so set it
1871                         live_turret = ent[0].ss;
1872                 } else {
1873                         if (next_flag) {
1874                                 // advance to next closest turret
1875                                 next_index = start_index + 1;
1876                                 if (next_index == num_live_turrets) {
1877                                         next_index = 0;
1878                                 }
1879                         } else {
1880                                 // go to next farther turret
1881                                 next_index = start_index - 1;
1882                                 if (next_index == -1) {
1883                                         next_index = num_live_turrets - 1;
1884                                 }
1885                         }
1886
1887                         // set the next turret to be targeted based on next_index
1888                         live_turret = ent[next_index].ss;
1889                 }
1890
1891                 //if (live_turret) {
1892                         // debug info
1893                 //      mprintf(("name %s, index: %d, type: %d\n", live_turret->system_info->subobj_name, next_index, ent[next_index].type));
1894                 //}
1895         }
1896
1897         if ( live_turret != NULL ) {
1898                 set_targeted_subsys(Player_ai, live_turret, Player_ai->target_objnum);
1899                 target_shipp->last_targeted_subobject[Player_num] = Player_ai->targeted_subsys;
1900         } else {
1901                 if (!auto_advance) {
1902                         snd_play( &Snds[SND_TARGET_FAIL]);
1903                 }
1904         }
1905 }
1906
1907
1908 // -------------------------------------------------------------------
1909 // hud_target_closest_locked_missile()
1910 //
1911 // Target the closest locked missile that is locked on locked_obj
1912 //
1913 //      input:  locked_obj      =>              pointer to object that you want to find 
1914 //                                                                              closest missile to
1915 //                                                                      
1916 void hud_target_closest_locked_missile(object *locked_obj)
1917 {
1918         object          *A, *nearest_obj=NULL;
1919         weapon          *wp;
1920         weapon_info     *wip;
1921         float                   nearest_dist, dist;
1922         int                     target_found = FALSE;
1923         missile_obj     *mo;
1924
1925         nearest_dist = 10000.0f;
1926
1927         for ( mo = GET_NEXT(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
1928                 Assert(mo->objnum >= 0 && mo->objnum < MAX_OBJECTS);
1929                 A = &Objects[mo->objnum];
1930
1931                 if (A->type != OBJ_WEAPON){
1932                         continue;
1933                 }
1934
1935                 Assert((A->instance >= 0) && (A->instance < MAX_WEAPONS));
1936                 wp = &Weapons[A->instance];
1937                 wip = &Weapon_info[wp->weapon_info_index];
1938
1939                 if ( wip->subtype != WP_MISSILE ){
1940                         continue;
1941                 }
1942
1943                 if ( !(wip->wi_flags & (WIF_HOMING_ASPECT|WIF_HOMING_HEAT) ) ){
1944                         continue;
1945                 }
1946
1947                 if(hud_target_invalid_awacs(A)){
1948                         continue;
1949                 }
1950
1951                 if (wp->homing_object == locked_obj) {
1952                         dist = vm_vec_dist_quick(&A->pos, &locked_obj->pos);            // Find distance!
1953
1954                         if (dist < nearest_dist) {
1955                                 nearest_obj = A;
1956                                 nearest_dist = dist;
1957                         }
1958                 }
1959         }       // end for 
1960
1961         if (nearest_dist < 10000.0f) {
1962                 Assert(nearest_obj);
1963                 set_target_objnum( Player_ai, OBJ_INDEX(nearest_obj) );
1964                 target_found = TRUE;
1965         }
1966
1967         if ( !target_found ){
1968                 snd_play( &Snds[SND_TARGET_FAIL], 0.0f );
1969         }
1970 }
1971
1972 //      Return bitmask of all opponents.
1973 int opposing_team_mask(int team_mask)
1974 {
1975         return ((TEAM_FRIENDLY | TEAM_NEUTRAL | TEAM_HOSTILE) & ~team_mask) | TEAM_TRAITOR;
1976 }
1977
1978 // select a new target, by auto-targeting
1979 void hud_target_auto_target_next()
1980 {
1981         if ( Framecount < 2 ) {
1982                 return;
1983         }
1984
1985         //      No auto-targeting after dead.
1986         if (Game_mode & (GM_DEAD | GM_DEAD_BLEW_UP))
1987                 return;
1988
1989         int     valid_team;
1990
1991         valid_team = opposing_team_mask(Player_ship->team);
1992
1993         // try target closest ship attacking player
1994         hud_target_closest(valid_team, OBJ_INDEX(Player_obj), FALSE, TRUE );
1995
1996         // if none, try targeting closest hostile fighter/bomber
1997         if ( Player_ai->target_objnum == -1 ){
1998                 hud_target_closest(valid_team, -1, FALSE, TRUE);
1999         }
2000
2001         // No fighter/bombers exists, so go ahead an target the closest hostile
2002         if ( Player_ai->target_objnum == -1 ){
2003                 hud_target_closest(valid_team, -1, FALSE);
2004         }
2005
2006         // um, ok.  Try targeting asteroids that are on a collision course for an escort ship
2007         if ( Player_ai->target_objnum == -1 ) {
2008                 asteroid_target_closest_danger();
2009         }
2010 }
2011
2012
2013 // Given that object 'targeter' is targetting object 'targetee', 
2014 // how far are they?   This uses the point closest to the targeter
2015 // object on the targetee's bounding box.  So if targeter is inside
2016 // targtee's bounding box, the distance is 0.
2017 float hud_find_target_distance( object *targetee, object *targeter )
2018 {
2019         vector tmp_pnt;
2020
2021         int model_num = -1;
2022         
2023         // Which model is it?
2024         switch( targetee->type )        {
2025         case OBJ_SHIP:
2026                 model_num = Ships[targetee->instance].modelnum;
2027                 break;
2028         case OBJ_DEBRIS:
2029 //              model_num = Debris[targetee->instance].model_num;
2030                 break;
2031         case OBJ_WEAPON:
2032                 // Don't find model_num since circles would work better
2033                 //model_num = Weapon_info[Weapons[targetee->instance].weapon_info_index].model_num;
2034                 break;
2035         case OBJ_ASTEROID:
2036                 // Don't find model_num since circles would work better
2037                 //model_num = Asteroid_info[Asteroids[targetee->instance].type].model_num;
2038                 break;
2039         case OBJ_JUMP_NODE:
2040                 // Don't find model_num since circles would work better
2041                 //model_num = Jump_nodes[targetee->instance].modelnum;
2042                 break;
2043         }
2044
2045         float dist = 0.0f;
2046
2047         // New way, that uses bounding box.     
2048         if ( model_num > -1 )   {
2049                 dist = model_find_closest_point( &tmp_pnt, model_num, -1, &targetee->orient, &targetee->pos, &targeter->pos );
2050         }  else {
2051                 // Old way, that uses radius.
2052                 dist = vm_vec_dist_quick(&targetee->pos, &targeter->pos) - targetee->radius;
2053                 if ( dist < 0.0f )      {
2054                         dist = 0.0f;
2055                 }
2056         }
2057         return dist;
2058 }
2059
2060 // hud_target_closest() will set the Players[Player_num].current_target to the closest
2061 // ship to the player that matches the team passed as a paramater
2062 //
2063 // The current algorithm is to simply iterate through the objects and calculate the 
2064 // magnitude of the vector that connects the player to the target. The smallest magnitude
2065 // is tracked, and then used to locate the closest hostile ship.  Note only the square of the
2066 // magnitude is required, since we are only comparing magnitudes
2067 //
2068 //      parameters:             team    => team of closest ship that should be targeted.
2069 //                         Default value is -1, if team doesn't matter.
2070 //
2071 //                                              attacked_objnum => object number of ship that is being attacked
2072 //                                              play_fail_snd   => boolean, whether to play SND_TARGET_FAIL
2073 //                                   (needed, since function called repeatedly when auto-targeting is
2074 //                                    enabled, and we don't want a string of fail sounds playing).
2075 //                                   This is a default parameter with a value of TRUE
2076 //                                              filter  => OPTIONAL parameter (default value 0): when set to TRUE, only
2077 //                                                                              fighters and bombers are considered for new targets
2078 //
2079 // returns:     TRUE  ==> a target was acquired
2080 //                              FALSE ==> no target was acquired
2081 //
2082 // eval target as closest struct
2083 typedef struct esct
2084 {
2085         int                             team;
2086         int                             filter;
2087         ship*                           shipp;
2088         float                           min_distance;
2089         int                             check_nearest_turret;
2090         int                             attacked_objnum;
2091         int                             check_all_turrets;
2092         int                             turret_attacking_target;                // check that turret is actually attacking the attacked_objnum
2093 } esct;
2094
2095 // evaluate a ship (and maybe turrets) as a potential target
2096 // check if shipp (or its turrets) is attacking attacked_objnum
2097 // special case for player trying to select target (don't check if turrets are aimed at player)
2098 void evaluate_ship_as_closest_target(esct *esct)
2099 {
2100         int targeting_player, turret_is_attacking;
2101         ship_subsys *ss;
2102         float new_distance;
2103
2104         // initialize
2105         esct->min_distance = FLT_MAX;
2106         esct->check_nearest_turret = FALSE;
2107         turret_is_attacking = FALSE;
2108
2109
2110         object *objp = &Objects[esct->shipp->objnum];
2111         Assert(objp->type == OBJ_SHIP);
2112         if (objp->type != OBJ_SHIP) {
2113                 return;
2114         }
2115
2116         // player being targeted, so we will want closest distance from player
2117         targeting_player = (esct->attacked_objnum == OBJ_INDEX(Player_obj));
2118
2119         // filter on team, except in multiplayer
2120         if ( !hud_team_matches_filter(esct->team, esct->shipp->team) ) {
2121                 // if we're in multiplayer dogfight, ignore this
2122                 if(!((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_DOGFIGHT))){
2123                         return;
2124                 }
2125         }
2126
2127         // check if player or ignore ship
2128         if ( (esct->shipp->objnum == OBJ_INDEX(Player_obj)) || (esct->shipp->flags & TARGET_SHIP_IGNORE_FLAGS) ) {
2129                 return;
2130         }
2131
2132         // bail if harmless
2133         if ( Ship_info[esct->shipp->ship_info_index].flags & SIF_HARMLESS ) {
2134                 return;
2135         }
2136
2137         // only look at targets that are AWACS valid
2138         if (hud_target_invalid_awacs(&Objects[esct->shipp->objnum])) {
2139                 return;
2140         }
2141
2142         // If filter is set, only target fighters and bombers
2143         if ( esct->filter ) {
2144                 if ( !(Ship_info[esct->shipp->ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER)) ) {
2145                         return;
2146                 }
2147         }
2148
2149         // find closest turret to player if BIG or HUGE ship
2150         if (Ship_info[esct->shipp->ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) {
2151                 for (ss=GET_FIRST(&esct->shipp->subsys_list); ss!=END_OF_LIST(&esct->shipp->subsys_list); ss=GET_NEXT(ss)) {
2152                         if ( (ss->system_info->type == SUBSYSTEM_TURRET) && (ss->current_hits > 0) ) {
2153
2154                                 if (esct->check_all_turrets || (ss->turret_enemy_objnum == esct->attacked_objnum)) {
2155                                         turret_is_attacking = 1;
2156                                         esct->check_nearest_turret = TRUE;
2157
2158                                         if ( !esct->turret_attacking_target || (esct->turret_attacking_target && (ss->turret_enemy_objnum == esct->attacked_objnum)) ) {
2159                                                 vector gsubpos;
2160                                                 // get world pos of subsystem
2161                                                 vm_vec_unrotate(&gsubpos, &ss->system_info->pnt, &objp->orient);
2162                                                 vm_vec_add2(&gsubpos, &objp->pos);
2163                                                 new_distance = vm_vec_dist_quick(&gsubpos, &Player_obj->pos);
2164                                                 
2165                                                 /*
2166                                                 // GET TURRET TYPE, FAVOR BEAM, FLAK, OTHER
2167                                                 int turret_type = ss->system_info->turret_weapon_type;
2168                                                 if (Weapon_info[turret_type].wi_flags & WIF_BEAM) {
2169                                                         new_distance *= 0.3f;
2170                                                 } else if (Weapon_info[turret_type].wi_flags & WIF_FLAK) {
2171                                                         new_distance *= 0.6f;
2172                                                 } */
2173
2174                                                 // get the closest distance
2175                                                 if (new_distance <= esct->min_distance) {
2176                                                         esct->min_distance = new_distance;
2177                                                 }
2178                                         }
2179                                 }
2180                         }
2181                 }
2182         }
2183
2184         // If no turret is attacking, check if objp is actually targetting attacked_objnum
2185         // dont bail if targeting is for player
2186         if ( !targeting_player && !turret_is_attacking ) {
2187                 ai_info *aip = &Ai_info[esct->shipp->ai_index];
2188
2189                 if (aip->target_objnum != esct->attacked_objnum) {
2190                         return;
2191                 }
2192
2193                 if ( (Game_mode & GM_NORMAL) && ( aip->mode != AIM_CHASE ) && (aip->mode != AIM_STRAFE) && (aip->mode != AIM_EVADE) && (aip->mode != AIM_EVADE_WEAPON) && (aip->mode != AIM_AVOID)) {
2194                         return;
2195                 }
2196         }
2197
2198         // consider the ship alone if there are no attacking turrets
2199         if ( !turret_is_attacking ) {
2200                 //new_distance = hud_find_target_distance(objp, Player_obj);
2201                 new_distance = vm_vec_dist_quick(&objp->pos, &Player_obj->pos);
2202                         
2203                 if (new_distance <= esct->min_distance) {
2204                         esct->min_distance = new_distance;
2205                         esct->check_nearest_turret = FALSE;
2206                 }
2207         }
2208 }
2209
2210 int hud_target_closest(int team, int attacked_objnum, int play_fail_snd, int filter, int get_closest_turret_attacking_player)
2211 {
2212         object  *A;
2213         object  *nearest_obj = &obj_used_list;
2214         ship            *shipp;
2215         ship_obj        *so;
2216         int             check_nearest_turret = FALSE;
2217
2218         // evaluate ship closest target struct
2219         esct            esct;
2220
2221         float           min_distance = FLT_MAX;
2222         int             target_found = FALSE;   
2223
2224         int             player_obj_index = OBJ_INDEX(Player_obj);
2225         ship_subsys *ss; //*nearest_turret_subsys = NULL, *ss;
2226
2227         if ( (attacked_objnum >= 0) && (attacked_objnum != player_obj_index) ) {
2228                 // bail if player does not have target
2229                 if ( Player_ai->target_objnum == -1) {
2230                         if ( Objects[attacked_objnum].type != OBJ_SHIP ) {
2231                                 goto Target_closest_done;
2232                         }
2233
2234                         // bail if ship is to be ignored
2235                         if (!(Ships[Objects[attacked_objnum].instance].flags & TARGET_SHIP_IGNORE_FLAGS)) {
2236                                 goto Target_closest_done;
2237                         }
2238                 }
2239         }
2240
2241         if (attacked_objnum == -1) {
2242                 attacked_objnum = player_obj_index;
2243         }
2244
2245         // check all turrets if for player.
2246         esct.check_all_turrets = (attacked_objnum == player_obj_index);
2247         esct.filter = filter;
2248         esct.team = team;
2249         esct.attacked_objnum = attacked_objnum;
2250         esct.turret_attacking_target = get_closest_turret_attacking_player;
2251
2252         for ( so=GET_FIRST(&Ship_obj_list); so!=END_OF_LIST(&Ship_obj_list); so=GET_NEXT(so) ) {
2253
2254                 A = &Objects[so->objnum];
2255                 shipp = &Ships[A->instance];    // get a pointer to the ship information
2256
2257                 // fill in rest of esct
2258                 esct.shipp = shipp;
2259
2260                 // check each shipp on list and update nearest obj and subsys
2261                 evaluate_ship_as_closest_target(&esct);
2262                 if (esct.min_distance < min_distance) {
2263                         target_found = TRUE;
2264                         min_distance = esct.min_distance;
2265                         nearest_obj = A;
2266                         check_nearest_turret = esct.check_nearest_turret;
2267                 }
2268         }
2269
2270         Target_closest_done:
2271
2272         // maybe ignore target if too far away
2273         // DKA 9/8/99 Remove distance check
2274         /*
2275         if (target_found) {
2276                 // get distance to nearest attacker
2277                 float dist = vm_vec_dist_quick(&Objects[attacked_objnum].pos, &nearest_obj->pos);
2278
2279                 // no distance limit for player obj
2280                 if ((attacked_objnum != player_obj_index) && (dist > MIN_DISTANCE_TO_CONSIDER_THREAT)) {
2281                         target_found = FALSE;
2282                 }
2283         } */
2284
2285         if (target_found) {
2286                 set_target_objnum(Player_ai, OBJ_INDEX(nearest_obj));
2287                 if ( check_nearest_turret ) {
2288
2289                         // if former subobject was not a turret do, not change subsystem
2290                         ss = Ships[nearest_obj->instance].last_targeted_subobject[Player_num];
2291                         if (ss == NULL || get_closest_turret_attacking_player) {
2292                                 // set_targeted_subsys(Player_ai, nearest_turret_subsys, OBJ_INDEX(nearest_obj));
2293                                 // update nearest turret with later func
2294                                 hud_target_live_turret(1, 1, get_closest_turret_attacking_player);
2295                                 Ships[nearest_obj->instance].last_targeted_subobject[Player_num] = Player_ai->targeted_subsys;
2296                         }
2297                 } else {
2298                         hud_restore_subsystem_target(&Ships[nearest_obj->instance]);
2299                 }
2300         } else {
2301                 // no target found, maybe play fail sound
2302                 if (play_fail_snd == TRUE) {
2303                         snd_play(&Snds[SND_TARGET_FAIL]);
2304                 }
2305         }
2306
2307         return target_found;
2308 }
2309
2310 // auto update closest turret to attack on big or huge ships
2311 void hud_update_closest_turret()
2312 {
2313         hud_target_live_turret(1, 1);
2314
2315 /*
2316         float nearest_distance, new_distance;
2317         ship_subsys     *ss, *closest_subsys;
2318         ship    *shipp;
2319         object *objp;
2320
2321         nearest_distance = FLT_MAX;
2322         objp = &Objects[Player_ai->target_objnum];
2323         shipp = &Ships[objp->instance];
2324         closest_subsys = NULL;
2325
2326
2327         Assert(Ship_info[shipp->ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP));
2328
2329         for (ss=GET_FIRST(&shipp->subsys_list); ss!=END_OF_LIST(&shipp->subsys_list); ss=GET_NEXT(ss)) {
2330                 if ( (ss->system_info->type == SUBSYSTEM_TURRET) && (ss->current_hits > 0) ) {
2331                         // make sure turret is not "unused"
2332                         if (ss->system_info->turret_weapon_type >= 0) {
2333                                 vector gsubpos;
2334                                 // get world pos of subsystem
2335                                 vm_vec_unrotate(&gsubpos, &ss->system_info->pnt, &objp->orient);
2336                                 vm_vec_add2(&gsubpos, &objp->pos);
2337                                 new_distance = vm_vec_dist_quick(&gsubpos, &Player_obj->pos);
2338
2339                                 // GET TURRET TYPE, FAVOR BEAM, FLAK, OTHER
2340                                 int turret_type = ss->system_info->turret_weapon_type;
2341                                 if (Weapon_info[turret_type].wi_flags & WIF_BEAM) {
2342                                         new_distance *= 0.3f;
2343                                 } else if (Weapon_info[turret_type].wi_flags & WIF_FLAK) {
2344                                         new_distance *= 0.6f;
2345                                 }
2346
2347                                 // check if facing and in view
2348                                 int facing = ship_subsystem_in_sight(objp, ss, &View_position, &gsubpos, 0);
2349
2350                                 if (facing) {
2351                                         new_distance *= 0.5f;
2352                                 }
2353                                 
2354                                 // get the closest distance
2355                                 if (new_distance <= nearest_distance) {
2356                                         nearest_distance = new_distance;
2357                                         closest_subsys = ss;
2358                                 }
2359                         }
2360                 }
2361         }
2362
2363         // check if new subsys to target
2364         if (Player_ai->targeted_subsys != NULL) {
2365                 set_targeted_subsys(Player_ai, closest_subsys, Player_ai->target_objnum);                       
2366                 shipp->last_targeted_subobject[Player_num] = Player_ai->targeted_subsys;
2367         }       
2368         */
2369 }
2370
2371
2372 // --------------------------------------------------------------------
2373 // hud_target_targets_target()
2374 //
2375 // Target your target's target.  Your target is specified by objnum passed
2376 // as a parameter.
2377 //
2378 void hud_target_targets_target()
2379 {
2380         object  *objp;
2381         int             tt_objnum;
2382
2383         if ( Player_ai->target_objnum < 0 || Player_ai->target_objnum >= MAX_OBJECTS ) {
2384                 goto ttt_fail;
2385         }
2386
2387         objp = &Objects[Player_ai->target_objnum];
2388         if ( objp->type != OBJ_SHIP ) {
2389                 goto ttt_fail;
2390         }
2391
2392         if (hud_target_invalid_awacs(objp)) {
2393                 goto ttt_fail;
2394         }
2395
2396         if ( Ships[objp->instance].flags & TARGET_SHIP_IGNORE_FLAGS ) {
2397                 goto ttt_fail;
2398         }
2399
2400         tt_objnum = Ai_info[Ships[objp->instance].ai_index].target_objnum;
2401         if ( tt_objnum < 0 || tt_objnum >= MAX_OBJECTS ) {
2402                 goto ttt_fail;
2403         }
2404
2405         if ( tt_objnum == OBJ_INDEX(Player_obj) ) {
2406                 goto ttt_fail;
2407         }
2408
2409         // if we've reached here, found player target's target
2410         set_target_objnum( Player_ai, tt_objnum );
2411         if (Objects[tt_objnum].type == OBJ_SHIP) {
2412                 hud_maybe_set_sorted_turret_subsys(&Ships[Objects[tt_objnum].instance]);
2413         }
2414         hud_restore_subsystem_target(&Ships[Objects[tt_objnum].instance]);
2415         return;
2416
2417         ttt_fail:
2418         snd_play( &Snds[SND_TARGET_FAIL], 0.0f );
2419 }
2420
2421 // Return !0 if target_objp is a valid object type for targeting in reticle, otherwise return 0
2422 int object_targetable_in_reticle(object *target_objp)
2423 {
2424         int obj_type;
2425         if (target_objp == Player_obj ) {
2426                 return 0;
2427         }
2428
2429         obj_type = target_objp->type;
2430                 
2431         if ( (obj_type == OBJ_SHIP) || (obj_type == OBJ_DEBRIS) || (obj_type == OBJ_WEAPON) || (obj_type == OBJ_ASTEROID) || (obj_type == OBJ_JUMP_NODE) ) {
2432                 return 1;
2433         }
2434
2435         return 0;
2436 }
2437
2438
2439 // hud_target_in_reticle_new() will target the object that is closest to the player, and who is 
2440 // intersected by a ray passed from the center of the reticle out along the forward vector of the 
2441 // player.
2442 //
2443 // targeting of objects of type OBJ_SHIP and OBJ_DEBRIS are supported
2444 //
2445 // Method: A ray is cast from the center of the reticle, and we keep track of any eligible object
2446 //         the ray intersects.  We take the ship closest to us that intersects an object.
2447 //
2448 //         Since this method may work poorly with objects that are far away, hud_target_in_reticle_old()
2449 //         is called if no intersections are found.
2450 //
2451 //
2452 #define TARGET_IN_RETICLE_DISTANCE      10000.0f
2453
2454 void hud_target_in_reticle_new()
2455 {
2456         vector  terminus;
2457         object  *A;
2458         mc_info mc;
2459         float           dist;
2460
2461         hud_reticle_clear_list(&Reticle_cur_list);
2462         Reticle_save_timestamp = timestamp(RESET_TARGET_IN_RETICLE);
2463
2464         //      Get 3d vector through center of reticle
2465         vm_vec_scale_add(&terminus, &Eye_position, &Player_obj->orient.v.fvec, TARGET_IN_RETICLE_DISTANCE);
2466
2467         mc.model_num = 0;
2468         for ( A = GET_FIRST(&obj_used_list); A !=END_OF_LIST(&obj_used_list); A = GET_NEXT(A) ) {
2469
2470                 if ( !object_targetable_in_reticle(A) ) {
2471                         continue;
2472                 }
2473                 
2474                 if ( A->type == OBJ_WEAPON ) {
2475                         if ( !(Weapon_info[Weapons[A->instance].weapon_info_index].wi_flags & WIF_BOMB) ){
2476                                 continue;
2477                         }
2478                 }
2479
2480                 if ( A->type == OBJ_SHIP ) {
2481                         if ( Ships[A->instance].flags & TARGET_SHIP_IGNORE_FLAGS ){
2482                                 continue;
2483                         }
2484                 }
2485
2486                 if(hud_target_invalid_awacs(A)){
2487                         continue;
2488                 }
2489
2490                 switch (A->type) {
2491                 case OBJ_SHIP:
2492                         mc.model_num = Ships[A->instance].modelnum;
2493                         break;
2494                 case OBJ_DEBRIS:
2495                         mc.model_num = Debris[A->instance].model_num;
2496                         break;
2497                 case OBJ_WEAPON:
2498                         mc.model_num = Weapon_info[Weapons[A->instance].weapon_info_index].model_num;
2499                         break;
2500                 case OBJ_ASTEROID:
2501                         {
2502 #ifndef FS2_DEMO
2503                         int subtype = 0;
2504                         subtype = Asteroids[A->instance].asteroid_subtype;
2505                         mc.model_num = Asteroid_info[Asteroids[A->instance].type].model_num[subtype];
2506 #endif
2507                         }
2508                         break;
2509                 case OBJ_JUMP_NODE:
2510                         mc.model_num = Jump_nodes[A->instance].modelnum;
2511                         break;
2512                 default:
2513                         Int3(); //      Illegal object type.
2514                 }
2515
2516                 model_clear_instance( mc.model_num );
2517                 mc.orient = &A->orient;                                                                         // The object's orient
2518                 mc.pos = &A->pos;                                                                                               // The object's position
2519                 mc.p0 = &Eye_position;                                                                          // Point 1 of ray to check
2520                 mc.p1 = &terminus;                                                                                      // Point 2 of ray to check
2521                 mc.flags = MC_CHECK_MODEL;      // | MC_ONLY_BOUND_BOX;         // check the model, but only its bounding box
2522
2523                 model_collide(&mc);
2524                 if ( mc.num_hits ) {
2525                         dist = vm_vec_dist_squared(&mc.hit_point_world, &Eye_position);
2526                         hud_reticle_list_update(A, dist, 0);
2527                 }
2528         }       // end for (go to next object)
2529
2530         hud_target_in_reticle_old();    // try the old method (works well with ships far away)
2531 }
2532
2533 // hud_target_in_reticle_old() will target the object that is closest to the reticle center and inside 
2534 // the reticle 
2535 //
2536 // targeting of objects of type OBJ_SHIP and OBJ_DEBRIS are supported
2537 //
2538 // 
2539 // Method:  take the dot product of the foward vector and the vector to target.  Take 
2540 //          the one that is closest to 1 and at least MIN_DOT_FOR_TARGET
2541 //
2542 //      IMPORTANT:  The MIN_DOT_FOR_TARGET value was arrived at by trial and error and
2543 //             is only valid for the HUD reticle in use at that time.
2544
2545 #define MIN_DOT_FOR_TARGET              0.9726// fov for targeting in reticle
2546
2547 void hud_target_in_reticle_old()
2548 {
2549         object  *A, *target_obj;
2550         float           dist, dot;
2551         vector  vec_to_target;
2552
2553         for ( A = GET_FIRST(&obj_used_list); A !=END_OF_LIST(&obj_used_list); A = GET_NEXT(A) ) {
2554
2555                 if ( !object_targetable_in_reticle(A) ) {
2556                         continue;
2557                 }
2558
2559                 if ( A->type == OBJ_WEAPON ) {
2560                         if ( !(Weapon_info[Weapons[A->instance].weapon_info_index].wi_flags & WIF_BOMB) ){
2561                                 continue;
2562                         }
2563                 }
2564
2565                 if ( A->type == OBJ_SHIP ) {
2566                         if ( Ships[A->instance].flags & TARGET_SHIP_IGNORE_FLAGS ){
2567                                 continue;
2568                         }
2569                 }
2570
2571                 if(hud_target_invalid_awacs(A)){
2572                         continue;
2573                 }
2574
2575                 if ( vm_vec_same( &A->pos, &Eye_position ) ) {
2576                         continue;
2577                 }
2578
2579                 dist = vm_vec_normalized_dir(&vec_to_target, &A->pos, &Eye_position);
2580                 dot = vm_vec_dot(&Player_obj->orient.v.fvec, &vec_to_target);
2581
2582                 if ( dot > MIN_DOT_FOR_TARGET ) {
2583                         hud_reticle_list_update(A, dot, 1);
2584                 }
2585         }
2586
2587         target_obj = hud_reticle_pick_target();
2588         if ( target_obj != NULL ) {
2589                 set_target_objnum( Player_ai, OBJ_INDEX(target_obj) );
2590                 if ( target_obj->type == OBJ_SHIP ) {
2591                         // if BIG|HUGE, maybe set subsys to turret
2592                         hud_maybe_set_sorted_turret_subsys(&Ships[target_obj->instance]);
2593                         hud_restore_subsystem_target(&Ships[target_obj->instance]);
2594                 }
2595         }       
2596         else {
2597                         snd_play( &Snds[SND_TARGET_FAIL], 0.0f );
2598         }
2599 }
2600
2601 // hud_target_subsystem_in_reticle() will target the subsystem that is within the reticle and 
2602 // is closest to the reticle center.  The current target is the only object that is searched for
2603 // subsystems
2604 // 
2605 // Method:  take the dot product of the foward vector and the vector to target.  Take 
2606 //          the one that is closest to 1 and at least MIN_DOT_FOR_TARGET
2607 //
2608 //      IMPORTANT:  The MIN_DOT_FOR_TARGET value was arrived at by trial and error and
2609 //             is only valid for the HUD reticle in use at that time.
2610 //
2611
2612 void hud_target_subsystem_in_reticle()
2613 {
2614         object* targetp;
2615         ship_subsys     *subsys;
2616         ship_subsys *nearest_subsys = NULL;
2617         vector subobj_pos;
2618
2619         float dist, dot, best_dot;
2620         vector vec_to_target;
2621         best_dot = -1.0f;
2622
2623         if ( Player_ai->target_objnum == -1){
2624                 hud_target_in_reticle_old();
2625         }
2626
2627         if ( Player_ai->target_objnum == -1) {
2628                 snd_play( &Snds[SND_TARGET_FAIL]);
2629                 return;
2630         }
2631         
2632         targetp = &Objects[Player_ai->target_objnum];
2633
2634         if ( targetp->type != OBJ_SHIP ){               // only targeting subsystems on ship
2635                 return;
2636         }
2637
2638         int shipnum = targetp->instance;
2639
2640         for (subsys = GET_FIRST(&Ships[shipnum].subsys_list); subsys != END_OF_LIST(&Ships[shipnum].subsys_list)  ; subsys = GET_NEXT( subsys ) ) {
2641                 get_subsystem_world_pos(targetp, subsys, &subobj_pos);
2642
2643                 dist = vm_vec_normalized_dir(&vec_to_target, &subobj_pos, &Eye_position);
2644                 dot = vm_vec_dot(&Player_obj->orient.v.fvec, &vec_to_target);
2645
2646                 if ( dot > best_dot ) {
2647                         best_dot = dot;
2648                         if ( best_dot > MIN_DOT_FOR_TARGET )
2649                                 nearest_subsys = subsys;
2650                 }
2651
2652                 Assert(best_dot <= 1.0f);
2653         } // end for
2654
2655         if ( nearest_subsys != NULL ) {
2656                 set_targeted_subsys(Player_ai, nearest_subsys, Player_ai->target_objnum);
2657                 HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Targeting subsystem %s.", 323), Player_ai->targeted_subsys->system_info->name);
2658                 Ships[shipnum].last_targeted_subobject[Player_num] =  Player_ai->targeted_subsys;
2659         }       
2660         else {
2661                 snd_play( &Snds[SND_TARGET_FAIL]);
2662         }
2663 }
2664
2665 #define T_LENGTH                                        8
2666 #define T_OFFSET_FROM_CIRCLE    -13
2667 #define T_BASE_LENGTH                   4
2668
2669 //      On entry:
2670 //              color set
2671 void hud_render_orientation_tee(object *from_objp, object *to_objp, matrix *from_orientp)
2672 {
2673         float           dot_product;
2674         vector  target_to_obj;
2675         float           x1,y1,x2,y2,x3,y3,x4,y4;
2676
2677         vm_vec_sub(&target_to_obj, &from_objp->pos, &to_objp->pos);
2678
2679         vm_vec_normalize(&target_to_obj);
2680
2681         // calculate the dot product between the target_to_player vector and the targets forward vector
2682         //
2683         // 0 - vectors are perpendicular
2684         // 1 - vectors are collinear and in the same direction (target is facing player)
2685         // -1 - vectors are collinear and in the opposite direction (target is facing away from player)
2686         dot_product = vm_vec_dotprod(&from_orientp->v.fvec, &target_to_obj);
2687
2688         if (vm_vec_dotprod(&from_orientp->v.rvec, &target_to_obj) >= 0) {
2689                 if (dot_product >= 0){
2690                         dot_product = -PI/2*dot_product + PI;
2691                 } else {
2692                         dot_product = -PI/2*dot_product - PI;
2693                 }
2694         }
2695         else {
2696                 dot_product *= PI/2; //(range is now -PI/2 => PI/2)
2697         }
2698
2699         y1 = (float)sin(dot_product) * (Outer_circle_radius[gr_screen.res] - T_OFFSET_FROM_CIRCLE);
2700         x1 = (float)cos(dot_product) * (Outer_circle_radius[gr_screen.res] - T_OFFSET_FROM_CIRCLE);
2701
2702         y1 += Hud_reticle_center[gr_screen.res][1];
2703         x1 += Hud_reticle_center[gr_screen.res][0];
2704
2705         x1 += HUD_offset_x;
2706         y1 += HUD_offset_y;
2707
2708         y2 = (float)sin(dot_product) * (Outer_circle_radius[gr_screen.res] - T_OFFSET_FROM_CIRCLE - T_LENGTH);
2709         x2 = (float)cos(dot_product) * (Outer_circle_radius[gr_screen.res] - T_OFFSET_FROM_CIRCLE - T_LENGTH);
2710
2711         y2 += Hud_reticle_center[gr_screen.res][1];
2712         x2 += Hud_reticle_center[gr_screen.res][0];
2713
2714         x2 += HUD_offset_x;
2715         y2 += HUD_offset_y;
2716
2717         x3 = x1 - T_BASE_LENGTH * (float)sin(dot_product);
2718         y3 = y1 + T_BASE_LENGTH * (float)cos(dot_product);
2719         x4 = x1 + T_BASE_LENGTH * (float)sin(dot_product);
2720         y4 = y1 - T_BASE_LENGTH * (float)cos(dot_product);
2721
2722         // HACK! Should be antialiased!
2723         gr_line(fl2i(x3),fl2i(y3),fl2i(x4),fl2i(y4));   // bottom of T
2724         gr_line(fl2i(x1),fl2i(y1),fl2i(x2),fl2i(y2));   // part of T pointing towards center
2725
2726 }
2727
2728 void hud_tri(float x1,float y1,float x2,float y2,float x3,float y3)
2729 {
2730         int i;
2731
2732         // Make the triangle always be the correct handiness so
2733         // the tmapper won't think its back-facing and throw it out.
2734         float det = (y2-y1)*(x3-x1) - (x2-x1)*(y3-y1);
2735         if ( det >= 0.0f )      {
2736                 float tmp;
2737
2738                 // swap y1 & y3
2739                 tmp = y1;
2740                 y1 = y3;
2741                 y3 = tmp;
2742
2743                 // swap x1 & x3
2744                 tmp = x1;
2745                 x1 = x3;
2746                 x3 = tmp;
2747         }
2748
2749         vertex * vertlist[3];
2750         vertex verts[3];
2751         
2752         for (i=0; i<3; i++ )    
2753                 vertlist[i] = &verts[i];
2754
2755         verts[0].sx = x1;       verts[0].sy = y1;
2756         verts[1].sx = x2;       verts[1].sy = y2;
2757         verts[2].sx = x3;       verts[2].sy = y3;
2758
2759         uint saved_mode = gr_zbuffer_get();
2760         
2761         gr_zbuffer_set( GR_ZBUFF_NONE );
2762         
2763         gr_tmapper( 3, vertlist, 0 );
2764
2765         gr_zbuffer_set( saved_mode );
2766 }
2767
2768
2769 void hud_tri_empty(float x1,float y1,float x2,float y2,float x3,float y3)
2770 {
2771         gr_line(fl2i(x1),fl2i(y1),fl2i(x2),fl2i(y2));
2772         gr_line(fl2i(x2),fl2i(y2),fl2i(x3),fl2i(y3));
2773         gr_line(fl2i(x3),fl2i(y3),fl2i(x1),fl2i(y1));
2774 }
2775
2776
2777 // Render a missile warning triangle that has a tail on it to indicate distance
2778 void hud_render_tail_missile_triangle(float ang, float xpos, float ypos, float cur_dist, int draw_solid, int draw_inside)
2779 {
2780         float x1=0.0f;
2781         float x2=0.0f;
2782         float y1=0.0f;
2783         float y2=0.0f;
2784         float xtail=0.0f;
2785         float ytail=0.0f;
2786
2787         float sin_ang, cos_ang, tail_len;
2788
2789         float max_tail_len=20.0f;
2790
2791         sin_ang=(float)sin(ang);
2792         cos_ang=(float)cos(ang);
2793
2794         if ( cur_dist < Min_warning_missile_dist ) {
2795                 tail_len = 0.0f;
2796         } else if ( cur_dist > Max_warning_missile_dist ) {
2797                 tail_len = max_tail_len;
2798         } else {
2799                 tail_len = cur_dist/Max_warning_missile_dist * max_tail_len;
2800         }
2801
2802         if ( draw_inside ) {                            
2803                 x1 = xpos - Target_triangle_base[gr_screen.res] * -sin_ang;
2804                 y1 = ypos + Target_triangle_base[gr_screen.res] * cos_ang;
2805                 x2 = xpos + Target_triangle_base[gr_screen.res] * -sin_ang;
2806                 y2 = ypos - Target_triangle_base[gr_screen.res] * cos_ang;
2807
2808                 xpos -= Target_triangle_height[gr_screen.res] * cos_ang;
2809                 ypos += Target_triangle_height[gr_screen.res] * sin_ang;
2810
2811                 if ( tail_len > 0 ) {
2812                         xtail = xpos - tail_len * cos_ang;
2813                         ytail = ypos + tail_len * sin_ang;
2814                 }
2815
2816         } else {                                
2817                 x1 = xpos - Target_triangle_base[gr_screen.res] * -sin_ang;
2818                 y1 = ypos + Target_triangle_base[gr_screen.res] * cos_ang;
2819                 x2 = xpos + Target_triangle_base[gr_screen.res] * -sin_ang;
2820                 y2 = ypos - Target_triangle_base[gr_screen.res] * cos_ang;
2821
2822                 xpos += Target_triangle_height[gr_screen.res] * cos_ang;
2823                 ypos -= Target_triangle_height[gr_screen.res] * sin_ang;
2824
2825                 if ( tail_len > 0 ) {
2826                         xtail = xpos + tail_len * cos_ang;
2827                         ytail = ypos - tail_len * sin_ang;
2828                 }
2829         }
2830
2831         if (draw_solid) {
2832                 hud_tri(xpos,ypos,x1,y1,x2,y2);
2833         } else {
2834                 hud_tri_empty(xpos,ypos,x1,y1,x2,y2);
2835         }
2836
2837         // draw the tail indicating length
2838         if ( tail_len > 0 ) {
2839                 gr_line(fl2i(xpos), fl2i(ypos), fl2i(xtail), fl2i(ytail));
2840         }
2841 }
2842
2843 // Render a missile warning triangle, that splits apart to indicate distance
2844 void hud_render_split_missile_triangle(float ang, float xpos, float ypos, float cur_dist, int draw_solid, int draw_inside)
2845 {
2846         // points to draw triangles
2847         float x1=0.0f;
2848         float y1=0.0f;
2849         float x2=0.0f;
2850         float y2=0.0f;
2851         float x3=0.0f;
2852         float y3=0.0f;
2853         float x4=0.0f;
2854         float y4=0.0f;
2855         float x5=0.0f;
2856         float y5=0.0f;
2857         float x6=0.0f;
2858         float y6=0.0f;
2859
2860         float triangle_sep, half_triangle_sep,sin_ang,cos_ang;
2861
2862         sin_ang=(float)sin(ang);
2863         cos_ang=(float)cos(ang);
2864
2865         if ( cur_dist < Min_warning_missile_dist ) {
2866                 triangle_sep = 0.0f;
2867         } else if ( cur_dist > Max_warning_missile_dist ) {
2868                 triangle_sep = Max_offscreen_tri_seperation[gr_screen.res]+Max_front_seperation[gr_screen.res];
2869         } else {
2870                 triangle_sep = (cur_dist/Max_warning_missile_dist) * (Max_offscreen_tri_seperation[gr_screen.res]+Max_front_seperation[gr_screen.res]);
2871         }
2872
2873         // calculate these values only once, since it will be used in several places
2874         half_triangle_sep = 0.5f * triangle_sep;
2875
2876         xpos = (float)floor(xpos);
2877         ypos = (float)floor(ypos);
2878
2879         if ( triangle_sep == 0 ) {
2880                 x1 = xpos - Target_triangle_base[gr_screen.res] * -sin_ang;
2881                 y1 = ypos + Target_triangle_base[gr_screen.res] * cos_ang;
2882                 x2 = xpos + Target_triangle_base[gr_screen.res] * -sin_ang;
2883                 y2 = ypos - Target_triangle_base[gr_screen.res] * cos_ang;
2884                 if ( draw_inside ) {
2885                 } else {
2886                         xpos += Target_triangle_height[gr_screen.res] * cos_ang;
2887                         ypos -= Target_triangle_height[gr_screen.res] * sin_ang;
2888                 }
2889                 if (draw_solid) {
2890                         hud_tri(xpos,ypos,x1,y1,x2,y2);
2891                 } else {
2892                         hud_tri_empty(xpos,ypos,x1,y1,x2,y2);
2893                 }
2894         } else {
2895                         // calc left side points
2896                         x5 = xpos - half_triangle_sep * -sin_ang;
2897                         y5 = ypos + half_triangle_sep * cos_ang;
2898
2899                         x6 = x5 - Target_triangle_base[gr_screen.res] * -sin_ang;
2900                         y6 = y5 + Target_triangle_base[gr_screen.res] * cos_ang;
2901
2902                         x4=x5;
2903                         y4=y5;
2904                         if ( draw_inside ) {
2905                                 x4 -= Target_triangle_height[gr_screen.res] * cos_ang;
2906                                 y4 += Target_triangle_height[gr_screen.res] * sin_ang;
2907                         } else {
2908                                 x4 += Target_triangle_height[gr_screen.res] * cos_ang;
2909                                 y4 -= Target_triangle_height[gr_screen.res] * sin_ang;
2910                         }
2911
2912                         // calc right side points
2913                         x2 = xpos + half_triangle_sep * -sin_ang;
2914                         y2 = ypos - half_triangle_sep * cos_ang;
2915
2916                         x3 = x2 + Target_triangle_base[gr_screen.res] * -sin_ang;
2917                         y3 = y2 - Target_triangle_base[gr_screen.res] * cos_ang;
2918
2919                         x1=x2;
2920                         y1=y2;
2921                         if ( draw_inside ) {
2922                                 x1 -= Target_triangle_height[gr_screen.res] * cos_ang;
2923                                 y1 += Target_triangle_height[gr_screen.res] * sin_ang;
2924                         } else {
2925                                 x1 += Target_triangle_height[gr_screen.res] * cos_ang;
2926                                 y1 -= Target_triangle_height[gr_screen.res] * sin_ang;
2927                         }
2928
2929                 // draw both tris with a line connecting them
2930                 if ( draw_solid ) {
2931                         hud_tri(x3,y3,x2,y2,x1,y1);
2932                         hud_tri(x4,y4,x5,y5,x6,y6);
2933                 } else {
2934                         hud_tri_empty(x3,y3,x2,y2,x1,y1);
2935                         hud_tri_empty(x4,y4,x5,y5,x6,y6);
2936                 }
2937                 gr_line(fl2i(x2+0.5f),fl2i(y2+0.5f),fl2i(x5+0.5f),fl2i(y5+0.5f));
2938         }
2939 }
2940
2941 //      Render a triangle on the outside of the targeting circle.
2942 //      Must be inside a g3_start_frame().
2943 //      If aspect_flag !0, then render filled, indicating aspect lock.
2944 // If show_interior !0, then point inwards to positions inside reticle
2945 void hud_render_triangle(vector *hostile_pos, int aspect_flag, int show_interior, int split_tri)
2946 {
2947         vertex  hostile_vertex;
2948         float           ang;
2949         float           xpos,ypos,cur_dist,sin_ang,cos_ang;
2950         int             draw_inside=0;
2951
2952         // determine if the closest firing object is within the targeting reticle (which means the triangle
2953         // is not drawn)
2954
2955         cur_dist = vm_vec_dist_quick(&Player_obj->pos, hostile_pos);
2956
2957         g3_rotate_vertex(&hostile_vertex, hostile_pos);
2958
2959         if (hostile_vertex.codes == 0)  {// on screen
2960                 float           projected_x, projected_y;
2961         
2962                 g3_project_vertex(&hostile_vertex);
2963
2964                 if (!(hostile_vertex.flags & PF_OVERFLOW)) {  // make sure point projected
2965                         float mag_squared;
2966
2967                         projected_x = hostile_vertex.sx;
2968                         projected_y = hostile_vertex.sy;
2969
2970                         mag_squared = (projected_x-Hud_reticle_center[gr_screen.res][0])*(projected_x-Hud_reticle_center[gr_screen.res][0]) + 
2971                                                           (projected_y-Hud_reticle_center[gr_screen.res][1])*(projected_y-Hud_reticle_center[gr_screen.res][1]);
2972
2973                         if ( mag_squared < Outer_circle_radius[gr_screen.res]*Outer_circle_radius[gr_screen.res] ) {
2974                                 if ( !show_interior ) {
2975                                         return;
2976                                 } else {
2977                                         draw_inside=1;
2978                                 }
2979                         }
2980                 }
2981         }
2982
2983         ang = atan2_safe(hostile_vertex.y,hostile_vertex.x);
2984         sin_ang=(float)sin(ang);
2985         cos_ang=(float)cos(ang);
2986         
2987         if ( draw_inside ) {
2988                 xpos = Hud_reticle_center[gr_screen.res][0] + cos_ang*(Outer_circle_radius[gr_screen.res]-7);
2989                 ypos = Hud_reticle_center[gr_screen.res][1] - sin_ang*(Outer_circle_radius[gr_screen.res]-7);
2990         } else {
2991                 xpos = Hud_reticle_center[gr_screen.res][0] + cos_ang*(Outer_circle_radius[gr_screen.res]+4);
2992                 ypos = Hud_reticle_center[gr_screen.res][1] - sin_ang*(Outer_circle_radius[gr_screen.res]+4);
2993         }
2994
2995         xpos += HUD_offset_x;
2996         ypos += HUD_offset_y;
2997         
2998         if ( split_tri ) {
2999 //              hud_render_split_missile_triangle(ang, xpos, ypos, cur_dist, aspect_flag, draw_inside);
3000                 hud_render_tail_missile_triangle(ang, xpos, ypos, cur_dist, aspect_flag, draw_inside);
3001         } else {
3002                 float x1=0.0f;
3003                 float x2=0.0f;
3004                 float y1=0.0f;
3005                 float y2=0.0f;
3006
3007                 if ( draw_inside ) {                            
3008                         x1 = xpos - Target_triangle_base[gr_screen.res] * -sin_ang;
3009                         y1 = ypos + Target_triangle_base[gr_screen.res] * cos_ang;
3010                         x2 = xpos + Target_triangle_base[gr_screen.res] * -sin_ang;
3011                         y2 = ypos - Target_triangle_base[gr_screen.res] * cos_ang;
3012
3013                         xpos -= Target_triangle_height[gr_screen.res] * cos_ang;
3014                         ypos += Target_triangle_height[gr_screen.res] * sin_ang;
3015
3016                 } else {                                
3017                         x1 = xpos - Target_triangle_base[gr_screen.res] * -sin_ang;
3018                         y1 = ypos + Target_triangle_base[gr_screen.res] * cos_ang;
3019                         x2 = xpos + Target_triangle_base[gr_screen.res] * -sin_ang;
3020                         y2 = ypos - Target_triangle_base[gr_screen.res] * cos_ang;
3021
3022                         xpos += Target_triangle_height[gr_screen.res] * cos_ang;
3023                         ypos -= Target_triangle_height[gr_screen.res] * sin_ang;
3024                 }
3025
3026                 if (aspect_flag) {
3027                         hud_tri(xpos,ypos,x1,y1,x2,y2);
3028                 } else {
3029                         hud_tri_empty(xpos,ypos,x1,y1,x2,y2);
3030                 }
3031         }
3032 }
3033
3034 //      Show all homing missiles locked onto the player.
3035 //      Also, play the beep!
3036 void hud_show_homing_missiles()
3037 {
3038         object          *A;
3039         missile_obj     *mo;
3040         weapon          *wp;
3041         float                   dist, nearest_dist;
3042         int                     closest_is_aspect=0;
3043
3044         gr_set_color_fast(&HUD_color_homing_indicator);
3045         nearest_dist = Homing_beep.max_cycle_dist;
3046
3047         for ( mo = GET_NEXT(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
3048                 A = &Objects[mo->objnum];
3049                 Assert((A->instance >= 0) && (A->instance < MAX_WEAPONS));
3050
3051                 wp = &Weapons[A->instance];
3052
3053                 if (wp->homing_object == Player_obj) {
3054                         hud_render_triangle(&A->pos, Weapon_info[wp->weapon_info_index].wi_flags & WIF_HOMING_ASPECT, 1, 1);
3055                         dist = vm_vec_dist_quick(&A->pos, &Player_obj->pos);
3056
3057                         if (dist < nearest_dist) {
3058                                 nearest_dist = dist;
3059                                 if ( Weapon_info[wp->weapon_info_index].wi_flags & WIF_HOMING_ASPECT ) {
3060                                         closest_is_aspect=1;
3061                                 } else {
3062                                         closest_is_aspect=0;
3063                                 }
3064                         }
3065                 }
3066         }
3067
3068         //      See if need to play warning beep.
3069         if (nearest_dist < Homing_beep.max_cycle_dist ) {
3070                 float   delta_time;
3071                 float cycle_time;
3072
3073                 delta_time = f2fl(Missiontime - Homing_beep.last_time_played);
3074
3075                 // figure out the cycle time by doing a linear interpretation
3076                 cycle_time = Homing_beep.min_cycle_time + (nearest_dist-Homing_beep.min_cycle_dist) * Homing_beep.precalced_interp;
3077
3078                 // play a new 'beep' if cycle time has elapsed
3079                 if ( (delta_time*1000) > cycle_time ) {
3080                         Homing_beep.last_time_played = Missiontime;
3081                         if ( snd_is_playing(Homing_beep.snd_handle) ) {
3082                                 snd_stop(Homing_beep.snd_handle);
3083                         }
3084
3085                         if ( closest_is_aspect ) {
3086                                 Homing_beep.snd_handle = snd_play(&Snds[SND_PROXIMITY_ASPECT_WARNING]);
3087                         } else {
3088                                 Homing_beep.snd_handle = snd_play(&Snds[SND_PROXIMITY_WARNING]);
3089                         }
3090                 }
3091         }
3092 }
3093
3094 // hud_show_orientation_tee() will draw the orientation gauge that orbits the inside of the 
3095 // outer reticle ring.  If the T is at 12 o'clock, the target is facing the player, if the T
3096 // is at 6 o'clock the target is facing away from the player.  If the T is at 3 or 9 o'clock 
3097 // the target is facing 90 away from the player.
3098 void hud_show_orientation_tee()
3099 {
3100         object* targetp;
3101         
3102         if (Player_ai->target_objnum == -1)
3103                 return;
3104
3105         targetp = &Objects[Player_ai->target_objnum];
3106         
3107         if ( hud_gauge_maybe_flash(HUD_ORIENTATION_TEE) == 1 ) {
3108                 hud_set_iff_color( targetp );
3109         } else {
3110                 hud_set_iff_color( targetp, 1);
3111         }
3112         hud_render_orientation_tee(targetp, Player_obj, &targetp->orient);
3113 }
3114
3115 // routine to draw a bounding box around a remote detonate missile and distance to
3116 void hud_show_remote_detonate_missile()
3117 {
3118         missile_obj     *mo;
3119         object  *mobjp;
3120         float distance;
3121         vertex target_point;
3122         int x1, x2, y1, y2;
3123
3124         // check for currently locked missiles (highest precedence)
3125         for ( mo = GET_FIRST(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
3126                 Assert(mo->objnum >= 0 && mo->objnum < MAX_OBJECTS);
3127                 mobjp = &Objects[mo->objnum];
3128
3129                 if ((Player_obj != NULL) && (mobjp->parent_sig == Player_obj->parent_sig)) {
3130                         if (Weapon_info[Weapons[mobjp->instance].weapon_info_index].wi_flags & WIF_REMOTE) {
3131                                 // get distance
3132                                 distance = hud_find_target_distance(mobjp, Player_obj);
3133
3134                                 // get box center point
3135                                 g3_rotate_vertex(&target_point,&mobjp->pos);
3136
3137                                 // project vertex
3138                                 g3_project_vertex(&target_point);
3139
3140                                 if (!(target_point.flags & PF_OVERFLOW)) {  // make sure point projected
3141                                         int modelnum, bound_rval;
3142
3143                                         switch ( mobjp->type ) {
3144                                         case OBJ_WEAPON:
3145                                                 modelnum = Weapon_info[Weapons[mobjp->instance].weapon_info_index].model_num;
3146                                                 bound_rval = model_find_2d_bound_min( modelnum, &mobjp->orient, &mobjp->pos,&x1,&y1,&x2,&y2 );
3147                                                 break;
3148
3149                                         default:
3150                                                 Int3(); // should never happen
3151                                                 return;
3152                                         }
3153
3154                                         if ( bound_rval == 0 ) {
3155                                                 // draw brackets and distance
3156                                                 int color;
3157                                                 color = hud_brackets_get_iff_color(MESSAGE_SENDER);
3158                                                 gr_set_color_fast(&IFF_colors[color][1]);
3159                                                 draw_bounding_brackets(x1-5,y1-5,x2+5,y2+5,0,0, distance, OBJ_INDEX(mobjp));
3160                                         }
3161
3162                                         // do only for the first remote detonate missile
3163                                         break;
3164                                 }
3165                         }
3166                 }
3167         }
3168 }
3169
3170 // routine to possibly draw a bouding box around a ship sending a message to the player
3171 void hud_show_message_sender()
3172 {
3173         object *targetp;
3174         vertex target_point;                                    // temp vertex used to find screen position for 3-D object;
3175         ship    *target_shipp;
3176         int     x1,x2,y1,y2;
3177
3178
3179         // don't draw brackets if no ship sending a message
3180         if ( Message_shipnum == -1 )
3181                 return;
3182
3183         targetp = &Objects[Ships[Message_shipnum].objnum];
3184         Assert ( targetp != NULL );
3185
3186         Assert ( targetp->type == OBJ_SHIP );
3187
3188         // Don't do this for the ship you're flying!
3189         if ( targetp == Player_obj ) {
3190                 return;
3191         }
3192
3193         Assert ( targetp->instance >=0 && targetp->instance < MAX_SHIPS );
3194         target_shipp = &Ships[Message_shipnum];
3195
3196         // check the object flags to see if this ship is gone.  If so, then don't do this stuff anymore
3197         if ( targetp->flags & OF_SHOULD_BE_DEAD ) {
3198                 Message_shipnum = -1;
3199                 return;
3200         }
3201
3202         // find the current target vertex 
3203         //
3204         g3_rotate_vertex(&target_point,&targetp->pos);
3205
3206         hud_set_iff_color( targetp, 1);
3207
3208         g3_project_vertex(&target_point);
3209
3210         if (!(target_point.flags & PF_OVERFLOW)) {  // make sure point projected
3211                 int modelnum, bound_rval;
3212
3213                 switch ( targetp->type ) {
3214                 case OBJ_SHIP:
3215                         modelnum = target_shipp->modelnum;
3216                         bound_rval = model_find_2d_bound_min( modelnum, &targetp->orient, &targetp->pos,&x1,&y1,&x2,&y2 );
3217                         break;
3218
3219                 default:
3220                         Int3(); // should never happen
3221                         return;
3222                 }
3223
3224                 if ( bound_rval == 0 ) {
3225                         int color;
3226                         color = hud_brackets_get_iff_color(MESSAGE_SENDER);
3227                         gr_set_color_fast(&IFF_colors[color][1]);
3228                         draw_bounding_brackets(x1-5,y1-5,x2+5,y2+5,10,10);
3229                 }
3230         }
3231
3232         if ( hud_gauge_active(HUD_OFFSCREEN_INDICATOR) ) {
3233                 if (target_point.codes != 0) { // target center is not on screen
3234                         // draw the offscreen indicator at the edge of the screen where the target is closest to
3235                         // AL 11-19-97: only show offscreen indicator if player sensors are functioning
3236                         if ( (OBJ_INDEX(targetp) != Player_ai->target_objnum) || (Message_shipnum == Objects[Player_ai->target_objnum].instance) ) {
3237                                 if ( hud_sensors_ok(Player_ship, 0) ) {
3238                                         float dist;
3239                                         gr_set_color_fast(&IFF_colors[IFF_COLOR_MESSAGE][1]);
3240                                         //dist = vm_vec_dist_quick(&Player_obj->pos, &targetp->pos);
3241                                         dist = hud_find_target_distance( targetp, Player_obj );
3242                                         hud_draw_offscreen_indicator(&target_point, &targetp->pos, dist);
3243                                 }
3244                         }
3245                 }
3246         }
3247 }
3248
3249 // hud_prune_hotkeys()
3250 //
3251 // Check for ships that are dying, departed or dead.  These should be removed from the player's
3252 // hotkey lists.
3253 void hud_prune_hotkeys()
3254 {
3255         int                             i;
3256         htarget_list    *hitem, *plist;
3257         object                  *objp;
3258         ship                            *sp;
3259
3260         for ( i = 0; i < MAX_KEYED_TARGETS; i++ ) {
3261                 plist = &(Players[Player_num].keyed_targets[i]);
3262                 if ( EMPTY( plist ) )                   // no items in list, then do nothing
3263                         continue;
3264
3265                 hitem = GET_FIRST(plist);
3266                 while ( hitem != END_OF_LIST(plist) ) {
3267                         int remove_item;
3268
3269                         remove_item = 0;
3270
3271                         objp = hitem->objp;
3272                         Assert ( objp != NULL );
3273                         if ( objp->type == OBJ_SHIP ) {
3274                                 Assert ( objp->instance >=0 && objp->instance < MAX_SHIPS );
3275                                 sp = &Ships[objp->instance];
3276                         } else {
3277                                 // if the object isn't a ship, it shouldn't be on the list, so remove it without question
3278                                 remove_item = 1;
3279                                 sp = NULL;
3280                         }
3281
3282                         // check to see if the object is dying -- if so, remove it from the list
3283                         // check to see if the ship is departing -- if so, remove it from the list
3284                         if ( remove_item || (objp->flags & OF_SHOULD_BE_DEAD) || (sp->flags & (SF_DEPARTING|SF_DYING)) ) {
3285                                 if ( sp != NULL ) {
3286                                         nprintf(("Network", "Hotkey: Pruning %s\n", sp->ship_name));
3287                                 }
3288
3289                                 htarget_list *temp;
3290                                 temp = GET_NEXT(hitem);
3291                                 list_remove( plist, hitem );
3292                                 list_append( &htarget_free_list, hitem );
3293                                 hitem->objp = NULL;
3294                                 hitem = temp;
3295                                 continue;
3296                         }
3297                         hitem = GET_NEXT( hitem );
3298                 }       // end while
3299         }       // end for
3300
3301         // save the hotkey sets with mission time reaches a certain point.  Code was put here because this
3302         // function always called for both single/multiplayer.  Maybe not the best location, but whatever.
3303         mission_hotkey_maybe_save_sets();
3304 }
3305
3306 int HUD_drew_selection_bracket_on_target;
3307
3308 // hud_show_selection_set draws some indicator around all the ships in the current selection set.  No
3309 // indicators will be drawn if there is only 1 ship in the set.
3310 void hud_show_selection_set()
3311 {
3312         htarget_list *hitem, *plist;
3313         object *targetp;
3314         int set, count;
3315         vertex target_point;                                    // temp vertex used to find screen position for 3-D object;
3316         vector target_vec;
3317
3318         HUD_drew_selection_bracket_on_target = 0;
3319
3320         set = Players[Player_num].current_hotkey_set;
3321         if ( set == -1 )
3322                 return;
3323
3324         Assert ( (set >= 0) && (set < MAX_KEYED_TARGETS) );
3325         plist = &(Players[Player_num].keyed_targets[set]);
3326
3327         count = 0;
3328         for ( hitem = GET_FIRST(plist); hitem != END_OF_LIST(plist); hitem = GET_NEXT(hitem) )
3329                 count++;
3330
3331         if ( count == 0 )       {       // only one ship, do nothing
3332                 Players[Player_num].current_hotkey_set = -1;
3333                 return;
3334         }
3335
3336         for ( hitem = GET_FIRST(plist); hitem != END_OF_LIST(plist); hitem = GET_NEXT(hitem) ) {
3337                 targetp = hitem->objp;
3338                 Assert ( targetp != NULL );
3339         
3340                 ship    *target_shipp = NULL;
3341
3342                 Assert ( targetp->type == OBJ_SHIP );
3343                 Assert ( targetp->instance >=0 && targetp->instance < MAX_SHIPS );
3344                 target_shipp = &Ships[targetp->instance];
3345
3346                 if ( (Game_mode & GM_MULTIPLAYER) && (target_shipp == Player_ship) ) {
3347                         continue;
3348                 }
3349
3350                 // find the current target vertex 
3351                 //
3352                 g3_rotate_vertex(&target_point,&targetp->pos);
3353
3354                 vm_vec_sub(&target_vec,&targetp->pos,&Player_obj->pos);
3355
3356                 int x1,x2,y1,y2;
3357
3358                 hud_set_iff_color( targetp, 1 );
3359
3360                 g3_project_vertex(&target_point);
3361
3362                 if (!(target_point.flags & PF_OVERFLOW)) {  // make sure point projected
3363                         int modelnum, bound_rval;
3364
3365                         switch ( targetp->type ) {
3366                         case OBJ_SHIP:
3367                                 modelnum = target_shipp->modelnum;
3368                                 bound_rval = model_find_2d_bound_min( modelnum, &targetp->orient, &targetp->pos,&x1,&y1,&x2,&y2 );
3369                                 break;
3370
3371                         default:
3372                                 Int3(); // should never happen
3373                                 return;
3374                         }
3375
3376                         if ( bound_rval == 0 ) {
3377                                 gr_set_color_fast(&IFF_colors[IFF_COLOR_SELECTION][1]);
3378                                 draw_bounding_brackets(x1-5,y1-5,x2+5,y2+5,5,5);
3379                                 if ( OBJ_INDEX(targetp) == Player_ai->target_objnum ) {
3380                                         HUD_drew_selection_bracket_on_target = 1;
3381                                 }
3382                         }
3383                 }
3384
3385                 if ( hud_gauge_active(HUD_OFFSCREEN_INDICATOR) ) {
3386                         if (target_point.codes != 0) { // target center is not on screen
3387                                 // draw the offscreen indicator at the edge of the screen where the target is closest to
3388                                 // AL 11-19-97: only show offscreen indicator if player sensors are functioning
3389
3390                                 if ( OBJ_INDEX(targetp) != Player_ai->target_objnum ) {
3391                                         if ( hud_sensors_ok(Player_ship, 0) ) {
3392                                                 float dist;
3393                                                 gr_set_color_fast(&IFF_colors[IFF_COLOR_SELECTION][1]);
3394                                                 //dist = vm_vec_dist_quick(&Player_obj->pos, &targetp->pos);
3395                                                 dist = hud_find_target_distance( targetp, Player_obj );
3396                                                 hud_draw_offscreen_indicator(&target_point, &targetp->pos, dist);
3397                                         }
3398                                 }
3399                         }
3400                 }
3401         }
3402 }
3403
3404 void hud_show_brackets(object *targetp, vertex *projected_v)
3405 {
3406         int x1,x2,y1,y2;
3407         int draw_box = TRUE;
3408         int team, bound_rc;
3409
3410         if ( Player->target_is_dying <= 0 ) {
3411                 int modelnum;
3412
3413                 switch ( targetp->type ) {
3414                 case OBJ_SHIP:
3415                         modelnum = Ships[targetp->instance].modelnum;
3416                         bound_rc = model_find_2d_bound_min( modelnum, &targetp->orient, &targetp->pos,&x1,&y1,&x2,&y2 );
3417                         if ( bound_rc != 0 ) {
3418                                 draw_box = FALSE;
3419                         }
3420                         break;
3421
3422                 case OBJ_DEBRIS:
3423                         modelnum = Debris[targetp->instance].model_num;
3424                         bound_rc = submodel_find_2d_bound_min( modelnum, Debris[targetp->instance].submodel_num, &targetp->orient, &targetp->pos,&x1,&y1,&x2,&y2 );
3425                         if ( bound_rc != 0 ) {
3426                                 draw_box = FALSE;
3427                         }
3428                         break;
3429
3430                 case OBJ_WEAPON:
3431                         Assert(Weapon_info[Weapons[targetp->instance].weapon_info_index].subtype == WP_MISSILE);
3432                         modelnum = Weapon_info[Weapons[targetp->instance].weapon_info_index].model_num;
3433                         bound_rc = model_find_2d_bound_min( modelnum, &targetp->orient, &targetp->pos,&x1,&y1,&x2,&y2 );
3434                         break;
3435
3436 #ifndef FS2_DEMO
3437                 case OBJ_ASTEROID:
3438                         {
3439                         int subtype = 0;
3440                         subtype = Asteroids[targetp->instance].asteroid_subtype;
3441                         modelnum = Asteroid_info[Asteroids[targetp->instance].type].model_num[subtype];
3442                         bound_rc = model_find_2d_bound_min( modelnum, &targetp->orient, &targetp->pos,&x1,&y1,&x2,&y2 );
3443                         }
3444                         break;
3445 #endif
3446
3447                 case OBJ_JUMP_NODE:
3448                         modelnum = Jump_nodes[targetp->instance].modelnum;
3449                         bound_rc = model_find_2d_bound_min( modelnum, &targetp->orient, &targetp->pos,&x1,&y1,&x2,&y2 );
3450                         break;
3451
3452                 default:
3453                         Int3(); // should never happen
3454                         return;
3455                 }
3456
3457                 Hud_target_w = x2-x1+1;
3458                 if ( Hud_target_w > gr_screen.clip_width ) {
3459                         Hud_target_w = gr_screen.clip_width;
3460                 }
3461
3462                 Hud_target_h = y2-y1+1;
3463                 if ( Hud_target_h > gr_screen.clip_height ) {
3464                         Hud_target_h = gr_screen.clip_height;
3465                 }
3466
3467                 if ( targetp->type == OBJ_ASTEROID ) {
3468                         if ( OBJ_INDEX(targetp) == Player_ai->target_objnum ) {
3469                                 team = TEAM_TRAITOR;
3470                         } else {
3471                                 team = SELECTION_SET;   
3472                         }
3473                 } else {
3474                         team = obj_team(targetp);
3475                 }
3476
3477                 if ( draw_box == TRUE ) {
3478                         float distance;
3479                         int color;
3480                         color = hud_brackets_get_iff_color(team);
3481                         // maybe color as tagged
3482                         if ( ship_is_tagged(targetp) ) {
3483                                 color = IFF_COLOR_TAGGED;
3484                         }
3485                         distance = hud_find_target_distance( targetp, Player_obj );
3486                         gr_set_color_fast(&IFF_colors[color][1]);
3487                         draw_bounding_brackets(x1-5,y1-5,x2+5,y2+5,0,0,distance, OBJ_INDEX(targetp));
3488                 }
3489
3490                 if ( targetp->type == OBJ_SHIP ) {
3491                         draw_bounding_brackets_subobject();
3492                 }
3493         }
3494 }
3495
3496 void hud_update_target_in_reticle(vertex *projected_v)
3497 {
3498         float mag_squared;
3499         mag_squared = (projected_v->sx-Hud_reticle_center[gr_screen.res][0])*(projected_v->sx-Hud_reticle_center[gr_screen.res][0]) + 
3500                                           (projected_v->sy-Hud_reticle_center[gr_screen.res][1])*(projected_v->sy-Hud_reticle_center[gr_screen.res][1]);
3501
3502         if (mag_squared < Outer_circle_radius[gr_screen.res]*Outer_circle_radius[gr_screen.res]) {
3503                 // this information can be used elsewhere
3504                 Target_in_reticle = 1;
3505         }
3506         else {
3507                 // this information can be used elsewhere
3508                 Target_in_reticle = 0;
3509         }
3510 }
3511
3512
3513
3514
3515 // hud_show_targeting_gauges() will display the targeting information on the HUD.  Called once per frame.
3516 //
3517 // Must be inside a g3_start_frame()
3518 // input:       frametime       =>              time in seconds since last update
3519 //                              in_cockpit      =>              flag (default value 1) indicating whether viewpoint is from cockpit or external
3520 void hud_show_targeting_gauges(float frametime, int in_cockpit)
3521 {
3522         vertex target_point;                                    // temp vertex used to find screen position for 3-D object;
3523         vector target_pos;
3524
3525         // draw the triangle that points to the closest hostile ship that is firing on the player
3526         // This is always drawn, even if there is no current target.  There is also a hook that will
3527         // maybe warn the player via a voice message of an attacking ship.
3528         if ( in_cockpit ) {
3529                 hud_show_hostile_triangle();
3530         }
3531
3532         if (Player_ai->target_objnum == -1)
3533                 return;
3534
3535         object * targetp = &Objects[Player_ai->target_objnum];
3536         Players[Player_num].lead_indicator_active = 0;
3537
3538         // check to see if there is even a current target
3539         if ( targetp == &obj_used_list ) {
3540                 return;
3541         }
3542                 
3543         Target_in_reticle = 0;
3544
3545         // AL 1/20/97: Point to targted subsystem if one exists
3546         if ( Player_ai->targeted_subsys != NULL ) {
3547                 get_subsystem_world_pos(targetp, Player_ai->targeted_subsys, &target_pos);
3548
3549                 Player_ai->current_target_distance = vm_vec_dist_quick(&target_pos,&Player_obj->pos);
3550         } else {
3551                 target_pos = targetp->pos;
3552
3553                 Player_ai->current_target_distance = hud_find_target_distance(targetp,Player_obj);
3554         }
3555
3556         // find the current target vertex 
3557         //
3558         // The 2D screen pos depends on the current viewer position and orientation.  
3559         g3_rotate_vertex(&target_point,&target_pos);
3560
3561
3562         hud_set_iff_color( targetp, 1 );
3563         g3_project_vertex(&target_point);
3564
3565         if (!(target_point.flags & PF_OVERFLOW)) {  // make sure point projected
3566                 if (target_point.codes == 0) { // target center is not on screen
3567                         hud_show_brackets(targetp, &target_point);
3568                 }
3569                 hud_update_target_in_reticle(&target_point);
3570         }
3571         else {
3572                 Hud_target_w = 0;
3573                 Hud_target_h = 0;
3574         }
3575
3576         // show the leading target indicator
3577         if ((hud_gauge_active(HUD_LEAD_INDICATOR)) && (!Player->target_is_dying)) {
3578                 hud_show_lead_indicator(&target_pos);
3579         }
3580
3581         if ( in_cockpit ) {
3582                 // show the indicator that orbits the outer reticle and points in the direction of the target
3583                 hud_show_target_triangle_indicator(&target_point);
3584
3585                 // draw the orientation tee that orbits the inside of the outer circle of the reticle
3586                 if ((hud_gauge_active(HUD_ORIENTATION_TEE)) && (!Player->target_is_dying)) {
3587                         hud_show_orientation_tee();
3588                 }
3589
3590                 // display the information about the target
3591                 if ( hud_gauge_active(HUD_TARGET_MONITOR) ){
3592                         if ( !hud_targetbox_static_maybe_blit(frametime) )
3593                                 hud_show_target_data(frametime);
3594                 }
3595
3596                 // update cargo scanning
3597                 hud_cargo_scan_update(targetp, frametime);
3598
3599                 // draw the shield icon for the current target
3600                 if ( hud_gauge_active(HUD_TARGET_SHIELD_ICON) ) {
3601                         hud_shield_show(targetp);
3602                 }
3603
3604                 // draw the mini target+shield gauge that sits near the bottom of the retcle
3605                 if ( hud_gauge_active(HUD_TARGET_MINI_ICON) ) {
3606                         int show_gauge_flag=1;
3607                         // is gauge configured as a popup?
3608                         if ( hud_gauge_is_popup(HUD_TARGET_MINI_ICON) ) {
3609                                 if ( !hud_gauge_popup_active(HUD_TARGET_MINI_ICON) ) {
3610                                         show_gauge_flag=0;
3611                                 }
3612                         }
3613                         
3614                         if ( show_gauge_flag ) {
3615                                 hud_shield_show_mini(targetp);
3616                         }
3617                 }
3618         } else {
3619                 Player->cargo_inspect_time = 0;
3620                 player_stop_cargo_scan_sound();
3621         }
3622
3623         // display the lock indicator
3624         if (!Player->target_is_dying) {
3625                 hud_update_lock_indicator(frametime);
3626                 hud_show_lock_indicator(frametime);
3627
3628                 // update and render artillery 
3629                 hud_artillery_update();
3630                 hud_artillery_render();
3631         }
3632
3633         // Point to offscreen target
3634         if ( hud_gauge_active(HUD_OFFSCREEN_INDICATOR) ) {
3635                 if (target_point.codes != 0) { // target center is not on screen
3636                         // draw the offscreen indicator at the edge of the screen where the target is closest to
3637                         Assert(Player_ai->target_objnum != -1);
3638
3639                         // AL 11-11-97: don't draw the indicator if the ship is messaging, the indicator is drawn
3640                         //                                              in the message sending color in hud_show_message_sender()
3641                         if ( Message_shipnum != Objects[Player_ai->target_objnum].instance ) {
3642                                 if ( hud_gauge_maybe_flash(HUD_OFFSCREEN_INDICATOR) != 1) {
3643                                         float dist;
3644                                         hud_set_iff_color( targetp, 1 );
3645                                         //dist = vm_vec_dist_quick(&Player_obj->pos, &target_pos);
3646                                         dist = hud_find_target_distance( targetp, Player_obj );
3647                                         hud_draw_offscreen_indicator(&target_point, &target_pos, dist);
3648                                 }
3649                         }
3650                 }
3651         }
3652 }
3653
3654 // hud_show_hostile_triangle() will draw an empty triangle that oribits around the outer
3655 // circle of the reticle.  It will point to the closest enemy that is firing on the player.
3656 // Currently, it points to the closest enemy that has the player as its target_objnum and has
3657 // SM_ATTACK or SM_SUPER_ATTACK as its ai submode.
3658
3659 void hud_show_hostile_triangle()
3660 {
3661         object* A;
3662         float min_distance=1e20f;
3663         float new_distance=0.0f;
3664         object* hostile_obj = &obj_used_list;
3665         object* nearest_obj = &obj_used_list;
3666         ai_info *aip;
3667         ship_obj        *so;
3668         ship            *sp;
3669         ship_subsys *ss, *nearest_turret_subsys = NULL;
3670
3671         int player_obj_index = OBJ_INDEX(Player_obj);
3672         int turret_is_attacking = 0;
3673         
3674         so = GET_FIRST(&Ship_obj_list);
3675         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list);  so = GET_NEXT(so) ) {
3676
3677                 A = &Objects[so->objnum];
3678                 sp = &Ships[A->instance];
3679
3680                 // only look at ships on other team
3681                 if ( (A == Player_obj) || (Ships[A->instance].team & Player_ship->team) ) {
3682                         continue;
3683                 }
3684
3685                 aip = &Ai_info[Ships[A->instance].ai_index];
3686
3687                 // dont look at ignore ships
3688                 if ( sp->flags & TARGET_SHIP_IGNORE_FLAGS ) {
3689                         continue;
3690                 }
3691
3692                 // always ignore cargo containers and navbuoys
3693                 if ( Ship_info[sp->ship_info_index].flags & SIF_HARMLESS ) {
3694                         continue;
3695                 }
3696
3697                 // check if ship is stealthy
3698                 if (awacs_get_level(&Objects[sp->objnum], Player_ship, 1) < 1) {
3699                         continue;
3700                 }
3701
3702                 turret_is_attacking = 0;
3703
3704                 // check if any turrets on ship are firing at the player (only on non fighter-bombers)
3705                 if ( !(Ship_info[sp->ship_info_index].flags & (SIF_FIGHTER|SIF_BOMBER)) ) {
3706                         for (ss = GET_FIRST(&sp->subsys_list); ss != END_OF_LIST(&sp->subsys_list); ss = GET_NEXT(ss) ) {
3707                                 if ( (ss->system_info->type == SUBSYSTEM_TURRET) && (ss->current_hits > 0) ) {
3708
3709                                         if ( ss->turret_enemy_objnum == player_obj_index ) {
3710                                                 turret_is_attacking = 1;                                                
3711
3712                                                 vector          gsubpos;
3713                                                 // get world pos of subsystem
3714                                                 vm_vec_unrotate(&gsubpos, &ss->system_info->pnt, &A->orient);
3715                                                 vm_vec_add2(&gsubpos, &A->pos);
3716                                                 new_distance = vm_vec_dist_quick(&gsubpos, &Player_obj->pos);
3717
3718                                                 if (new_distance <= min_distance) {
3719                                                         min_distance=new_distance;
3720                                                         nearest_obj = A;
3721                                                         nearest_turret_subsys = ss;
3722                                                 }
3723                                         }
3724                                 }
3725                         }
3726                 }
3727
3728                 if ( !turret_is_attacking ) {
3729                         // check for ships attacking the player
3730                         if ( aip->target_objnum != Player_ship->objnum ) {
3731                                 continue;
3732                         }
3733
3734                         // ignore enemy if not in chase mode
3735                         if ( (Game_mode & GM_NORMAL) && (aip->mode != AIM_CHASE) ) {
3736                                 continue;
3737                         }
3738
3739                         new_distance = vm_vec_dist_quick(&A->pos, &Player_obj->pos);
3740
3741                         if (new_distance <= min_distance) {
3742                                 min_distance=new_distance;
3743                                 nearest_obj = A;
3744                                 nearest_turret_subsys = NULL;
3745                         }
3746                 }
3747         }
3748
3749         if ( nearest_obj == &obj_used_list ) {
3750                 return;
3751         }
3752
3753         if ( min_distance > MIN_DISTANCE_TO_CONSIDER_THREAT ) {
3754                 return;
3755         }
3756
3757         hostile_obj = nearest_obj;
3758
3759         // hook to maybe warn player about this attacking ship
3760         ship_maybe_warn_player(&Ships[nearest_obj->instance], min_distance);
3761
3762         // check if the closest firing hostile is the current target, if so return
3763         if (OBJ_INDEX(hostile_obj) == Player_ai->target_objnum)
3764                 return;
3765
3766         if ( hud_gauge_active(HUD_HOSTILE_TRIANGLE) ) {
3767                 if ( hud_gauge_maybe_flash(HUD_HOSTILE_TRIANGLE) != 1 ) {
3768 //                      hud_set_iff_color( TEAM_HOSTILE, 1 );   //      Note: This should really be TEAM_HOSTILE, not opposite of Player_ship->team.
3769                         hud_set_iff_color( hostile_obj, 1 );
3770                         hud_render_triangle(&hostile_obj->pos, 0, 1, 0);
3771                 }
3772         }
3773 }
3774
3775 // Return the bank number for the primary weapon that can fire the farthest, from
3776 // the number of active primary weapons
3777 // input: range =>      output parameter... it is the range of the selected bank
3778 int hud_get_best_primary_bank(float *range)
3779 {
3780         int     i, best_bank, bank_to_fire, num_to_test;
3781         float   weapon_range, farthest_weapon_range;
3782         ship_weapon     *swp;
3783         weapon_info     *wip;
3784
3785         swp = &Player_ship->weapons;
3786
3787         farthest_weapon_range = 0.0f;
3788         best_bank = -1;
3789
3790         if ( Player_ship->flags & SF_PRIMARY_LINKED ) {
3791                 num_to_test = swp->num_primary_banks;
3792         } else {
3793                 num_to_test = min(1, swp->num_primary_banks);
3794         }
3795
3796         for ( i = 0; i < num_to_test; i++ ) {
3797                 
3798                 bank_to_fire = (swp->current_primary_bank+i)%2; //      Max supported banks is 2
3799
3800                 // calculate the range of the weapon, and only display the lead target indicator when
3801                 // if the weapon can actually hit the target
3802                 Assert(bank_to_fire >= 0);
3803                 Assert(swp->primary_bank_weapons[bank_to_fire] >= 0);
3804                 wip = &Weapon_info[swp->primary_bank_weapons[bank_to_fire]];
3805                 weapon_range = wip->max_speed * wip->lifetime;
3806
3807                 if ( weapon_range > farthest_weapon_range ) {
3808                         best_bank = bank_to_fire;
3809                         farthest_weapon_range = weapon_range;
3810                 }
3811         }
3812
3813         *range = farthest_weapon_range;
3814         return best_bank;
3815 }
3816
3817 // -----------------------------------------------------------------------------
3818 //      polish_predicted_target_pos()
3819 // 
3820 // Called by the draw lead indicator code to predict where the enemy is going to be
3821 //
3822 void polish_predicted_target_pos(vector *enemy_pos, vector *predicted_enemy_pos, float dist_to_enemy, vector *last_delta_vec, int num_polish_steps) 
3823 {
3824         int     iteration;
3825         vector  player_pos = Player_obj->pos;   
3826         float           time_to_enemy;
3827         vector  last_predicted_enemy_pos = *predicted_enemy_pos;
3828
3829         ship *shipp;
3830         shipp = &Ships[Player_obj->instance];
3831         Assert(shipp->weapons.current_primary_bank < shipp->weapons.num_primary_banks);
3832         weapon_info     *wip = &Weapon_info[shipp->weapons.primary_bank_weapons[shipp->weapons.current_primary_bank]];
3833
3834         float   weapon_speed = wip->max_speed;
3835
3836         vm_vec_zero(last_delta_vec);
3837
3838         for (iteration=0; iteration < num_polish_steps; iteration++) {
3839                 dist_to_enemy = vm_vec_dist_quick(predicted_enemy_pos, &player_pos);
3840                 time_to_enemy = dist_to_enemy/weapon_speed;
3841 //              vm_vec_scale_add(predicted_enemy_pos, enemy_pos, &Objects[Player_ai->target_objnum].orient.fvec, en_physp->speed * time_to_enemy);
3842                 vm_vec_scale_add(predicted_enemy_pos, enemy_pos, &Objects[Player_ai->target_objnum].phys_info.vel, time_to_enemy);
3843                 vm_vec_sub(last_delta_vec, predicted_enemy_pos, &last_predicted_enemy_pos);
3844                 last_predicted_enemy_pos= *predicted_enemy_pos;
3845         }
3846 }
3847
3848 // determine the correct frame to draw for the lead indicator
3849 // 0 -> center only     (in secondary range only)
3850 // 1 -> full                    (in secondary and primary range)
3851 //      2 -> oustide only       (in primary range only)
3852 //
3853 // input:       prange  =>      range of current primary weapon
3854 //                              srange  =>      range of current secondary weapon
3855 //                              dist_to_target  =>      current dist to target
3856 //
3857 // exit:                0-2     =>      frame offset
3858 //                              -1              =>      don't draw anything
3859 int hudtarget_lead_indicator_pick_frame(float prange, float srange, float dist_to_target)
3860 {
3861         int frame_offset=-1;
3862         int in_prange=0, in_srange=0;
3863
3864         if ( dist_to_target < prange ) {
3865                 in_prange=1;
3866         }
3867
3868         if ( dist_to_target < srange ) {
3869                 in_srange=1;
3870         }
3871
3872         if ( in_prange && in_srange ) {
3873                 frame_offset=1;
3874         } else if ( in_prange && !in_srange ) {
3875                 frame_offset=2;
3876         } else if ( !in_prange && in_srange ) {
3877                 frame_offset=0;
3878         } else {
3879                 frame_offset=-1;
3880         }
3881
3882         return frame_offset;
3883 }
3884
3885         // decide what frame of lead indicator to draw
3886
3887 // hud_show_lead_indicator() determine where to draw the lead target box and display it
3888 void hud_show_lead_indicator(vector *target_world_pos)
3889 {
3890         vector          target_moving_direction, last_delta_vector, source_pos;
3891         vector          *rel_pos;
3892         vertex          lead_target_vertex;
3893         object          *targetp;
3894         polymodel       *po;
3895         ship_weapon     *swp;
3896         weapon_info     *wip;
3897         float                   dist_to_target, time_to_target, target_moved_dist, prange, srange;
3898         int                     bank_to_fire, indicator_frame, frame_offset;
3899
3900         if (Player_ai->target_objnum == -1)
3901                 return;
3902
3903         targetp = &Objects[Player_ai->target_objnum];
3904         if ( (targetp->type != OBJ_SHIP) && (targetp->type != OBJ_WEAPON) && (targetp->type != OBJ_ASTEROID) ) {
3905                 return;
3906         }
3907
3908         // only allow bombs to have lead indicator displayed
3909         if ( targetp->type == OBJ_WEAPON ) {
3910                 if ( !(Weapon_info[Weapons[targetp->instance].weapon_info_index].wi_flags & WIF_BOMB) ) {
3911                         return;
3912                 }
3913         }
3914
3915         // If the target is out of range, then draw the correct frame for the lead indicator
3916         if ( Lead_indicator_gauge.first_frame == -1 ) {
3917                 Int3();
3918                 return;
3919         }
3920         
3921         po = model_get( Player_ship->modelnum );
3922         swp = &Player_ship->weapons;
3923
3924         // Added to take care of situation where there are no primary banks on the player ship
3925         // (this may not be possible, depending on what we decide for the weapons loadout rules)
3926         if ( swp->num_primary_banks == 0 )
3927                 return;
3928
3929         bank_to_fire = hud_get_best_primary_bank(&prange);
3930         if ( bank_to_fire < 0 )
3931                 return;
3932         wip = &Weapon_info[swp->primary_bank_weapons[bank_to_fire]];
3933                         
3934         if (po->n_guns && bank_to_fire != -1 ) {
3935                 rel_pos = &po->gun_banks[bank_to_fire].pnt[0];
3936         } else {
3937                 rel_pos = NULL;
3938         }
3939
3940         // source_pos will contain the world coordinate of where to base the lead indicator prediction
3941         // from.  Normally, this will be the world pos of the gun turret of the currently selected primary
3942         // weapon.
3943         source_pos = Player_obj->pos;
3944         if (rel_pos != NULL) {
3945                 vector  gun_point;
3946                 vm_vec_unrotate(&gun_point, rel_pos, &Player_obj->orient);
3947                 vm_vec_add2(&source_pos, &gun_point);
3948         } 
3949         
3950         // Determine "accurate" distance to target.  This is the distance from the player ship
3951         // to the closest point on the bounding box of the target
3952         dist_to_target = hud_find_target_distance(targetp, Player_obj);
3953
3954         srange = ship_get_secondary_weapon_range(Player_ship);
3955
3956         if ( swp->current_secondary_bank >= 0 ) {
3957                 weapon_info     *wip;
3958                 int bank = swp->current_secondary_bank;
3959                 wip = &Weapon_info[swp->secondary_bank_weapons[bank]];
3960                 if ( wip->wi_flags & WIF_HOMING_ASPECT ) {
3961                         if ( !Player->target_in_lock_cone ) {
3962                                 srange = -1.0f;
3963                         }
3964                 }
3965         }
3966
3967         frame_offset=hudtarget_lead_indicator_pick_frame(prange, srange, dist_to_target);
3968         if ( frame_offset < 0 ) {
3969                 return;
3970         }
3971
3972         indicator_frame = Lead_indicator_gauge.first_frame + frame_offset;
3973
3974         Assert(wip->max_speed != 0);
3975         time_to_target = dist_to_target / wip->max_speed;
3976
3977         target_moved_dist = targetp->phys_info.speed * time_to_target;
3978
3979         target_moving_direction = targetp->phys_info.vel;
3980
3981         // if we've reached here, the lead target indicator will be displayed
3982         Players[Player_num].lead_indicator_active = 1;
3983
3984         // test if the target is moving at all
3985         if ( vm_vec_mag_quick(&targetp->phys_info.vel) < 0.1f)          // Find distance!
3986                 Players[Player_num].lead_target_pos =  *target_world_pos;
3987         else {
3988                 vm_vec_normalize(&target_moving_direction);
3989                 vm_vec_scale(&target_moving_direction,target_moved_dist);
3990                 vm_vec_add(&Players[Player_num].lead_target_pos, target_world_pos, &target_moving_direction );
3991                 polish_predicted_target_pos(target_world_pos, &Players[Player_num].lead_target_pos, dist_to_target, &last_delta_vector, 1); // Not used:, float time_to_enemy)
3992         }
3993
3994         g3_rotate_vertex(&lead_target_vertex,&Players[Player_num].lead_target_pos);
3995
3996         if (lead_target_vertex.codes == 0) { // on screen
3997
3998                 g3_project_vertex(&lead_target_vertex);
3999                 if (!(lead_target_vertex.flags & PF_OVERFLOW)) {
4000
4001                         if ( hud_gauge_maybe_flash(HUD_LEAD_INDICATOR) == 1 ) {
4002                                 hud_set_iff_color(targetp, 0);
4003                         } else {
4004                                 hud_set_iff_color(targetp, 1);
4005                         }
4006
4007                         if ( indicator_frame >= 0 ) {
4008                                 GR_AABITMAP(indicator_frame, fl2i(lead_target_vertex.sx - Lead_indicator_half[gr_screen.res][0]),  fl2i(lead_target_vertex.sy - Lead_indicator_half[gr_screen.res][1]));                                
4009                         }
4010                 }
4011         }
4012 }
4013
4014 // hud_cease_subsystem_targeting() will cease targeting the current targets subsystems
4015 //
4016 void hud_cease_subsystem_targeting(int print_message)
4017 {
4018         int ship_index;
4019
4020         ship_index = Objects[Player_ai->target_objnum].instance;
4021         if ( ship_index < 0 )
4022                 return;
4023
4024         Ships[ship_index].last_targeted_subobject[Player_num] = NULL;
4025         Player_ai->targeted_subsys = NULL;
4026         Player_ai->targeted_subsys_parent = -1;
4027         if ( print_message ) {
4028                 HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Deactivating sub-system targeting", 324));
4029         }
4030
4031         hud_stop_looped_locking_sounds();
4032         hud_lock_reset();
4033 }
4034
4035 // hud_cease_targeting() will cease all targeting (main target and subsystem)
4036 //
4037 void hud_cease_targeting()
4038 {
4039         set_target_objnum( Player_ai, -1 );
4040         Players[Player_num].flags &= ~PLAYER_FLAGS_AUTO_TARGETING;
4041         hud_cease_subsystem_targeting(0);
4042         HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Deactivating targeting system", 325));
4043         hud_lock_reset();
4044 }
4045
4046 // hud_restore_subsystem_target() will remember the last targeted subsystem
4047 // on a target.
4048 //
4049 void hud_restore_subsystem_target(ship* shipp)
4050 {
4051         // check if there was a previously targeted sub-system for this target
4052         if ( shipp->last_targeted_subobject[Player_num] != NULL ) {
4053                 Player_ai->targeted_subsys = shipp->last_targeted_subobject[Player_num];
4054                 Player_ai->targeted_subsys_parent = Player_ai->target_objnum;
4055         }
4056         else {
4057                 Player_ai->targeted_subsys = NULL;
4058                 Player_ai->targeted_subsys_parent = -1;
4059         }
4060 }
4061  
4062 // --------------------------------------------------------------------------------
4063 // get_subsystem_world_pos() returns the world position for a given subobject on a ship
4064 //
4065 vector* get_subsystem_world_pos(object* parent_obj, ship_subsys* subsys, vector* world_pos)
4066 {
4067         if (subsys == NULL) {
4068                 *world_pos = parent_obj->pos;
4069                 return world_pos;
4070         }
4071         
4072         vm_vec_unrotate(world_pos, &subsys->system_info->pnt, &parent_obj->orient);
4073         vm_vec_add2(world_pos, &parent_obj->pos);
4074
4075         return world_pos;
4076 }
4077
4078 // If Pl_objp is docking, see if it (or the dockee) are in the target view.  If so, flash dock
4079 // text on the HUD.
4080 void hud_maybe_flash_docking_text(object *objp)
4081 {
4082         ai_info *aip;
4083         int             docker_objnum, dockee_objnum;
4084
4085         if ( Player_ai->target_objnum < 0 ) {
4086                 return;
4087         }
4088         
4089         if ( objp->type != OBJ_SHIP ) {
4090                 return;
4091         }
4092         
4093         aip = &Ai_info[Ships[objp->instance].ai_index];
4094         docker_objnum = -1;
4095         dockee_objnum = -1;
4096
4097         if ( aip->ai_flags & AIF_DOCKED ) {
4098                 docker_objnum = OBJ_INDEX(objp);
4099                 dockee_objnum = aip->dock_objnum;
4100         }
4101
4102         if ( (Player_ai->target_objnum == docker_objnum) || (Player_ai->target_objnum == dockee_objnum) ) {
4103                 hud_targetbox_start_flash(TBOX_FLASH_DOCKED, 2000);
4104         }
4105 }
4106
4107
4108 // ----------------------------------------------------------------------------
4109 // hud_target_change_check()
4110 //
4111 // called once per frame to account for when the target changes
4112 //
4113 void hud_target_change_check()
4114 {
4115         float current_speed=0.0f;
4116
4117         // Check if player subsystem target has changed, and reset necessary player flag
4118         if ( Player_ai->targeted_subsys != Player_ai->last_subsys_target ) {
4119                 Player->subsys_in_view=-1;
4120         }
4121
4122         // check if the main target has changed
4123         if (Player_ai->last_target != Player_ai->target_objnum) {
4124
4125                 if ( Player_ai->target_objnum != -1){
4126                         snd_play( &Snds[SND_TARGET_ACQUIRE], 0.0f );
4127                 }
4128
4129                 // if we have a hotkey set active, see if new target is in set.  If not in
4130                 // set, deselect the current hotkey set.
4131                 if (    Player->current_hotkey_set != -1 ) {
4132                         htarget_list *hitem, *plist;
4133
4134                         plist = &(Player->keyed_targets[Player->current_hotkey_set]);
4135                         for ( hitem = GET_FIRST(plist); hitem != END_OF_LIST(plist); hitem = GET_NEXT(hitem) ) {
4136                                 if ( OBJ_INDEX(hitem->objp) == Player_ai->target_objnum ){
4137                                         break;
4138                                 }
4139                         }
4140                         if ( hitem == END_OF_LIST(plist) ){
4141                                 Player->current_hotkey_set = -1;
4142                         }
4143                 }
4144
4145                 player_stop_cargo_scan_sound();
4146                 hud_shield_hit_reset();
4147                 hud_targetbox_init_flash();
4148                 hud_targetbox_start_flash(TBOX_FLASH_NAME);
4149                 hud_gauge_popup_start(HUD_TARGET_MINI_ICON);
4150                 Player->cargo_inspect_time=0;
4151                 Player->locking_subsys=NULL;
4152                 Player->locking_on_center=0;
4153                 Player->locking_subsys_parent=-1;
4154
4155                 Player_ai->current_target_dist_trend = NO_CHANGE;
4156                 Player_ai->current_target_speed_trend = NO_CHANGE;
4157
4158                 if ( Players[Player_num].flags & PLAYER_FLAGS_AUTO_MATCH_SPEED ) {
4159                         Players[Player_num].flags &= ~PLAYER_FLAGS_MATCH_TARGET;
4160 //                      player_match_target_speed("", "", XSTR("Matching speed of newly acquired target",-1));
4161                         player_match_target_speed();
4162                 }
4163                 else {
4164                         if ( Players[Player_num].flags & PLAYER_FLAGS_MATCH_TARGET )
4165                                 Players[Player_num].flags &= ~PLAYER_FLAGS_MATCH_TARGET;                // no more target matching.
4166                 }
4167
4168                 hud_lock_reset();
4169
4170                 if ( Player_ai->target_objnum != -1) {
4171                         if ( Objects[Player_ai->target_objnum].type == OBJ_SHIP ) {
4172                                 hud_restore_subsystem_target(&Ships[Objects[Player_ai->target_objnum].instance]);
4173                         }
4174                 }
4175
4176                 // if this target is docked, then flash DOCKING on the hud for a couple of seconds
4177                 hud_targetbox_end_flash(TBOX_FLASH_DOCKED);
4178                 if ( Player_ai->target_objnum >= 0 ) {
4179                         hud_maybe_flash_docking_text(&Objects[Player_ai->target_objnum]);
4180                 }
4181         }
4182         else {
4183                 if (Player_ai->current_target_distance < Player_ai->last_dist-0.01){
4184                         Player_ai->current_target_dist_trend = DECREASING;
4185                 } else if (Player_ai->current_target_distance > Player_ai->last_dist+0.01){
4186                         Player_ai->current_target_dist_trend = INCREASING;
4187                 } else {
4188                         Player_ai->current_target_dist_trend = NO_CHANGE;
4189                 }
4190
4191                 current_speed = Objects[Player_ai->target_objnum].phys_info.speed;
4192
4193                 if (current_speed < Player_ai->last_speed-0.01){
4194                         Player_ai->current_target_speed_trend = DECREASING;
4195                 } else if (current_speed > Player_ai->last_speed+0.01) {
4196                         Player_ai->current_target_speed_trend = INCREASING;
4197                 } else {
4198                         Player_ai->current_target_speed_trend = NO_CHANGE;
4199                 }
4200
4201                 if ( Players[Player_num].flags & PLAYER_FLAGS_AUTO_MATCH_SPEED ) {
4202                         if ( !(Players[Player_num].flags & PLAYER_FLAGS_MATCH_TARGET) ) {
4203 //                              player_match_target_speed("", "", XSTR("Matching target speed",-1));
4204                                 player_match_target_speed();
4205                         }
4206                 }
4207         }
4208
4209         Player_ai->last_dist = Player_ai->current_target_distance;
4210         Player_ai->last_speed = current_speed;
4211
4212         Player_ai->last_target = Player_ai->target_objnum;
4213         Player_ai->last_subsys_target = Player_ai->targeted_subsys;
4214 }
4215
4216 // ---------------------------------------------------------------------
4217 // hud_draw_offscreen_indicator()
4218 //
4219 // draws the offscreen target indicator
4220 //
4221 void hud_draw_offscreen_indicator(vertex* target_point, vector *tpos, float distance)
4222 {
4223         char buf[32];
4224         int w = 0, h = 0;
4225         int on_top, on_right, on_left, on_bottom;
4226         float target_x, target_y;
4227
4228         float xpos,ypos;
4229         // points to draw triangles
4230         float x1=0.0f;
4231         float y1=0.0f;
4232         float x2=0.0f;
4233         float y2=0.0f;
4234         float x3=0.0f;
4235         float y3=0.0f;
4236         float x4=0.0f;
4237         float y4=0.0f;
4238         float x5=0.0f;
4239         float y5=0.0f;
4240         float x6=0.0f;
4241         float y6=0.0f;
4242
4243         vector targ_to_player;
4244         float dist_behind;
4245         float triangle_sep;
4246         float half_gauge_length, half_triangle_sep;
4247         int in_front;
4248
4249         // calculate the dot product between the players forward vector and the vector connecting
4250         // the player to the target. Normalize targ_to_player since we want the dot product
4251         // to range between 0 -> 1.
4252         vm_vec_sub(&targ_to_player, &Player_obj->pos, tpos);
4253         vm_vec_normalize(&targ_to_player);
4254         dist_behind = vm_vec_dot(&Player_obj->orient.v.fvec, &targ_to_player);
4255
4256         in_front = 0;
4257
4258         if (dist_behind < 0) {  // still in front of player, but not in view
4259                 in_front = 1;
4260                 dist_behind = dist_behind + 1.0f;
4261                 if (dist_behind > 0.2 ){
4262                         triangle_sep = ( dist_behind ) * Max_front_seperation[gr_screen.res];
4263                 } else {
4264                         triangle_sep = 0.0f;
4265                 }               
4266         }
4267         else {
4268                 triangle_sep = dist_behind * Max_offscreen_tri_seperation[gr_screen.res] + Max_offscreen_tri_seperation[gr_screen.res];
4269         }
4270
4271         if ( triangle_sep > Max_offscreen_tri_seperation[gr_screen.res] + Max_front_seperation[gr_screen.res]){
4272                 triangle_sep = Max_offscreen_tri_seperation[gr_screen.res] + Max_front_seperation[gr_screen.res];
4273         }
4274
4275         // calculate these values only once, since it will be used in several places
4276         half_triangle_sep = 0.5f * triangle_sep;
4277         half_gauge_length = half_triangle_sep + Offscreen_tri_base[gr_screen.res];
4278
4279         target_x = target_point->x;
4280         target_y = target_point->y;
4281
4282         // We need to find the screen (x,y) for where to draw the offscreen indicator
4283         //
4284         // The best way I've found is to draw a line from the eye_pos to the target, and
4285         // then use clip_line() to find the screen (x,y) for where the line hits the edge
4286         // of the screen.
4287         //
4288         // The weird thing about clip_line() is that is flips around the two verticies,
4289         // so I use eye_vertex->sx and eye_vertex->sy for the off-screen indicator (x,y)
4290         //
4291         vertex *eye_vertex = NULL;
4292         vertex real_eye_vertex;
4293         eye_vertex = &real_eye_vertex;  // this is needed since clip line takes a **vertex
4294         vector eye_pos;
4295         vm_vec_add( &eye_pos, &Eye_position, &View_matrix.v.fvec);
4296         g3_rotate_vertex(eye_vertex, &eye_pos);
4297
4298         ubyte codes_or;
4299         codes_or = (ubyte)(target_point->codes | eye_vertex->codes);
4300         clip_line(&target_point,&eye_vertex,codes_or,0);
4301         
4302         if (!(target_point->flags&PF_PROJECTED))
4303                 g3_project_vertex(target_point);
4304
4305         if (!(eye_vertex->flags&PF_PROJECTED))
4306                 g3_project_vertex(eye_vertex);
4307
4308         if (eye_vertex->flags&PF_OVERFLOW) {
4309                 Int3();                 //      This is unlikely to happen, but can if a clip goes through the player's eye.
4310                 Player_ai->target_objnum = -1;
4311                 return;
4312         } 
4313         
4314         if (target_point->flags & PF_TEMP_POINT)
4315                 free_temp_point(target_point);
4316
4317         if (eye_vertex->flags & PF_TEMP_POINT)
4318                 free_temp_point(eye_vertex);
4319
4320         xpos = eye_vertex->sx;
4321         ypos = eye_vertex->sy;
4322
4323         on_left = on_right = on_top = on_bottom = 0;
4324         xpos = (xpos<1) ? 0 : xpos;
4325         ypos = (ypos<1) ? 0 : ypos;
4326
4327         if ( xpos <= gr_screen.clip_left ) {
4328                 xpos = i2fl(gr_screen.clip_left);
4329                 on_left = TRUE;
4330
4331                 if ( ypos < (half_gauge_length - gr_screen.clip_top) )
4332                         ypos = half_gauge_length;
4333
4334
4335                 if ( ypos > (gr_screen.clip_bottom - half_gauge_length) ) 
4336                         ypos = gr_screen.clip_bottom - half_gauge_length;
4337
4338         }
4339         else if ( xpos >= gr_screen.clip_right) {
4340                 xpos = i2fl(gr_screen.clip_right);
4341                 on_right = TRUE;
4342
4343                 if ( ypos < (half_gauge_length - gr_screen.clip_top) )
4344                         ypos = half_gauge_length;
4345
4346                 if ( ypos > (gr_screen.clip_bottom - half_gauge_length) ) 
4347                         ypos = gr_screen.clip_bottom - half_gauge_length;
4348
4349         }
4350         else if ( ypos <= gr_screen.clip_top ) {
4351                 ypos = i2fl(gr_screen.clip_top);
4352                 on_top = TRUE;
4353
4354                 if ( xpos < ( half_gauge_length - gr_screen.clip_left) )
4355                         xpos = half_gauge_length;
4356
4357                 if ( xpos > (gr_screen.clip_right - half_gauge_length) ) 
4358                         xpos = gr_screen.clip_right - half_gauge_length;
4359
4360         }
4361         else if ( ypos >= gr_screen.clip_bottom ) {
4362                 ypos = i2fl(gr_screen.clip_bottom);
4363                 on_bottom = TRUE;
4364
4365                 if ( xpos < ( half_gauge_length - gr_screen.clip_left) )
4366                         xpos = half_gauge_length;
4367
4368                 if ( xpos > (gr_screen.clip_right - half_gauge_length) ) 
4369                         xpos = gr_screen.clip_right - half_gauge_length;
4370         }
4371         else {
4372                 Int3();
4373                 return;
4374         }
4375
4376         //      The offscreen target triangles are drawn according the the diagram below
4377         //
4378         //
4379         //
4380         //                        x3                            x3
4381         //                 /    |                               | \.
4382         //               /              |                               |   \.
4383         //              x1___x2                         x2___x1
4384         //                              |                               |
4385         //              ......|...........|...............(xpos,ypos)
4386         //                              |                               |
4387         //              x4___x5                         x5___x4
4388         //               \              |                               |         /
4389         //                 \    |                               |       /
4390         //                        x6                            x6
4391         //
4392         //
4393
4394         xpos = (float)floor(xpos);
4395         ypos = (float)floor(ypos);
4396
4397         if ( hud_gauge_active(HUD_OFFSCREEN_RANGE) && (distance > 0) ) {
4398                 sprintf(buf,"%d",fl2i(distance+0.5f));
4399                 hud_num_make_mono(buf);
4400                 gr_get_string_size(&w, &h, buf);        
4401         } else {
4402                 buf[0] = 0;
4403         }
4404
4405         if (on_right) {
4406                 x1 = x4 = (xpos+2);
4407                         
4408                 x2 = x3 = x5 = x6 = x1 - Offscreen_tri_height[gr_screen.res];
4409                 y1 = y2 = ypos - half_triangle_sep;
4410                 y3 = y2 - Offscreen_tri_base[gr_screen.res];
4411
4412                 y4 = y5 = ypos + half_triangle_sep;
4413                 y6 = y5 + Offscreen_tri_base[gr_screen.res];
4414
4415                 if ( buf[0] ) {
4416                         gr_string( fl2i(xpos - w - 10), fl2i(ypos - h/2.0f+0.5f), buf);
4417                 }
4418         }
4419         else if (on_left) {
4420                 x1 = x4 = (xpos-1);
4421                         
4422                 x2 = x3 = x5 = x6 = x1 + Offscreen_tri_height[gr_screen.res];
4423                 y1 = y2 = ypos - half_triangle_sep;
4424                 y3 = y2 - Offscreen_tri_base[gr_screen.res];
4425
4426                 y4 = y5 = ypos + half_triangle_sep;
4427                 y6 = y5 + Offscreen_tri_base[gr_screen.res];
4428
4429                 if ( buf[0] ) {
4430                         gr_string(fl2i(xpos + 10), fl2i(ypos - h/2.0f+0.5f), buf);
4431                 }
4432         }
4433         else if (on_top) {
4434                 y1 = y4 = (ypos-1);
4435                         
4436                 y2 = y3 = y5 = y6 = y1 + Offscreen_tri_height[gr_screen.res];
4437                 x1 = x2 = xpos - half_triangle_sep;
4438                 x3 = x2 - Offscreen_tri_base[gr_screen.res];
4439
4440                 x4 = x5 = xpos + half_triangle_sep;
4441                 x6 = x5 + Offscreen_tri_base[gr_screen.res];
4442
4443                 if ( buf[0] ) {
4444                         gr_string(fl2i(xpos - w/2.0f+0.5f), fl2i(ypos+10), buf);
4445                 }
4446         }
4447         else if (on_bottom) {
4448                 y1 = y4 = (ypos+2);
4449                         
4450                 y2 = y3 = y5 = y6 = y1 - Offscreen_tri_height[gr_screen.res];
4451                 x1 = x2 = xpos - half_triangle_sep;
4452                 x3 = x2 - Offscreen_tri_base[gr_screen.res];
4453
4454                 x4 = x5 = xpos + half_triangle_sep;
4455                 x6 = x5 + Offscreen_tri_base[gr_screen.res];
4456
4457                 if ( buf[0] ) {
4458                         gr_string(fl2i(xpos - w/2.0f+0.5f), fl2i(ypos-h-10), buf);
4459                 }
4460         }
4461
4462         hud_tri(x3,y3,x2,y2,x1,y1);
4463         hud_tri(x4,y4,x5,y5,x6,y6);
4464         if (on_right || on_bottom){
4465                 gr_line(fl2i(x2),fl2i(y2),fl2i(x5),fl2i(y5));
4466         } else if (on_left) {
4467                 gr_line(fl2i(x2-1),fl2i(y2),fl2i(x5-1),fl2i(y5));
4468         } else {
4469                 gr_line(fl2i(x2),fl2i(y2-1),fl2i(x5),fl2i(y5-1));
4470         }
4471
4472 }
4473
4474 //      Render the HUD afterburner energy gauge
4475 void hud_show_afterburner_gauge()
4476 {
4477         float percent_left;
4478         int     clip_h,w,h;     
4479
4480         if ( Energy_bar_gauges.first_frame == -1 ){
4481                 return;
4482         }
4483
4484         Assert(Player_ship);
4485         if ( !(Ship_info[Player_ship->ship_info_index].flags & SIF_AFTERBURNER) ) {
4486                 percent_left = 0.0f;
4487         } else {
4488                 percent_left = Player_ship->afterburner_fuel/Ship_info[Player_ship->ship_info_index].afterburner_fuel_capacity;
4489         }
4490
4491         if ( percent_left > 1 ) {
4492                 percent_left = 1.0f;
4493         }
4494         
4495         clip_h = fl2i( (1.0f - percent_left) * Aburn_coords[gr_screen.res][3] + 0.5f );
4496
4497         bm_get_info(Energy_bar_gauges.first_frame,&w,&h);
4498         
4499         if ( clip_h > 0) {
4500                 GR_AABITMAP_EX(Energy_bar_gauges.first_frame, Aburn_coords[gr_screen.res][0], Aburn_coords[gr_screen.res][1],w,clip_h,0,0);             
4501         }
4502
4503         if ( clip_h <= Aburn_coords[gr_screen.res][3] ) {               
4504                 GR_AABITMAP_EX(Energy_bar_gauges.first_frame+1, Aburn_coords[gr_screen.res][0], Aburn_coords[gr_screen.res][1]+clip_h,w,h-clip_h,0,clip_h);
4505         }       
4506 }
4507
4508 //      Render the player weapon energy on the HUD
4509 void hud_show_weapon_energy_gauge()
4510 {
4511         float percent_left;
4512         int     clip_h, i, w, h;
4513
4514         if ( Energy_bar_gauges.first_frame == -1 ){
4515                 return;
4516         }
4517
4518         if ( Player_ship->weapons.num_primary_banks <= 0 ){
4519                 return;
4520         }
4521
4522         percent_left = Player_ship->weapon_energy/Ship_info[Player_ship->ship_info_index].max_weapon_reserve;
4523         if ( percent_left > 1 ) {
4524                 percent_left = 1.0f;
4525         }
4526         
4527         if ( percent_left <= 0.3 ) {
4528                 char buf[32];
4529                 if ( percent_left < 0.1 ) {
4530                         gr_set_color_fast(&Color_bright_red);
4531                 }
4532                 sprintf(buf,XSTR( "%d%%", 326), fl2i(percent_left*100+0.5f));
4533                 hud_num_make_mono(buf);
4534                 gr_string(Weapon_energy_text_coords[gr_screen.res][0], Weapon_energy_text_coords[gr_screen.res][1], buf);
4535         }
4536
4537         hud_set_gauge_color(HUD_WEAPONS_ENERGY);
4538         for ( i = 0; i < Player_ship->weapons.num_primary_banks; i++ ) {
4539                 if ( !timestamp_elapsed(Weapon_flash_info.flash_duration[i]) ) {
4540                         if ( Weapon_flash_info.is_bright & (1<<i) ) {
4541                                 // hud_set_bright_color();
4542                                 hud_set_gauge_color(HUD_WEAPONS_ENERGY, HUD_C_BRIGHT);
4543                                 break;
4544                         }
4545                 }
4546         }
4547
4548         clip_h = fl2i( (1.0f - percent_left) * Wenergy_coords[gr_screen.res][3] + 0.5f );
4549
4550         bm_get_info(Energy_bar_gauges.first_frame+2,&w,&h);
4551         
4552         if ( clip_h > 0 ) {
4553                 GR_AABITMAP_EX(Energy_bar_gauges.first_frame+2, Wenergy_coords[gr_screen.res][0], Wenergy_coords[gr_screen.res][1], w,clip_h,0,0);              
4554         }
4555
4556         if ( clip_h <= Wenergy_coords[gr_screen.res][3] ) {
4557                 GR_AABITMAP_EX(Energy_bar_gauges.first_frame+3, Wenergy_coords[gr_screen.res][0], Wenergy_coords[gr_screen.res][1] + clip_h, w,h-clip_h,0,clip_h);              
4558         }
4559
4560         // hud_set_default_color();
4561 }
4562
4563 // --------------------------------------------------------------------------------------
4564 //      hud_show_target_triangle_indicator()
4565 //
4566 //      Draw the solid triangle that orbits the reticle and points to the nearest target
4567 //
4568 void hud_show_target_triangle_indicator(vertex *projected_v)
4569 {
4570         float x3,y3,x4,y4;
4571         float xpos,ypos,ang;
4572         
4573         if ( Player_ai->target_objnum == -1)
4574                 return;
4575
4576         object *targetp = &Objects[Player_ai->target_objnum];
4577
4578         // draw the targeting triangle that orbits the outside of the outer circle of the reticle
4579         if ((hud_gauge_active(HUD_TARGET_TRIANGLE)) && !Player->target_is_dying && !Target_in_reticle) {
4580                 if ( hud_gauge_maybe_flash(HUD_TARGET_TRIANGLE) == 1 ) {
4581                         return;
4582                 }
4583
4584                 hud_set_iff_color(targetp, 1);
4585
4586                 ang = atan2_safe(projected_v->y,projected_v->x);
4587                 xpos = Hud_reticle_center[gr_screen.res][0] + (float)cos(ang)*(Outer_circle_radius[gr_screen.res]+4);
4588                 ypos = Hud_reticle_center[gr_screen.res][1] - (float)sin(ang)*(Outer_circle_radius[gr_screen.res]+4);
4589
4590                 xpos += HUD_offset_x;
4591                 ypos += HUD_offset_y;
4592                         
4593                 x3 = xpos - Target_triangle_base[gr_screen.res] * (float)sin(-ang);
4594                 y3 = ypos + Target_triangle_base[gr_screen.res] * (float)cos(-ang);
4595                 x4 = xpos + Target_triangle_base[gr_screen.res] * (float)sin(-ang);
4596                 y4 = ypos - Target_triangle_base[gr_screen.res] * (float)cos(-ang);
4597
4598                 xpos += Target_triangle_height[gr_screen.res] * (float)cos(ang);
4599                 ypos -= Target_triangle_height[gr_screen.res] * (float)sin(ang);
4600
4601                 hud_tri(xpos,ypos,x3,y3,x4,y4);
4602         }
4603 }
4604
4605 // called from hud_show_weapons() to plot out the secondary weapon name and amo 
4606 void hud_show_secondary_weapon(int count, ship_weapon *sw, int dual_fire)
4607 {
4608         char    ammo_str[32];
4609         char    weapon_name[NAME_LENGTH + 10];
4610         int     i, w, h, np;
4611         weapon_info     *wip;
4612
4613         np = 1;
4614         if ( sw->num_primary_banks == 2 ) {
4615                 np = 0;
4616         } 
4617
4618         for ( i = 0; i < count; i++ ) {
4619                 hud_maybe_flash_weapon(sw->num_primary_banks+i);
4620                 wip = &Weapon_info[sw->secondary_bank_weapons[i]];
4621                 
4622                 // HACK - make Cluster Bomb fit on the HUD.
4623                 if(!stricmp(wip->name,"cluster bomb")){
4624                         strcpy(weapon_name, NOX("Cluster"));
4625                 } else {
4626                         strcpy(weapon_name, wip->name);
4627                 }
4628
4629                 hud_end_string_at_first_hash_symbol(weapon_name);
4630                 
4631                 if ( sw->current_secondary_bank == i ) {
4632                         emp_hud_printf(Weapon_sunlinked_x[gr_screen.res], Weapon_secondary_y[gr_screen.res][i] - np*12, EG_NULL, "%c", Lcl_special_chars + 2);                  
4633
4634                         if ( dual_fire ) {
4635                                 emp_hud_printf(Weapon_slinked_x[gr_screen.res], Weapon_secondary_y[gr_screen.res][i] - np*12, EG_NULL, "%c", Lcl_special_chars + 2);                            
4636                         }
4637
4638                         emp_hud_string(Weapon_secondary_name_x[gr_screen.res], Weapon_secondary_y[gr_screen.res][i] - np*12, i ? EG_WEAPON_S1 : EG_WEAPON_S2, weapon_name);
4639
4640                         if ( (sw->secondary_bank_ammo[i] > 0) && (sw->current_secondary_bank >= 0) ) {
4641                                 int ms_till_fire = timestamp_until(sw->next_secondary_fire_stamp[sw->current_secondary_bank]);
4642                                 if ( (ms_till_fire >= 500) && ((wip->fire_wait >= 1 ) || (ms_till_fire > wip->fire_wait*1000)) ) {
4643                                         emp_hud_printf(Weapon_secondary_reload_x[gr_screen.res], Weapon_secondary_y[gr_screen.res][i] - np*12, EG_NULL, "%d", fl2i(ms_till_fire/1000.0f +0.5f));                                        
4644                                 }
4645                         }
4646                 } else {
4647                         emp_hud_string(Weapon_secondary_name_x[gr_screen.res], Weapon_secondary_y[gr_screen.res][i] - np*12, i ? EG_WEAPON_S1 : EG_WEAPON_S2, weapon_name);                     
4648                 }
4649
4650                 // print out the ammo right justified
4651                 sprintf(ammo_str, "%d", sw->secondary_bank_ammo[i]);
4652                 hud_num_make_mono(ammo_str);
4653                 gr_get_string_size(&w, &h, ammo_str);
4654
4655                 emp_hud_string(Weapon_secondary_ammo_x[gr_screen.res] - w, Weapon_secondary_y[gr_screen.res][i] - np*12, EG_NULL, ammo_str);            
4656
4657                 hud_set_gauge_color(HUD_WEAPONS_GAUGE);
4658         }
4659 }
4660
4661 // start the weapon line (on the HUD) flashing
4662 void hud_start_flash_weapon(int index)
4663 {
4664         if ( index >= MAX_WEAPON_FLASH_LINES ) {
4665                 Int3(); // Get Alan
4666                 return;
4667         }
4668
4669         if ( timestamp_elapsed(Weapon_flash_info.flash_duration[index]) ) {
4670                 Weapon_flash_info.flash_next[index] = timestamp(TBOX_FLASH_INTERVAL);
4671                 Weapon_flash_info.is_bright &= ~(1<<index);
4672         }
4673
4674         Weapon_flash_info.flash_duration[index] = timestamp(TBOX_FLASH_DURATION);
4675 }
4676
4677 // maybe change the text color for the weapon line indicated by index 
4678 void hud_maybe_flash_weapon(int index)
4679 {
4680         if ( index >= MAX_WEAPON_FLASH_LINES ) {
4681                 Int3(); // Get Alan
4682                 return;
4683         }
4684
4685         // hud_set_default_color();
4686         hud_set_gauge_color(HUD_WEAPONS_GAUGE);
4687         if ( !timestamp_elapsed(Weapon_flash_info.flash_duration[index]) ) {
4688                 if ( timestamp_elapsed(Weapon_flash_info.flash_next[index]) ) {
4689                         Weapon_flash_info.flash_next[index] = timestamp(TBOX_FLASH_INTERVAL);
4690                         Weapon_flash_info.is_bright ^= (1<<index);
4691                 }
4692
4693                 if ( Weapon_flash_info.is_bright & (1<<index) ) {
4694                         hud_set_gauge_color(HUD_WEAPONS_GAUGE, HUD_C_BRIGHT);
4695                         // hud_set_bright_color();
4696                 } else {
4697                         hud_set_gauge_color(HUD_WEAPONS_GAUGE, HUD_C_DIM);
4698                         // hud_set_dim_color();
4699                 }
4700         }
4701 }
4702
4703 // render the coutermeasure HUD gauge
4704 void hud_show_cmeasure_gague()
4705 {
4706         if ( Cmeasure_gauge.first_frame == -1 ) {
4707                 Int3(); // failed to load coutermeasure gauge background
4708                 return;
4709         }
4710
4711         // hud_set_default_color();
4712         hud_set_gauge_color(HUD_CMEASURE_GAUGE);
4713
4714         // blit the background
4715         GR_AABITMAP(Cmeasure_gauge.first_frame, Cm_coords[gr_screen.res][0], Cm_coords[gr_screen.res][1]);      
4716
4717         // blit text
4718         gr_string(Cm_text_coords[gr_screen.res][0], Cm_text_coords[gr_screen.res][1], XSTR( "cm.", 327));
4719         if ( !Player_ship ) {
4720                 Int3(); // player ship doesn't exist?
4721                 return;
4722         }
4723         gr_printf(Cm_text_val_coords[gr_screen.res][0], Cm_text_val_coords[gr_screen.res][1], NOX("%02d"),Player_ship->cmeasure_count);
4724 }
4725
4726
4727 // ------------------------------------------------------------------
4728 // hud_show_weapons()
4729 //
4730 // Show the player's primary and secondary weapons, along with ammo and % energy
4731 //
4732 void hud_show_weapons()
4733 {
4734         ship_weapon     *sw;
4735         int                     np, ns;         // np == num primary, ns == num secondary
4736         char                    name[NAME_LENGTH];      
4737
4738         if(Player_obj->type == OBJ_OBSERVER)
4739                 return;
4740
4741         Assert(Player_obj->type == OBJ_SHIP);
4742         Assert(Player_obj->instance >= 0 && Player_obj->instance < MAX_SHIPS);
4743
4744         sw = &Ships[Player_obj->instance].weapons;
4745
4746         np = sw->num_primary_banks;
4747         ns = sw->num_secondary_banks;
4748
4749         // NOTE:  I hate to hard-code numbers, but there is no clean way to organize these coords... they
4750         //        are all over the place.  UGLY.
4751
4752         // BAH. You're a moron, above guy. :)
4753
4754         hud_set_gauge_color(HUD_WEAPONS_GAUGE);
4755
4756         // draw top of primary display
4757         GR_AABITMAP(Weapon_gauges[0].first_frame, Weapon_gauge_primary_coords[gr_screen.res][0][0], Weapon_gauge_primary_coords[gr_screen.res][0][1]);  
4758         
4759         emp_hud_string(Weapon_title_coords[gr_screen.res][0], Weapon_title_coords[gr_screen.res][1], EG_WEAPON_TITLE, XSTR( "weapons", 328));           
4760
4761         switch ( np ) {
4762         case 0:         
4763                 // draw bottom of border                
4764                 GR_AABITMAP(Weapon_gauges[2].first_frame, Weapon_gauge_primary_coords[gr_screen.res][1][0], Weapon_gauge_primary_coords[gr_screen.res][1][1]);          
4765
4766                 emp_hud_string(Weapon_pname_coords[gr_screen.res][0][0], Weapon_pname_coords[gr_screen.res][0][1], EG_WEAPON_P1, XSTR( "<none>", 329));         
4767
4768                 np = 1;
4769                 break;
4770
4771         case 1:
4772                 // draw bottom of border
4773                 GR_AABITMAP(Weapon_gauges[2].first_frame, Weapon_gauge_primary_coords[gr_screen.res][1][0], Weapon_gauge_primary_coords[gr_screen.res][1][1]);
4774
4775                 strcpy(name, Weapon_info[sw->primary_bank_weapons[0]].name);
4776                 if (Lcl_gr) {
4777                         lcl_translate_wep_name(name);
4778                 }
4779                 
4780                 // maybe modify name here to fit
4781                 if ( hud_gauge_maybe_flash(HUD_WEAPONS_GAUGE) == 1 ) {
4782                         // hud_set_bright_color();
4783                         hud_set_gauge_color(HUD_WEAPONS_GAUGE, HUD_C_BRIGHT);
4784                 } else {
4785                         hud_maybe_flash_weapon(0);
4786                 }
4787                 
4788                 emp_hud_printf(Weapon_plink_coords[gr_screen.res][0][0], Weapon_plink_coords[gr_screen.res][0][1], EG_NULL, "%c", Lcl_special_chars + 2);
4789                 emp_hud_printf(Weapon_pname_coords[gr_screen.res][0][0], Weapon_pname_coords[gr_screen.res][0][1], EG_WEAPON_P2, "%s", name);                                   
4790                 break;
4791
4792         case 2:
4793                 // draw border to accomodate second primary weapon
4794                 GR_AABITMAP(Weapon_gauges[1].first_frame, Weapon_gauge_primary_coords[gr_screen.res][1][0], Weapon_gauge_primary_coords[gr_screen.res][1][1]);          
4795
4796                 // draw bottom of border
4797                 GR_AABITMAP(Weapon_gauges[2].first_frame, Weapon_gauge_primary_coords[gr_screen.res][2][0], Weapon_gauge_primary_coords[gr_screen.res][2][1]);
4798
4799                 strcpy(name, Weapon_info[sw->primary_bank_weapons[0]].name);
4800                 if (Lcl_gr) {
4801                         lcl_translate_wep_name(name);
4802                 }
4803                 // maybe modify name here to fit
4804
4805                 if ( hud_gauge_maybe_flash(HUD_WEAPONS_GAUGE) == 1 ) {
4806                         // hud_set_bright_color();
4807                         hud_set_gauge_color(HUD_WEAPONS_GAUGE, HUD_C_BRIGHT);
4808                 } else {
4809                         hud_maybe_flash_weapon(0);
4810                 }
4811                 if ( (sw->current_primary_bank == 0) || (Player_ship->flags & SF_PRIMARY_LINKED) ) {
4812                         emp_hud_printf(Weapon_plink_coords[gr_screen.res][0][0], Weapon_plink_coords[gr_screen.res][0][1], EG_NULL, "%c", Lcl_special_chars + 2);                       
4813                 }
4814                 emp_hud_printf(Weapon_pname_coords[gr_screen.res][0][0], Weapon_pname_coords[gr_screen.res][0][1], EG_WEAPON_P1, "%s", name);                   
4815
4816                 strcpy(name, Weapon_info[sw->primary_bank_weapons[1]].name);
4817                 if (Lcl_gr) {
4818                         lcl_translate_wep_name(name);
4819                 }
4820                 // maybe modify name here to fit
4821                 if ( hud_gauge_maybe_flash(HUD_WEAPONS_GAUGE) == 1 ) {
4822                         // hud_set_bright_color();
4823                         hud_set_gauge_color(HUD_WEAPONS_GAUGE, HUD_C_BRIGHT);
4824                 } else {
4825                         hud_maybe_flash_weapon(1);
4826                 }
4827                 if ( sw->current_primary_bank == 1 || (Player_ship->flags & SF_PRIMARY_LINKED) ) {
4828                         emp_hud_printf(Weapon_plink_coords[gr_screen.res][1][0], Weapon_plink_coords[gr_screen.res][1][1], EG_NULL, "%c", Lcl_special_chars + 2);
4829                 }
4830                 emp_hud_printf(Weapon_pname_coords[gr_screen.res][1][0], Weapon_pname_coords[gr_screen.res][1][1], EG_WEAPON_P2, "%s", name);           
4831                 np = 0;
4832                 break;
4833
4834         default:
4835                 Int3(); // can't happen - get Alan
4836                 return;
4837
4838         } // end switch
4839
4840         hud_set_gauge_color(HUD_WEAPONS_GAUGE);
4841
4842         switch ( ns ) {
4843         case 0:
4844                 // draw the bottom of the secondary weapons
4845                 GR_AABITMAP(Weapon_gauges[4].first_frame, Weapon_gauge_secondary_coords[gr_screen.res][0][0], Weapon_gauge_secondary_coords[gr_screen.res][0][1] - 12*np - 1);          
4846
4847                 emp_hud_string(Weapon_pname_coords[gr_screen.res][0][0], Weapon_secondary_y[gr_screen.res][0] - np*12, EG_WEAPON_S1, XSTR( "<none>", 329));             
4848                 break;
4849
4850         case 1:
4851                 // draw the bottom of the secondary weapons
4852                 GR_AABITMAP(Weapon_gauges[4].first_frame, Weapon_gauge_secondary_coords[gr_screen.res][1][0], Weapon_gauge_secondary_coords[gr_screen.res][1][1] - 12*np - 1);          
4853
4854                 hud_show_secondary_weapon(1, sw, Player_ship->flags & SF_SECONDARY_DUAL_FIRE);
4855                 break;
4856
4857         case 2:
4858                 // draw the middle border, only present when there are 2 or more secondaries
4859                 GR_AABITMAP(Weapon_gauges[3].first_frame, Weapon_gauge_secondary_coords[gr_screen.res][2][0], Weapon_gauge_secondary_coords[gr_screen.res][2][1] - np*12);              
4860
4861                 // draw the bottom of the secondary weapons
4862                 GR_AABITMAP(Weapon_gauges[4].first_frame, Weapon_gauge_secondary_coords[gr_screen.res][3][0], Weapon_gauge_secondary_coords[gr_screen.res][3][1] - 12*np);              
4863
4864                 hud_show_secondary_weapon(2, sw, Player_ship->flags & SF_SECONDARY_DUAL_FIRE);
4865                 break;
4866
4867         case 3:
4868                 // draw the middle border, only present when there are 2 or more secondaries
4869                 GR_AABITMAP(Weapon_gauges[3].first_frame, Weapon_gauge_secondary_coords[gr_screen.res][2][0], Weapon_gauge_secondary_coords[gr_screen.res][2][1] - np*12);              
4870
4871                 // draw the bottm border, only present when there are 3 secondaries
4872                 GR_AABITMAP(Weapon_gauges[3].first_frame, Weapon_gauge_secondary_coords[gr_screen.res][3][0], Weapon_gauge_secondary_coords[gr_screen.res][3][1] - np*12);              
4873
4874                 // draw the bottom of the secondary weapons
4875                 GR_AABITMAP(Weapon_gauges[4].first_frame, Weapon_gauge_secondary_coords[gr_screen.res][4][0], Weapon_gauge_secondary_coords[gr_screen.res][4][1] - 12*np);              
4876
4877                 hud_show_secondary_weapon(3, sw, Player_ship->flags & SF_SECONDARY_DUAL_FIRE);
4878                 break;
4879
4880         default:
4881                 Int3(); // can't happen - get Alan
4882                 return;
4883
4884         } // end switch
4885 }
4886
4887 // check if targeting is possible based on sensors strength
4888 int hud_sensors_ok(ship *sp, int show_msg)
4889 {
4890         float   sensors_str;
4891
4892         // If playing on lowest skill level, sensors don't affect targeting
4893         // If dead, still allow player to target, despite any subsystem damage
4894         // If i'm a multiplayer observer, allow me to target
4895         if ( (Game_skill_level == 0) || (Game_mode & GM_DEAD) || ((Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_OBSERVER)) ) {
4896                 return 1;
4897         }
4898
4899         // if the ship is currently being affected by EMP
4900         if(emp_active_local()){
4901                 return 0;
4902         }
4903
4904         // ensure targeting functions are not disabled through damage
4905         sensors_str = ship_get_subsystem_strength( sp, SUBSYSTEM_SENSORS );
4906         if ( (sensors_str < MIN_SENSOR_STR_TO_TARGET) || (ship_subsys_disrupted(sp, SUBSYSTEM_SENSORS)) ) {
4907                 if ( show_msg ) {
4908                         HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Targeting is disabled due to sensors damage", 330));
4909                         snd_play(&Snds[SND_TARGET_FAIL]);
4910                 }
4911                 return 0;
4912         } else {
4913                 return 1;
4914         }
4915 }
4916
4917 int hud_communications_state(ship *sp, int show_msg)
4918 {
4919         float str;
4920         int     comm_state = COMM_OK;
4921
4922         // If playing on the lowest skill level, communications always ok
4923         // If dead, still allow player to communicate, despite any subsystem damage
4924         if ( Game_skill_level == 0 || (Game_mode & GM_DEAD) ) {
4925                 return comm_state;
4926         }
4927
4928         str = ship_get_subsystem_strength( sp, SUBSYSTEM_COMMUNICATION );
4929 //      str = 1.0f; // DEBUG CODE! MK, change, 11/12/97, comm system could be taken out by one laser, too frustrating.
4930                                         //      Change this back when comm systems have been better placed.
4931         
4932         if ( (str <= 0.01) || ship_subsys_disrupted(sp, SUBSYSTEM_COMMUNICATION) ) {
4933                 if ( show_msg ) {
4934                         HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Messaging is restricted due to communications damage", 331));
4935                 }
4936                 comm_state = COMM_DESTROYED;
4937         } else if ( str < MIN_COMM_STR_TO_MESSAGE ) {
4938                 comm_state = COMM_DAMAGED;
4939         }
4940
4941         return comm_state;
4942 }
4943
4944 // target the next or previous hostile/friendly ship
4945 void hud_target_next_list(int hostile, int next_flag)
4946 {
4947         object  *A, *min_obj, *max_obj, *nearest_obj;
4948         ship            *shipp;
4949         ship_obj        *so;
4950 //      vector  target_vec;
4951         float           cur_dist, min_dist, max_dist, new_dist, nearest_dist, diff;     
4952         int             timestamp_val, valid_team;
4953
4954         if ( hostile ) {
4955                 timestamp_val = Tl_hostile_reset_timestamp;
4956                 Tl_hostile_reset_timestamp = timestamp(TL_RESET);
4957                 valid_team = opposing_team_mask(Player_ship->team);
4958         } else {
4959                 timestamp_val = Tl_friendly_reset_timestamp;
4960                 Tl_friendly_reset_timestamp = timestamp(TL_RESET);
4961                 valid_team = Player_ship->team;
4962         }
4963
4964         // If no target is selected, then simply target the closest ship
4965         if ( Player_ai->target_objnum == -1 || timestamp_elapsed(timestamp_val) ) {
4966                 hud_target_closest(valid_team);
4967                 return;
4968         }
4969
4970         cur_dist = hud_find_target_distance(&Objects[Player_ai->target_objnum],Player_obj);
4971
4972         min_obj = max_obj = nearest_obj = NULL;
4973         min_dist = 1e20f;
4974         max_dist = 0.0f;
4975         if ( next_flag ) {
4976                 nearest_dist = 1e20f;
4977         } else {
4978                 nearest_dist = 0.0f;
4979         }
4980
4981         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
4982                 A = &Objects[so->objnum];
4983                 shipp = &Ships[A->instance];    // get a pointer to the ship information
4984
4985                 if ( (A == Player_obj) || (shipp->flags & TARGET_SHIP_IGNORE_FLAGS) )
4986                         continue;
4987
4988                 // choose from the correct team
4989                 if ( !hud_team_matches_filter(valid_team, shipp->team) ) {
4990                         // if we're in multiplayer dogfight, ignore this
4991                         if(!((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_DOGFIGHT))){
4992                                 continue;
4993                         }
4994                 }
4995
4996                 // always ignore navbuoys and cargo
4997                 if ( Ship_info[shipp->ship_info_index].flags & (SIF_CARGO | SIF_NAVBUOY) ) {
4998                         continue;
4999                 }
5000
5001                 // don't use object if it is already a target
5002                 if ( OBJ_INDEX(A) == Player_ai->target_objnum ) {
5003                         continue;
5004                 }
5005
5006                 if(hud_target_invalid_awacs(A)){
5007                         continue;
5008                 }
5009
5010                 new_dist = hud_find_target_distance(A,Player_obj);
5011                         
5012                 if (new_dist <= min_dist) {
5013                         min_dist = new_dist;
5014                         min_obj = A;
5015                 }
5016
5017                 if (new_dist >= max_dist) {
5018                         max_dist = new_dist;
5019                         max_obj = A;
5020                 }
5021
5022                 if ( next_flag ) {
5023                         diff = new_dist - cur_dist;
5024                         if ( diff > 0 ) {
5025                                 if ( diff < ( nearest_dist - cur_dist ) ) {
5026                                         nearest_dist = new_dist;
5027                                         nearest_obj = A;
5028                                 }
5029                         }
5030                 } else {
5031                         diff = cur_dist - new_dist;
5032                         if ( diff > 0 ) {
5033                                 if ( diff < ( cur_dist - nearest_dist ) ) {
5034                                         nearest_dist = new_dist;
5035                                         nearest_obj = A;
5036                                 }
5037                         }
5038                 }
5039         }
5040
5041         if ( nearest_obj == NULL ) {
5042
5043                 if ( next_flag ) {
5044                         if ( min_obj != NULL ) {
5045                                 nearest_obj = min_obj;
5046                         }
5047                 } else {
5048                         if ( max_obj != NULL ) {
5049                                 nearest_obj = max_obj;
5050                         }
5051                 }
5052         }
5053
5054         if (nearest_obj != NULL) {
5055                 // set new target
5056                 set_target_objnum( Player_ai, OBJ_INDEX(nearest_obj) );
5057
5058                 // maybe set new turret subsystem
5059                 hud_maybe_set_sorted_turret_subsys(&Ships[nearest_obj->instance]);
5060                 hud_restore_subsystem_target(&Ships[nearest_obj->instance]);
5061         }
5062         else {
5063                 snd_play( &Snds[SND_TARGET_FAIL], 0.0f );
5064         }
5065 }
5066
5067 // draw auto-target icon
5068 void hud_auto_target_icon()
5069 {
5070         int frame_offset;
5071
5072         if ( Players[Player_num].flags & PLAYER_FLAGS_AUTO_TARGETING ) {
5073                 frame_offset = 1;
5074         } else {
5075                 frame_offset = 0;
5076         }
5077
5078         // draw the box background
5079         hud_set_gauge_color(HUD_AUTO_TARGET);
5080         GR_AABITMAP(Toggle_gauge.first_frame+frame_offset, Toggle_target_gauge_coords[gr_screen.res][0], Toggle_target_gauge_coords[gr_screen.res][1]); 
5081
5082         // draw the text on top
5083         if (frame_offset == 1) {
5084                 color text_color;
5085                 gr_init_alphacolor(&text_color, 0, 0, 0, Toggle_text_alpha);
5086                 gr_set_color_fast(&text_color);
5087         
5088         }
5089         gr_string(Hud_toggle_coords[gr_screen.res][TOGGLE_TEXT_AUTOT][0], Hud_toggle_coords[gr_screen.res][TOGGLE_TEXT_AUTOT][1], XSTR("auto", 1463));
5090         gr_string(Hud_toggle_coords[gr_screen.res][TOGGLE_TEXT_TARGET][0], Hud_toggle_coords[gr_screen.res][TOGGLE_TEXT_TARGET][1], XSTR("target", 1465));
5091 }
5092
5093 // draw auto-speed match icon
5094 void hud_auto_speed_match_icon()
5095 {
5096         int frame_offset;
5097
5098         if ( Players[Player_num].flags & PLAYER_FLAGS_AUTO_MATCH_SPEED ) {
5099                 frame_offset = 3;
5100         } else {
5101                 frame_offset = 2;
5102         }
5103
5104         hud_set_gauge_color(HUD_AUTO_SPEED);
5105
5106         GR_AABITMAP(Toggle_gauge.first_frame+frame_offset, Toggle_speed_gauge_coords[gr_screen.res][0], Toggle_speed_gauge_coords[gr_screen.res][1]);   
5107
5108         // draw the text on top
5109         if (frame_offset == 3) {
5110                 color text_color;
5111                 gr_init_alphacolor(&text_color, 0, 0, 0, Toggle_text_alpha);
5112                 gr_set_color_fast(&text_color);
5113         }
5114         gr_string(Hud_toggle_coords[gr_screen.res][TOGGLE_TEXT_AUTOS][0], Hud_toggle_coords[gr_screen.res][TOGGLE_TEXT_AUTOS][1], XSTR("auto", 1463));
5115         gr_string(Hud_toggle_coords[gr_screen.res][TOGGLE_TEXT_SPEED][0], Hud_toggle_coords[gr_screen.res][TOGGLE_TEXT_SPEED][1], XSTR("speed", 1464));
5116 }
5117
5118 // display the auto-targeting and auto-speed-matching icons on the HUD
5119 void hud_show_auto_icons()
5120 {
5121         int show_flag;
5122         if ( Toggle_gauge.first_frame == -1 ) 
5123                 return;
5124
5125         // display auto target icon
5126         if ( hud_gauge_active(HUD_AUTO_TARGET) ) {
5127                 show_flag=1;
5128                 // is gauge configured as a popup?
5129                 if ( hud_gauge_is_popup(HUD_AUTO_TARGET) ) {
5130                         if ( !hud_gauge_popup_active(HUD_AUTO_TARGET) ) {
5131                                 show_flag=0;
5132                         }
5133                 }
5134                 
5135                 if ( show_flag ) {
5136                         hud_auto_target_icon();
5137                 }
5138         }
5139
5140         // display auto speed match icon
5141         if ( hud_gauge_active(HUD_AUTO_SPEED) ) {
5142                 show_flag=1;
5143                 // is gauge configured as a popup?
5144                 if ( hud_gauge_is_popup(HUD_AUTO_SPEED) ) {
5145                         if ( !hud_gauge_popup_active(HUD_AUTO_SPEED) ) {
5146                                 show_flag=0;
5147                         }
5148                 }
5149                 
5150                 if ( show_flag ) {
5151                         hud_auto_speed_match_icon();
5152                 }
5153         }
5154 }
5155
5156 // Set the player target to the closest friendly repair ship
5157 // input:       goal_objnum     =>      Try to find repair ship where aip->goal_objnum matches this
5158 // output:      1       =>      A repair ship was targeted
5159 //                              0       =>      No targeting change
5160 int hud_target_closest_repair_ship(int goal_objnum)
5161 {
5162         object  *A;
5163         object  *nearest_obj=&obj_used_list;
5164         ship            *shipp;
5165         ship_obj        *so;
5166         float           min_distance=1e20f;
5167         float           new_distance=0.0f;
5168         int             rval=0;
5169
5170         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5171                 A = &Objects[so->objnum];
5172                 shipp = &Ships[A->instance];    // get a pointer to the ship information
5173
5174                 // ignore all ships that aren't repair ships 
5175                 if ( !(Ship_info[shipp->ship_info_index].flags & SIF_SUPPORT) ) {
5176                         continue;
5177                 }
5178
5179                 if ( (A == Player_obj) || (shipp->flags & TARGET_SHIP_IGNORE_FLAGS) )
5180                         continue;
5181
5182                 // only consider friendly ships
5183                 if ( !hud_team_matches_filter(Player_ship->team, shipp->team)) {
5184                         // if we're in multiplayer dogfight, ignore this
5185                         if(!((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_DOGFIGHT))){
5186                                 continue;
5187                         }
5188                 }
5189
5190                 if(hud_target_invalid_awacs(A)){
5191                         continue;
5192                 }
5193
5194                 if ( goal_objnum >= 0 ) {
5195                         if ( Ai_info[shipp->ai_index].goal_objnum != goal_objnum ) {
5196                                 continue;
5197                         }
5198                 }
5199
5200                 new_distance = hud_find_target_distance(A,Player_obj);
5201                         
5202                 if (new_distance <= min_distance) {
5203                         min_distance=new_distance;
5204                         nearest_obj = A;
5205                 }
5206         }
5207
5208         if (nearest_obj != &obj_used_list) {
5209                 set_target_objnum( Player_ai, OBJ_INDEX(nearest_obj) );
5210                 hud_restore_subsystem_target(&Ships[nearest_obj->instance]);
5211                 rval=1;
5212         }
5213         else {
5214                 // inform player how to get a support ship
5215                 if ( goal_objnum == -1 ) {
5216                         HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "No support ships in area.  Use messaging to call one in.", 332));
5217                 }
5218                 rval=0;
5219         }
5220
5221         return rval;
5222 }
5223
5224 void hud_target_toggle_hidden_from_sensors()
5225 {
5226         if ( TARGET_SHIP_IGNORE_FLAGS & SF_HIDDEN_FROM_SENSORS ) {
5227                 TARGET_SHIP_IGNORE_FLAGS &= ~SF_HIDDEN_FROM_SENSORS;
5228                 HUD_sourced_printf(HUD_SOURCE_HIDDEN, NOX("Target hiding from sensors disabled"));
5229         } else {
5230                 TARGET_SHIP_IGNORE_FLAGS |= SF_HIDDEN_FROM_SENSORS;
5231                 HUD_sourced_printf(HUD_SOURCE_HIDDEN, NOX("Target hiding from sensors enabled"));
5232         }
5233 }
5234
5235 // target the closest uninspected object
5236 void hud_target_closest_uninspected_object()
5237 {
5238         object  *A, *nearest_obj = NULL;
5239         ship            *shipp;
5240         ship_obj        *so;
5241         float           min_distance = 1e20f;
5242         float           new_distance = 0.0f;
5243
5244         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5245
5246                 A = &Objects[so->objnum];
5247                 shipp = &Ships[A->instance];    // get a pointer to the ship information
5248
5249                 if ( (A == Player_obj) || (shipp->flags & TARGET_SHIP_IGNORE_FLAGS) ){
5250                         continue;
5251                 }
5252
5253                 if(hud_target_invalid_awacs(A)){
5254                         continue;
5255                 }
5256
5257                 // ignore all non-cargo carrying craft
5258                 if ( !hud_target_ship_can_be_scanned(shipp) ) {
5259                         continue;
5260                 }
5261
5262                 new_distance = hud_find_target_distance(A,Player_obj);
5263                         
5264                 if (new_distance <= min_distance) {
5265                         min_distance=new_distance;
5266                         nearest_obj = A;
5267                 }
5268         }
5269
5270         if (nearest_obj != NULL) {
5271                 set_target_objnum( Player_ai, OBJ_INDEX(nearest_obj) );
5272                 hud_restore_subsystem_target(&Ships[nearest_obj->instance]);
5273         }
5274         else {
5275                 snd_play( &Snds[SND_TARGET_FAIL] );
5276         }
5277 }
5278
5279 // target the next or previous uninspected/unscanned object
5280 void hud_target_uninspected_object(int next_flag)
5281 {
5282         object  *A, *min_obj, *max_obj, *nearest_obj;
5283         ship            *shipp;
5284         ship_obj        *so;
5285         float           cur_dist, min_dist, max_dist, new_dist, nearest_dist, diff;     
5286
5287         // If no target is selected, then simply target the closest uninspected cargo
5288         if ( Player_ai->target_objnum == -1 || timestamp_elapsed(Target_next_uninspected_object_timestamp) ) {
5289                 Target_next_uninspected_object_timestamp = timestamp(TL_RESET);
5290                 hud_target_closest_uninspected_object();
5291                 return;
5292         }
5293
5294         Target_next_uninspected_object_timestamp = timestamp(TL_RESET);
5295
5296         cur_dist = hud_find_target_distance(&Objects[Player_ai->target_objnum], Player_obj);
5297
5298         min_obj = max_obj = nearest_obj = NULL;
5299         min_dist = 1e20f;
5300         max_dist = 0.0f;
5301         if ( next_flag ) {
5302                 nearest_dist = 1e20f;
5303         } else {
5304                 nearest_dist = 0.0f;
5305         }
5306
5307         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5308                 A = &Objects[so->objnum];
5309                 shipp = &Ships[A->instance];    // get a pointer to the ship information
5310
5311                 if ( (A == Player_obj) || (shipp->flags & TARGET_SHIP_IGNORE_FLAGS) )
5312                         continue;
5313
5314                 // ignore all non-cargo carrying craft
5315                 if ( !hud_target_ship_can_be_scanned(shipp) ) {
5316                         continue;
5317                 }
5318
5319                 // don't use object if it is already a target
5320                 if ( OBJ_INDEX(A) == Player_ai->target_objnum ) {
5321                         continue;
5322                 }
5323
5324                 if(hud_target_invalid_awacs(A)){
5325                         continue;
5326                 }
5327
5328                 new_dist = hud_find_target_distance(A, Player_obj);
5329                         
5330                 if (new_dist <= min_dist) {
5331                         min_dist = new_dist;
5332                         min_obj = A;
5333                 }
5334
5335                 if (new_dist >= max_dist) {
5336                         max_dist = new_dist;
5337                         max_obj = A;
5338                 }
5339
5340                 if ( next_flag ) {
5341                         diff = new_dist - cur_dist;
5342                         if ( diff > 0 ) {
5343                                 if ( diff < ( nearest_dist - cur_dist ) ) {
5344                                         nearest_dist = new_dist;
5345                                         nearest_obj = A;
5346                                 }
5347                         }
5348                 } else {
5349                         diff = cur_dist - new_dist;
5350                         if ( diff > 0 ) {
5351                                 if ( diff < ( cur_dist - nearest_dist ) ) {
5352                                         nearest_dist = new_dist;
5353                                         nearest_obj = A;
5354                                 }
5355                         }
5356                 }
5357         }
5358
5359         if ( nearest_obj == NULL ) {
5360
5361                 if ( next_flag ) {
5362                         if ( min_obj != NULL ) {
5363                                 nearest_obj = min_obj;
5364                         }
5365                 } else {
5366                         if ( max_obj != NULL ) {
5367                                 nearest_obj = max_obj;
5368                         }
5369                 }
5370         }
5371
5372         if (nearest_obj != NULL) {
5373                 set_target_objnum( Player_ai, OBJ_INDEX(nearest_obj) );
5374                 hud_restore_subsystem_target(&Ships[nearest_obj->instance]);
5375         }
5376         else {
5377                 snd_play( &Snds[SND_TARGET_FAIL] );
5378         }
5379 }
5380
5381 // ----------------------------------------------------------------
5382 //
5383 // Target Last Transmission Sender code START
5384 //
5385 // ----------------------------------------------------------------
5386
5387 typedef struct transmit_target
5388 {
5389         int objnum;
5390         int objsig;
5391 } transmit_target;
5392
5393 static int Transmit_target_next_slot = 0;
5394 static int Transmit_target_current_slot = -1;
5395 static int Transmit_target_reset_timer = timestamp(0);
5396
5397 #define MAX_TRANSMIT_TARGETS    10
5398 static transmit_target Transmit_target_list[MAX_TRANSMIT_TARGETS];
5399
5400 // called once per level to initialize the target last transmission sender list
5401 void hud_target_last_transmit_level_init()
5402 {
5403         int i;
5404
5405         for ( i = 0; i < MAX_TRANSMIT_TARGETS; i++ ) {
5406                 Transmit_target_list[i].objnum = -1;
5407                 Transmit_target_list[i].objsig = -1;
5408         }
5409
5410         Transmit_target_next_slot = 0;
5411         Transmit_target_current_slot = 0;
5412         Transmit_target_reset_timer = timestamp(0);
5413 }
5414
5415 // internal function only.. used to find index for last recorded ship transmission 
5416 int hud_target_last_transmit_newest()
5417 {
5418         int latest_slot;
5419
5420         latest_slot = Transmit_target_next_slot - 1;
5421         if ( latest_slot < 0 ) {
5422                 latest_slot = MAX_TRANSMIT_TARGETS - 1;
5423         }
5424
5425         return latest_slot;
5426 }
5427
5428 // called externally to set the player target to the last ship which sent a tranmission to the player
5429 void hud_target_last_transmit()
5430 {
5431         int i;
5432
5433         if ( Transmit_target_current_slot < 0 ) {
5434                 Transmit_target_current_slot = hud_target_last_transmit_newest();
5435         }
5436
5437         // If timed out, then simply target the last ship to transmit
5438         if ( timestamp_elapsed(Transmit_target_reset_timer) ) {
5439                 Transmit_target_current_slot = hud_target_last_transmit_newest();
5440         }
5441
5442         Transmit_target_reset_timer = timestamp(TL_RESET);
5443
5444         int play_fail_sound = 1;
5445         int transmit_index = Transmit_target_current_slot;
5446         Assert(transmit_index >= 0);
5447         for ( i = 0; i < MAX_TRANSMIT_TARGETS; i++ ) {
5448                 if ( Transmit_target_list[transmit_index].objnum >= 0 ) {
5449                         int transmit_objnum = Transmit_target_list[transmit_index].objnum;
5450                         
5451                         if ( Player_ai->target_objnum == transmit_objnum ) {
5452                                 play_fail_sound = 0;
5453                         } else {
5454                                 if ( Transmit_target_list[transmit_index].objsig == Objects[Transmit_target_list[transmit_index].objnum].signature ) {
5455                                         if ( !(Ships[Objects[transmit_objnum].instance].flags & TARGET_SHIP_IGNORE_FLAGS) ) {
5456                                                 Transmit_target_current_slot = transmit_index-1;
5457                                                 if ( Transmit_target_current_slot < 0 ) {
5458                                                         Transmit_target_current_slot = MAX_TRANSMIT_TARGETS - 1;
5459                                                 }
5460                                                 break;
5461                                         }
5462                                 }
5463                         }
5464                 }
5465
5466                 transmit_index--;
5467                 if ( transmit_index < 0 ) {
5468                         transmit_index = MAX_TRANSMIT_TARGETS - 1;
5469                 }
5470         }
5471
5472         if ( i == MAX_TRANSMIT_TARGETS ) {
5473                 if ( play_fail_sound ) {
5474                         snd_play( &Snds[SND_TARGET_FAIL] );
5475                 }
5476                 Transmit_target_current_slot = -1;
5477                 return;
5478         }
5479
5480         if(hud_target_invalid_awacs(&Objects[Transmit_target_list[transmit_index].objnum])){
5481                 return;
5482         }
5483
5484         // target new ship!
5485         // Fix bug in targeting due to Alt-Y (target last ship sending transmission).
5486         // Was just bogus code in the call to hud_restore_subsystem_target(). -- MK, 9/15/99, 1:59 pm.
5487         int targeted_objnum;
5488         targeted_objnum = Transmit_target_list[transmit_index].objnum;
5489         Assert((targeted_objnum >= 0) && (targeted_objnum < MAX_OBJECTS));
5490
5491         if ((targeted_objnum >= 0) && (targeted_objnum < MAX_OBJECTS)) {
5492                 set_target_objnum( Player_ai, Transmit_target_list[transmit_index].objnum );
5493                 hud_restore_subsystem_target(&Ships[Objects[Transmit_target_list[transmit_index].objnum].instance]);
5494         }
5495 }
5496
5497 // called externally to add a message sender to the list
5498 void hud_target_last_transmit_add(int ship_num)
5499 {
5500         object  *ship_objp;
5501         int             ship_objnum;
5502
5503         ship_objnum = Ships[ship_num].objnum;
5504         Assert(ship_objnum >= 0 && ship_objnum < MAX_OBJECTS);
5505         ship_objp = &Objects[ship_objnum];
5506         Assert(ship_objp->type == OBJ_SHIP);
5507
5508         Transmit_target_list[Transmit_target_next_slot].objnum = ship_objnum;
5509         Transmit_target_list[Transmit_target_next_slot].objsig = ship_objp->signature;
5510         Transmit_target_next_slot++;
5511         if ( Transmit_target_next_slot >= MAX_TRANSMIT_TARGETS ) {
5512                 Transmit_target_next_slot = 0;
5513         }
5514 }
5515
5516 // target a random ship (useful for EMP stuff)
5517 void hud_target_random_ship()
5518 {
5519         int shipnum;
5520         int objnum;
5521
5522         shipnum = ship_get_random_ship();
5523         if((shipnum < 0) || (Ships[shipnum].objnum < 0)){
5524                 return;
5525         }
5526         objnum = Ships[shipnum].objnum;
5527
5528         if((objnum >= 0) && (Player_ai != NULL) && !hud_target_invalid_awacs(&Objects[objnum])){        
5529                 // never target yourself
5530                 if(objnum == OBJ_INDEX(Player_obj)){
5531                         set_target_objnum(Player_ai, -1);
5532                 } else {
5533                         set_target_objnum(Player_ai, objnum);
5534                 }
5535         }
5536 }
5537
5538 // ----------------------------------------------------------------
5539 //
5540 // Target Last Transmission Sender code END
5541 //
5542 // ----------------------------------------------------------------
5543
5544 void hudtarget_page_in()
5545 {
5546         int i;
5547
5548         for ( i = 0; i < NUM_WEAPON_GAUGES; i++ ) {
5549                 bm_page_in_aabitmap( Weapon_gauges[i].first_frame, Weapon_gauges[i].num_frames);
5550         }
5551         bm_page_in_aabitmap( Lead_indicator_gauge.first_frame, Lead_indicator_gauge.num_frames);
5552         bm_page_in_aabitmap( Energy_bar_gauges.first_frame, Energy_bar_gauges.num_frames);
5553         bm_page_in_aabitmap( Toggle_gauge.first_frame, Toggle_gauge.num_frames);
5554         bm_page_in_aabitmap( Cmeasure_gauge.first_frame, Cmeasure_gauge.num_frames);
5555 }
5556