2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
4 * All source code herein is the property of Volition, Inc. You may not sell
5 * or otherwise commercially exploit the source or things you created based on
10 * $Logfile: /Freespace2/code/Hud/HUDtarget.cpp $
15 * C module to provide HUD targeting functions
18 * Revision 1.6 2004/09/20 01:31:44 theoddone33
21 * Revision 1.5 2003/05/25 02:30:42 taylor
24 * Revision 1.4 2002/06/17 06:33:09 relnev
25 * ryan's struct patch for gcc 2.95
27 * Revision 1.3 2002/06/09 04:41:21 relnev
28 * added copyright header
30 * Revision 1.2 2002/05/07 03:16:45 theoddone33
31 * The Great Newline Fix
33 * Revision 1.1.1.1 2002/05/03 03:28:09 root
37 * 43 11/01/99 11:22a Jefff
38 * some weapon name translations
40 * 42 9/15/99 1:59p Mikek
41 * Fix bug in targeting due to Alt-Y (target last ship sending
42 * transmission). Was just bogus code in the call to
43 * hud_restore_subsystem_target().pm.
45 * 41 9/08/99 11:42p Andsager
46 * Remove maximum target distance check when finding enemy target
48 * 40 9/07/99 11:26p Andsager
49 * Fix "r" targeting key, making evaluate_ship_as_closest_target() and
50 * hud_target_live_turret() consider if turret is targeting player
52 * 39 9/01/99 12:58a Andsager
53 * Make all targeting keys able to choose turret on BIG|HUGE ships
55 * 38 8/24/99 2:55p Andsager
56 * Add new prioritized turret selection code.
58 * 37 8/18/99 10:59p Andsager
59 * Enable "b" key to target bombers.
61 * 36 8/17/99 8:32p Jefff
62 * fixes to auto-speed/target so it looks much better in the nebula
64 * 35 8/17/99 7:15p Jefff
65 * auto-target & auto-speed text drawn in code
67 * 34 8/04/99 9:54a Andsager
68 * Auto target turrets on big ships.
70 * 33 8/02/99 4:03p Andsager
71 * target closest favors beams and flak
73 * 32 8/01/99 12:39p Dave
74 * Added HUD contrast control key (for nebula).
76 * 31 7/30/99 11:10a Andsager
77 * Modify hud_target_closest() to consider turrets against all ships, not
80 * 30 7/15/99 5:41p Andsager
83 * 29 7/15/99 9:20a Andsager
84 * FS2_DEMO initial checkin
86 * 28 7/09/99 12:00a Andsager
87 * Added target box with distance for remote detonate weapons
89 * 27 7/07/99 4:31p Andsager
90 * Make closest_attacking_hostile guage respect stealth. Kill a bunch of
93 * 26 7/01/99 11:44a Dave
94 * Updated object sound system to allow multiple obj sounds per ship.
95 * Added hit-by-beam sound. Added killed by beam sound.
97 * 25 6/17/99 9:04a Andsager
100 * 24 6/16/99 5:32p Andsager
103 * 23 6/15/99 9:24a Andsager
104 * Make hotkeys work with stealth
106 * 22 6/10/99 3:43p Dave
107 * Do a better job of syncing text colors to HUD gauges.
109 * 21 6/09/99 2:55p Andsager
110 * Allow multiple asteroid subtypes (of large, medium, small) and follow
113 * 20 6/08/99 8:35a Jasenw
114 * new coords for new lead indicator
116 * 19 6/07/99 4:20p Andsager
117 * Add HUD color for tagged object. Apply to target and radar.
119 * 18 6/02/99 3:23p Andsager
120 * Make AI aware of team visibility. Allow player targeting with team
121 * visibility info. Make stealth ships not targetable by AI in nebula
124 * 17 5/24/99 9:00a Andsager
125 * modify target next/prev live turret only when turret has a weapon
127 * 16 4/20/99 6:39p Dave
128 * Almost done with artillery targeting. Added support for downloading
129 * images on the PXO screen.
131 * 15 2/25/99 4:19p Dave
132 * Added multiplayer_beta defines. Added cd_check define. Fixed a few
133 * release build warnings. Added more data to the squad war request and
136 * 14 1/27/99 3:02p Anoop
137 * Fixed 640 HUD afterburner and weapon energy gauge.
139 * 13 1/25/99 5:03a Dave
140 * First run of stealth, AWACS and TAG missile support. New mission type
143 * 12 1/07/99 10:07a Jasen
146 * 11 1/07/99 9:06a Jasen
149 * 10 1/06/99 2:24p Dave
150 * Stubs and release build fixes.
152 * 9 12/29/98 7:21p Dave
153 * Put in a bunch of missing hi-res coord globalizations.
155 * 8 12/29/98 2:29p Jasen
156 * added new coords for some 1024 HUD stuff.
158 * 7 12/28/98 3:17p Dave
159 * Support for multiple hud bitmap filenames for hi-res mode.
161 * 5 12/21/98 5:03p Dave
162 * Modified all hud elements to be multi-resolution friendly.
164 * 4 11/05/98 4:18p Dave
165 * First run nebula support. Beefed up localization a bit. Removed all
166 * conditional compiles for foreign versions. Modified mission file
169 * 3 10/13/98 9:28a Dave
170 * Started neatening up freespace.h. Many variables renamed and
171 * reorganized. Added AlphaColors.[h,cpp]
173 * 2 10/07/98 10:53a Dave
176 * 1 10/07/98 10:49a Dave
178 * 407 9/21/98 9:27a Dave
179 * Special case code to draw Cluster Bomb as Cluster on the HUD.
181 * 406 8/28/98 3:28p Dave
182 * EMP effect done. AI effects may need some tweaking as required.
184 * 405 8/25/98 1:48p Dave
185 * First rev of EMP effect. Player side stuff basically done. Next comes
188 * 404 6/12/98 4:52p Hoffoss
189 * Added support for special characters in in forgeign languages.
191 * 403 6/09/98 5:17p Lawrance
192 * French/German localization
194 * 402 6/09/98 10:31a Hoffoss
195 * Created index numbers for all xstr() references. Any new xstr() stuff
196 * added from here on out should be added to the end if the list. The
197 * current list count can be found in FreeSpace.cpp (search for
200 * 401 5/27/98 1:24p Allender
201 * make targeting dots work (as well as other targeting features) properly
202 * in multiplayer. Don't query for CD when entering debrief in
205 * 400 5/27/98 1:20p Mike
206 * Fix bug in target nearest ship attacking target.
212 #include "hudtarget.h"
213 #include "hudreticle.h"
218 #include "3dinternal.h"
220 #include "linklist.h"
225 #include "freespace.h" // for flFrametime
229 #include "missionparse.h"
230 #include "player.h" // for MAX_PLAYERS
232 #include "hudbrackets.h"
234 #include "eventmusic.h"
236 #include "missionmessage.h"
239 #include "hudtargetbox.h"
241 #include "subsysdamage.h"
242 #include "hudshield.h"
243 #include "missionhotkey.h"
244 #include "asteroid.h"
245 #include "jumpnode.h"
248 #include "alphacolors.h"
249 #include "localize.h"
251 #include "hudartillery.h"
253 // If any of these bits in the ship->flags are set, ignore this ship when targetting
254 int TARGET_SHIP_IGNORE_FLAGS = (SF_EXPLODED|SF_DEPART_WARP|SF_DYING|SF_ARRIVING_STAGE_1|SF_HIDDEN_FROM_SENSORS);
256 // Global values for the target bracket width and height, used for debugging
257 int Hud_target_w, Hud_target_h;
259 // offscreen triangle that point the the off-screen target
260 float Offscreen_tri_base[GR_NUM_RESOLUTIONS] = {
264 float Offscreen_tri_height[GR_NUM_RESOLUTIONS] = {
268 float Max_offscreen_tri_seperation[GR_NUM_RESOLUTIONS] = {
272 float Max_front_seperation[GR_NUM_RESOLUTIONS] = {
277 // The following variables are global to this file, and do not need to be persistent from frame-to-frame
278 // This means the variables are not player-specific
279 static int Target_in_reticle = 0;
281 extern object obj_used_list; // dummy node in linked list of active objects
282 extern char *Cargo_names[];
284 // shader is used to shade the target box
285 shader Training_msg_glass;
287 // the target triangle (that orbits the reticle) dimensions
288 float Target_triangle_base[GR_NUM_RESOLUTIONS] = {
292 float Target_triangle_height[GR_NUM_RESOLUTIONS] = {
297 // stuff for hotkey targeting lists
298 htarget_list htarget_items[MAX_HOTKEY_TARGET_ITEMS];
299 htarget_list htarget_free_list;
301 // coordinates and widths used to render the HUD afterburner energy gauge
302 int Aburn_coords[GR_NUM_RESOLUTIONS][4] = {
311 // coordinates and widths used to render the HUD weapons energy gauge
312 int Wenergy_coords[GR_NUM_RESOLUTIONS][4] = {
321 #define MIN_DISTANCE_TO_CONSIDER_THREAT 1500 // min distance to show hostile warning triangle
323 //////////////////////////////////////////////////////////////////////////
324 // lists for target in reticle cycling
325 //////////////////////////////////////////////////////////////////////////
326 #define RL_USED (1<<0)
327 #define RL_USE_DOT (1<<1) // use dot product result, not distance
329 typedef struct _reticle_list {
330 _reticle_list *next, *prev;
336 #define RESET_TARGET_IN_RETICLE 750
337 int Reticle_save_timestamp;
338 reticle_list Reticle_cur_list;
339 reticle_list Reticle_save_list;
340 #define MAX_RETICLE_TARGETS 50
341 reticle_list Reticle_list[MAX_RETICLE_TARGETS];
343 //////////////////////////////////////////////////////////////////////////
344 // used for closest target cycling
345 //////////////////////////////////////////////////////////////////////////
346 #define TL_RESET 1500
347 #define TURRET_RESET 1000
348 static int Tl_hostile_reset_timestamp;
349 static int Tl_friendly_reset_timestamp;
350 static int Target_next_uninspected_object_timestamp;
351 static int Target_newest_ship_timestamp;
352 static int Target_next_turret_timestamp;
354 // animation frames for the hud targeting gauges
355 // frames: 0 => out of range lead
356 // 1 => in range lead
357 float Lead_indicator_half[GR_NUM_RESOLUTIONS][2] = {
367 hud_frames Lead_indicator_gauge;
368 int Lead_indicator_gauge_loaded = 0;
369 const char *Lead_fname[GR_NUM_RESOLUTIONS] = {
374 // animation frames for the afterburner gauge and the weapon energy gauge
375 // frames: 0 => afterburner dark
376 // 1 => afterburner light
377 // 2 => gun energy dark
378 // 3 => gun energy light
379 hud_frames Energy_bar_gauges;
380 int Energy_bar_gauges_loaded = 0;
381 const char *Energy_fname[GR_NUM_RESOLUTIONS] = {
385 int Weapon_energy_text_coords[GR_NUM_RESOLUTIONS][2] = {
394 // animation frames for the countermeasures gauge
395 // frames: 0 => background
396 hud_frames Cmeasure_gauge;
397 int Cmeasure_gauge_loaded = 0;
398 int Cm_coords[GR_NUM_RESOLUTIONS][2] = {
406 int Cm_text_coords[GR_NUM_RESOLUTIONS][2] = {
414 int Cm_text_val_coords[GR_NUM_RESOLUTIONS][2] = {
422 const char *Cm_fname[GR_NUM_RESOLUTIONS] = {
427 // animation frames for the auto-target and auto-match_speed icons
428 // frames: 0 => auto-target off
429 // 1 => auto-target on
430 // 2 => auto-match-speed on
431 // 3 => auto-match-speed off
432 hud_frames Toggle_gauge;
433 int Toggle_gauge_loaded = 0;
434 int Toggle_target_gauge_coords[GR_NUM_RESOLUTIONS][2] = {
442 int Toggle_speed_gauge_coords[GR_NUM_RESOLUTIONS][2] = {
450 const char *Toggle_fname[GR_NUM_RESOLUTIONS] = {
455 #define TOGGLE_TEXT_AUTOT 0
456 #define TOGGLE_TEXT_TARGET 1
457 #define TOGGLE_TEXT_AUTOS 2
458 #define TOGGLE_TEXT_SPEED 3
460 static int Hud_toggle_coords[GR_NUM_RESOLUTIONS][4][2] = {
476 static int Toggle_text_alpha = 255;
479 // animation files for the weapons gauge
480 #define NUM_WEAPON_GAUGES 5
481 hud_frames Weapon_gauges[NUM_WEAPON_GAUGES];
482 int Weapon_gauges_loaded = 0;
484 int Weapon_gauge_primary_coords[GR_NUM_RESOLUTIONS][3][2] = {
486 // based on the # of primaries
487 {509, 273}, // top of weapon gauge, first frame, always
488 {497, 293}, // for the first primary
489 {497, 305} // for the second primary
492 // based on the # of primaries
493 {892, 525}, // top of weapon gauge, first frame, always
494 {880, 545}, // for the first primary
495 {880, 557} // for the second primary
498 int Weapon_gauge_secondary_coords[GR_NUM_RESOLUTIONS][5][2] = {
500 // based on the # of secondaries
501 {497, 318}, // bottom of gauge, 0 secondaries
502 {497, 318}, // bottom of gauge, 1 secondaries
503 {497, 317}, // middle of gauge, 2 secondaries AND middle of gauge, 3 secondaries
504 {497, 326}, // bottom of gauge, 2 secondaries AND middle of gauge, 3 secondaries
505 {497, 335} // bottom of gauge, 3 secondaries
508 // based on the # of secondaries
509 {880, 570}, // bottom of gauge, 0 secondaries
510 {880, 570}, // bottom of gauge, 1 secondaries
511 {880, 569}, // middle of gauge, 2 secondaries AND middle of gauge, 3 secondaries
512 {880, 578}, // bottom of gauge, 2 secondaries AND middle of gauge, 3 secondaries
513 {880, 587} // bottom of gauge, 3 secondaries
516 int Weapon_title_coords[GR_NUM_RESOLUTIONS][2] = {
524 int Weapon_plink_coords[GR_NUM_RESOLUTIONS][2][2] = {
526 {530, 285}, // fire-linked thingie, for the first primary
527 {530, 295} // fire-linked thingie, for the second primary
530 {913, 537}, // fire-linked thingie, for the first primary
531 {913, 547} // fire-linked thingie, for the second primary
534 int Weapon_pname_coords[GR_NUM_RESOLUTIONS][2][2] = {
536 {536, 285}, // weapon name, first primary
537 {536, 295} // weapon name, second primary
540 {919, 537}, // weapon name, first primary
541 {919, 547} // weapon name, second primary
544 int Weapon_slinked_x[GR_NUM_RESOLUTIONS] = {
545 525, // where to draw the second thingie if this weapon is fire-linked
548 int Weapon_sunlinked_x[GR_NUM_RESOLUTIONS] = {
549 530, // where to draw the first thingie if this weapon is selected at all (fire-linked or not)
552 int Weapon_secondary_y[GR_NUM_RESOLUTIONS][3] = {
554 309, // y location of where to draw text for the first secondary
555 318, // y location of where to draw text for the second secondary
556 327 // y location of where to draw text for the third secondary
559 561, // y location of where to draw text for the third secondary
560 570, // y location of where to draw text for the third secondary
561 579 // y location of where to draw text for the third secondary
564 int Weapon_secondary_name_x[GR_NUM_RESOLUTIONS] = {
565 536, // x location of where to draw weapon name
568 int Weapon_secondary_ammo_x[GR_NUM_RESOLUTIONS] = {
569 525, // x location of where to draw weapon ammo count
572 int Weapon_secondary_reload_x[GR_NUM_RESOLUTIONS] = {
573 615, // x location of where to draw the weapon reload time
576 const char *Weapon_gauge_fnames[GR_NUM_RESOLUTIONS][NUM_WEAPON_GAUGES] =
596 // Flash the line for a weapon. This normally occurs when the player tries to fire that
597 // weapon, but the firing fails (due to lack of energy or damaged weapons subsystem).
598 #define MAX_WEAPON_FLASH_LINES 7 // 3 primary and 4 secondary
599 typedef struct weapon_flash
601 int flash_duration[MAX_WEAPON_FLASH_LINES];
602 int flash_next[MAX_WEAPON_FLASH_LINES];
605 weapon_flash Weapon_flash_info;
607 // Data used for the proximity warning
608 typedef struct homing_beep_info
610 int snd_handle; // sound handle for last played beep
611 fix last_time_played; // time beep was last played
612 int min_cycle_time; // time (in ms) for fastest cycling of the sound
613 int max_cycle_time; // time (in ms) for slowest cycling of the sound
614 float min_cycle_dist; // distance at which fastest cycling occurs
615 float max_cycle_dist; // distance at which slowest cycling occurs
616 float precalced_interp; // a precalculated value used in a linear interpretation
619 homing_beep_info Homing_beep = { -1, 0, 150, 1000, 30.0f, 1500.0f, 1.729412f };
621 // Set at the start of a mission, used to decide how to draw the separation for the warning missile indicators
622 float Min_warning_missile_dist;
623 float Max_warning_missile_dist;
625 void hud_maybe_flash_weapon(int index);
627 // if a given object should be ignored because of AWACS effects
628 int hud_target_invalid_awacs(object *objp)
630 // if objp is ship object, first check if can be targeted with team info
631 if (objp->type == OBJ_SHIP) {
632 if (Player_ship != NULL) {
633 if (ship_is_visible_by_team(objp->instance, Player_ship->team)) {
639 // check for invalid status
640 if((Player_ship != NULL) && (awacs_get_level(objp, Player_ship) < 1.0f)){
648 ship_subsys *advance_subsys(ship_subsys *cur, int next_flag)
651 return GET_NEXT(cur);
653 return GET_LAST(cur);
657 // select a sorted turret subsystem on a ship if no other subsys has been selected
658 void hud_maybe_set_sorted_turret_subsys(ship *shipp)
660 SDL_assert((Player_ai->target_objnum >= 0) && (Player_ai->target_objnum < MAX_OBJECTS));
661 if (!((Player_ai->target_objnum >= 0) && (Player_ai->target_objnum < MAX_OBJECTS))) {
664 SDL_assert(Objects[Player_ai->target_objnum].type == OBJ_SHIP);
665 if (Objects[Player_ai->target_objnum].type != OBJ_SHIP) {
669 if (Ship_info[shipp->ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) {
670 if (shipp->last_targeted_subobject[Player_num] == NULL) {
671 hud_target_live_turret(1, 1);
677 // -----------------------------------------------------------------------
678 // clear out the linked list of targets in the reticle
679 void hud_reticle_clear_list(reticle_list *rlist)
682 for ( cur = GET_FIRST(rlist); cur != END_OF_LIST(rlist); cur = GET_NEXT(cur) ) {
688 // --------------------------------------------------------------------------------------
689 // hud_reticle_list_init()
690 void hud_reticle_list_init()
694 for ( i = 0; i < MAX_RETICLE_TARGETS; i++ ) {
695 Reticle_list[i].flags = 0;
698 Reticle_save_timestamp = 1;
699 list_init(&Reticle_save_list);
700 list_init(&Reticle_cur_list);
703 // --------------------------------------------------------------------------------------
704 // hud_check_reticle_list()
707 void hud_check_reticle_list()
709 reticle_list *rl, *temp;
711 // cull dying objects from reticle list
712 rl = GET_FIRST(&Reticle_cur_list);
713 while( rl !=END_OF_LIST(&Reticle_cur_list) ) {
715 if ( rl->objp->flags & OF_SHOULD_BE_DEAD ) {
716 list_remove( &Reticle_cur_list, rl );
722 if ( timestamp_elapsed(Reticle_save_timestamp) ) {
723 hud_reticle_clear_list(&Reticle_save_list);
724 Reticle_save_timestamp = timestamp(RESET_TARGET_IN_RETICLE);
728 // --------------------------------------------------------------------------------------
729 // hud_reticle_list_find_free()
732 int hud_reticle_list_find_free()
736 // find a free reticle_list element
737 for ( i = 0; i < MAX_RETICLE_TARGETS; i++ ) {
738 if ( !(Reticle_list[i].flags & RL_USED) ) {
743 if ( i == MAX_RETICLE_TARGETS ) {
744 // nprintf(("Warning","Warning ==> Ran out of reticle target elements...\n"));
751 // --------------------------------------------------------------------------------------
752 // hud_stuff_reticle_list()
755 #define RETICLE_DEFAULT_DIST 100000.0f
756 #define RETICLE_DEFAULT_DOT 1.0f
757 void hud_stuff_reticle_list(reticle_list *rl, object *objp, float measure, int dot_flag)
761 rl->dist = RETICLE_DEFAULT_DIST;
762 rl->flags |= RL_USE_DOT;
766 rl->dot = RETICLE_DEFAULT_DOT;
771 // --------------------------------------------------------------------------------------
772 // hud_reticle_list_update()
774 // Update Reticle_cur_list with an object that lies in the reticle
776 // parmeters: objp => object pointer to target
777 // measure => distance or dot product, depending on dot_flag
778 // dot_flag => if 0, measure is distance, if 1 measure is dot
780 void hud_reticle_list_update(object *objp, float measure, int dot_flag)
782 reticle_list *rl, *new_rl;
785 for ( rl = GET_FIRST(&Reticle_cur_list); rl != END_OF_LIST(&Reticle_cur_list); rl = GET_NEXT(rl) ) {
786 if ( rl->objp == objp )
790 i = hud_reticle_list_find_free();
794 new_rl = &Reticle_list[i];
795 new_rl->flags |= RL_USED;
796 hud_stuff_reticle_list(new_rl, objp, measure, dot_flag);
798 int was_inserted = 0;
800 if ( EMPTY(&Reticle_cur_list) ) {
801 list_insert(&Reticle_cur_list, new_rl);
805 for ( rl = GET_FIRST(&Reticle_cur_list); rl != END_OF_LIST(&Reticle_cur_list); rl = GET_NEXT(rl) ) {
807 // compare based on distance
808 if ( measure < rl->dist ) {
809 list_insert_before(rl, new_rl);
815 // compare based on dot
816 if ( measure > rl->dot ) {
817 list_insert_before(rl, new_rl);
825 if ( !was_inserted ) {
826 list_append(&Reticle_cur_list, new_rl);
830 // --------------------------------------------------------------------------------------
831 // hud_reticle_pick_target()
833 // Pick a target from Reticle_cur_list, based on what is in Reticle_save_list
836 object *hud_reticle_pick_target()
838 reticle_list *cur_rl, *save_rl, *new_rl;
844 if ( EMPTY(&Reticle_cur_list) ) {
848 // As a first step, see if both ships and debris are in the list. If so, cull the debris.
849 int debris_in_list = 0;
850 int ship_in_list = 0;
851 for ( cur_rl = GET_FIRST(&Reticle_cur_list); cur_rl != END_OF_LIST(&Reticle_cur_list); cur_rl = GET_NEXT(cur_rl) ) {
852 if ( (cur_rl->objp->type == OBJ_SHIP) || (cur_rl->objp->type == OBJ_JUMP_NODE) ) {
857 if ( cur_rl->objp->type == OBJ_WEAPON ) {
858 if ( Weapon_info[Weapons[cur_rl->objp->instance].weapon_info_index].subtype == WP_MISSILE ) {
864 if ( (cur_rl->objp->type == OBJ_DEBRIS) || (cur_rl->objp->type == OBJ_ASTEROID) ) {
870 if ( ship_in_list && debris_in_list ) {
872 reticle_list *rl, *next;
874 rl = GET_FIRST(&Reticle_cur_list);
875 while ( rl != &Reticle_cur_list ) {
877 if ( (rl->objp->type == OBJ_DEBRIS) || (rl->objp->type == OBJ_ASTEROID) ){
878 list_remove(&Reticle_cur_list,rl);
885 for ( cur_rl = GET_FIRST(&Reticle_cur_list); cur_rl != END_OF_LIST(&Reticle_cur_list); cur_rl = GET_NEXT(cur_rl) ) {
887 for ( save_rl = GET_FIRST(&Reticle_save_list); save_rl != END_OF_LIST(&Reticle_save_list); save_rl = GET_NEXT(save_rl) ) {
888 if ( cur_rl->objp == save_rl->objp ) {
894 if ( !in_save_list ) {
895 return_objp = cur_rl->objp;
896 i = hud_reticle_list_find_free();
900 new_rl = &Reticle_list[i];
901 new_rl->flags |= RL_USED;
902 if ( cur_rl->flags & RL_USE_DOT ) {
903 hud_stuff_reticle_list(new_rl, cur_rl->objp, cur_rl->dot, 1);
906 hud_stuff_reticle_list(new_rl, cur_rl->objp, cur_rl->dist, 0);
909 list_append(&Reticle_save_list, new_rl);
914 if ( return_objp == NULL && !EMPTY(&Reticle_cur_list) ) {
915 i = hud_reticle_list_find_free();
918 new_rl = &Reticle_list[i];
919 cur_rl = GET_FIRST(&Reticle_cur_list);
921 return_objp = cur_rl->objp;
922 hud_reticle_clear_list(&Reticle_save_list);
923 list_append(&Reticle_save_list, new_rl);
929 // hud_target_hotkey_add_remove takes as it's parameter which hotkey (1-0) to add/remove the current
930 // target from. This functio behaves like the Shift-<selection> does in Windows -- using shift # will toggle
931 // the current target in and out of the selection set.
932 void hud_target_hotkey_add_remove( int k, object *ctarget, int how_to_add )
934 htarget_list *hitem, *plist;
936 // don't do anything if a standalone multiplayer server
937 if ( MULTIPLAYER_STANDALONE )
940 if ( k < 0 || k > 7 ) {
941 nprintf(("Warning", "Bogus hotkey %d sent to hud_target_hotkey_add_remove\n"));
945 plist = &(Players[Player_num].keyed_targets[k]);
947 // we must operate only on ships
948 if ( ctarget->type != OBJ_SHIP )
951 // don't allow player into hotkey set
952 if ( ctarget == Player_obj )
955 // don't put dying or departing
956 if ( Ships[ctarget->instance].flags & (SF_DYING|SF_DEPARTING) )
959 // don't add mission file added hotkey assignments if there are player added assignments
960 // already in the list
961 if ( (how_to_add == HOTKEY_MISSION_FILE_ADDED) && NOT_EMPTY(plist) ) {
962 for ( hitem = GET_FIRST(plist); hitem != END_OF_LIST(plist); hitem = GET_NEXT(hitem) ) {
963 if ( hitem->how_added == HOTKEY_USER_ADDED )
968 // determine if the current target is currently in the set or not
969 for ( hitem = GET_FIRST(plist); hitem != END_OF_LIST(plist); hitem = GET_NEXT(hitem) ) {
970 if ( hitem->objp == ctarget )
974 // if hitem == end of the list, then the target should be added, else it should be removed
975 if ( hitem == END_OF_LIST(plist) ) {
976 if ( EMPTY(&htarget_free_list) ) {
977 Int3(); // get Allender -- no more free hotkey target items
981 nprintf(("network", "Hotkey: Adding %s\n", Ships[ctarget->instance].ship_name));
982 hitem = GET_FIRST( &htarget_free_list );
983 list_remove( &htarget_free_list, hitem );
984 list_append( plist, hitem );
985 hitem->objp = ctarget;
986 hitem->how_added = how_to_add;
988 nprintf(("network", "Hotkey: Removing %s\n", Ships[ctarget->instance].ship_name));
989 list_remove( plist, hitem );
990 list_append( &htarget_free_list, hitem );
991 hitem->objp = NULL; // for safety
995 // the following function clears the hotkey set given by parameter passed in
996 void hud_target_hotkey_clear( int k )
998 htarget_list *hitem, *plist, *temp;
1000 plist = &(Players[Player_num].keyed_targets[k]);
1001 hitem = GET_FIRST(plist);
1002 while ( hitem != END_OF_LIST(plist) ) {
1003 temp = GET_NEXT(hitem);
1004 list_remove( plist, hitem );
1005 list_append( &htarget_free_list, hitem );
1009 if ( Players[Player_num].current_hotkey_set == k ) // clear this variable if we removed the bindings
1010 Players[Player_num].current_hotkey_set = -1;
1013 // the next function sets the current selected set to be N. If there is just one ship in the selection
1014 // set, this ship will become the new target. If there is more than one ship in the selection set,
1015 // then the current_target will remain what it was.
1016 void hud_target_hotkey_select( int k )
1018 int visible_count = 0;
1019 htarget_list *hitem, *plist, *target, *next_target, *first_target;
1022 plist = &(Players[Player_num].keyed_targets[k]);
1024 if ( EMPTY( plist ) ) // no items in list, then do nothing
1027 // a simple walk of the list to get the count
1028 for ( hitem = GET_FIRST(plist); hitem != END_OF_LIST(plist); hitem = GET_NEXT(hitem) ){
1029 if (awacs_get_level(hitem->objp, Player_ship, 1) > 1) {
1034 // no visible ships in list
1035 if (visible_count == 0) {
1039 // set the current target to be the "next" ship in the list. Scan the list to see if our
1040 // current target is in the set. If so, target the next ship in the list, otherwise target
1042 // set first_target - first visible item in list
1043 // target - item in list that is the player's currently selected target
1044 // next_target - next visible item in list following target
1045 target_objnum = Player_ai->target_objnum;
1048 first_target = NULL;
1049 for ( hitem = GET_FIRST(plist); hitem != END_OF_LIST(plist); hitem = GET_NEXT(hitem) ) {
1051 if (awacs_get_level(hitem->objp, Player_ship, 1) > 1) {
1052 // get the first valid target
1053 if (first_target == NULL) {
1054 first_target = hitem;
1057 // get the next target in the list following the player currently selected target
1058 if (target != NULL) {
1059 next_target = hitem;
1064 // mark the player currently selected target
1065 if ( OBJ_INDEX(hitem->objp) == target_objnum ) {
1070 // if current target is not in list, then target and next_target will be NULL
1071 // so we use the first found target
1072 if (target == NULL) {
1073 SDL_assert(first_target != NULL);
1074 if (first_target != NULL) {
1075 target = first_target;
1076 next_target = first_target;
1078 // this should not happen
1083 // update target if more than 1 is visible
1084 if (visible_count > 1) {
1085 // next already found (after current target in list)
1086 if (next_target != NULL) {
1087 target = next_target;
1090 // next is before current target, so search from start of list
1091 for ( hitem = GET_FIRST(plist); hitem != END_OF_LIST(plist); hitem = GET_NEXT(hitem) ) {
1092 if (awacs_get_level(hitem->objp, Player_ship, 1) > 1) {
1100 SDL_assert( target != END_OF_LIST(plist) );
1102 if ( Player_obj != target->objp ){
1103 set_target_objnum( Player_ai, OBJ_INDEX(target->objp) );
1106 Players[Player_num].current_hotkey_set = k;
1109 // hud_init_targeting_colors() will initalize the shader and gradient objects used
1113 color HUD_color_homing_indicator;
1115 void hud_make_shader(shader *sh, int r, int g, int b, float dimmer = 1000.0f)
1119 // The m matrix converts all colors to shades of green
1120 float tmp = 0.0015625f * i2fl(HUD_color_alpha+1.0f) / 16.0f;
1125 cf = (i2fl(r) / dimmer)*(i2fl(HUD_color_alpha) / 15.0f);
1127 gr_create_shader( sh, rf, gf, bf, cf );
1130 void hud_init_targeting_colors()
1132 gr_init_color( &HUD_color_homing_indicator, 0x7f, 0x7f, 0 ); // yellow
1134 hud_make_shader(&Training_msg_glass, 61, 61, 85, 500.0f);
1136 hud_init_brackets();
1139 void hud_keyed_targets_clear()
1143 // clear out the keyed target list
1144 for (i = 0; i < MAX_KEYED_TARGETS; i++ )
1145 list_init( &(Players[Player_num].keyed_targets[i]) );
1146 Players[Player_num].current_hotkey_set = -1;
1148 // place all of the hoykey target items back onto the free list
1149 list_init( &htarget_free_list );
1150 for ( i = 0; i < MAX_HOTKEY_TARGET_ITEMS; i++ )
1151 list_append( &htarget_free_list, &htarget_items[i] );
1154 // Init data used for the weapons display on the HUD
1155 void hud_weapons_init()
1159 Weapon_flash_info.is_bright = 0;
1160 for ( i = 0; i < MAX_WEAPON_FLASH_LINES; i++ ) {
1161 Weapon_flash_info.flash_duration[i] = 1;
1162 Weapon_flash_info.flash_next[i] = 1;
1165 if ( !Weapon_gauges_loaded ) {
1166 for ( i = 0; i < NUM_WEAPON_GAUGES; i++ ) {
1167 Weapon_gauges[i].first_frame = bm_load_animation(Weapon_gauge_fnames[gr_screen.res][i], &Weapon_gauges[i].num_frames);
1168 if ( Weapon_gauges[i].first_frame < 0 ) {
1169 Warning(LOCATION,"Cannot load hud ani: %s\n",Weapon_gauge_fnames[gr_screen.res][i]);
1172 Weapon_gauges_loaded = 1;
1176 // init data used to play the homing "proximity warning" sound
1177 void hud_init_homing_beep()
1179 Homing_beep.snd_handle = -1;
1180 Homing_beep.last_time_played = 0;
1181 Homing_beep.precalced_interp = (Homing_beep.max_cycle_dist-Homing_beep.min_cycle_dist) / (Homing_beep.max_cycle_time - Homing_beep.min_cycle_time );
1184 // hud_init_targeting() will set the current target to point to the dummy node
1185 // in the object used list
1187 void hud_init_targeting()
1189 SDL_assert(Player_ai != NULL);
1191 // make sure there is no current target
1192 set_target_objnum( Player_ai, -1 );
1193 Player_ai->last_target = -1;
1194 Player_ai->last_subsys_target = NULL;
1195 Player_ai->last_dist = 0.0f;
1196 Player_ai->last_speed = 0.0f;
1198 hud_keyed_targets_clear();
1199 hud_init_missile_lock();
1200 hud_init_artillery();
1202 // Init the lists that hold targets in reticle (to allow cycling of targets in reticle)
1203 hud_reticle_list_init();
1204 hud_init_homing_beep();
1206 // Load in the frames need for the lead indicator
1207 if (!Lead_indicator_gauge_loaded) {
1208 Lead_indicator_gauge.first_frame = bm_load_animation(Lead_fname[gr_screen.res], &Lead_indicator_gauge.num_frames);
1209 if ( Lead_indicator_gauge.first_frame < 0 ) {
1210 Warning(LOCATION,"Cannot load hud ani: %s\n", Lead_fname[gr_screen.res]);
1212 Lead_indicator_gauge_loaded = 1;
1215 if (!Energy_bar_gauges_loaded) {
1216 Energy_bar_gauges.first_frame = bm_load_animation(Energy_fname[gr_screen.res], &Energy_bar_gauges.num_frames);
1217 if ( Energy_bar_gauges.first_frame < 0 ) {
1218 Warning(LOCATION,"Cannot load hud ani: %s\n", Energy_fname[gr_screen.res]);
1220 Energy_bar_gauges_loaded = 1;
1223 if (!Toggle_gauge_loaded) {
1224 Toggle_gauge.first_frame = bm_load_animation(Toggle_fname[gr_screen.res], &Toggle_gauge.num_frames);
1225 if ( Toggle_gauge.first_frame < 0 ) {
1226 Warning(LOCATION,"Cannot load hud ani: %s\n", Toggle_fname[gr_screen.res]);
1228 Toggle_gauge_loaded = 1;
1231 if (!Cmeasure_gauge_loaded) {
1232 Cmeasure_gauge.first_frame = bm_load_animation(Cm_fname[gr_screen.res], &Cmeasure_gauge.num_frames);
1233 if ( Cmeasure_gauge.first_frame < 0 ) {
1234 Warning(LOCATION,"Cannot load hud ani: %s\n", Cm_fname[gr_screen.res]);
1236 Cmeasure_gauge_loaded = 1;
1242 Min_warning_missile_dist = 2.5f*Player_obj->radius;
1243 Max_warning_missile_dist = 1500.0f;
1245 Tl_hostile_reset_timestamp = timestamp(0);
1246 Tl_friendly_reset_timestamp = timestamp(0);
1247 Target_next_uninspected_object_timestamp = timestamp(0);
1248 Target_newest_ship_timestamp = timestamp(0);
1249 Target_next_turret_timestamp = timestamp(0);
1251 if(The_mission.flags & MISSION_FLAG_FULLNEB) {
1252 Toggle_text_alpha = 127;
1254 Toggle_text_alpha = 160;
1260 // Target the next or previous subobject on the currently selected ship, based on next_flag.
1261 void hud_target_subobject_common(int next_flag)
1263 if (Player_ai->target_objnum == -1) {
1264 HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "No target selected.", 322));
1265 snd_play( &Snds[SND_TARGET_FAIL] );
1269 if (Objects[Player_ai->target_objnum].type != OBJ_SHIP) {
1270 snd_play( &Snds[SND_TARGET_FAIL]);
1274 ship_subsys *start, *start2, *A;
1275 ship_subsys *subsys_to_target=NULL;
1278 target_shipp = &Ships[Objects[Player_ai->target_objnum].instance];
1280 if (!Player_ai->targeted_subsys) {
1281 start = GET_FIRST(&target_shipp->subsys_list);
1283 start = Player_ai->targeted_subsys;
1286 start2 = advance_subsys(start, next_flag);
1288 for ( A = start2; A != start; A = advance_subsys(A, next_flag) ) {
1290 if ( A == &target_shipp->subsys_list ) {
1295 if ( A->system_info->type == SUBSYSTEM_TURRET ) {
1299 subsys_to_target = A;
1304 if ( subsys_to_target == NULL ) {
1305 snd_play( &Snds[SND_TARGET_FAIL]);
1307 set_targeted_subsys(Player_ai, subsys_to_target, Player_ai->target_objnum);
1308 target_shipp->last_targeted_subobject[Player_num] = Player_ai->targeted_subsys;
1312 object *advance_fb(object *objp, int next_flag)
1315 return GET_NEXT(objp);
1317 return GET_LAST(objp);
1320 // Target the previous subobject on the currently selected ship.
1323 void hud_target_prev_subobject()
1325 hud_target_subobject_common(0);
1328 void hud_target_next_subobject()
1330 hud_target_subobject_common(1);
1333 // hud_target_next() will set the Players[Player_num].current_target to the next target in the object
1334 // used list whose team matches the team parameter. The player is NOT included in the target list.
1336 // parameters: team => team of ship to target next. Default value is -1, if team doesn't matter.
1339 void hud_target_common(int team, int next_flag)
1341 object *A, *start, *start2;
1343 int is_ship, target_found = FALSE;
1345 if (Player_ai->target_objnum == -1)
1346 start = &obj_used_list;
1348 start = &Objects[Player_ai->target_objnum];
1350 start2 = advance_fb(start, next_flag);
1352 for ( A = start2; A != start; A = advance_fb(A, next_flag) ) {
1355 if ( A == &obj_used_list ) {
1359 if (A == Player_obj || ( A->type != OBJ_SHIP && A->type != OBJ_WEAPON && A->type != OBJ_JUMP_NODE) ){
1363 if(hud_target_invalid_awacs(A)){
1367 if ( A->type == OBJ_WEAPON ) {
1368 if ( !(Weapon_info[Weapons[A->instance].weapon_info_index].wi_flags & WIF_BOMB) ){
1373 if ( A->type == OBJ_SHIP ) {
1374 if ( Ships[A->instance].flags & TARGET_SHIP_IGNORE_FLAGS ){
1380 if ( vm_vec_same( &A->pos, &Eye_position ) ) {
1385 shipp = &Ships[A->instance]; // get a pointer to the ship information
1387 if ( !hud_team_matches_filter(team, shipp->team) ) {
1388 // if we're in multiplayer dogfight, ignore this
1389 if(!((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_DOGFIGHT))){
1394 if ( A == Player_obj || (shipp->flags & TARGET_SHIP_IGNORE_FLAGS) ){
1398 // if we've reached here, it is a valid next target
1399 if ( Player_ai->target_objnum != A-Objects ) {
1400 target_found = TRUE;
1401 set_target_objnum( Player_ai, OBJ_INDEX(A) );
1402 // if ship is BIG|HUGE and last subsys is NULL, get turret
1403 hud_maybe_set_sorted_turret_subsys(shipp);
1404 hud_restore_subsystem_target(shipp);
1407 target_found = TRUE;
1408 set_target_objnum( Player_ai, OBJ_INDEX(A) );
1414 if ( target_found == FALSE ) {
1415 snd_play( &Snds[SND_TARGET_FAIL] );
1419 void hud_target_next(int team)
1421 hud_target_common(team, 1);
1424 void hud_target_prev(int team)
1426 hud_target_common(team, 0);
1429 // -------------------------------------------------------------------
1430 // advance_missile_obj()
1432 missile_obj *advance_missile_obj(missile_obj *mo, int next_flag)
1435 return GET_NEXT(mo);
1438 return GET_LAST(mo);
1441 ship_obj *advance_ship(ship_obj *so, int next_flag)
1444 return GET_NEXT(so);
1447 return GET_LAST(so);
1450 ship_obj *get_ship_obj_ptr_from_index(int index);
1451 // -------------------------------------------------------------------
1452 // hud_target_missile()
1454 // Target the closest locked missile that is locked on locked_obj
1456 // input: source_obj => pointer to object that fired weapon
1457 // next_flag => 0 -> previous 1 -> next
1459 // NOTE: this function is only allows targeting bombs
1460 void hud_target_missile(object *source_obj, int next_flag)
1462 missile_obj *end, *start, *mo;
1463 object *A, *target_objp;
1467 int target_found = 0;
1469 if ( source_obj->type != OBJ_SHIP )
1472 SDL_assert( Ships[source_obj->instance].ai_index != -1 );
1473 aip = &Ai_info[Ships[source_obj->instance].ai_index];
1475 end = &Missile_obj_list;
1476 if (aip->target_objnum != -1) {
1477 target_objp = &Objects[aip->target_objnum];
1478 if ( target_objp->type == OBJ_WEAPON && Weapon_info[Weapons[target_objp->instance].weapon_info_index].subtype == WP_MISSILE ) { // must be a missile
1479 end = missile_obj_return_address(Weapons[target_objp->instance].missile_list_index);
1483 start = advance_missile_obj(end, next_flag);
1485 for ( mo = start; mo != end; mo = advance_missile_obj(mo, next_flag) ) {
1486 if ( mo == &Missile_obj_list ){
1490 SDL_assert(mo->objnum >= 0 && mo->objnum < MAX_OBJECTS);
1491 A = &Objects[mo->objnum];
1493 SDL_assert(A->type == OBJ_WEAPON);
1494 SDL_assert((A->instance >= 0) && (A->instance < MAX_WEAPONS));
1495 wp = &Weapons[A->instance];
1496 wip = &Weapon_info[wp->weapon_info_index];
1498 // only allow targeting of bombs
1499 if ( !(wip->wi_flags & WIF_BOMB) ) {
1503 // only allow targeting of hostile bombs
1504 if ( (obj_team(A) == Player_ship->team) && (Player_ship->team != TEAM_TRAITOR) ) {
1508 if(hud_target_invalid_awacs(A)){
1512 // if we've reached here, got a new target
1513 target_found = TRUE;
1514 set_target_objnum( aip, OBJ_INDEX(A) );
1518 if ( !target_found ) {
1519 // if no bomb is found, search for bombers
1520 ship_obj *start, *so;
1522 //extern ship_obj *Ship_objs;
1523 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) ) {
1524 int index = Ships[Objects[aip->target_objnum].instance].ship_list_index;
1525 start = get_ship_obj_ptr_from_index(index);
1527 start = GET_FIRST(&Ship_obj_list);
1530 for (so=advance_ship(start, next_flag); so!=start; so=advance_ship(so, next_flag)) {
1531 object *ship_obj = &Objects[so->objnum];
1533 // don't look at header
1534 if (so == &Ship_obj_list) {
1538 // only allow targeting of hostile bombs
1539 if ( (obj_team(ship_obj) == Player_ship->team) && (Player_ship->team != TEAM_TRAITOR) ) {
1543 if(hud_target_invalid_awacs(ship_obj)){
1547 // check if ship type is bomber
1548 if ( !(Ship_info[Ships[ship_obj->instance].ship_info_index].flags & SIF_BOMBER) ) {
1553 if ( Ships[ship_obj->instance].flags & TARGET_SHIP_IGNORE_FLAGS ){
1558 target_found = TRUE;
1559 set_target_objnum( aip, OBJ_INDEX(ship_obj) );
1564 if ( !target_found ) {
1565 snd_play( &Snds[SND_TARGET_FAIL], 0.0f );
1569 // Return !0 if shipp can be scanned, otherwise return 0
1570 int hud_target_ship_can_be_scanned(ship *shipp)
1574 sip = &Ship_info[shipp->ship_info_index];
1576 // ignore cargo that has already been scanned
1577 if ( shipp->flags & SF_CARGO_REVEALED ) {
1581 // allow ships with scannable flag set
1582 if ( shipp->flags & SF_SCANNABLE ) {
1586 // ignore ships that don't carry cargo
1587 if ( !(sip->flags & (SIF_CARGO|SIF_FREIGHTER)) ) {
1594 // target the next/prev uninspected cargo container
1595 void hud_target_uninspected_cargo(int next_flag)
1597 object *A, *start, *start2;
1599 int target_found = 0;
1601 if (Player_ai->target_objnum == -1) {
1602 start = &obj_used_list;
1604 start = &Objects[Player_ai->target_objnum];
1607 start2 = advance_fb(start, next_flag);
1609 for ( A = start2; A != start; A = advance_fb(A, next_flag) ) {
1610 if ( A == &obj_used_list ) {
1614 if (A == Player_obj || (A->type != OBJ_SHIP) ) {
1618 shipp = &Ships[A->instance]; // get a pointer to the ship information
1620 if ( shipp->flags & TARGET_SHIP_IGNORE_FLAGS ) {
1624 // ignore all non-cargo carrying craft
1625 if ( !hud_target_ship_can_be_scanned(shipp) ) {
1629 if(hud_target_invalid_awacs(A)){
1633 // if we've reached here, it is a valid next target
1634 if ( Player_ai->target_objnum != OBJ_INDEX(A) ) {
1635 target_found = TRUE;
1636 set_target_objnum( Player_ai, OBJ_INDEX(A) );
1640 if ( target_found == FALSE ) {
1641 snd_play( &Snds[SND_TARGET_FAIL]);
1645 // target the newest ship in the area
1646 void hud_target_newest_ship()
1648 object *A, *player_target_objp;
1649 object *newest_obj=NULL;
1652 uint current_target_arrived_time = 0xffffffff, newest_time = 0;
1654 if ( Player_ai->target_objnum >= 0 ) {
1655 player_target_objp = &Objects[Player_ai->target_objnum];
1656 if ( player_target_objp->type == OBJ_SHIP ) {
1657 current_target_arrived_time = Ships[player_target_objp->instance].create_time;
1660 player_target_objp = NULL;
1663 // If no target is selected, then simply target the closest uninspected cargo
1664 if ( Player_ai->target_objnum == -1 || timestamp_elapsed(Target_newest_ship_timestamp) ) {
1665 current_target_arrived_time = 0xffffffff;
1668 Target_newest_ship_timestamp = timestamp(TL_RESET);
1670 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
1671 A = &Objects[so->objnum];
1672 shipp = &Ships[A->instance]; // get a pointer to the ship information
1674 if ( (A == Player_obj) || (shipp->flags & TARGET_SHIP_IGNORE_FLAGS) )
1678 if ( Ship_info[shipp->ship_info_index].flags & SIF_NAVBUOY ) {
1682 if ( A == player_target_objp ) {
1686 if(hud_target_invalid_awacs(A)){
1690 if ( (shipp->create_time >= newest_time) && (shipp->create_time <= current_target_arrived_time) ) {
1691 newest_time = shipp->create_time;
1697 set_target_objnum( Player_ai, OBJ_INDEX(newest_obj) );
1698 // if BIG|HUGE and no selected subsystem, get sorted turret
1699 hud_maybe_set_sorted_turret_subsys(&Ships[newest_obj->instance]);
1700 hud_restore_subsystem_target(&Ships[newest_obj->instance]);
1703 snd_play( &Snds[SND_TARGET_FAIL]);
1708 #define TYPE_FACING_BEAM 1
1709 #define TYPE_FACING_FLAK 2
1710 #define TYPE_FACING_MISSILE 3
1711 #define TYPE_FACING_LASER 4
1712 #define TYPE_NONFACING_BEAM 5
1713 #define TYPE_NONFACING_FLAK 6
1714 #define TYPE_NONFACING_MISSILE 7
1715 #define TYPE_NONFACING_LASER 8
1716 #define TYPE_NONFACING_INC 4
1718 typedef struct eval_next_turret {
1724 int sort_turret_func(const void *e1, const void *e2)
1726 eval_next_turret *p1 = (eval_next_turret*)e1;
1727 eval_next_turret *p2 = (eval_next_turret*)e2;
1729 SDL_assert(p1->type != TYPE_NONE);
1730 SDL_assert(p2->type != TYPE_NONE);
1732 if (p1->type != p2->type) {
1733 return (p1->type - p2->type);
1735 float delta_dist = p1->dist - p2->dist;
1736 if (delta_dist < 0) {
1738 } else if (delta_dist > 0) {
1746 // target the next/prev live turret on the current target
1747 // auto_advance from hud_update_closest_turret
1748 void hud_target_live_turret(int next_flag, int auto_advance, int only_player_target)
1751 ship_subsys *live_turret=NULL;
1754 eval_next_turret ent[MAX_MODEL_SUBSYSTEMS];
1755 int num_live_turrets = 0;
1757 // make sure we're targeting a ship
1758 if (Player_ai->target_objnum == -1 && !auto_advance) {
1759 snd_play(&Snds[SND_TARGET_FAIL]);
1763 // only targeting subsystems on ship
1764 if ((Objects[Player_ai->target_objnum].type != OBJ_SHIP) && (!auto_advance)) {
1765 snd_play( &Snds[SND_TARGET_FAIL]);
1769 // set some pointers
1770 objp = &Objects[Player_ai->target_objnum];
1771 target_shipp = &Ships[objp->instance];
1774 int timestamp_val = 0;
1775 if (!auto_advance) {
1776 timestamp_val = Target_next_turret_timestamp;
1777 Target_next_turret_timestamp = timestamp(TURRET_RESET);
1780 // If no target is selected, then simply target the closest (or facing) turret
1781 int last_subsys_turret = FALSE;
1782 if (Player_ai->targeted_subsys != NULL) {
1783 if (Player_ai->targeted_subsys->system_info->type == SUBSYSTEM_TURRET) {
1784 if (Player_ai->targeted_subsys->system_info->turret_weapon_type >= 0) {
1785 last_subsys_turret = TRUE;
1790 // do we want the closest turret (or the one our ship is pointing at)
1791 int get_closest_turret = (auto_advance || !last_subsys_turret || timestamp_elapsed(timestamp_val));
1793 // initialize eval struct
1794 memset(ent,0, sizeof(ent));
1795 int use_straigh_ahead_turret = FALSE;
1797 // go through list of turrets
1798 for (A=GET_FIRST(&target_shipp->subsys_list); A!=END_OF_LIST(&target_shipp->subsys_list); A=GET_NEXT(A)) {
1800 if (A->system_info->type == SUBSYSTEM_TURRET) {
1801 // check turret has hit points and has a weapon
1802 if ( (A->current_hits > 0) && (A->system_info->turret_weapon_type >= 0) ) {
1803 if ( !only_player_target || (A->turret_enemy_objnum == OBJ_INDEX(Player_obj)) ) {
1804 vector gsubpos, vec_to_subsys;
1805 float distance, dot;
1806 // get world pos of subsystem and its distance
1807 get_subsystem_world_pos(objp, A, &gsubpos);
1808 distance = vm_vec_normalized_dir(&vec_to_subsys, &gsubpos, &View_position);
1810 // check if facing and in view
1811 int facing = ship_subsystem_in_sight(objp, A, &View_position, &gsubpos, 0);
1813 if (!auto_advance && get_closest_turret && !only_player_target) {
1814 // if within 3 degrees and not previous subsys, use subsys in front
1815 dot = vm_vec_dotprod(&vec_to_subsys, &Player_obj->orient.v.fvec);
1816 if ((dot > 0.9986) && facing) {
1817 use_straigh_ahead_turret = TRUE;
1822 // set weapon_type to allow sort of ent on type
1823 if (Weapon_info[A->system_info->turret_weapon_type].wi_flags & WIF_BEAM) {
1824 ent[num_live_turrets].type = TYPE_FACING_BEAM;
1825 } else if (Weapon_info[A->system_info->turret_weapon_type].wi_flags & WIF_FLAK) {
1826 ent[num_live_turrets].type = TYPE_FACING_FLAK;
1828 if (Weapon_info[A->system_info->turret_weapon_type].subtype == WP_MISSILE) {
1829 ent[num_live_turrets].type = TYPE_FACING_MISSILE;
1830 } else if (Weapon_info[A->system_info->turret_weapon_type].subtype == WP_LASER) {
1831 ent[num_live_turrets].type = TYPE_FACING_LASER;
1834 ent[num_live_turrets].type = TYPE_FACING_LASER;
1838 // fill out ent struct
1839 ent[num_live_turrets].ss = A;
1840 ent[num_live_turrets].dist = distance;
1842 ent[num_live_turrets].type += TYPE_NONFACING_INC;
1850 // sort the list if we're not using turret straigh ahead of us
1851 if (!use_straigh_ahead_turret) {
1852 qsort(ent, num_live_turrets, sizeof(eval_next_turret), sort_turret_func);
1855 if (use_straigh_ahead_turret) {
1856 // use the straight ahead turret
1859 // check if we have a currently targeted turret and find its position after the sort
1860 int i, start_index, next_index;
1861 if (get_closest_turret) {
1865 for (i=0; i<num_live_turrets; i++) {
1866 if (ent[i].ss == Player_ai->targeted_subsys) {
1871 // check that we started with a turret
1872 if (start_index == -1) {
1877 // set next live turret
1878 if (num_live_turrets == 0) {
1881 } else if (num_live_turrets == 1 || get_closest_turret) {
1882 // only 1 live turret, so set it
1883 live_turret = ent[0].ss;
1886 // advance to next closest turret
1887 next_index = start_index + 1;
1888 if (next_index == num_live_turrets) {
1892 // go to next farther turret
1893 next_index = start_index - 1;
1894 if (next_index == -1) {
1895 next_index = num_live_turrets - 1;
1899 // set the next turret to be targeted based on next_index
1900 live_turret = ent[next_index].ss;
1903 //if (live_turret) {
1905 // mprintf(("name %s, index: %d, type: %d\n", live_turret->system_info->subobj_name, next_index, ent[next_index].type));
1909 if ( live_turret != NULL ) {
1910 set_targeted_subsys(Player_ai, live_turret, Player_ai->target_objnum);
1911 target_shipp->last_targeted_subobject[Player_num] = Player_ai->targeted_subsys;
1913 if (!auto_advance) {
1914 snd_play( &Snds[SND_TARGET_FAIL]);
1920 // -------------------------------------------------------------------
1921 // hud_target_closest_locked_missile()
1923 // Target the closest locked missile that is locked on locked_obj
1925 // input: locked_obj => pointer to object that you want to find
1926 // closest missile to
1928 void hud_target_closest_locked_missile(object *locked_obj)
1930 object *A, *nearest_obj=NULL;
1933 float nearest_dist, dist;
1934 int target_found = FALSE;
1937 nearest_dist = 10000.0f;
1939 for ( mo = GET_NEXT(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
1940 SDL_assert(mo->objnum >= 0 && mo->objnum < MAX_OBJECTS);
1941 A = &Objects[mo->objnum];
1943 if (A->type != OBJ_WEAPON){
1947 SDL_assert((A->instance >= 0) && (A->instance < MAX_WEAPONS));
1948 wp = &Weapons[A->instance];
1949 wip = &Weapon_info[wp->weapon_info_index];
1951 if ( wip->subtype != WP_MISSILE ){
1955 if ( !(wip->wi_flags & (WIF_HOMING_ASPECT|WIF_HOMING_HEAT) ) ){
1959 if(hud_target_invalid_awacs(A)){
1963 if (wp->homing_object == locked_obj) {
1964 dist = vm_vec_dist_quick(&A->pos, &locked_obj->pos); // Find distance!
1966 if (dist < nearest_dist) {
1968 nearest_dist = dist;
1973 if (nearest_dist < 10000.0f) {
1974 SDL_assert(nearest_obj);
1975 set_target_objnum( Player_ai, OBJ_INDEX(nearest_obj) );
1976 target_found = TRUE;
1979 if ( !target_found ){
1980 snd_play( &Snds[SND_TARGET_FAIL], 0.0f );
1984 // Return bitmask of all opponents.
1985 int opposing_team_mask(int team_mask)
1987 return ((TEAM_FRIENDLY | TEAM_NEUTRAL | TEAM_HOSTILE) & ~team_mask) | TEAM_TRAITOR;
1990 // select a new target, by auto-targeting
1991 void hud_target_auto_target_next()
1993 if ( Framecount < 2 ) {
1997 // No auto-targeting after dead.
1998 if (Game_mode & (GM_DEAD | GM_DEAD_BLEW_UP))
2003 valid_team = opposing_team_mask(Player_ship->team);
2005 // try target closest ship attacking player
2006 hud_target_closest(valid_team, OBJ_INDEX(Player_obj), FALSE, TRUE );
2008 // if none, try targeting closest hostile fighter/bomber
2009 if ( Player_ai->target_objnum == -1 ){
2010 hud_target_closest(valid_team, -1, FALSE, TRUE);
2013 // No fighter/bombers exists, so go ahead an target the closest hostile
2014 if ( Player_ai->target_objnum == -1 ){
2015 hud_target_closest(valid_team, -1, FALSE);
2018 // um, ok. Try targeting asteroids that are on a collision course for an escort ship
2019 if ( Player_ai->target_objnum == -1 ) {
2020 asteroid_target_closest_danger();
2025 // Given that object 'targeter' is targetting object 'targetee',
2026 // how far are they? This uses the point closest to the targeter
2027 // object on the targetee's bounding box. So if targeter is inside
2028 // targtee's bounding box, the distance is 0.
2029 float hud_find_target_distance( object *targetee, object *targeter )
2035 // Which model is it?
2036 switch( targetee->type ) {
2038 model_num = Ships[targetee->instance].modelnum;
2041 // model_num = Debris[targetee->instance].model_num;
2044 // Don't find model_num since circles would work better
2045 //model_num = Weapon_info[Weapons[targetee->instance].weapon_info_index].model_num;
2048 // Don't find model_num since circles would work better
2049 //model_num = Asteroid_info[Asteroids[targetee->instance].type].model_num;
2052 // Don't find model_num since circles would work better
2053 //model_num = Jump_nodes[targetee->instance].modelnum;
2059 // New way, that uses bounding box.
2060 if ( model_num > -1 ) {
2061 dist = model_find_closest_point( &tmp_pnt, model_num, -1, &targetee->orient, &targetee->pos, &targeter->pos );
2063 // Old way, that uses radius.
2064 dist = vm_vec_dist_quick(&targetee->pos, &targeter->pos) - targetee->radius;
2065 if ( dist < 0.0f ) {
2072 // hud_target_closest() will set the Players[Player_num].current_target to the closest
2073 // ship to the player that matches the team passed as a paramater
2075 // The current algorithm is to simply iterate through the objects and calculate the
2076 // magnitude of the vector that connects the player to the target. The smallest magnitude
2077 // is tracked, and then used to locate the closest hostile ship. Note only the square of the
2078 // magnitude is required, since we are only comparing magnitudes
2080 // parameters: team => team of closest ship that should be targeted.
2081 // Default value is -1, if team doesn't matter.
2083 // attacked_objnum => object number of ship that is being attacked
2084 // play_fail_snd => boolean, whether to play SND_TARGET_FAIL
2085 // (needed, since function called repeatedly when auto-targeting is
2086 // enabled, and we don't want a string of fail sounds playing).
2087 // This is a default parameter with a value of TRUE
2088 // filter => OPTIONAL parameter (default value 0): when set to TRUE, only
2089 // fighters and bombers are considered for new targets
2091 // returns: TRUE ==> a target was acquired
2092 // FALSE ==> no target was acquired
2094 // eval target as closest struct
2101 int check_nearest_turret;
2102 int attacked_objnum;
2103 int check_all_turrets;
2104 int turret_attacking_target; // check that turret is actually attacking the attacked_objnum
2107 // evaluate a ship (and maybe turrets) as a potential target
2108 // check if shipp (or its turrets) is attacking attacked_objnum
2109 // special case for player trying to select target (don't check if turrets are aimed at player)
2110 void evaluate_ship_as_closest_target(esct *esct)
2112 int targeting_player, turret_is_attacking;
2117 esct->min_distance = FLT_MAX;
2118 esct->check_nearest_turret = FALSE;
2119 turret_is_attacking = FALSE;
2122 object *objp = &Objects[esct->shipp->objnum];
2123 SDL_assert(objp->type == OBJ_SHIP);
2124 if (objp->type != OBJ_SHIP) {
2128 // player being targeted, so we will want closest distance from player
2129 targeting_player = (esct->attacked_objnum == OBJ_INDEX(Player_obj));
2131 // filter on team, except in multiplayer
2132 if ( !hud_team_matches_filter(esct->team, esct->shipp->team) ) {
2133 // if we're in multiplayer dogfight, ignore this
2134 if(!((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_DOGFIGHT))){
2139 // check if player or ignore ship
2140 if ( (esct->shipp->objnum == OBJ_INDEX(Player_obj)) || (esct->shipp->flags & TARGET_SHIP_IGNORE_FLAGS) ) {
2145 if ( Ship_info[esct->shipp->ship_info_index].flags & SIF_HARMLESS ) {
2149 // only look at targets that are AWACS valid
2150 if (hud_target_invalid_awacs(&Objects[esct->shipp->objnum])) {
2154 // If filter is set, only target fighters and bombers
2155 if ( esct->filter ) {
2156 if ( !(Ship_info[esct->shipp->ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER)) ) {
2161 // find closest turret to player if BIG or HUGE ship
2162 if (Ship_info[esct->shipp->ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) {
2163 for (ss=GET_FIRST(&esct->shipp->subsys_list); ss!=END_OF_LIST(&esct->shipp->subsys_list); ss=GET_NEXT(ss)) {
2164 if ( (ss->system_info->type == SUBSYSTEM_TURRET) && (ss->current_hits > 0) ) {
2166 if (esct->check_all_turrets || (ss->turret_enemy_objnum == esct->attacked_objnum)) {
2167 turret_is_attacking = 1;
2168 esct->check_nearest_turret = TRUE;
2170 if ( !esct->turret_attacking_target || (esct->turret_attacking_target && (ss->turret_enemy_objnum == esct->attacked_objnum)) ) {
2172 // get world pos of subsystem
2173 vm_vec_unrotate(&gsubpos, &ss->system_info->pnt, &objp->orient);
2174 vm_vec_add2(&gsubpos, &objp->pos);
2175 new_distance = vm_vec_dist_quick(&gsubpos, &Player_obj->pos);
2178 // GET TURRET TYPE, FAVOR BEAM, FLAK, OTHER
2179 int turret_type = ss->system_info->turret_weapon_type;
2180 if (Weapon_info[turret_type].wi_flags & WIF_BEAM) {
2181 new_distance *= 0.3f;
2182 } else if (Weapon_info[turret_type].wi_flags & WIF_FLAK) {
2183 new_distance *= 0.6f;
2186 // get the closest distance
2187 if (new_distance <= esct->min_distance) {
2188 esct->min_distance = new_distance;
2196 // If no turret is attacking, check if objp is actually targetting attacked_objnum
2197 // dont bail if targeting is for player
2198 if ( !targeting_player && !turret_is_attacking ) {
2199 ai_info *aip = &Ai_info[esct->shipp->ai_index];
2201 if (aip->target_objnum != esct->attacked_objnum) {
2205 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)) {
2210 // consider the ship alone if there are no attacking turrets
2211 if ( !turret_is_attacking ) {
2212 //new_distance = hud_find_target_distance(objp, Player_obj);
2213 new_distance = vm_vec_dist_quick(&objp->pos, &Player_obj->pos);
2215 if (new_distance <= esct->min_distance) {
2216 esct->min_distance = new_distance;
2217 esct->check_nearest_turret = FALSE;
2222 int hud_target_closest(int team, int attacked_objnum, int play_fail_snd, int filter, int get_closest_turret_attacking_player)
2225 object *nearest_obj = &obj_used_list;
2228 int check_nearest_turret = FALSE;
2230 // evaluate ship closest target struct
2233 float min_distance = FLT_MAX;
2234 int target_found = FALSE;
2236 int player_obj_index = OBJ_INDEX(Player_obj);
2237 ship_subsys *ss; //*nearest_turret_subsys = NULL, *ss;
2239 if ( (attacked_objnum >= 0) && (attacked_objnum != player_obj_index) ) {
2240 // bail if player does not have target
2241 if ( Player_ai->target_objnum == -1) {
2242 if ( Objects[attacked_objnum].type != OBJ_SHIP ) {
2243 goto Target_closest_done;
2246 // bail if ship is to be ignored
2247 if (!(Ships[Objects[attacked_objnum].instance].flags & TARGET_SHIP_IGNORE_FLAGS)) {
2248 goto Target_closest_done;
2253 if (attacked_objnum == -1) {
2254 attacked_objnum = player_obj_index;
2257 // check all turrets if for player.
2258 esct.check_all_turrets = (attacked_objnum == player_obj_index);
2259 esct.filter = filter;
2261 esct.attacked_objnum = attacked_objnum;
2262 esct.turret_attacking_target = get_closest_turret_attacking_player;
2264 for ( so=GET_FIRST(&Ship_obj_list); so!=END_OF_LIST(&Ship_obj_list); so=GET_NEXT(so) ) {
2266 A = &Objects[so->objnum];
2267 shipp = &Ships[A->instance]; // get a pointer to the ship information
2269 // fill in rest of esct
2272 // check each shipp on list and update nearest obj and subsys
2273 evaluate_ship_as_closest_target(&esct);
2274 if (esct.min_distance < min_distance) {
2275 target_found = TRUE;
2276 min_distance = esct.min_distance;
2278 check_nearest_turret = esct.check_nearest_turret;
2282 Target_closest_done:
2284 // maybe ignore target if too far away
2285 // DKA 9/8/99 Remove distance check
2288 // get distance to nearest attacker
2289 float dist = vm_vec_dist_quick(&Objects[attacked_objnum].pos, &nearest_obj->pos);
2291 // no distance limit for player obj
2292 if ((attacked_objnum != player_obj_index) && (dist > MIN_DISTANCE_TO_CONSIDER_THREAT)) {
2293 target_found = FALSE;
2298 set_target_objnum(Player_ai, OBJ_INDEX(nearest_obj));
2299 if ( check_nearest_turret ) {
2301 // if former subobject was not a turret do, not change subsystem
2302 ss = Ships[nearest_obj->instance].last_targeted_subobject[Player_num];
2303 if (ss == NULL || get_closest_turret_attacking_player) {
2304 // set_targeted_subsys(Player_ai, nearest_turret_subsys, OBJ_INDEX(nearest_obj));
2305 // update nearest turret with later func
2306 hud_target_live_turret(1, 1, get_closest_turret_attacking_player);
2307 Ships[nearest_obj->instance].last_targeted_subobject[Player_num] = Player_ai->targeted_subsys;
2310 hud_restore_subsystem_target(&Ships[nearest_obj->instance]);
2313 // no target found, maybe play fail sound
2314 if (play_fail_snd == TRUE) {
2315 snd_play(&Snds[SND_TARGET_FAIL]);
2319 return target_found;
2322 // auto update closest turret to attack on big or huge ships
2323 void hud_update_closest_turret()
2325 hud_target_live_turret(1, 1);
2328 float nearest_distance, new_distance;
2329 ship_subsys *ss, *closest_subsys;
2333 nearest_distance = FLT_MAX;
2334 objp = &Objects[Player_ai->target_objnum];
2335 shipp = &Ships[objp->instance];
2336 closest_subsys = NULL;
2339 SDL_assert(Ship_info[shipp->ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP));
2341 for (ss=GET_FIRST(&shipp->subsys_list); ss!=END_OF_LIST(&shipp->subsys_list); ss=GET_NEXT(ss)) {
2342 if ( (ss->system_info->type == SUBSYSTEM_TURRET) && (ss->current_hits > 0) ) {
2343 // make sure turret is not "unused"
2344 if (ss->system_info->turret_weapon_type >= 0) {
2346 // get world pos of subsystem
2347 vm_vec_unrotate(&gsubpos, &ss->system_info->pnt, &objp->orient);
2348 vm_vec_add2(&gsubpos, &objp->pos);
2349 new_distance = vm_vec_dist_quick(&gsubpos, &Player_obj->pos);
2351 // GET TURRET TYPE, FAVOR BEAM, FLAK, OTHER
2352 int turret_type = ss->system_info->turret_weapon_type;
2353 if (Weapon_info[turret_type].wi_flags & WIF_BEAM) {
2354 new_distance *= 0.3f;
2355 } else if (Weapon_info[turret_type].wi_flags & WIF_FLAK) {
2356 new_distance *= 0.6f;
2359 // check if facing and in view
2360 int facing = ship_subsystem_in_sight(objp, ss, &View_position, &gsubpos, 0);
2363 new_distance *= 0.5f;
2366 // get the closest distance
2367 if (new_distance <= nearest_distance) {
2368 nearest_distance = new_distance;
2369 closest_subsys = ss;
2375 // check if new subsys to target
2376 if (Player_ai->targeted_subsys != NULL) {
2377 set_targeted_subsys(Player_ai, closest_subsys, Player_ai->target_objnum);
2378 shipp->last_targeted_subobject[Player_num] = Player_ai->targeted_subsys;
2384 // --------------------------------------------------------------------
2385 // hud_target_targets_target()
2387 // Target your target's target. Your target is specified by objnum passed
2390 void hud_target_targets_target()
2395 if ( Player_ai->target_objnum < 0 || Player_ai->target_objnum >= MAX_OBJECTS ) {
2399 objp = &Objects[Player_ai->target_objnum];
2400 if ( objp->type != OBJ_SHIP ) {
2404 if (hud_target_invalid_awacs(objp)) {
2408 if ( Ships[objp->instance].flags & TARGET_SHIP_IGNORE_FLAGS ) {
2412 tt_objnum = Ai_info[Ships[objp->instance].ai_index].target_objnum;
2413 if ( tt_objnum < 0 || tt_objnum >= MAX_OBJECTS ) {
2417 if ( tt_objnum == OBJ_INDEX(Player_obj) ) {
2421 // if we've reached here, found player target's target
2422 set_target_objnum( Player_ai, tt_objnum );
2423 if (Objects[tt_objnum].type == OBJ_SHIP) {
2424 hud_maybe_set_sorted_turret_subsys(&Ships[Objects[tt_objnum].instance]);
2426 hud_restore_subsystem_target(&Ships[Objects[tt_objnum].instance]);
2430 snd_play( &Snds[SND_TARGET_FAIL], 0.0f );
2433 // Return !0 if target_objp is a valid object type for targeting in reticle, otherwise return 0
2434 int object_targetable_in_reticle(object *target_objp)
2437 if (target_objp == Player_obj ) {
2441 obj_type = target_objp->type;
2443 if ( (obj_type == OBJ_SHIP) || (obj_type == OBJ_DEBRIS) || (obj_type == OBJ_WEAPON) || (obj_type == OBJ_ASTEROID) || (obj_type == OBJ_JUMP_NODE) ) {
2451 // hud_target_in_reticle_new() will target the object that is closest to the player, and who is
2452 // intersected by a ray passed from the center of the reticle out along the forward vector of the
2455 // targeting of objects of type OBJ_SHIP and OBJ_DEBRIS are supported
2457 // Method: A ray is cast from the center of the reticle, and we keep track of any eligible object
2458 // the ray intersects. We take the ship closest to us that intersects an object.
2460 // Since this method may work poorly with objects that are far away, hud_target_in_reticle_old()
2461 // is called if no intersections are found.
2464 #define TARGET_IN_RETICLE_DISTANCE 10000.0f
2466 void hud_target_in_reticle_new()
2473 hud_reticle_clear_list(&Reticle_cur_list);
2474 Reticle_save_timestamp = timestamp(RESET_TARGET_IN_RETICLE);
2476 // Get 3d vector through center of reticle
2477 vm_vec_scale_add(&terminus, &Eye_position, &Player_obj->orient.v.fvec, TARGET_IN_RETICLE_DISTANCE);
2480 for ( A = GET_FIRST(&obj_used_list); A !=END_OF_LIST(&obj_used_list); A = GET_NEXT(A) ) {
2482 if ( !object_targetable_in_reticle(A) ) {
2486 if ( A->type == OBJ_WEAPON ) {
2487 if ( !(Weapon_info[Weapons[A->instance].weapon_info_index].wi_flags & WIF_BOMB) ){
2492 if ( A->type == OBJ_SHIP ) {
2493 if ( Ships[A->instance].flags & TARGET_SHIP_IGNORE_FLAGS ){
2498 if(hud_target_invalid_awacs(A)){
2504 mc.model_num = Ships[A->instance].modelnum;
2507 mc.model_num = Debris[A->instance].model_num;
2510 mc.model_num = Weapon_info[Weapons[A->instance].weapon_info_index].model_num;
2514 #if !(defined(FS2_DEMO) || defined(FS1_DEMO))
2516 subtype = Asteroids[A->instance].asteroid_subtype;
2517 mc.model_num = Asteroid_info[Asteroids[A->instance].type].model_num[subtype];
2522 mc.model_num = Jump_nodes[A->instance].modelnum;
2525 Int3(); // Illegal object type.
2528 model_clear_instance( mc.model_num );
2529 mc.orient = &A->orient; // The object's orient
2530 mc.pos = &A->pos; // The object's position
2531 mc.p0 = &Eye_position; // Point 1 of ray to check
2532 mc.p1 = &terminus; // Point 2 of ray to check
2533 mc.flags = MC_CHECK_MODEL; // | MC_ONLY_BOUND_BOX; // check the model, but only its bounding box
2536 if ( mc.num_hits ) {
2537 dist = vm_vec_dist_squared(&mc.hit_point_world, &Eye_position);
2538 hud_reticle_list_update(A, dist, 0);
2540 } // end for (go to next object)
2542 hud_target_in_reticle_old(); // try the old method (works well with ships far away)
2545 // hud_target_in_reticle_old() will target the object that is closest to the reticle center and inside
2548 // targeting of objects of type OBJ_SHIP and OBJ_DEBRIS are supported
2551 // Method: take the dot product of the foward vector and the vector to target. Take
2552 // the one that is closest to 1 and at least MIN_DOT_FOR_TARGET
2554 // IMPORTANT: The MIN_DOT_FOR_TARGET value was arrived at by trial and error and
2555 // is only valid for the HUD reticle in use at that time.
2557 #define MIN_DOT_FOR_TARGET 0.9726// fov for targeting in reticle
2559 void hud_target_in_reticle_old()
2561 object *A, *target_obj;
2563 vector vec_to_target;
2565 for ( A = GET_FIRST(&obj_used_list); A !=END_OF_LIST(&obj_used_list); A = GET_NEXT(A) ) {
2567 if ( !object_targetable_in_reticle(A) ) {
2571 if ( A->type == OBJ_WEAPON ) {
2572 if ( !(Weapon_info[Weapons[A->instance].weapon_info_index].wi_flags & WIF_BOMB) ){
2577 if ( A->type == OBJ_SHIP ) {
2578 if ( Ships[A->instance].flags & TARGET_SHIP_IGNORE_FLAGS ){
2583 if(hud_target_invalid_awacs(A)){
2587 if ( vm_vec_same( &A->pos, &Eye_position ) ) {
2591 vm_vec_normalized_dir(&vec_to_target, &A->pos, &Eye_position);
2592 dot = vm_vec_dot(&Player_obj->orient.v.fvec, &vec_to_target);
2594 if ( dot > MIN_DOT_FOR_TARGET ) {
2595 hud_reticle_list_update(A, dot, 1);
2599 target_obj = hud_reticle_pick_target();
2600 if ( target_obj != NULL ) {
2601 set_target_objnum( Player_ai, OBJ_INDEX(target_obj) );
2602 if ( target_obj->type == OBJ_SHIP ) {
2603 // if BIG|HUGE, maybe set subsys to turret
2604 hud_maybe_set_sorted_turret_subsys(&Ships[target_obj->instance]);
2605 hud_restore_subsystem_target(&Ships[target_obj->instance]);
2609 snd_play( &Snds[SND_TARGET_FAIL], 0.0f );
2613 // hud_target_subsystem_in_reticle() will target the subsystem that is within the reticle and
2614 // is closest to the reticle center. The current target is the only object that is searched for
2617 // Method: take the dot product of the foward vector and the vector to target. Take
2618 // the one that is closest to 1 and at least MIN_DOT_FOR_TARGET
2620 // IMPORTANT: The MIN_DOT_FOR_TARGET value was arrived at by trial and error and
2621 // is only valid for the HUD reticle in use at that time.
2624 void hud_target_subsystem_in_reticle()
2627 ship_subsys *subsys;
2628 ship_subsys *nearest_subsys = NULL;
2631 float dot, best_dot;
2632 vector vec_to_target;
2635 if ( Player_ai->target_objnum == -1){
2636 hud_target_in_reticle_old();
2639 if ( Player_ai->target_objnum == -1) {
2640 snd_play( &Snds[SND_TARGET_FAIL]);
2644 targetp = &Objects[Player_ai->target_objnum];
2646 if ( targetp->type != OBJ_SHIP ){ // only targeting subsystems on ship
2650 int shipnum = targetp->instance;
2652 for (subsys = GET_FIRST(&Ships[shipnum].subsys_list); subsys != END_OF_LIST(&Ships[shipnum].subsys_list) ; subsys = GET_NEXT( subsys ) ) {
2653 get_subsystem_world_pos(targetp, subsys, &subobj_pos);
2655 vm_vec_normalized_dir(&vec_to_target, &subobj_pos, &Eye_position);
2656 dot = vm_vec_dot(&Player_obj->orient.v.fvec, &vec_to_target);
2658 if ( dot > best_dot ) {
2660 if ( best_dot > MIN_DOT_FOR_TARGET )
2661 nearest_subsys = subsys;
2664 SDL_assert(best_dot <= 1.0f);
2667 if ( nearest_subsys != NULL ) {
2668 set_targeted_subsys(Player_ai, nearest_subsys, Player_ai->target_objnum);
2669 HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Targeting subsystem %s.", 323), Player_ai->targeted_subsys->system_info->name);
2670 Ships[shipnum].last_targeted_subobject[Player_num] = Player_ai->targeted_subsys;
2673 snd_play( &Snds[SND_TARGET_FAIL]);
2678 #define T_OFFSET_FROM_CIRCLE -13
2679 #define T_BASE_LENGTH 4
2683 void hud_render_orientation_tee(object *from_objp, object *to_objp, matrix *from_orientp)
2686 vector target_to_obj;
2687 float x1,y1,x2,y2,x3,y3,x4,y4;
2689 vm_vec_sub(&target_to_obj, &from_objp->pos, &to_objp->pos);
2691 vm_vec_normalize(&target_to_obj);
2693 // calculate the dot product between the target_to_player vector and the targets forward vector
2695 // 0 - vectors are perpendicular
2696 // 1 - vectors are collinear and in the same direction (target is facing player)
2697 // -1 - vectors are collinear and in the opposite direction (target is facing away from player)
2698 dot_product = vm_vec_dotprod(&from_orientp->v.fvec, &target_to_obj);
2700 if (vm_vec_dotprod(&from_orientp->v.rvec, &target_to_obj) >= 0) {
2701 if (dot_product >= 0){
2702 dot_product = -PI/2*dot_product + PI;
2704 dot_product = -PI/2*dot_product - PI;
2708 dot_product *= PI/2; //(range is now -PI/2 => PI/2)
2711 y1 = (float)sin(dot_product) * (Outer_circle_radius[gr_screen.res] - T_OFFSET_FROM_CIRCLE);
2712 x1 = (float)cos(dot_product) * (Outer_circle_radius[gr_screen.res] - T_OFFSET_FROM_CIRCLE);
2714 y1 += Hud_reticle_center[gr_screen.res][1];
2715 x1 += Hud_reticle_center[gr_screen.res][0];
2720 y2 = (float)sin(dot_product) * (Outer_circle_radius[gr_screen.res] - T_OFFSET_FROM_CIRCLE - T_LENGTH);
2721 x2 = (float)cos(dot_product) * (Outer_circle_radius[gr_screen.res] - T_OFFSET_FROM_CIRCLE - T_LENGTH);
2723 y2 += Hud_reticle_center[gr_screen.res][1];
2724 x2 += Hud_reticle_center[gr_screen.res][0];
2729 x3 = x1 - T_BASE_LENGTH * (float)sin(dot_product);
2730 y3 = y1 + T_BASE_LENGTH * (float)cos(dot_product);
2731 x4 = x1 + T_BASE_LENGTH * (float)sin(dot_product);
2732 y4 = y1 - T_BASE_LENGTH * (float)cos(dot_product);
2734 // HACK! Should be antialiased!
2735 gr_line(fl2i(x3),fl2i(y3),fl2i(x4),fl2i(y4)); // bottom of T
2736 gr_line(fl2i(x1),fl2i(y1),fl2i(x2),fl2i(y2)); // part of T pointing towards center
2740 void hud_tri(float x1,float y1,float x2,float y2,float x3,float y3)
2744 // Make the triangle always be the correct handiness so
2745 // the tmapper won't think its back-facing and throw it out.
2746 float det = (y2-y1)*(x3-x1) - (x2-x1)*(y3-y1);
2747 if ( det >= 0.0f ) {
2761 vertex * vertlist[3];
2764 for (i=0; i<3; i++ )
2765 vertlist[i] = &verts[i];
2767 verts[0].sx = x1; verts[0].sy = y1;
2768 verts[1].sx = x2; verts[1].sy = y2;
2769 verts[2].sx = x3; verts[2].sy = y3;
2771 uint saved_mode = gr_zbuffer_get();
2773 gr_zbuffer_set( GR_ZBUFF_NONE );
2775 gr_tmapper( 3, vertlist, 0 );
2777 gr_zbuffer_set( saved_mode );
2781 void hud_tri_empty(float x1,float y1,float x2,float y2,float x3,float y3)
2783 gr_line(fl2i(x1),fl2i(y1),fl2i(x2),fl2i(y2));
2784 gr_line(fl2i(x2),fl2i(y2),fl2i(x3),fl2i(y3));
2785 gr_line(fl2i(x3),fl2i(y3),fl2i(x1),fl2i(y1));
2789 // Render a missile warning triangle that has a tail on it to indicate distance
2790 void hud_render_tail_missile_triangle(float ang, float xpos, float ypos, float cur_dist, int draw_solid, int draw_inside)
2799 float sin_ang, cos_ang, tail_len;
2801 float max_tail_len=20.0f;
2803 sin_ang=(float)sin(ang);
2804 cos_ang=(float)cos(ang);
2806 if ( cur_dist < Min_warning_missile_dist ) {
2808 } else if ( cur_dist > Max_warning_missile_dist ) {
2809 tail_len = max_tail_len;
2811 tail_len = cur_dist/Max_warning_missile_dist * max_tail_len;
2814 if ( draw_inside ) {
2815 x1 = xpos - Target_triangle_base[gr_screen.res] * -sin_ang;
2816 y1 = ypos + Target_triangle_base[gr_screen.res] * cos_ang;
2817 x2 = xpos + Target_triangle_base[gr_screen.res] * -sin_ang;
2818 y2 = ypos - Target_triangle_base[gr_screen.res] * cos_ang;
2820 xpos -= Target_triangle_height[gr_screen.res] * cos_ang;
2821 ypos += Target_triangle_height[gr_screen.res] * sin_ang;
2823 if ( tail_len > 0 ) {
2824 xtail = xpos - tail_len * cos_ang;
2825 ytail = ypos + tail_len * sin_ang;
2829 x1 = xpos - Target_triangle_base[gr_screen.res] * -sin_ang;
2830 y1 = ypos + Target_triangle_base[gr_screen.res] * cos_ang;
2831 x2 = xpos + Target_triangle_base[gr_screen.res] * -sin_ang;
2832 y2 = ypos - Target_triangle_base[gr_screen.res] * cos_ang;
2834 xpos += Target_triangle_height[gr_screen.res] * cos_ang;
2835 ypos -= Target_triangle_height[gr_screen.res] * sin_ang;
2837 if ( tail_len > 0 ) {
2838 xtail = xpos + tail_len * cos_ang;
2839 ytail = ypos - tail_len * sin_ang;
2844 hud_tri(xpos,ypos,x1,y1,x2,y2);
2846 hud_tri_empty(xpos,ypos,x1,y1,x2,y2);
2849 // draw the tail indicating length
2850 if ( tail_len > 0 ) {
2851 gr_line(fl2i(xpos), fl2i(ypos), fl2i(xtail), fl2i(ytail));
2855 // Render a missile warning triangle, that splits apart to indicate distance
2856 void hud_render_split_missile_triangle(float ang, float xpos, float ypos, float cur_dist, int draw_solid, int draw_inside)
2858 // points to draw triangles
2872 float triangle_sep, half_triangle_sep,sin_ang,cos_ang;
2874 sin_ang=(float)sin(ang);
2875 cos_ang=(float)cos(ang);
2877 if ( cur_dist < Min_warning_missile_dist ) {
2878 triangle_sep = 0.0f;
2879 } else if ( cur_dist > Max_warning_missile_dist ) {
2880 triangle_sep = Max_offscreen_tri_seperation[gr_screen.res]+Max_front_seperation[gr_screen.res];
2882 triangle_sep = (cur_dist/Max_warning_missile_dist) * (Max_offscreen_tri_seperation[gr_screen.res]+Max_front_seperation[gr_screen.res]);
2885 // calculate these values only once, since it will be used in several places
2886 half_triangle_sep = 0.5f * triangle_sep;
2888 xpos = (float)floor(xpos);
2889 ypos = (float)floor(ypos);
2891 if ( triangle_sep == 0 ) {
2892 x1 = xpos - Target_triangle_base[gr_screen.res] * -sin_ang;
2893 y1 = ypos + Target_triangle_base[gr_screen.res] * cos_ang;
2894 x2 = xpos + Target_triangle_base[gr_screen.res] * -sin_ang;
2895 y2 = ypos - Target_triangle_base[gr_screen.res] * cos_ang;
2896 if ( draw_inside ) {
2898 xpos += Target_triangle_height[gr_screen.res] * cos_ang;
2899 ypos -= Target_triangle_height[gr_screen.res] * sin_ang;
2902 hud_tri(xpos,ypos,x1,y1,x2,y2);
2904 hud_tri_empty(xpos,ypos,x1,y1,x2,y2);
2907 // calc left side points
2908 x5 = xpos - half_triangle_sep * -sin_ang;
2909 y5 = ypos + half_triangle_sep * cos_ang;
2911 x6 = x5 - Target_triangle_base[gr_screen.res] * -sin_ang;
2912 y6 = y5 + Target_triangle_base[gr_screen.res] * cos_ang;
2916 if ( draw_inside ) {
2917 x4 -= Target_triangle_height[gr_screen.res] * cos_ang;
2918 y4 += Target_triangle_height[gr_screen.res] * sin_ang;
2920 x4 += Target_triangle_height[gr_screen.res] * cos_ang;
2921 y4 -= Target_triangle_height[gr_screen.res] * sin_ang;
2924 // calc right side points
2925 x2 = xpos + half_triangle_sep * -sin_ang;
2926 y2 = ypos - half_triangle_sep * cos_ang;
2928 x3 = x2 + Target_triangle_base[gr_screen.res] * -sin_ang;
2929 y3 = y2 - Target_triangle_base[gr_screen.res] * cos_ang;
2933 if ( draw_inside ) {
2934 x1 -= Target_triangle_height[gr_screen.res] * cos_ang;
2935 y1 += Target_triangle_height[gr_screen.res] * sin_ang;
2937 x1 += Target_triangle_height[gr_screen.res] * cos_ang;
2938 y1 -= Target_triangle_height[gr_screen.res] * sin_ang;
2941 // draw both tris with a line connecting them
2943 hud_tri(x3,y3,x2,y2,x1,y1);
2944 hud_tri(x4,y4,x5,y5,x6,y6);
2946 hud_tri_empty(x3,y3,x2,y2,x1,y1);
2947 hud_tri_empty(x4,y4,x5,y5,x6,y6);
2949 gr_line(fl2i(x2+0.5f),fl2i(y2+0.5f),fl2i(x5+0.5f),fl2i(y5+0.5f));
2953 // Render a triangle on the outside of the targeting circle.
2954 // Must be inside a g3_start_frame().
2955 // If aspect_flag !0, then render filled, indicating aspect lock.
2956 // If show_interior !0, then point inwards to positions inside reticle
2957 void hud_render_triangle(vector *hostile_pos, int aspect_flag, int show_interior, int split_tri)
2959 vertex hostile_vertex;
2961 float xpos,ypos,cur_dist,sin_ang,cos_ang;
2964 // determine if the closest firing object is within the targeting reticle (which means the triangle
2967 cur_dist = vm_vec_dist_quick(&Player_obj->pos, hostile_pos);
2969 g3_rotate_vertex(&hostile_vertex, hostile_pos);
2971 if (hostile_vertex.codes == 0) {// on screen
2972 float projected_x, projected_y;
2974 g3_project_vertex(&hostile_vertex);
2976 if (!(hostile_vertex.flags & PF_OVERFLOW)) { // make sure point projected
2979 projected_x = hostile_vertex.sx;
2980 projected_y = hostile_vertex.sy;
2982 mag_squared = (projected_x-Hud_reticle_center[gr_screen.res][0])*(projected_x-Hud_reticle_center[gr_screen.res][0]) +
2983 (projected_y-Hud_reticle_center[gr_screen.res][1])*(projected_y-Hud_reticle_center[gr_screen.res][1]);
2985 if ( mag_squared < Outer_circle_radius[gr_screen.res]*Outer_circle_radius[gr_screen.res] ) {
2986 if ( !show_interior ) {
2995 ang = atan2_safe(hostile_vertex.y,hostile_vertex.x);
2996 sin_ang=(float)sin(ang);
2997 cos_ang=(float)cos(ang);
2999 if ( draw_inside ) {
3000 xpos = Hud_reticle_center[gr_screen.res][0] + cos_ang*(Outer_circle_radius[gr_screen.res]-7);
3001 ypos = Hud_reticle_center[gr_screen.res][1] - sin_ang*(Outer_circle_radius[gr_screen.res]-7);
3003 xpos = Hud_reticle_center[gr_screen.res][0] + cos_ang*(Outer_circle_radius[gr_screen.res]+4);
3004 ypos = Hud_reticle_center[gr_screen.res][1] - sin_ang*(Outer_circle_radius[gr_screen.res]+4);
3007 xpos += HUD_offset_x;
3008 ypos += HUD_offset_y;
3011 // hud_render_split_missile_triangle(ang, xpos, ypos, cur_dist, aspect_flag, draw_inside);
3012 hud_render_tail_missile_triangle(ang, xpos, ypos, cur_dist, aspect_flag, draw_inside);
3019 if ( draw_inside ) {
3020 x1 = xpos - Target_triangle_base[gr_screen.res] * -sin_ang;
3021 y1 = ypos + Target_triangle_base[gr_screen.res] * cos_ang;
3022 x2 = xpos + Target_triangle_base[gr_screen.res] * -sin_ang;
3023 y2 = ypos - Target_triangle_base[gr_screen.res] * cos_ang;
3025 xpos -= Target_triangle_height[gr_screen.res] * cos_ang;
3026 ypos += Target_triangle_height[gr_screen.res] * sin_ang;
3029 x1 = xpos - Target_triangle_base[gr_screen.res] * -sin_ang;
3030 y1 = ypos + Target_triangle_base[gr_screen.res] * cos_ang;
3031 x2 = xpos + Target_triangle_base[gr_screen.res] * -sin_ang;
3032 y2 = ypos - Target_triangle_base[gr_screen.res] * cos_ang;
3034 xpos += Target_triangle_height[gr_screen.res] * cos_ang;
3035 ypos -= Target_triangle_height[gr_screen.res] * sin_ang;
3039 hud_tri(xpos,ypos,x1,y1,x2,y2);
3041 hud_tri_empty(xpos,ypos,x1,y1,x2,y2);
3046 // Show all homing missiles locked onto the player.
3047 // Also, play the beep!
3048 void hud_show_homing_missiles()
3053 float dist, nearest_dist;
3054 int closest_is_aspect=0;
3056 gr_set_color_fast(&HUD_color_homing_indicator);
3057 nearest_dist = Homing_beep.max_cycle_dist;
3059 for ( mo = GET_NEXT(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
3060 A = &Objects[mo->objnum];
3061 SDL_assert((A->instance >= 0) && (A->instance < MAX_WEAPONS));
3063 wp = &Weapons[A->instance];
3065 if (wp->homing_object == Player_obj) {
3066 hud_render_triangle(&A->pos, Weapon_info[wp->weapon_info_index].wi_flags & WIF_HOMING_ASPECT, 1, 1);
3067 dist = vm_vec_dist_quick(&A->pos, &Player_obj->pos);
3069 if (dist < nearest_dist) {
3070 nearest_dist = dist;
3071 if ( Weapon_info[wp->weapon_info_index].wi_flags & WIF_HOMING_ASPECT ) {
3072 closest_is_aspect=1;
3074 closest_is_aspect=0;
3080 // See if need to play warning beep.
3081 if (nearest_dist < Homing_beep.max_cycle_dist ) {
3085 delta_time = f2fl(Missiontime - Homing_beep.last_time_played);
3087 // figure out the cycle time by doing a linear interpretation
3088 cycle_time = Homing_beep.min_cycle_time + (nearest_dist-Homing_beep.min_cycle_dist) * Homing_beep.precalced_interp;
3090 // play a new 'beep' if cycle time has elapsed
3091 if ( (delta_time*1000) > cycle_time ) {
3092 Homing_beep.last_time_played = Missiontime;
3093 if ( snd_is_playing(Homing_beep.snd_handle) ) {
3094 snd_stop(Homing_beep.snd_handle);
3097 if ( closest_is_aspect ) {
3098 Homing_beep.snd_handle = snd_play(&Snds[SND_PROXIMITY_ASPECT_WARNING]);
3100 Homing_beep.snd_handle = snd_play(&Snds[SND_PROXIMITY_WARNING]);
3106 // hud_show_orientation_tee() will draw the orientation gauge that orbits the inside of the
3107 // outer reticle ring. If the T is at 12 o'clock, the target is facing the player, if the T
3108 // is at 6 o'clock the target is facing away from the player. If the T is at 3 or 9 o'clock
3109 // the target is facing 90 away from the player.
3110 void hud_show_orientation_tee()
3114 if (Player_ai->target_objnum == -1)
3117 targetp = &Objects[Player_ai->target_objnum];
3119 if ( hud_gauge_maybe_flash(HUD_ORIENTATION_TEE) == 1 ) {
3120 hud_set_iff_color( targetp );
3122 hud_set_iff_color( targetp, 1);
3124 hud_render_orientation_tee(targetp, Player_obj, &targetp->orient);
3127 // routine to draw a bounding box around a remote detonate missile and distance to
3128 void hud_show_remote_detonate_missile()
3133 vertex target_point;
3136 // check for currently locked missiles (highest precedence)
3137 for ( mo = GET_FIRST(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
3138 SDL_assert(mo->objnum >= 0 && mo->objnum < MAX_OBJECTS);
3139 mobjp = &Objects[mo->objnum];
3141 if ((Player_obj != NULL) && (mobjp->parent_sig == Player_obj->parent_sig)) {
3142 if (Weapon_info[Weapons[mobjp->instance].weapon_info_index].wi_flags & WIF_REMOTE) {
3144 distance = hud_find_target_distance(mobjp, Player_obj);
3146 // get box center point
3147 g3_rotate_vertex(&target_point,&mobjp->pos);
3150 g3_project_vertex(&target_point);
3152 if (!(target_point.flags & PF_OVERFLOW)) { // make sure point projected
3153 int modelnum, bound_rval;
3155 switch ( mobjp->type ) {
3157 modelnum = Weapon_info[Weapons[mobjp->instance].weapon_info_index].model_num;
3158 bound_rval = model_find_2d_bound_min( modelnum, &mobjp->orient, &mobjp->pos,&x1,&y1,&x2,&y2 );
3162 Int3(); // should never happen
3166 if ( bound_rval == 0 ) {
3167 // draw brackets and distance
3169 color = hud_brackets_get_iff_color(MESSAGE_SENDER);
3170 gr_set_color_fast(&IFF_colors[color][1]);
3171 draw_bounding_brackets(x1-5,y1-5,x2+5,y2+5,0,0, distance, OBJ_INDEX(mobjp));
3174 // do only for the first remote detonate missile
3182 // routine to possibly draw a bouding box around a ship sending a message to the player
3183 void hud_show_message_sender()
3186 vertex target_point; // temp vertex used to find screen position for 3-D object;
3191 // don't draw brackets if no ship sending a message
3192 if ( Message_shipnum == -1 )
3195 targetp = &Objects[Ships[Message_shipnum].objnum];
3196 SDL_assert ( targetp != NULL );
3198 SDL_assert ( targetp->type == OBJ_SHIP );
3200 // Don't do this for the ship you're flying!
3201 if ( targetp == Player_obj ) {
3205 SDL_assert ( targetp->instance >=0 && targetp->instance < MAX_SHIPS );
3206 target_shipp = &Ships[Message_shipnum];
3208 // check the object flags to see if this ship is gone. If so, then don't do this stuff anymore
3209 if ( targetp->flags & OF_SHOULD_BE_DEAD ) {
3210 Message_shipnum = -1;
3214 // find the current target vertex
3216 g3_rotate_vertex(&target_point,&targetp->pos);
3218 hud_set_iff_color( targetp, 1);
3220 g3_project_vertex(&target_point);
3222 if (!(target_point.flags & PF_OVERFLOW)) { // make sure point projected
3223 int modelnum, bound_rval;
3225 switch ( targetp->type ) {
3227 modelnum = target_shipp->modelnum;
3228 bound_rval = model_find_2d_bound_min( modelnum, &targetp->orient, &targetp->pos,&x1,&y1,&x2,&y2 );
3232 Int3(); // should never happen
3236 if ( bound_rval == 0 ) {
3238 color = hud_brackets_get_iff_color(MESSAGE_SENDER);
3239 gr_set_color_fast(&IFF_colors[color][1]);
3240 draw_bounding_brackets(x1-5,y1-5,x2+5,y2+5,10,10);
3244 if ( hud_gauge_active(HUD_OFFSCREEN_INDICATOR) ) {
3245 if (target_point.codes != 0) { // target center is not on screen
3246 // draw the offscreen indicator at the edge of the screen where the target is closest to
3247 // AL 11-19-97: only show offscreen indicator if player sensors are functioning
3248 if ( (OBJ_INDEX(targetp) != Player_ai->target_objnum) || (Message_shipnum == Objects[Player_ai->target_objnum].instance) ) {
3249 if ( hud_sensors_ok(Player_ship, 0) ) {
3251 gr_set_color_fast(&IFF_colors[IFF_COLOR_MESSAGE][1]);
3252 //dist = vm_vec_dist_quick(&Player_obj->pos, &targetp->pos);
3253 dist = hud_find_target_distance( targetp, Player_obj );
3254 hud_draw_offscreen_indicator(&target_point, &targetp->pos, dist);
3261 // hud_prune_hotkeys()
3263 // Check for ships that are dying, departed or dead. These should be removed from the player's
3265 void hud_prune_hotkeys()
3268 htarget_list *hitem, *plist;
3272 for ( i = 0; i < MAX_KEYED_TARGETS; i++ ) {
3273 plist = &(Players[Player_num].keyed_targets[i]);
3274 if ( EMPTY( plist ) ) // no items in list, then do nothing
3277 hitem = GET_FIRST(plist);
3278 while ( hitem != END_OF_LIST(plist) ) {
3284 SDL_assert ( objp != NULL );
3285 if ( objp->type == OBJ_SHIP ) {
3286 SDL_assert ( objp->instance >=0 && objp->instance < MAX_SHIPS );
3287 sp = &Ships[objp->instance];
3289 // if the object isn't a ship, it shouldn't be on the list, so remove it without question
3294 // check to see if the object is dying -- if so, remove it from the list
3295 // check to see if the ship is departing -- if so, remove it from the list
3296 if ( remove_item || (objp->flags & OF_SHOULD_BE_DEAD) || (sp->flags & (SF_DEPARTING|SF_DYING)) ) {
3298 nprintf(("Network", "Hotkey: Pruning %s\n", sp->ship_name));
3302 temp = GET_NEXT(hitem);
3303 list_remove( plist, hitem );
3304 list_append( &htarget_free_list, hitem );
3309 hitem = GET_NEXT( hitem );
3313 // save the hotkey sets with mission time reaches a certain point. Code was put here because this
3314 // function always called for both single/multiplayer. Maybe not the best location, but whatever.
3315 mission_hotkey_maybe_save_sets();
3318 int HUD_drew_selection_bracket_on_target;
3320 // hud_show_selection_set draws some indicator around all the ships in the current selection set. No
3321 // indicators will be drawn if there is only 1 ship in the set.
3322 void hud_show_selection_set()
3324 htarget_list *hitem, *plist;
3327 vertex target_point; // temp vertex used to find screen position for 3-D object;
3330 HUD_drew_selection_bracket_on_target = 0;
3332 set = Players[Player_num].current_hotkey_set;
3336 SDL_assert ( (set >= 0) && (set < MAX_KEYED_TARGETS) );
3337 plist = &(Players[Player_num].keyed_targets[set]);
3340 for ( hitem = GET_FIRST(plist); hitem != END_OF_LIST(plist); hitem = GET_NEXT(hitem) )
3343 if ( count == 0 ) { // only one ship, do nothing
3344 Players[Player_num].current_hotkey_set = -1;
3348 for ( hitem = GET_FIRST(plist); hitem != END_OF_LIST(plist); hitem = GET_NEXT(hitem) ) {
3349 targetp = hitem->objp;
3350 SDL_assert ( targetp != NULL );
3352 ship *target_shipp = NULL;
3354 SDL_assert ( targetp->type == OBJ_SHIP );
3355 SDL_assert ( targetp->instance >=0 && targetp->instance < MAX_SHIPS );
3356 target_shipp = &Ships[targetp->instance];
3358 if ( (Game_mode & GM_MULTIPLAYER) && (target_shipp == Player_ship) ) {
3362 // find the current target vertex
3364 g3_rotate_vertex(&target_point,&targetp->pos);
3366 vm_vec_sub(&target_vec,&targetp->pos,&Player_obj->pos);
3370 hud_set_iff_color( targetp, 1 );
3372 g3_project_vertex(&target_point);
3374 if (!(target_point.flags & PF_OVERFLOW)) { // make sure point projected
3375 int modelnum, bound_rval;
3377 switch ( targetp->type ) {
3379 modelnum = target_shipp->modelnum;
3380 bound_rval = model_find_2d_bound_min( modelnum, &targetp->orient, &targetp->pos,&x1,&y1,&x2,&y2 );
3384 Int3(); // should never happen
3388 if ( bound_rval == 0 ) {
3389 gr_set_color_fast(&IFF_colors[IFF_COLOR_SELECTION][1]);
3390 draw_bounding_brackets(x1-5,y1-5,x2+5,y2+5,5,5);
3391 if ( OBJ_INDEX(targetp) == Player_ai->target_objnum ) {
3392 HUD_drew_selection_bracket_on_target = 1;
3397 if ( hud_gauge_active(HUD_OFFSCREEN_INDICATOR) ) {
3398 if (target_point.codes != 0) { // target center is not on screen
3399 // draw the offscreen indicator at the edge of the screen where the target is closest to
3400 // AL 11-19-97: only show offscreen indicator if player sensors are functioning
3402 if ( OBJ_INDEX(targetp) != Player_ai->target_objnum ) {
3403 if ( hud_sensors_ok(Player_ship, 0) ) {
3405 gr_set_color_fast(&IFF_colors[IFF_COLOR_SELECTION][1]);
3406 //dist = vm_vec_dist_quick(&Player_obj->pos, &targetp->pos);
3407 dist = hud_find_target_distance( targetp, Player_obj );
3408 hud_draw_offscreen_indicator(&target_point, &targetp->pos, dist);
3416 void hud_show_brackets(object *targetp, vertex *projected_v)
3419 int draw_box = TRUE;
3422 if ( Player->target_is_dying <= 0 ) {
3425 switch ( targetp->type ) {
3427 modelnum = Ships[targetp->instance].modelnum;
3428 bound_rc = model_find_2d_bound_min( modelnum, &targetp->orient, &targetp->pos,&x1,&y1,&x2,&y2 );
3429 if ( bound_rc != 0 ) {
3435 modelnum = Debris[targetp->instance].model_num;
3436 bound_rc = submodel_find_2d_bound_min( modelnum, Debris[targetp->instance].submodel_num, &targetp->orient, &targetp->pos,&x1,&y1,&x2,&y2 );
3437 if ( bound_rc != 0 ) {
3443 SDL_assert(Weapon_info[Weapons[targetp->instance].weapon_info_index].subtype == WP_MISSILE);
3444 modelnum = Weapon_info[Weapons[targetp->instance].weapon_info_index].model_num;
3445 model_find_2d_bound_min( modelnum, &targetp->orient, &targetp->pos,&x1,&y1,&x2,&y2 );
3448 #if !(defined(FS2_DEMO) || defined(FS1_DEMO))
3452 subtype = Asteroids[targetp->instance].asteroid_subtype;
3453 modelnum = Asteroid_info[Asteroids[targetp->instance].type].model_num[subtype];
3454 model_find_2d_bound_min( modelnum, &targetp->orient, &targetp->pos,&x1,&y1,&x2,&y2 );
3460 modelnum = Jump_nodes[targetp->instance].modelnum;
3461 model_find_2d_bound_min( modelnum, &targetp->orient, &targetp->pos,&x1,&y1,&x2,&y2 );
3465 Int3(); // should never happen
3469 Hud_target_w = x2-x1+1;
3470 if ( Hud_target_w > gr_screen.clip_width ) {
3471 Hud_target_w = gr_screen.clip_width;
3474 Hud_target_h = y2-y1+1;
3475 if ( Hud_target_h > gr_screen.clip_height ) {
3476 Hud_target_h = gr_screen.clip_height;
3479 if ( targetp->type == OBJ_ASTEROID ) {
3480 if ( OBJ_INDEX(targetp) == Player_ai->target_objnum ) {
3481 team = TEAM_TRAITOR;
3483 team = SELECTION_SET;
3486 team = obj_team(targetp);
3489 if ( draw_box == TRUE ) {
3492 color = hud_brackets_get_iff_color(team);
3493 // maybe color as tagged
3494 if ( ship_is_tagged(targetp) ) {
3495 color = IFF_COLOR_TAGGED;
3497 distance = hud_find_target_distance( targetp, Player_obj );
3498 gr_set_color_fast(&IFF_colors[color][1]);
3499 draw_bounding_brackets(x1-5,y1-5,x2+5,y2+5,0,0,distance, OBJ_INDEX(targetp));
3502 if ( targetp->type == OBJ_SHIP ) {
3503 draw_bounding_brackets_subobject();
3508 void hud_update_target_in_reticle(vertex *projected_v)
3511 mag_squared = (projected_v->sx-Hud_reticle_center[gr_screen.res][0])*(projected_v->sx-Hud_reticle_center[gr_screen.res][0]) +
3512 (projected_v->sy-Hud_reticle_center[gr_screen.res][1])*(projected_v->sy-Hud_reticle_center[gr_screen.res][1]);
3514 if (mag_squared < Outer_circle_radius[gr_screen.res]*Outer_circle_radius[gr_screen.res]) {
3515 // this information can be used elsewhere
3516 Target_in_reticle = 1;
3519 // this information can be used elsewhere
3520 Target_in_reticle = 0;
3527 // hud_show_targeting_gauges() will display the targeting information on the HUD. Called once per frame.
3529 // Must be inside a g3_start_frame()
3530 // input: frametime => time in seconds since last update
3531 // in_cockpit => flag (default value 1) indicating whether viewpoint is from cockpit or external
3532 void hud_show_targeting_gauges(float frametime, int in_cockpit)
3534 vertex target_point; // temp vertex used to find screen position for 3-D object;
3537 // draw the triangle that points to the closest hostile ship that is firing on the player
3538 // This is always drawn, even if there is no current target. There is also a hook that will
3539 // maybe warn the player via a voice message of an attacking ship.
3541 hud_show_hostile_triangle();
3544 if (Player_ai->target_objnum == -1)
3547 object * targetp = &Objects[Player_ai->target_objnum];
3548 Players[Player_num].lead_indicator_active = 0;
3550 // check to see if there is even a current target
3551 if ( targetp == &obj_used_list ) {
3555 Target_in_reticle = 0;
3557 // AL 1/20/97: Point to targted subsystem if one exists
3558 if ( Player_ai->targeted_subsys != NULL ) {
3559 get_subsystem_world_pos(targetp, Player_ai->targeted_subsys, &target_pos);
3561 Player_ai->current_target_distance = vm_vec_dist_quick(&target_pos,&Player_obj->pos);
3563 target_pos = targetp->pos;
3565 Player_ai->current_target_distance = hud_find_target_distance(targetp,Player_obj);
3568 // find the current target vertex
3570 // The 2D screen pos depends on the current viewer position and orientation.
3571 g3_rotate_vertex(&target_point,&target_pos);
3574 hud_set_iff_color( targetp, 1 );
3575 g3_project_vertex(&target_point);
3577 if (!(target_point.flags & PF_OVERFLOW)) { // make sure point projected
3578 if (target_point.codes == 0) { // target center is not on screen
3579 hud_show_brackets(targetp, &target_point);
3581 hud_update_target_in_reticle(&target_point);
3588 // show the leading target indicator
3589 if ((hud_gauge_active(HUD_LEAD_INDICATOR)) && (!Player->target_is_dying)) {
3590 hud_show_lead_indicator(&target_pos);
3594 // show the indicator that orbits the outer reticle and points in the direction of the target
3595 hud_show_target_triangle_indicator(&target_point);
3597 // draw the orientation tee that orbits the inside of the outer circle of the reticle
3598 if ((hud_gauge_active(HUD_ORIENTATION_TEE)) && (!Player->target_is_dying)) {
3599 hud_show_orientation_tee();
3602 // display the information about the target
3603 if ( hud_gauge_active(HUD_TARGET_MONITOR) ){
3604 if ( !hud_targetbox_static_maybe_blit(frametime) )
3605 hud_show_target_data(frametime);
3608 // update cargo scanning
3609 hud_cargo_scan_update(targetp, frametime);
3611 // draw the shield icon for the current target
3612 if ( hud_gauge_active(HUD_TARGET_SHIELD_ICON) ) {
3613 hud_shield_show(targetp);
3616 // draw the mini target+shield gauge that sits near the bottom of the retcle
3617 if ( hud_gauge_active(HUD_TARGET_MINI_ICON) ) {
3618 int show_gauge_flag=1;
3619 // is gauge configured as a popup?
3620 if ( hud_gauge_is_popup(HUD_TARGET_MINI_ICON) ) {
3621 if ( !hud_gauge_popup_active(HUD_TARGET_MINI_ICON) ) {
3626 if ( show_gauge_flag ) {
3627 hud_shield_show_mini(targetp);
3631 Player->cargo_inspect_time = 0;
3632 player_stop_cargo_scan_sound();
3635 // display the lock indicator
3636 if (!Player->target_is_dying) {
3637 hud_update_lock_indicator(frametime);
3638 hud_show_lock_indicator(frametime);
3640 // update and render artillery
3641 hud_artillery_update();
3642 hud_artillery_render();
3645 // Point to offscreen target
3646 if ( hud_gauge_active(HUD_OFFSCREEN_INDICATOR) ) {
3647 if (target_point.codes != 0) { // target center is not on screen
3648 // draw the offscreen indicator at the edge of the screen where the target is closest to
3649 SDL_assert(Player_ai->target_objnum != -1);
3651 // AL 11-11-97: don't draw the indicator if the ship is messaging, the indicator is drawn
3652 // in the message sending color in hud_show_message_sender()
3653 if ( Message_shipnum != Objects[Player_ai->target_objnum].instance ) {
3654 if ( hud_gauge_maybe_flash(HUD_OFFSCREEN_INDICATOR) != 1) {
3656 hud_set_iff_color( targetp, 1 );
3657 //dist = vm_vec_dist_quick(&Player_obj->pos, &target_pos);
3658 dist = hud_find_target_distance( targetp, Player_obj );
3659 hud_draw_offscreen_indicator(&target_point, &target_pos, dist);
3666 // hud_show_hostile_triangle() will draw an empty triangle that oribits around the outer
3667 // circle of the reticle. It will point to the closest enemy that is firing on the player.
3668 // Currently, it points to the closest enemy that has the player as its target_objnum and has
3669 // SM_ATTACK or SM_SUPER_ATTACK as its ai submode.
3671 void hud_show_hostile_triangle()
3674 float min_distance=1e20f;
3675 float new_distance=0.0f;
3676 object* hostile_obj = &obj_used_list;
3677 object* nearest_obj = &obj_used_list;
3683 int player_obj_index = OBJ_INDEX(Player_obj);
3684 int turret_is_attacking = 0;
3686 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
3688 A = &Objects[so->objnum];
3689 sp = &Ships[A->instance];
3691 // only look at ships on other team
3692 if ( (A == Player_obj) || (Ships[A->instance].team & Player_ship->team) ) {
3696 aip = &Ai_info[Ships[A->instance].ai_index];
3698 // dont look at ignore ships
3699 if ( sp->flags & TARGET_SHIP_IGNORE_FLAGS ) {
3703 // always ignore cargo containers and navbuoys
3704 if ( Ship_info[sp->ship_info_index].flags & SIF_HARMLESS ) {
3708 // check if ship is stealthy
3709 if (awacs_get_level(&Objects[sp->objnum], Player_ship, 1) < 1) {
3713 turret_is_attacking = 0;
3715 // check if any turrets on ship are firing at the player (only on non fighter-bombers)
3716 if ( !(Ship_info[sp->ship_info_index].flags & (SIF_FIGHTER|SIF_BOMBER)) ) {
3717 for (ss = GET_FIRST(&sp->subsys_list); ss != END_OF_LIST(&sp->subsys_list); ss = GET_NEXT(ss) ) {
3718 if ( (ss->system_info->type == SUBSYSTEM_TURRET) && (ss->current_hits > 0) ) {
3720 if ( ss->turret_enemy_objnum == player_obj_index ) {
3721 turret_is_attacking = 1;
3724 // get world pos of subsystem
3725 vm_vec_unrotate(&gsubpos, &ss->system_info->pnt, &A->orient);
3726 vm_vec_add2(&gsubpos, &A->pos);
3727 new_distance = vm_vec_dist_quick(&gsubpos, &Player_obj->pos);
3729 if (new_distance <= min_distance) {
3730 min_distance=new_distance;
3738 if ( !turret_is_attacking ) {
3739 // check for ships attacking the player
3740 if ( aip->target_objnum != Player_ship->objnum ) {
3744 // ignore enemy if not in chase mode
3745 if ( (Game_mode & GM_NORMAL) && (aip->mode != AIM_CHASE) ) {
3749 new_distance = vm_vec_dist_quick(&A->pos, &Player_obj->pos);
3751 if (new_distance <= min_distance) {
3752 min_distance=new_distance;
3758 if ( nearest_obj == &obj_used_list ) {
3762 if ( min_distance > MIN_DISTANCE_TO_CONSIDER_THREAT ) {
3766 hostile_obj = nearest_obj;
3768 // hook to maybe warn player about this attacking ship
3769 ship_maybe_warn_player(&Ships[nearest_obj->instance], min_distance);
3771 // check if the closest firing hostile is the current target, if so return
3772 if (OBJ_INDEX(hostile_obj) == Player_ai->target_objnum)
3775 if ( hud_gauge_active(HUD_HOSTILE_TRIANGLE) ) {
3776 if ( hud_gauge_maybe_flash(HUD_HOSTILE_TRIANGLE) != 1 ) {
3777 // hud_set_iff_color( TEAM_HOSTILE, 1 ); // Note: This should really be TEAM_HOSTILE, not opposite of Player_ship->team.
3778 hud_set_iff_color( hostile_obj, 1 );
3779 hud_render_triangle(&hostile_obj->pos, 0, 1, 0);
3784 // Return the bank number for the primary weapon that can fire the farthest, from
3785 // the number of active primary weapons
3786 // input: range => output parameter... it is the range of the selected bank
3787 int hud_get_best_primary_bank(float *range)
3789 int i, best_bank, bank_to_fire, num_to_test;
3790 float weapon_range, farthest_weapon_range;
3794 swp = &Player_ship->weapons;
3796 farthest_weapon_range = 0.0f;
3799 if ( Player_ship->flags & SF_PRIMARY_LINKED ) {
3800 num_to_test = swp->num_primary_banks;
3802 num_to_test = min(1, swp->num_primary_banks);
3805 for ( i = 0; i < num_to_test; i++ ) {
3807 bank_to_fire = (swp->current_primary_bank+i)%2; // Max supported banks is 2
3809 // calculate the range of the weapon, and only display the lead target indicator when
3810 // if the weapon can actually hit the target
3811 SDL_assert(bank_to_fire >= 0);
3812 SDL_assert(swp->primary_bank_weapons[bank_to_fire] >= 0);
3813 wip = &Weapon_info[swp->primary_bank_weapons[bank_to_fire]];
3814 weapon_range = wip->max_speed * wip->lifetime;
3816 if ( weapon_range > farthest_weapon_range ) {
3817 best_bank = bank_to_fire;
3818 farthest_weapon_range = weapon_range;
3822 *range = farthest_weapon_range;
3826 // -----------------------------------------------------------------------------
3827 // polish_predicted_target_pos()
3829 // Called by the draw lead indicator code to predict where the enemy is going to be
3831 void polish_predicted_target_pos(vector *enemy_pos, vector *predicted_enemy_pos, float dist_to_enemy, vector *last_delta_vec, int num_polish_steps)
3834 vector player_pos = Player_obj->pos;
3835 float time_to_enemy;
3836 vector last_predicted_enemy_pos = *predicted_enemy_pos;
3839 shipp = &Ships[Player_obj->instance];
3840 SDL_assert(shipp->weapons.current_primary_bank < shipp->weapons.num_primary_banks);
3841 weapon_info *wip = &Weapon_info[shipp->weapons.primary_bank_weapons[shipp->weapons.current_primary_bank]];
3843 float weapon_speed = wip->max_speed;
3845 vm_vec_zero(last_delta_vec);
3847 for (iteration=0; iteration < num_polish_steps; iteration++) {
3848 dist_to_enemy = vm_vec_dist_quick(predicted_enemy_pos, &player_pos);
3849 time_to_enemy = dist_to_enemy/weapon_speed;
3850 // vm_vec_scale_add(predicted_enemy_pos, enemy_pos, &Objects[Player_ai->target_objnum].orient.fvec, en_physp->speed * time_to_enemy);
3851 vm_vec_scale_add(predicted_enemy_pos, enemy_pos, &Objects[Player_ai->target_objnum].phys_info.vel, time_to_enemy);
3852 vm_vec_sub(last_delta_vec, predicted_enemy_pos, &last_predicted_enemy_pos);
3853 last_predicted_enemy_pos= *predicted_enemy_pos;
3857 // determine the correct frame to draw for the lead indicator
3858 // 0 -> center only (in secondary range only)
3859 // 1 -> full (in secondary and primary range)
3860 // 2 -> oustide only (in primary range only)
3862 // input: prange => range of current primary weapon
3863 // srange => range of current secondary weapon
3864 // dist_to_target => current dist to target
3866 // exit: 0-2 => frame offset
3867 // -1 => don't draw anything
3868 int hudtarget_lead_indicator_pick_frame(float prange, float srange, float dist_to_target)
3870 int frame_offset=-1;
3871 int in_prange=0, in_srange=0;
3873 if ( dist_to_target < prange ) {
3877 if ( dist_to_target < srange ) {
3881 if ( in_prange && in_srange ) {
3883 } else if ( in_prange && !in_srange ) {
3885 } else if ( !in_prange && in_srange ) {
3891 return frame_offset;
3894 // decide what frame of lead indicator to draw
3896 // hud_show_lead_indicator() determine where to draw the lead target box and display it
3897 void hud_show_lead_indicator(vector *target_world_pos)
3899 vector target_moving_direction, last_delta_vector, source_pos;
3901 vertex lead_target_vertex;
3906 float dist_to_target, time_to_target, target_moved_dist, prange, srange;
3907 int bank_to_fire, indicator_frame, frame_offset;
3909 if (Player_ai->target_objnum == -1)
3912 targetp = &Objects[Player_ai->target_objnum];
3913 if ( (targetp->type != OBJ_SHIP) && (targetp->type != OBJ_WEAPON) && (targetp->type != OBJ_ASTEROID) ) {
3917 // only allow bombs to have lead indicator displayed
3918 if ( targetp->type == OBJ_WEAPON ) {
3919 if ( !(Weapon_info[Weapons[targetp->instance].weapon_info_index].wi_flags & WIF_BOMB) ) {
3924 // If the target is out of range, then draw the correct frame for the lead indicator
3925 if ( Lead_indicator_gauge.first_frame == -1 ) {
3930 po = model_get( Player_ship->modelnum );
3931 swp = &Player_ship->weapons;
3933 // Added to take care of situation where there are no primary banks on the player ship
3934 // (this may not be possible, depending on what we decide for the weapons loadout rules)
3935 if ( swp->num_primary_banks == 0 )
3938 bank_to_fire = hud_get_best_primary_bank(&prange);
3939 if ( bank_to_fire < 0 )
3941 wip = &Weapon_info[swp->primary_bank_weapons[bank_to_fire]];
3943 if (po->n_guns && bank_to_fire != -1 ) {
3944 rel_pos = &po->gun_banks[bank_to_fire].pnt[0];
3949 // source_pos will contain the world coordinate of where to base the lead indicator prediction
3950 // from. Normally, this will be the world pos of the gun turret of the currently selected primary
3952 source_pos = Player_obj->pos;
3953 if (rel_pos != NULL) {
3955 vm_vec_unrotate(&gun_point, rel_pos, &Player_obj->orient);
3956 vm_vec_add2(&source_pos, &gun_point);
3959 // Determine "accurate" distance to target. This is the distance from the player ship
3960 // to the closest point on the bounding box of the target
3961 dist_to_target = hud_find_target_distance(targetp, Player_obj);
3963 srange = ship_get_secondary_weapon_range(Player_ship);
3965 if ( swp->current_secondary_bank >= 0 ) {
3967 int bank = swp->current_secondary_bank;
3968 wip = &Weapon_info[swp->secondary_bank_weapons[bank]];
3969 if ( wip->wi_flags & WIF_HOMING_ASPECT ) {
3970 if ( !Player->target_in_lock_cone ) {
3976 frame_offset=hudtarget_lead_indicator_pick_frame(prange, srange, dist_to_target);
3977 if ( frame_offset < 0 ) {
3981 indicator_frame = Lead_indicator_gauge.first_frame + frame_offset;
3983 SDL_assert(wip->max_speed != 0);
3984 time_to_target = dist_to_target / wip->max_speed;
3986 target_moved_dist = targetp->phys_info.speed * time_to_target;
3988 target_moving_direction = targetp->phys_info.vel;
3990 // if we've reached here, the lead target indicator will be displayed
3991 Players[Player_num].lead_indicator_active = 1;
3993 // test if the target is moving at all
3994 if ( vm_vec_mag_quick(&targetp->phys_info.vel) < 0.1f) // Find distance!
3995 Players[Player_num].lead_target_pos = *target_world_pos;
3997 vm_vec_normalize(&target_moving_direction);
3998 vm_vec_scale(&target_moving_direction,target_moved_dist);
3999 vm_vec_add(&Players[Player_num].lead_target_pos, target_world_pos, &target_moving_direction );
4000 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)
4003 g3_rotate_vertex(&lead_target_vertex,&Players[Player_num].lead_target_pos);
4005 if (lead_target_vertex.codes == 0) { // on screen
4007 g3_project_vertex(&lead_target_vertex);
4008 if (!(lead_target_vertex.flags & PF_OVERFLOW)) {
4010 if ( hud_gauge_maybe_flash(HUD_LEAD_INDICATOR) == 1 ) {
4011 hud_set_iff_color(targetp, 0);
4013 hud_set_iff_color(targetp, 1);
4016 if ( indicator_frame >= 0 ) {
4017 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]));
4023 // hud_cease_subsystem_targeting() will cease targeting the current targets subsystems
4025 void hud_cease_subsystem_targeting(int print_message)
4029 ship_index = Objects[Player_ai->target_objnum].instance;
4030 if ( ship_index < 0 )
4033 Ships[ship_index].last_targeted_subobject[Player_num] = NULL;
4034 Player_ai->targeted_subsys = NULL;
4035 Player_ai->targeted_subsys_parent = -1;
4036 if ( print_message ) {
4037 HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Deactivating sub-system targeting", 324));
4040 hud_stop_looped_locking_sounds();
4044 // hud_cease_targeting() will cease all targeting (main target and subsystem)
4046 void hud_cease_targeting()
4048 set_target_objnum( Player_ai, -1 );
4049 Players[Player_num].flags &= ~PLAYER_FLAGS_AUTO_TARGETING;
4050 hud_cease_subsystem_targeting(0);
4051 HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Deactivating targeting system", 325));
4055 // hud_restore_subsystem_target() will remember the last targeted subsystem
4058 void hud_restore_subsystem_target(ship* shipp)
4060 // check if there was a previously targeted sub-system for this target
4061 if ( shipp->last_targeted_subobject[Player_num] != NULL ) {
4062 Player_ai->targeted_subsys = shipp->last_targeted_subobject[Player_num];
4063 Player_ai->targeted_subsys_parent = Player_ai->target_objnum;
4066 Player_ai->targeted_subsys = NULL;
4067 Player_ai->targeted_subsys_parent = -1;
4071 // --------------------------------------------------------------------------------
4072 // get_subsystem_world_pos() returns the world position for a given subobject on a ship
4074 vector* get_subsystem_world_pos(object* parent_obj, ship_subsys* subsys, vector* world_pos)
4076 if (subsys == NULL) {
4077 *world_pos = parent_obj->pos;
4081 vm_vec_unrotate(world_pos, &subsys->system_info->pnt, &parent_obj->orient);
4082 vm_vec_add2(world_pos, &parent_obj->pos);
4087 // If Pl_objp is docking, see if it (or the dockee) are in the target view. If so, flash dock
4089 void hud_maybe_flash_docking_text(object *objp)
4092 int docker_objnum, dockee_objnum;
4094 if ( Player_ai->target_objnum < 0 ) {
4098 if ( objp->type != OBJ_SHIP ) {
4102 aip = &Ai_info[Ships[objp->instance].ai_index];
4106 if ( aip->ai_flags & AIF_DOCKED ) {
4107 docker_objnum = OBJ_INDEX(objp);
4108 dockee_objnum = aip->dock_objnum;
4111 if ( (Player_ai->target_objnum == docker_objnum) || (Player_ai->target_objnum == dockee_objnum) ) {
4112 hud_targetbox_start_flash(TBOX_FLASH_DOCKED, 2000);
4117 // ----------------------------------------------------------------------------
4118 // hud_target_change_check()
4120 // called once per frame to account for when the target changes
4122 void hud_target_change_check()
4124 float current_speed=0.0f;
4126 // Check if player subsystem target has changed, and reset necessary player flag
4127 if ( Player_ai->targeted_subsys != Player_ai->last_subsys_target ) {
4128 Player->subsys_in_view=-1;
4131 // check if the main target has changed
4132 if (Player_ai->last_target != Player_ai->target_objnum) {
4134 if ( Player_ai->target_objnum != -1){
4135 snd_play( &Snds[SND_TARGET_ACQUIRE], 0.0f );
4138 // if we have a hotkey set active, see if new target is in set. If not in
4139 // set, deselect the current hotkey set.
4140 if ( Player->current_hotkey_set != -1 ) {
4141 htarget_list *hitem, *plist;
4143 plist = &(Player->keyed_targets[Player->current_hotkey_set]);
4144 for ( hitem = GET_FIRST(plist); hitem != END_OF_LIST(plist); hitem = GET_NEXT(hitem) ) {
4145 if ( OBJ_INDEX(hitem->objp) == Player_ai->target_objnum ){
4149 if ( hitem == END_OF_LIST(plist) ){
4150 Player->current_hotkey_set = -1;
4154 player_stop_cargo_scan_sound();
4155 hud_shield_hit_reset();
4156 hud_targetbox_init_flash();
4157 hud_targetbox_start_flash(TBOX_FLASH_NAME);
4158 hud_gauge_popup_start(HUD_TARGET_MINI_ICON);
4159 Player->cargo_inspect_time=0;
4160 Player->locking_subsys=NULL;
4161 Player->locking_on_center=0;
4162 Player->locking_subsys_parent=-1;
4164 Player_ai->current_target_dist_trend = NO_CHANGE;
4165 Player_ai->current_target_speed_trend = NO_CHANGE;
4167 if ( Players[Player_num].flags & PLAYER_FLAGS_AUTO_MATCH_SPEED ) {
4168 Players[Player_num].flags &= ~PLAYER_FLAGS_MATCH_TARGET;
4169 // player_match_target_speed("", "", XSTR("Matching speed of newly acquired target",-1));
4170 player_match_target_speed();
4173 if ( Players[Player_num].flags & PLAYER_FLAGS_MATCH_TARGET )
4174 Players[Player_num].flags &= ~PLAYER_FLAGS_MATCH_TARGET; // no more target matching.
4179 if ( Player_ai->target_objnum != -1) {
4180 if ( Objects[Player_ai->target_objnum].type == OBJ_SHIP ) {
4181 hud_restore_subsystem_target(&Ships[Objects[Player_ai->target_objnum].instance]);
4185 // if this target is docked, then flash DOCKING on the hud for a couple of seconds
4186 hud_targetbox_end_flash(TBOX_FLASH_DOCKED);
4187 if ( Player_ai->target_objnum >= 0 ) {
4188 hud_maybe_flash_docking_text(&Objects[Player_ai->target_objnum]);
4192 if (Player_ai->current_target_distance < Player_ai->last_dist-0.01){
4193 Player_ai->current_target_dist_trend = DECREASING;
4194 } else if (Player_ai->current_target_distance > Player_ai->last_dist+0.01){
4195 Player_ai->current_target_dist_trend = INCREASING;
4197 Player_ai->current_target_dist_trend = NO_CHANGE;
4200 current_speed = Objects[Player_ai->target_objnum].phys_info.speed;
4202 if (current_speed < Player_ai->last_speed-0.01){
4203 Player_ai->current_target_speed_trend = DECREASING;
4204 } else if (current_speed > Player_ai->last_speed+0.01) {
4205 Player_ai->current_target_speed_trend = INCREASING;
4207 Player_ai->current_target_speed_trend = NO_CHANGE;
4210 if ( Players[Player_num].flags & PLAYER_FLAGS_AUTO_MATCH_SPEED ) {
4211 if ( !(Players[Player_num].flags & PLAYER_FLAGS_MATCH_TARGET) ) {
4212 // player_match_target_speed("", "", XSTR("Matching target speed",-1));
4213 player_match_target_speed();
4218 Player_ai->last_dist = Player_ai->current_target_distance;
4219 Player_ai->last_speed = current_speed;
4221 Player_ai->last_target = Player_ai->target_objnum;
4222 Player_ai->last_subsys_target = Player_ai->targeted_subsys;
4225 // ---------------------------------------------------------------------
4226 // hud_draw_offscreen_indicator()
4228 // draws the offscreen target indicator
4230 void hud_draw_offscreen_indicator(vertex* target_point, vector *tpos, float distance)
4234 int on_top, on_right, on_left, on_bottom;
4237 // points to draw triangles
4251 vector targ_to_player;
4254 float half_gauge_length, half_triangle_sep;
4256 // calculate the dot product between the players forward vector and the vector connecting
4257 // the player to the target. Normalize targ_to_player since we want the dot product
4258 // to range between 0 -> 1.
4259 vm_vec_sub(&targ_to_player, &Player_obj->pos, tpos);
4260 vm_vec_normalize(&targ_to_player);
4261 dist_behind = vm_vec_dot(&Player_obj->orient.v.fvec, &targ_to_player);
4263 if (dist_behind < 0) { // still in front of player, but not in view
4264 dist_behind = dist_behind + 1.0f;
4265 if (dist_behind > 0.2 ){
4266 triangle_sep = ( dist_behind ) * Max_front_seperation[gr_screen.res];
4268 triangle_sep = 0.0f;
4272 triangle_sep = dist_behind * Max_offscreen_tri_seperation[gr_screen.res] + Max_offscreen_tri_seperation[gr_screen.res];
4275 if ( triangle_sep > Max_offscreen_tri_seperation[gr_screen.res] + Max_front_seperation[gr_screen.res]){
4276 triangle_sep = Max_offscreen_tri_seperation[gr_screen.res] + Max_front_seperation[gr_screen.res];
4279 // calculate these values only once, since it will be used in several places
4280 half_triangle_sep = 0.5f * triangle_sep;
4281 half_gauge_length = half_triangle_sep + Offscreen_tri_base[gr_screen.res];
4283 // We need to find the screen (x,y) for where to draw the offscreen indicator
4285 // The best way I've found is to draw a line from the eye_pos to the target, and
4286 // then use clip_line() to find the screen (x,y) for where the line hits the edge
4289 // The weird thing about clip_line() is that is flips around the two verticies,
4290 // so I use eye_vertex->sx and eye_vertex->sy for the off-screen indicator (x,y)
4292 vertex *eye_vertex = NULL;
4293 vertex real_eye_vertex;
4294 eye_vertex = &real_eye_vertex; // this is needed since clip line takes a **vertex
4296 vm_vec_add( &eye_pos, &Eye_position, &View_matrix.v.fvec);
4297 g3_rotate_vertex(eye_vertex, &eye_pos);
4300 codes_or = (ubyte)(target_point->codes | eye_vertex->codes);
4301 clip_line(&target_point,&eye_vertex,codes_or,0);
4303 if (!(target_point->flags&PF_PROJECTED))
4304 g3_project_vertex(target_point);
4306 if (!(eye_vertex->flags&PF_PROJECTED))
4307 g3_project_vertex(eye_vertex);
4309 if (eye_vertex->flags&PF_OVERFLOW) {
4310 Int3(); // This is unlikely to happen, but can if a clip goes through the player's eye.
4311 Player_ai->target_objnum = -1;
4315 if (target_point->flags & PF_TEMP_POINT)
4316 free_temp_point(target_point);
4318 if (eye_vertex->flags & PF_TEMP_POINT)
4319 free_temp_point(eye_vertex);
4321 xpos = eye_vertex->sx;
4322 ypos = eye_vertex->sy;
4324 on_left = on_right = on_top = on_bottom = 0;
4325 xpos = (xpos<1) ? 0 : xpos;
4326 ypos = (ypos<1) ? 0 : ypos;
4328 if ( xpos <= gr_screen.clip_left ) {
4329 xpos = i2fl(gr_screen.clip_left);
4332 if ( ypos < (half_gauge_length - gr_screen.clip_top) )
4333 ypos = half_gauge_length;
4336 if ( ypos > (gr_screen.clip_bottom - half_gauge_length) )
4337 ypos = gr_screen.clip_bottom - half_gauge_length;
4340 else if ( xpos >= gr_screen.clip_right) {
4341 xpos = i2fl(gr_screen.clip_right);
4344 if ( ypos < (half_gauge_length - gr_screen.clip_top) )
4345 ypos = half_gauge_length;
4347 if ( ypos > (gr_screen.clip_bottom - half_gauge_length) )
4348 ypos = gr_screen.clip_bottom - half_gauge_length;
4351 else if ( ypos <= gr_screen.clip_top ) {
4352 ypos = i2fl(gr_screen.clip_top);
4355 if ( xpos < ( half_gauge_length - gr_screen.clip_left) )
4356 xpos = half_gauge_length;
4358 if ( xpos > (gr_screen.clip_right - half_gauge_length) )
4359 xpos = gr_screen.clip_right - half_gauge_length;
4362 else if ( ypos >= gr_screen.clip_bottom ) {
4363 ypos = i2fl(gr_screen.clip_bottom);
4366 if ( xpos < ( half_gauge_length - gr_screen.clip_left) )
4367 xpos = half_gauge_length;
4369 if ( xpos > (gr_screen.clip_right - half_gauge_length) )
4370 xpos = gr_screen.clip_right - half_gauge_length;
4377 // The offscreen target triangles are drawn according the the diagram below
4386 // ......|...........|...............(xpos,ypos)
4395 xpos = (float)floor(xpos);
4396 ypos = (float)floor(ypos);
4398 if ( hud_gauge_active(HUD_OFFSCREEN_RANGE) && (distance > 0) ) {
4399 SDL_snprintf(buf, SDL_arraysize(buf), "%d", fl2i(distance+0.5f));
4400 hud_num_make_mono(buf);
4401 gr_get_string_size(&w, &h, buf);
4409 x2 = x3 = x5 = x6 = x1 - Offscreen_tri_height[gr_screen.res];
4410 y1 = y2 = ypos - half_triangle_sep;
4411 y3 = y2 - Offscreen_tri_base[gr_screen.res];
4413 y4 = y5 = ypos + half_triangle_sep;
4414 y6 = y5 + Offscreen_tri_base[gr_screen.res];
4417 gr_string( fl2i(xpos - w - 10), fl2i(ypos - h/2.0f+0.5f), buf);
4423 x2 = x3 = x5 = x6 = x1 + Offscreen_tri_height[gr_screen.res];
4424 y1 = y2 = ypos - half_triangle_sep;
4425 y3 = y2 - Offscreen_tri_base[gr_screen.res];
4427 y4 = y5 = ypos + half_triangle_sep;
4428 y6 = y5 + Offscreen_tri_base[gr_screen.res];
4431 gr_string(fl2i(xpos + 10), fl2i(ypos - h/2.0f+0.5f), buf);
4437 y2 = y3 = y5 = y6 = y1 + Offscreen_tri_height[gr_screen.res];
4438 x1 = x2 = xpos - half_triangle_sep;
4439 x3 = x2 - Offscreen_tri_base[gr_screen.res];
4441 x4 = x5 = xpos + half_triangle_sep;
4442 x6 = x5 + Offscreen_tri_base[gr_screen.res];
4445 gr_string(fl2i(xpos - w/2.0f+0.5f), fl2i(ypos+10), buf);
4448 else if (on_bottom) {
4451 y2 = y3 = y5 = y6 = y1 - Offscreen_tri_height[gr_screen.res];
4452 x1 = x2 = xpos - half_triangle_sep;
4453 x3 = x2 - Offscreen_tri_base[gr_screen.res];
4455 x4 = x5 = xpos + half_triangle_sep;
4456 x6 = x5 + Offscreen_tri_base[gr_screen.res];
4459 gr_string(fl2i(xpos - w/2.0f+0.5f), fl2i(ypos-h-10), buf);
4463 hud_tri(x3,y3,x2,y2,x1,y1);
4464 hud_tri(x4,y4,x5,y5,x6,y6);
4465 if (on_right || on_bottom){
4466 gr_line(fl2i(x2),fl2i(y2),fl2i(x5),fl2i(y5));
4467 } else if (on_left) {
4468 gr_line(fl2i(x2-1),fl2i(y2),fl2i(x5-1),fl2i(y5));
4470 gr_line(fl2i(x2),fl2i(y2-1),fl2i(x5),fl2i(y5-1));
4475 // Render the HUD afterburner energy gauge
4476 void hud_show_afterburner_gauge()
4481 if ( Energy_bar_gauges.first_frame == -1 ){
4485 SDL_assert(Player_ship);
4486 if ( !(Ship_info[Player_ship->ship_info_index].flags & SIF_AFTERBURNER) ) {
4487 percent_left = 0.0f;
4489 percent_left = Player_ship->afterburner_fuel/Ship_info[Player_ship->ship_info_index].afterburner_fuel_capacity;
4492 if ( percent_left > 1 ) {
4493 percent_left = 1.0f;
4496 clip_h = fl2i( (1.0f - percent_left) * Aburn_coords[gr_screen.res][3] + 0.5f );
4498 bm_get_info(Energy_bar_gauges.first_frame,&w,&h);
4501 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);
4504 if ( clip_h <= Aburn_coords[gr_screen.res][3] ) {
4505 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);
4509 // Render the player weapon energy on the HUD
4510 void hud_show_weapon_energy_gauge()
4513 int clip_h, i, w, h;
4515 if ( Energy_bar_gauges.first_frame == -1 ){
4519 if ( Player_ship->weapons.num_primary_banks <= 0 ){
4523 percent_left = Player_ship->weapon_energy/Ship_info[Player_ship->ship_info_index].max_weapon_reserve;
4524 if ( percent_left > 1 ) {
4525 percent_left = 1.0f;
4528 if ( percent_left <= 0.3 ) {
4530 if ( percent_left < 0.1 ) {
4531 gr_set_color_fast(&Color_bright_red);
4533 SDL_snprintf(buf, SDL_arraysize(buf), XSTR( "%d%%", 326), fl2i(percent_left*100+0.5f));
4534 hud_num_make_mono(buf);
4535 gr_string(Weapon_energy_text_coords[gr_screen.res][0], Weapon_energy_text_coords[gr_screen.res][1], buf);
4538 hud_set_gauge_color(HUD_WEAPONS_ENERGY);
4539 for ( i = 0; i < Player_ship->weapons.num_primary_banks; i++ ) {
4540 if ( !timestamp_elapsed(Weapon_flash_info.flash_duration[i]) ) {
4541 if ( Weapon_flash_info.is_bright & (1<<i) ) {
4542 // hud_set_bright_color();
4543 hud_set_gauge_color(HUD_WEAPONS_ENERGY, HUD_C_BRIGHT);
4549 clip_h = fl2i( (1.0f - percent_left) * Wenergy_coords[gr_screen.res][3] + 0.5f );
4551 bm_get_info(Energy_bar_gauges.first_frame+2,&w,&h);
4554 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);
4557 if ( clip_h <= Wenergy_coords[gr_screen.res][3] ) {
4558 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);
4561 // hud_set_default_color();
4564 // --------------------------------------------------------------------------------------
4565 // hud_show_target_triangle_indicator()
4567 // Draw the solid triangle that orbits the reticle and points to the nearest target
4569 void hud_show_target_triangle_indicator(vertex *projected_v)
4572 float xpos,ypos,ang;
4574 if ( Player_ai->target_objnum == -1)
4577 object *targetp = &Objects[Player_ai->target_objnum];
4579 // draw the targeting triangle that orbits the outside of the outer circle of the reticle
4580 if ((hud_gauge_active(HUD_TARGET_TRIANGLE)) && !Player->target_is_dying && !Target_in_reticle) {
4581 if ( hud_gauge_maybe_flash(HUD_TARGET_TRIANGLE) == 1 ) {
4585 hud_set_iff_color(targetp, 1);
4587 ang = atan2_safe(projected_v->y,projected_v->x);
4588 xpos = Hud_reticle_center[gr_screen.res][0] + (float)cos(ang)*(Outer_circle_radius[gr_screen.res]+4);
4589 ypos = Hud_reticle_center[gr_screen.res][1] - (float)sin(ang)*(Outer_circle_radius[gr_screen.res]+4);
4591 xpos += HUD_offset_x;
4592 ypos += HUD_offset_y;
4594 x3 = xpos - Target_triangle_base[gr_screen.res] * (float)sin(-ang);
4595 y3 = ypos + Target_triangle_base[gr_screen.res] * (float)cos(-ang);
4596 x4 = xpos + Target_triangle_base[gr_screen.res] * (float)sin(-ang);
4597 y4 = ypos - Target_triangle_base[gr_screen.res] * (float)cos(-ang);
4599 xpos += Target_triangle_height[gr_screen.res] * (float)cos(ang);
4600 ypos -= Target_triangle_height[gr_screen.res] * (float)sin(ang);
4602 hud_tri(xpos,ypos,x3,y3,x4,y4);
4606 // called from hud_show_weapons() to plot out the secondary weapon name and amo
4607 void hud_show_secondary_weapon(int count, ship_weapon *sw, int dual_fire)
4610 char weapon_name[NAME_LENGTH + 10];
4615 if ( sw->num_primary_banks == 2 ) {
4619 for ( i = 0; i < count; i++ ) {
4620 hud_maybe_flash_weapon(sw->num_primary_banks+i);
4621 wip = &Weapon_info[sw->secondary_bank_weapons[i]];
4623 // HACK - make Cluster Bomb fit on the HUD.
4624 if(!SDL_strcasecmp(wip->name,"cluster bomb")){
4625 SDL_strlcpy(weapon_name, NOX("Cluster"), SDL_arraysize(weapon_name));
4627 SDL_strlcpy(weapon_name, wip->name, SDL_arraysize(weapon_name));
4630 hud_end_string_at_first_hash_symbol(weapon_name);
4632 if ( sw->current_secondary_bank == i ) {
4633 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);
4636 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);
4639 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);
4641 if ( (sw->secondary_bank_ammo[i] > 0) && (sw->current_secondary_bank >= 0) ) {
4642 int ms_till_fire = timestamp_until(sw->next_secondary_fire_stamp[sw->current_secondary_bank]);
4643 if ( (ms_till_fire >= 500) && ((wip->fire_wait >= 1 ) || (ms_till_fire > wip->fire_wait*1000)) ) {
4644 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));
4648 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);
4651 // print out the ammo right justified
4652 SDL_snprintf(ammo_str, SDL_arraysize(ammo_str), "%d", sw->secondary_bank_ammo[i]);
4653 hud_num_make_mono(ammo_str);
4654 gr_get_string_size(&w, &h, ammo_str);
4656 emp_hud_string(Weapon_secondary_ammo_x[gr_screen.res] - w, Weapon_secondary_y[gr_screen.res][i] - np*12, EG_NULL, ammo_str);
4658 hud_set_gauge_color(HUD_WEAPONS_GAUGE);
4662 // start the weapon line (on the HUD) flashing
4663 void hud_start_flash_weapon(int index)
4665 if ( index >= MAX_WEAPON_FLASH_LINES ) {
4670 if ( timestamp_elapsed(Weapon_flash_info.flash_duration[index]) ) {
4671 Weapon_flash_info.flash_next[index] = timestamp(TBOX_FLASH_INTERVAL);
4672 Weapon_flash_info.is_bright &= ~(1<<index);
4675 Weapon_flash_info.flash_duration[index] = timestamp(TBOX_FLASH_DURATION);
4678 // maybe change the text color for the weapon line indicated by index
4679 void hud_maybe_flash_weapon(int index)
4681 if ( index >= MAX_WEAPON_FLASH_LINES ) {
4686 // hud_set_default_color();
4687 hud_set_gauge_color(HUD_WEAPONS_GAUGE);
4688 if ( !timestamp_elapsed(Weapon_flash_info.flash_duration[index]) ) {
4689 if ( timestamp_elapsed(Weapon_flash_info.flash_next[index]) ) {
4690 Weapon_flash_info.flash_next[index] = timestamp(TBOX_FLASH_INTERVAL);
4691 Weapon_flash_info.is_bright ^= (1<<index);
4694 if ( Weapon_flash_info.is_bright & (1<<index) ) {
4695 hud_set_gauge_color(HUD_WEAPONS_GAUGE, HUD_C_BRIGHT);
4696 // hud_set_bright_color();
4698 hud_set_gauge_color(HUD_WEAPONS_GAUGE, HUD_C_DIM);
4699 // hud_set_dim_color();
4704 // render the coutermeasure HUD gauge
4705 void hud_show_cmeasure_gague()
4707 if ( Cmeasure_gauge.first_frame == -1 ) {
4708 Int3(); // failed to load coutermeasure gauge background
4712 // hud_set_default_color();
4713 hud_set_gauge_color(HUD_CMEASURE_GAUGE);
4715 // blit the background
4716 GR_AABITMAP(Cmeasure_gauge.first_frame, Cm_coords[gr_screen.res][0], Cm_coords[gr_screen.res][1]);
4719 gr_string(Cm_text_coords[gr_screen.res][0], Cm_text_coords[gr_screen.res][1], XSTR( "cm.", 327));
4720 if ( !Player_ship ) {
4721 Int3(); // player ship doesn't exist?
4724 gr_printf(Cm_text_val_coords[gr_screen.res][0], Cm_text_val_coords[gr_screen.res][1], NOX("%02d"),Player_ship->cmeasure_count);
4728 // ------------------------------------------------------------------
4729 // hud_show_weapons()
4731 // Show the player's primary and secondary weapons, along with ammo and % energy
4733 void hud_show_weapons()
4736 int np, ns; // np == num primary, ns == num secondary
4737 char name[NAME_LENGTH];
4739 if(Player_obj->type == OBJ_OBSERVER)
4742 SDL_assert(Player_obj->type == OBJ_SHIP);
4743 SDL_assert(Player_obj->instance >= 0 && Player_obj->instance < MAX_SHIPS);
4745 sw = &Ships[Player_obj->instance].weapons;
4747 np = sw->num_primary_banks;
4748 ns = sw->num_secondary_banks;
4750 // NOTE: I hate to hard-code numbers, but there is no clean way to organize these coords... they
4751 // are all over the place. UGLY.
4753 // BAH. You're a moron, above guy. :)
4755 hud_set_gauge_color(HUD_WEAPONS_GAUGE);
4757 // draw top of primary display
4758 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]);
4760 emp_hud_string(Weapon_title_coords[gr_screen.res][0], Weapon_title_coords[gr_screen.res][1], EG_WEAPON_TITLE, XSTR( "weapons", 328));
4764 // draw bottom of border
4765 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]);
4767 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));
4773 // draw bottom of border
4774 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]);
4776 SDL_strlcpy(name, Weapon_info[sw->primary_bank_weapons[0]].name, SDL_arraysize(name));
4778 lcl_translate_wep_name(name, SDL_arraysize(name));
4781 // maybe modify name here to fit
4782 if ( hud_gauge_maybe_flash(HUD_WEAPONS_GAUGE) == 1 ) {
4783 // hud_set_bright_color();
4784 hud_set_gauge_color(HUD_WEAPONS_GAUGE, HUD_C_BRIGHT);
4786 hud_maybe_flash_weapon(0);
4789 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);
4790 emp_hud_printf(Weapon_pname_coords[gr_screen.res][0][0], Weapon_pname_coords[gr_screen.res][0][1], EG_WEAPON_P2, "%s", name);
4794 // draw border to accomodate second primary weapon
4795 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]);
4797 // draw bottom of border
4798 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]);
4800 SDL_strlcpy(name, Weapon_info[sw->primary_bank_weapons[0]].name, SDL_arraysize(name));
4802 lcl_translate_wep_name(name, SDL_arraysize(name));
4804 // maybe modify name here to fit
4806 if ( hud_gauge_maybe_flash(HUD_WEAPONS_GAUGE) == 1 ) {
4807 // hud_set_bright_color();
4808 hud_set_gauge_color(HUD_WEAPONS_GAUGE, HUD_C_BRIGHT);
4810 hud_maybe_flash_weapon(0);
4812 if ( (sw->current_primary_bank == 0) || (Player_ship->flags & SF_PRIMARY_LINKED) ) {
4813 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);
4815 emp_hud_printf(Weapon_pname_coords[gr_screen.res][0][0], Weapon_pname_coords[gr_screen.res][0][1], EG_WEAPON_P1, "%s", name);
4817 SDL_strlcpy(name, Weapon_info[sw->primary_bank_weapons[1]].name, SDL_arraysize(name));
4819 lcl_translate_wep_name(name, SDL_arraysize(name));
4821 // maybe modify name here to fit
4822 if ( hud_gauge_maybe_flash(HUD_WEAPONS_GAUGE) == 1 ) {
4823 // hud_set_bright_color();
4824 hud_set_gauge_color(HUD_WEAPONS_GAUGE, HUD_C_BRIGHT);
4826 hud_maybe_flash_weapon(1);
4828 if ( sw->current_primary_bank == 1 || (Player_ship->flags & SF_PRIMARY_LINKED) ) {
4829 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);
4831 emp_hud_printf(Weapon_pname_coords[gr_screen.res][1][0], Weapon_pname_coords[gr_screen.res][1][1], EG_WEAPON_P2, "%s", name);
4836 Int3(); // can't happen - get Alan
4841 hud_set_gauge_color(HUD_WEAPONS_GAUGE);
4845 // draw the bottom of the secondary weapons
4846 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);
4848 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));
4852 // draw the bottom of the secondary weapons
4853 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);
4855 hud_show_secondary_weapon(1, sw, Player_ship->flags & SF_SECONDARY_DUAL_FIRE);
4859 // draw the middle border, only present when there are 2 or more secondaries
4860 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);
4862 // draw the bottom of the secondary weapons
4863 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);
4865 hud_show_secondary_weapon(2, sw, Player_ship->flags & SF_SECONDARY_DUAL_FIRE);
4869 // draw the middle border, only present when there are 2 or more secondaries
4870 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);
4872 // draw the bottm border, only present when there are 3 secondaries
4873 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);
4875 // draw the bottom of the secondary weapons
4876 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);
4878 hud_show_secondary_weapon(3, sw, Player_ship->flags & SF_SECONDARY_DUAL_FIRE);
4882 Int3(); // can't happen - get Alan
4888 // check if targeting is possible based on sensors strength
4889 int hud_sensors_ok(ship *sp, int show_msg)
4893 // If playing on lowest skill level, sensors don't affect targeting
4894 // If dead, still allow player to target, despite any subsystem damage
4895 // If i'm a multiplayer observer, allow me to target
4896 if ( (Game_skill_level == 0) || (Game_mode & GM_DEAD) || ((Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_OBSERVER)) ) {
4900 // if the ship is currently being affected by EMP
4901 if(emp_active_local()){
4905 // ensure targeting functions are not disabled through damage
4906 sensors_str = ship_get_subsystem_strength( sp, SUBSYSTEM_SENSORS );
4907 if ( (sensors_str < MIN_SENSOR_STR_TO_TARGET) || (ship_subsys_disrupted(sp, SUBSYSTEM_SENSORS)) ) {
4909 HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Targeting is disabled due to sensors damage", 330));
4910 snd_play(&Snds[SND_TARGET_FAIL]);
4918 int hud_communications_state(ship *sp, int show_msg)
4921 int comm_state = COMM_OK;
4923 // If playing on the lowest skill level, communications always ok
4924 // If dead, still allow player to communicate, despite any subsystem damage
4925 if ( Game_skill_level == 0 || (Game_mode & GM_DEAD) ) {
4929 str = ship_get_subsystem_strength( sp, SUBSYSTEM_COMMUNICATION );
4930 // str = 1.0f; // DEBUG CODE! MK, change, 11/12/97, comm system could be taken out by one laser, too frustrating.
4931 // Change this back when comm systems have been better placed.
4933 if ( (str <= 0.01) || ship_subsys_disrupted(sp, SUBSYSTEM_COMMUNICATION) ) {
4935 HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Messaging is restricted due to communications damage", 331));
4937 comm_state = COMM_DESTROYED;
4938 } else if ( str < MIN_COMM_STR_TO_MESSAGE ) {
4939 comm_state = COMM_DAMAGED;
4945 // target the next or previous hostile/friendly ship
4946 void hud_target_next_list(int hostile, int next_flag)
4948 object *A, *min_obj, *max_obj, *nearest_obj;
4951 // vector target_vec;
4952 float cur_dist, min_dist, max_dist, new_dist, nearest_dist, diff;
4953 int timestamp_val, valid_team;
4956 timestamp_val = Tl_hostile_reset_timestamp;
4957 Tl_hostile_reset_timestamp = timestamp(TL_RESET);
4958 valid_team = opposing_team_mask(Player_ship->team);
4960 timestamp_val = Tl_friendly_reset_timestamp;
4961 Tl_friendly_reset_timestamp = timestamp(TL_RESET);
4962 valid_team = Player_ship->team;
4965 // If no target is selected, then simply target the closest ship
4966 if ( Player_ai->target_objnum == -1 || timestamp_elapsed(timestamp_val) ) {
4967 hud_target_closest(valid_team);
4971 cur_dist = hud_find_target_distance(&Objects[Player_ai->target_objnum],Player_obj);
4973 min_obj = max_obj = nearest_obj = NULL;
4977 nearest_dist = 1e20f;
4979 nearest_dist = 0.0f;
4982 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
4983 A = &Objects[so->objnum];
4984 shipp = &Ships[A->instance]; // get a pointer to the ship information
4986 if ( (A == Player_obj) || (shipp->flags & TARGET_SHIP_IGNORE_FLAGS) )
4989 // choose from the correct team
4990 if ( !hud_team_matches_filter(valid_team, shipp->team) ) {
4991 // if we're in multiplayer dogfight, ignore this
4992 if(!((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_DOGFIGHT))){
4997 // always ignore navbuoys and cargo
4998 if ( Ship_info[shipp->ship_info_index].flags & (SIF_CARGO | SIF_NAVBUOY) ) {
5002 // don't use object if it is already a target
5003 if ( OBJ_INDEX(A) == Player_ai->target_objnum ) {
5007 if(hud_target_invalid_awacs(A)){
5011 new_dist = hud_find_target_distance(A,Player_obj);
5013 if (new_dist <= min_dist) {
5014 min_dist = new_dist;
5018 if (new_dist >= max_dist) {
5019 max_dist = new_dist;
5024 diff = new_dist - cur_dist;
5026 if ( diff < ( nearest_dist - cur_dist ) ) {
5027 nearest_dist = new_dist;
5032 diff = cur_dist - new_dist;
5034 if ( diff < ( cur_dist - nearest_dist ) ) {
5035 nearest_dist = new_dist;
5042 if ( nearest_obj == NULL ) {
5045 if ( min_obj != NULL ) {
5046 nearest_obj = min_obj;
5049 if ( max_obj != NULL ) {
5050 nearest_obj = max_obj;
5055 if (nearest_obj != NULL) {
5057 set_target_objnum( Player_ai, OBJ_INDEX(nearest_obj) );
5059 // maybe set new turret subsystem
5060 hud_maybe_set_sorted_turret_subsys(&Ships[nearest_obj->instance]);
5061 hud_restore_subsystem_target(&Ships[nearest_obj->instance]);
5064 snd_play( &Snds[SND_TARGET_FAIL], 0.0f );
5068 // draw auto-target icon
5069 void hud_auto_target_icon()
5073 if ( Players[Player_num].flags & PLAYER_FLAGS_AUTO_TARGETING ) {
5079 // draw the box background
5080 hud_set_gauge_color(HUD_AUTO_TARGET);
5081 GR_AABITMAP(Toggle_gauge.first_frame+frame_offset, Toggle_target_gauge_coords[gr_screen.res][0], Toggle_target_gauge_coords[gr_screen.res][1]);
5083 #ifndef MAKE_FS1 // Text already on bitmap
5084 // draw the text on top
5085 if (frame_offset == 1) {
5087 gr_init_alphacolor(&text_color, 0, 0, 0, Toggle_text_alpha, AC_TYPE_HUD);
5088 gr_set_color_fast(&text_color);
5091 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));
5092 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));
5096 // draw auto-speed match icon
5097 void hud_auto_speed_match_icon()
5101 if ( Players[Player_num].flags & PLAYER_FLAGS_AUTO_MATCH_SPEED ) {
5107 hud_set_gauge_color(HUD_AUTO_SPEED);
5109 GR_AABITMAP(Toggle_gauge.first_frame+frame_offset, Toggle_speed_gauge_coords[gr_screen.res][0], Toggle_speed_gauge_coords[gr_screen.res][1]);
5111 #ifndef MAKE_FS1 // Text already on bitmap
5112 // draw the text on top
5113 if (frame_offset == 3) {
5115 gr_init_alphacolor(&text_color, 0, 0, 0, Toggle_text_alpha, AC_TYPE_HUD);
5116 gr_set_color_fast(&text_color);
5118 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));
5119 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));
5123 // display the auto-targeting and auto-speed-matching icons on the HUD
5124 void hud_show_auto_icons()
5127 if ( Toggle_gauge.first_frame == -1 )
5130 // display auto target icon
5131 if ( hud_gauge_active(HUD_AUTO_TARGET) ) {
5133 // is gauge configured as a popup?
5134 if ( hud_gauge_is_popup(HUD_AUTO_TARGET) ) {
5135 if ( !hud_gauge_popup_active(HUD_AUTO_TARGET) ) {
5141 hud_auto_target_icon();
5145 // display auto speed match icon
5146 if ( hud_gauge_active(HUD_AUTO_SPEED) ) {
5148 // is gauge configured as a popup?
5149 if ( hud_gauge_is_popup(HUD_AUTO_SPEED) ) {
5150 if ( !hud_gauge_popup_active(HUD_AUTO_SPEED) ) {
5156 hud_auto_speed_match_icon();
5161 // Set the player target to the closest friendly repair ship
5162 // input: goal_objnum => Try to find repair ship where aip->goal_objnum matches this
5163 // output: 1 => A repair ship was targeted
5164 // 0 => No targeting change
5165 int hud_target_closest_repair_ship(int goal_objnum)
5168 object *nearest_obj=&obj_used_list;
5171 float min_distance=1e20f;
5172 float new_distance=0.0f;
5175 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5176 A = &Objects[so->objnum];
5177 shipp = &Ships[A->instance]; // get a pointer to the ship information
5179 // ignore all ships that aren't repair ships
5180 if ( !(Ship_info[shipp->ship_info_index].flags & SIF_SUPPORT) ) {
5184 if ( (A == Player_obj) || (shipp->flags & TARGET_SHIP_IGNORE_FLAGS) )
5187 // only consider friendly ships
5188 if ( !hud_team_matches_filter(Player_ship->team, shipp->team)) {
5189 // if we're in multiplayer dogfight, ignore this
5190 if(!((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_DOGFIGHT))){
5195 if(hud_target_invalid_awacs(A)){
5199 if ( goal_objnum >= 0 ) {
5200 if ( Ai_info[shipp->ai_index].goal_objnum != goal_objnum ) {
5205 new_distance = hud_find_target_distance(A,Player_obj);
5207 if (new_distance <= min_distance) {
5208 min_distance=new_distance;
5213 if (nearest_obj != &obj_used_list) {
5214 set_target_objnum( Player_ai, OBJ_INDEX(nearest_obj) );
5215 hud_restore_subsystem_target(&Ships[nearest_obj->instance]);
5219 // inform player how to get a support ship
5220 if ( goal_objnum == -1 ) {
5221 HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "No support ships in area. Use messaging to call one in.", 332));
5229 void hud_target_toggle_hidden_from_sensors()
5231 if ( TARGET_SHIP_IGNORE_FLAGS & SF_HIDDEN_FROM_SENSORS ) {
5232 TARGET_SHIP_IGNORE_FLAGS &= ~SF_HIDDEN_FROM_SENSORS;
5233 HUD_sourced_printf(HUD_SOURCE_HIDDEN, NOX("Target hiding from sensors disabled"));
5235 TARGET_SHIP_IGNORE_FLAGS |= SF_HIDDEN_FROM_SENSORS;
5236 HUD_sourced_printf(HUD_SOURCE_HIDDEN, NOX("Target hiding from sensors enabled"));
5240 // target the closest uninspected object
5241 void hud_target_closest_uninspected_object()
5243 object *A, *nearest_obj = NULL;
5246 float min_distance = 1e20f;
5247 float new_distance = 0.0f;
5249 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5251 A = &Objects[so->objnum];
5252 shipp = &Ships[A->instance]; // get a pointer to the ship information
5254 if ( (A == Player_obj) || (shipp->flags & TARGET_SHIP_IGNORE_FLAGS) ){
5258 if(hud_target_invalid_awacs(A)){
5262 // ignore all non-cargo carrying craft
5263 if ( !hud_target_ship_can_be_scanned(shipp) ) {
5267 new_distance = hud_find_target_distance(A,Player_obj);
5269 if (new_distance <= min_distance) {
5270 min_distance=new_distance;
5275 if (nearest_obj != NULL) {
5276 set_target_objnum( Player_ai, OBJ_INDEX(nearest_obj) );
5277 hud_restore_subsystem_target(&Ships[nearest_obj->instance]);
5280 snd_play( &Snds[SND_TARGET_FAIL] );
5284 // target the next or previous uninspected/unscanned object
5285 void hud_target_uninspected_object(int next_flag)
5287 object *A, *min_obj, *max_obj, *nearest_obj;
5290 float cur_dist, min_dist, max_dist, new_dist, nearest_dist, diff;
5292 // If no target is selected, then simply target the closest uninspected cargo
5293 if ( Player_ai->target_objnum == -1 || timestamp_elapsed(Target_next_uninspected_object_timestamp) ) {
5294 Target_next_uninspected_object_timestamp = timestamp(TL_RESET);
5295 hud_target_closest_uninspected_object();
5299 Target_next_uninspected_object_timestamp = timestamp(TL_RESET);
5301 cur_dist = hud_find_target_distance(&Objects[Player_ai->target_objnum], Player_obj);
5303 min_obj = max_obj = nearest_obj = NULL;
5307 nearest_dist = 1e20f;
5309 nearest_dist = 0.0f;
5312 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5313 A = &Objects[so->objnum];
5314 shipp = &Ships[A->instance]; // get a pointer to the ship information
5316 if ( (A == Player_obj) || (shipp->flags & TARGET_SHIP_IGNORE_FLAGS) )
5319 // ignore all non-cargo carrying craft
5320 if ( !hud_target_ship_can_be_scanned(shipp) ) {
5324 // don't use object if it is already a target
5325 if ( OBJ_INDEX(A) == Player_ai->target_objnum ) {
5329 if(hud_target_invalid_awacs(A)){
5333 new_dist = hud_find_target_distance(A, Player_obj);
5335 if (new_dist <= min_dist) {
5336 min_dist = new_dist;
5340 if (new_dist >= max_dist) {
5341 max_dist = new_dist;
5346 diff = new_dist - cur_dist;
5348 if ( diff < ( nearest_dist - cur_dist ) ) {
5349 nearest_dist = new_dist;
5354 diff = cur_dist - new_dist;
5356 if ( diff < ( cur_dist - nearest_dist ) ) {
5357 nearest_dist = new_dist;
5364 if ( nearest_obj == NULL ) {
5367 if ( min_obj != NULL ) {
5368 nearest_obj = min_obj;
5371 if ( max_obj != NULL ) {
5372 nearest_obj = max_obj;
5377 if (nearest_obj != NULL) {
5378 set_target_objnum( Player_ai, OBJ_INDEX(nearest_obj) );
5379 hud_restore_subsystem_target(&Ships[nearest_obj->instance]);
5382 snd_play( &Snds[SND_TARGET_FAIL] );
5386 // ----------------------------------------------------------------
5388 // Target Last Transmission Sender code START
5390 // ----------------------------------------------------------------
5392 typedef struct transmit_target
5398 static int Transmit_target_next_slot = 0;
5399 static int Transmit_target_current_slot = -1;
5400 static int Transmit_target_reset_timer = timestamp(0);
5402 #define MAX_TRANSMIT_TARGETS 10
5403 static transmit_target Transmit_target_list[MAX_TRANSMIT_TARGETS];
5405 // called once per level to initialize the target last transmission sender list
5406 void hud_target_last_transmit_level_init()
5410 for ( i = 0; i < MAX_TRANSMIT_TARGETS; i++ ) {
5411 Transmit_target_list[i].objnum = -1;
5412 Transmit_target_list[i].objsig = -1;
5415 Transmit_target_next_slot = 0;
5416 Transmit_target_current_slot = 0;
5417 Transmit_target_reset_timer = timestamp(0);
5420 // internal function only.. used to find index for last recorded ship transmission
5421 int hud_target_last_transmit_newest()
5425 latest_slot = Transmit_target_next_slot - 1;
5426 if ( latest_slot < 0 ) {
5427 latest_slot = MAX_TRANSMIT_TARGETS - 1;
5433 // called externally to set the player target to the last ship which sent a tranmission to the player
5434 void hud_target_last_transmit()
5438 if ( Transmit_target_current_slot < 0 ) {
5439 Transmit_target_current_slot = hud_target_last_transmit_newest();
5442 // If timed out, then simply target the last ship to transmit
5443 if ( timestamp_elapsed(Transmit_target_reset_timer) ) {
5444 Transmit_target_current_slot = hud_target_last_transmit_newest();
5447 Transmit_target_reset_timer = timestamp(TL_RESET);
5449 int play_fail_sound = 1;
5450 int transmit_index = Transmit_target_current_slot;
5451 SDL_assert(transmit_index >= 0);
5452 for ( i = 0; i < MAX_TRANSMIT_TARGETS; i++ ) {
5453 if ( Transmit_target_list[transmit_index].objnum >= 0 ) {
5454 int transmit_objnum = Transmit_target_list[transmit_index].objnum;
5456 if ( Player_ai->target_objnum == transmit_objnum ) {
5457 play_fail_sound = 0;
5459 if ( Transmit_target_list[transmit_index].objsig == Objects[Transmit_target_list[transmit_index].objnum].signature ) {
5460 if ( !(Ships[Objects[transmit_objnum].instance].flags & TARGET_SHIP_IGNORE_FLAGS) ) {
5461 Transmit_target_current_slot = transmit_index-1;
5462 if ( Transmit_target_current_slot < 0 ) {
5463 Transmit_target_current_slot = MAX_TRANSMIT_TARGETS - 1;
5472 if ( transmit_index < 0 ) {
5473 transmit_index = MAX_TRANSMIT_TARGETS - 1;
5477 if ( i == MAX_TRANSMIT_TARGETS ) {
5478 if ( play_fail_sound ) {
5479 snd_play( &Snds[SND_TARGET_FAIL] );
5481 Transmit_target_current_slot = -1;
5485 if(hud_target_invalid_awacs(&Objects[Transmit_target_list[transmit_index].objnum])){
5490 // Fix bug in targeting due to Alt-Y (target last ship sending transmission).
5491 // Was just bogus code in the call to hud_restore_subsystem_target(). -- MK, 9/15/99, 1:59 pm.
5492 int targeted_objnum;
5493 targeted_objnum = Transmit_target_list[transmit_index].objnum;
5494 SDL_assert((targeted_objnum >= 0) && (targeted_objnum < MAX_OBJECTS));
5496 if ((targeted_objnum >= 0) && (targeted_objnum < MAX_OBJECTS)) {
5497 set_target_objnum( Player_ai, Transmit_target_list[transmit_index].objnum );
5498 hud_restore_subsystem_target(&Ships[Objects[Transmit_target_list[transmit_index].objnum].instance]);
5502 // called externally to add a message sender to the list
5503 void hud_target_last_transmit_add(int ship_num)
5508 ship_objnum = Ships[ship_num].objnum;
5509 SDL_assert(ship_objnum >= 0 && ship_objnum < MAX_OBJECTS);
5510 ship_objp = &Objects[ship_objnum];
5511 SDL_assert(ship_objp->type == OBJ_SHIP);
5513 Transmit_target_list[Transmit_target_next_slot].objnum = ship_objnum;
5514 Transmit_target_list[Transmit_target_next_slot].objsig = ship_objp->signature;
5515 Transmit_target_next_slot++;
5516 if ( Transmit_target_next_slot >= MAX_TRANSMIT_TARGETS ) {
5517 Transmit_target_next_slot = 0;
5521 // target a random ship (useful for EMP stuff)
5522 void hud_target_random_ship()
5527 shipnum = ship_get_random_ship();
5528 if((shipnum < 0) || (Ships[shipnum].objnum < 0)){
5531 objnum = Ships[shipnum].objnum;
5533 if((objnum >= 0) && (Player_ai != NULL) && !hud_target_invalid_awacs(&Objects[objnum])){
5534 // never target yourself
5535 if(objnum == OBJ_INDEX(Player_obj)){
5536 set_target_objnum(Player_ai, -1);
5538 set_target_objnum(Player_ai, objnum);
5543 // ----------------------------------------------------------------
5545 // Target Last Transmission Sender code END
5547 // ----------------------------------------------------------------
5549 void hudtarget_page_in()
5553 for ( i = 0; i < NUM_WEAPON_GAUGES; i++ ) {
5554 bm_page_in_aabitmap( Weapon_gauges[i].first_frame, Weapon_gauges[i].num_frames);
5556 bm_page_in_aabitmap( Lead_indicator_gauge.first_frame, Lead_indicator_gauge.num_frames);
5557 bm_page_in_aabitmap( Energy_bar_gauges.first_frame, Energy_bar_gauges.num_frames);
5558 bm_page_in_aabitmap( Toggle_gauge.first_frame, Toggle_gauge.num_frames);
5559 bm_page_in_aabitmap( Cmeasure_gauge.first_frame, Cmeasure_gauge.num_frames);