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