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