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