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