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