1 /*
2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
3 *
4 * All source code herein is the property of Volition, Inc. You may not sell
5 * or otherwise commercially exploit the source or things you created based on the
6 * source.
7 *
8 */
9
10
11
12
13 #include "asteroid/asteroid.h"
14 #include "cmdline/cmdline.h"
15 #include "debris/debris.h"
16 #include "freespace.h" // for flFrametime
17 #include "gamesnd/gamesnd.h"
18 #include "globalincs/alphacolors.h"
19 #include "globalincs/linklist.h"
20 #include "graphics/matrix.h"
21 #include "hud/hudartillery.h"
22 #include "hud/hudbrackets.h"
23 #include "hud/hudlock.h"
24 #include "hud/hudmessage.h"
25 #include "hud/hudparse.h"
26 #include "hud/hudreticle.h"
27 #include "hud/hudshield.h"
28 #include "hud/hudtarget.h"
29 #include "hud/hudtargetbox.h"
30 #include "iff_defs/iff_defs.h"
31 #include "io/timer.h"
32 #include "jumpnode/jumpnode.h"
33 #include "localization/localize.h"
34 #include "mission/missionhotkey.h"
35 #include "mission/missionmessage.h"
36 #include "model/model.h"
37 #include "network/multi.h"
38 #include "network/multiutil.h"
39 #include "object/object.h"
40 #include "libs/renderdoc/renderdoc.h"
41 #include "parse/parselo.h"
42 #include "playerman/player.h"
43 #include "render/3dinternal.h"
44 #include "ship/awacs.h"
45 #include "ship/ship.h"
46 #include "ship/subsysdamage.h"
47 #include "weapon/emp.h"
48 #include "weapon/weapon.h"
49
50 // Global values for the target bracket width and height, used for debugging
51 int Hud_target_w, Hud_target_h;
52
53 // offscreen triangle that point the the off-screen target
54 float Offscreen_tri_base[GR_NUM_RESOLUTIONS] = {
55 6.0f,
56 9.5f
57 };
58 float Offscreen_tri_height[GR_NUM_RESOLUTIONS] = {
59 7.0f,
60 11.0f
61 };
62 float Max_offscreen_tri_seperation[GR_NUM_RESOLUTIONS] = {
63 10.0f,
64 16.0f
65 };
66 float Max_front_seperation[GR_NUM_RESOLUTIONS] = {
67 10.0f,
68 16.0f
69 };
70
71 SCP_vector<target_display_info> target_display_list;
72
73 // The following variables are global to this file, and do not need to be persistent from frame-to-frame
74 // This means the variables are not player-specific
75 object* hostile_obj = NULL;
76
77 static int ballistic_hud_index = 0; // Goober5000
78
79 extern object obj_used_list; // dummy node in linked list of active objects
80 extern char *Cargo_names[];
81
82 // shader is used to shade the target box
83 shader Training_msg_glass;
84
85 // the target triangle (that orbits the reticle) dimensions
86 float Target_triangle_base[GR_NUM_RESOLUTIONS] = {
87 6.0f,
88 9.5f
89 };
90 float Target_triangle_height[GR_NUM_RESOLUTIONS] = {
91 7.0f,
92 11.0f
93 };
94
95 // stuff for hotkey targeting lists
96 htarget_list htarget_items[MAX_HOTKEY_TARGET_ITEMS];
97 htarget_list htarget_free_list;
98
99 /*
100 // coordinates and widths used to render the HUD afterburner energy gauge
101 int current_hud->Aburn_coords[GR_NUM_RESOLUTIONS][4] = {
102 { // GR_640
103 171, 265, 60, 60
104 },
105 { // GR_1024
106 274, 424, 86, 96
107 }
108 };
109
110 // coordinates and widths used to render the HUD weapons energy gauge
111 int current_hud->Wenergy_coords[GR_NUM_RESOLUTIONS][4] = {
112 { // GR_640
113 416, 265, 60, 60
114 },
115 { // GR_1024
116 666, 424, 86, 96
117 }
118 };*/
119
120 #define MIN_DISTANCE_TO_CONSIDER_THREAT 1500 // min distance to show hostile warning triangle
121
122 //////////////////////////////////////////////////////////////////////////
123 // lists for target in reticle cycling
124 //////////////////////////////////////////////////////////////////////////
125 #define RL_USED (1<<0)
126 #define RL_USE_DOT (1<<1) // use dot product result, not distance
127
128 typedef struct _reticle_list {
129 _reticle_list *next, *prev;
130 object *objp;
131 float dist, dot;
132 int flags;
133 } reticle_list;
134
135 #define RESET_TARGET_IN_RETICLE 750
136 int Reticle_save_timestamp;
137 reticle_list Reticle_cur_list;
138 reticle_list Reticle_save_list;
139 #define MAX_RETICLE_TARGETS 50
140 reticle_list Reticle_list[MAX_RETICLE_TARGETS];
141
142 //////////////////////////////////////////////////////////////////////////
143 // used for closest target cycling
144 //////////////////////////////////////////////////////////////////////////
145 #define TL_RESET 1500
146 #define TURRET_RESET 1000
147 static int Tl_hostile_reset_timestamp;
148 static int Tl_friendly_reset_timestamp;
149 static int Target_next_uninspected_object_timestamp;
150 static int Target_newest_ship_timestamp;
151 static int Target_next_turret_timestamp;
152
153 // animation frames for the hud targeting gauges
154 // frames: 0 => out of range lead
155 // 1 => in range lead
156 float Lead_indicator_half[NUM_HUD_RETICLE_STYLES][GR_NUM_RESOLUTIONS][2] =
157 {
158 {
159 { // GR_640
160 12.5f, // half-width
161 12.5f // half-height
162 },
163 { // GR_1024
164 20.0f, // half-width
165 20.0f // half-height
166 }
167 },
168 {
169 { // GR_640
170 8.0f, // half-width
171 8.0f // half-height
172 },
173 { // GR_1024
174 13.0f, // half-width
175 13.0f // half-height
176 }
177 }
178 };
179 hud_frames Lead_indicator_gauge;
180 int Lead_indicator_gauge_loaded = 0;
181 char Lead_fname[NUM_HUD_RETICLE_STYLES][GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN] =
182 {
183 { "lead1_fs1", "2_lead1_fs1" },
184 { "lead1", "2_lead1" }
185 };
186
187 // animation frames for the countermeasures gauge
188 // frames: 0 => background
189 hud_frames Cmeasure_gauge;
190 int Cmeasure_gauge_loaded = 0;
191 int Cm_coords[GR_NUM_RESOLUTIONS][2] = {
192 { // GR_640
193 497, 343
194 },
195 { // GR_1024
196 880, 602
197 }
198 };
199 int Cm_text_coords[GR_NUM_RESOLUTIONS][2] = {
200 { // GR_640
201 533, 347
202 },
203 { // GR_1024
204 916, 606
205 }
206 };
207 int Cm_text_val_coords[GR_NUM_RESOLUTIONS][2] = {
208 { // GR_640
209 506, 347
210 },
211 { // GR_1024
212 889, 606
213 }
214 };
215 char Cm_fname[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN] = {
216 "countermeasure1",
217 "countermeasure1"
218 };
219
220 #define TOGGLE_TEXT_AUTOT 0
221 #define TOGGLE_TEXT_TARGET 1
222 #define TOGGLE_TEXT_AUTOS 2
223 #define TOGGLE_TEXT_SPEED 3
224
225 int Toggle_text_alpha = 255;
226
227
228 // animation files for the weapons gauge
229 #define NUM_WEAPON_GAUGES 5
230 hud_frames Weapon_gauges[NUM_HUD_SETTINGS][NUM_WEAPON_GAUGES];
231 hud_frames New_weapon;
232 int Weapon_gauges_loaded = 0;
233 // for primaries
234 int Weapon_gauge_primary_coords[NUM_HUD_SETTINGS][GR_NUM_RESOLUTIONS][3][2] =
235 {
236 { // normal HUD
237 { // GR_640
238 // based on the # of primaries
239 {509, 273}, // top of weapon gauge, first frame, always
240 {497, 293}, // for the first primary
241 {497, 305} // for the second primary
242 },
243 { // GR_1024
244 // based on the # of primaries
245 {892, 525}, // top of weapon gauge, first frame, always
246 {880, 545}, // for the first primary
247 {880, 557} // for the second primary
248 }
249 },
250 { // ballistic HUD - slightly different alignment
251 { // GR_640
252 // based on the # of primaries
253 {485, 273}, // top of weapon gauge, first frame, always
254 {485, 293}, // for the first primary
255 {485, 305} // for the second primary
256 },
257 { // GR_1024
258 // based on the # of primaries
259 {868, 525}, // top of weapon gauge, first frame, always
260 {868, 545}, // for the first primary
261 {868, 557} // for the second primary
262 }
263 }
264 };
265 int Weapon_gauge_secondary_coords[NUM_HUD_SETTINGS][GR_NUM_RESOLUTIONS][5][2] =
266 {
267 { // normal HUD
268 { // GR_640
269 // based on the # of secondaries
270 {497, 318}, // bottom of gauge, 0 secondaries
271 {497, 318}, // bottom of gauge, 1 secondaries
272 {497, 317}, // middle of gauge, 2 secondaries AND middle of gauge, 3 secondaries
273 {497, 326}, // bottom of gauge, 2 secondaries AND middle of gauge, 3 secondaries
274 {497, 335} // bottom of gauge, 3 secondaries
275 },
276 { // GR_1024
277 // based on the # of secondaries
278 {880, 570}, // bottom of gauge, 0 secondaries
279 {880, 570}, // bottom of gauge, 1 secondaries
280 {880, 569}, // middle of gauge, 2 secondaries AND middle of gauge, 3 secondaries
281 {880, 578}, // bottom of gauge, 2 secondaries AND middle of gauge, 3 secondaries
282 {880, 587} // bottom of gauge, 3 secondaries
283 }
284 },
285 { // ballistic HUD - slightly different alignment
286 { // GR_640
287 // based on the # of secondaries
288 {485, 318}, // bottom of gauge, 0 secondaries
289 {485, 318}, // bottom of gauge, 1 secondaries
290 {485, 317}, // middle of gauge, 2 secondaries AND middle of gauge, 3 secondaries
291 {485, 326}, // bottom of gauge, 2 secondaries AND middle of gauge, 3 secondaries
292 {485, 335} // bottom of gauge, 3 secondaries
293 },
294 { // GR_1024
295 // based on the # of secondaries
296 {868, 570}, // bottom of gauge, 0 secondaries
297 {868, 570}, // bottom of gauge, 1 secondaries
298 {868, 569}, // middle of gauge, 2 secondaries AND middle of gauge, 3 secondaries
299 {868, 578}, // bottom of gauge, 2 secondaries AND middle of gauge, 3 secondaries
300 {868, 587} // bottom of gauge, 3 secondaries
301 }
302 }
303 };
304 int Weapon_title_coords[NUM_HUD_SETTINGS][GR_NUM_RESOLUTIONS][2] =
305 {
306 { // normal HUD
307 { // GR_640
308 518, 274
309 },
310 { // GR_1024
311 901, 527
312 }
313 },
314 { // ballistic HUD - slightly different alignment
315 { // GR_640
316 487, 274
317 },
318 { // GR_1024
319 870, 527
320 }
321 }
322 };
323 int Weapon_plink_coords[GR_NUM_RESOLUTIONS][2][2] = {
324 { // GR_640
325 {530, 285}, // fire-linked thingie, for the first primary
326 {530, 295} // fire-linked thingie, for the second primary
327 },
328 { // GR_1024
329 {913, 537}, // fire-linked thingie, for the first primary
330 {913, 547} // fire-linked thingie, for the second primary
331 }
332 };
333 int Weapon_pname_coords[GR_NUM_RESOLUTIONS][2][2] = {
334 { // GR_640
335 {536, 285}, // weapon name, first primary
336 {536, 295} // weapon name, second primary
337 },
338 { // GR_1024
339 {919, 537}, // weapon name, first primary
340 {919, 547} // weapon name, second primary
341 }
342 };
343 int Weapon_slinked_x[GR_NUM_RESOLUTIONS] = {
344 525, // where to draw the second thingie if this weapon is fire-linked
345 908
346 };
347 int Weapon_sunlinked_x[GR_NUM_RESOLUTIONS] = {
348 530, // where to draw the first thingie if this weapon is selected at all (fire-linked or not)
349 913
350 };
351 int Weapon_secondary_y[GR_NUM_RESOLUTIONS][3] = {
352 { // GR_640
353 309, // y location of where to draw text for the first secondary
354 318, // y location of where to draw text for the second secondary
355 327 // y location of where to draw text for the third secondary
356 },
357 { // GR_1024
358 561, // y location of where to draw text for the third secondary
359 570, // y location of where to draw text for the third secondary
360 579 // y location of where to draw text for the third secondary
361 }
362 };
363 int Weapon_primary_y[GR_NUM_RESOLUTIONS][2] = {
364 { // GR_640
365 285, // y location of where to draw text for the first primary
366 295 // y location of where to draw text for the second primary
367 },
368 { // GR_1024
369 537, // y location of where to draw text for the first primary
370 547 // y location of where to draw text for the second primary
371 }
372 };
373 int Weapon_secondary_name_x[GR_NUM_RESOLUTIONS] = {
374 536, // x location of where to draw weapon name
375 919
376 };
377 int Weapon_secondary_ammo_x[GR_NUM_RESOLUTIONS] = {
378 525, // x location of where to draw weapon ammo count
379 908
380 };
381 int Weapon_primary_ammo_x[GR_NUM_RESOLUTIONS] = {
382 525, // x location of where to draw primary weapon ammo count
383 908
384 };
385 int Weapon_secondary_reload_x[GR_NUM_RESOLUTIONS] = {
386 615, // x location of where to draw the weapon reload time
387 998
388 };
389 const char *Weapon_gauge_fnames[NUM_HUD_SETTINGS][GR_NUM_RESOLUTIONS][NUM_WEAPON_GAUGES] =
390 {
391 //XSTR:OFF
392 { // normal HUD
393 { // GR_640
394 "weapons1",
395 "weapons2",
396 "weapons3",
397 "weapons4",
398 "weapons5"
399 },
400 { // GR_1024
401 "weapons1",
402 "weapons2",
403 "weapons3",
404 "weapons4",
405 "weapons5"
406 }
407 },
408 { // ballistic HUD - slightly different alignment
409 { // GR_640
410 "weapons1_b",
411 "weapons2_b",
412 "weapons3_b",
413 "weapons4_b",
414 "weapons5_b"
415 },
416 { // GR_1024
417 "weapons1_b",
418 "weapons2_b",
419 "weapons3_b",
420 "weapons4_b",
421 "weapons5_b"
422 }
423 }
424 //XSTR:ON
425 };
426
427 // Flash the line for a weapon. This normally occurs when the player tries to fire that
428 // weapon, but the firing fails (due to lack of energy or damaged weapons subsystem).
429 #define MAX_WEAPON_FLASH_LINES 7 // 3 primary and 4 secondary
430 typedef struct weapon_flash
431 {
432 int flash_duration[MAX_WEAPON_FLASH_LINES];
433 int flash_next[MAX_WEAPON_FLASH_LINES];
434 int is_bright;
435 } weapon_flash;
436 weapon_flash Weapon_flash_info;
437
438 // Data used for the proximity warning
439 typedef struct homing_beep_info
440 {
441 sound_handle snd_handle; // sound handle for last played beep
442 fix last_time_played; // time beep was last played
443 int min_cycle_time; // time (in ms) for fastest cycling of the sound
444 int max_cycle_time; // time (in ms) for slowest cycling of the sound
445 float min_cycle_dist; // distance at which fastest cycling occurs
446 float max_cycle_dist; // distance at which slowest cycling occurs
447 float precalced_interp; // a precalculated value used in a linear interpretation
448 } homing_beep_info;
449
450 homing_beep_info Homing_beep = {sound_handle::invalid(), 0, 150, 1000, 30.0f, 1500.0f, 1.729412f};
451
452 // Set at the start of a mission, used to decide how to draw the separation for the warning missile indicators
453 float Min_warning_missile_dist;
454 float Max_warning_missile_dist;
455
456 void hud_maybe_flash_weapon(int index);
457
458 // if a given object should be ignored because of AWACS effects
hud_target_invalid_awacs(object * objp)459 int hud_target_invalid_awacs(object *objp)
460 {
461 // if objp is ship object, first check if can be targeted with team info
462 if (objp->type == OBJ_SHIP) {
463 if (Player_ship != NULL) {
464 if (ship_is_visible_by_team(objp, Player_ship)) {
465 return 0;
466 }
467 }
468 }
469
470 // check for invalid status
471 if((Player_ship != NULL) && (awacs_get_level(objp, Player_ship) < 1.0f)){
472 return 1;
473 }
474
475 // valid
476 return 0;
477 }
478
advance_subsys(ship_subsys * cur,int next_flag)479 ship_subsys *advance_subsys(ship_subsys *cur, int next_flag)
480 {
481 if (next_flag) {
482 return GET_NEXT(cur);
483 } else {
484 return GET_LAST(cur);
485 }
486 }
487
488 // select a sorted turret subsystem on a ship if no other subsys has been selected
hud_maybe_set_sorted_turret_subsys(ship * shipp)489 void hud_maybe_set_sorted_turret_subsys(ship *shipp)
490 {
491 Assert((Player_ai->target_objnum >= 0) && (Player_ai->target_objnum < MAX_OBJECTS));
492 if (!((Player_ai->target_objnum >= 0) && (Player_ai->target_objnum < MAX_OBJECTS))) {
493 return;
494 }
495 Assert(Objects[Player_ai->target_objnum].type == OBJ_SHIP);
496 if (Objects[Player_ai->target_objnum].type != OBJ_SHIP) {
497 return;
498 }
499
500 if (Ship_info[shipp->ship_info_index].is_big_or_huge()) {
501 if (shipp->last_targeted_subobject[Player_num] == NULL) {
502 hud_target_live_turret(1, 1);
503 }
504 }
505
506 }
507
508 // -----------------------------------------------------------------------
509 // clear out the linked list of targets in the reticle
hud_reticle_clear_list(reticle_list * rlist)510 void hud_reticle_clear_list(reticle_list *rlist)
511 {
512 reticle_list *cur;
513 for ( cur = GET_FIRST(rlist); cur != END_OF_LIST(rlist); cur = GET_NEXT(cur) ) {
514 cur->flags = 0;
515 }
516 list_init(rlist);
517 }
518
519 // --------------------------------------------------------------------------------------
520 // hud_reticle_list_init()
hud_reticle_list_init()521 void hud_reticle_list_init()
522 {
523 int i;
524
525 for ( i = 0; i < MAX_RETICLE_TARGETS; i++ ) {
526 Reticle_list[i].flags = 0;
527 }
528
529 Reticle_save_timestamp = 1;
530 list_init(&Reticle_save_list);
531 list_init(&Reticle_cur_list);
532 }
533
534 // --------------------------------------------------------------------------------------
535 // hud_check_reticle_list()
536 //
537 //
hud_check_reticle_list()538 void hud_check_reticle_list()
539 {
540 reticle_list *rl, *temp;
541
542 // cull dying objects from reticle list
543 rl = GET_FIRST(&Reticle_cur_list);
544 while( rl !=END_OF_LIST(&Reticle_cur_list) ) {
545 temp = GET_NEXT(rl);
546 if ( rl->objp->flags[Object::Object_Flags::Should_be_dead] ) {
547 list_remove( &Reticle_cur_list, rl );
548 rl->flags = 0;
549 }
550 rl = temp;
551 }
552
553 if ( timestamp_elapsed(Reticle_save_timestamp) ) {
554 hud_reticle_clear_list(&Reticle_save_list);
555 Reticle_save_timestamp = timestamp(RESET_TARGET_IN_RETICLE);
556 }
557 }
558
559 // --------------------------------------------------------------------------------------
560 // hud_reticle_list_find_free()
561 //
562 //
hud_reticle_list_find_free()563 int hud_reticle_list_find_free()
564 {
565 int i;
566
567 // find a free reticle_list element
568 for ( i = 0; i < MAX_RETICLE_TARGETS; i++ ) {
569 if ( !(Reticle_list[i].flags & RL_USED) ) {
570 break;
571 }
572 }
573
574 if ( i == MAX_RETICLE_TARGETS ) {
575 // nprintf(("Warning","Warning ==> Ran out of reticle target elements...\n"));
576 return -1;
577 }
578
579 return i;
580 }
581
582 // --------------------------------------------------------------------------------------
583 // hud_stuff_reticle_list()
584 //
585 //
586 #define RETICLE_DEFAULT_DIST 100000.0f
587 #define RETICLE_DEFAULT_DOT 1.0f
hud_stuff_reticle_list(reticle_list * rl,object * objp,float measure,int dot_flag)588 void hud_stuff_reticle_list(reticle_list *rl, object *objp, float measure, int dot_flag)
589 {
590 if ( dot_flag ) {
591 rl->dot = measure;
592 rl->dist = RETICLE_DEFAULT_DIST;
593 rl->flags |= RL_USE_DOT;
594 }
595 else {
596 rl->dist = measure;
597 rl->dot = RETICLE_DEFAULT_DOT;
598 }
599 rl->objp = objp;
600 }
601
602 // --------------------------------------------------------------------------------------
603 // hud_reticle_list_update()
604 //
605 // Update Reticle_cur_list with an object that lies in the reticle
606 //
607 // parmeters: objp => object pointer to target
608 // measure => distance or dot product, depending on dot_flag
609 // dot_flag => if 0, measure is distance, if 1 measure is dot
610 //
hud_reticle_list_update(object * objp,float measure,int dot_flag)611 void hud_reticle_list_update(object *objp, float measure, int dot_flag)
612 {
613 reticle_list *rl, *new_rl;
614 int i;
615 SCP_list<CJumpNode>::iterator jnp;
616
617 if (objp->type == OBJ_JUMP_NODE) {
618 for (jnp = Jump_nodes.begin(); jnp != Jump_nodes.end(); ++jnp) {
619 if( jnp->GetSCPObject() != objp )
620 continue;
621
622 if( jnp->IsHidden() )
623 return;
624 }
625 }
626
627 for ( rl = GET_FIRST(&Reticle_cur_list); rl != END_OF_LIST(&Reticle_cur_list); rl = GET_NEXT(rl) ) {
628 if ( rl->objp == objp )
629 return;
630 }
631
632 i = hud_reticle_list_find_free();
633 if ( i == -1 )
634 return;
635
636 new_rl = &Reticle_list[i];
637 new_rl->flags |= RL_USED;
638 hud_stuff_reticle_list(new_rl, objp, measure, dot_flag);
639
640 int was_inserted = 0;
641
642 if ( EMPTY(&Reticle_cur_list) ) {
643 list_insert(&Reticle_cur_list, new_rl);
644 was_inserted = 1;
645 }
646 else {
647 for ( rl = GET_FIRST(&Reticle_cur_list); rl != END_OF_LIST(&Reticle_cur_list); rl = GET_NEXT(rl) ) {
648 if ( !dot_flag ) {
649 // compare based on distance
650 if ( measure < rl->dist ) {
651 list_insert_before(rl, new_rl);
652 was_inserted = 1;
653 break;
654 }
655 }
656 else {
657 // compare based on dot
658 if ( measure > rl->dot ) {
659 list_insert_before(rl, new_rl);
660 was_inserted = 1;
661 break;
662 }
663 }
664 } // end for
665 }
666
667 if ( !was_inserted ) {
668 list_append(&Reticle_cur_list, new_rl);
669 }
670 }
671
672 // --------------------------------------------------------------------------------------
673 // hud_reticle_pick_target()
674 //
675 // Pick a target from Reticle_cur_list, based on what is in Reticle_save_list
676 //
677 //
hud_reticle_pick_target()678 object *hud_reticle_pick_target()
679 {
680 reticle_list *cur_rl, *save_rl, *new_rl;
681 object *return_objp;
682 int in_save_list, i;
683
684 return_objp = NULL;
685
686 // As a first step, see if both ships and debris are in the list. If so, cull the debris.
687 int debris_in_list = 0;
688 int ship_in_list = 0;
689 for ( cur_rl = GET_FIRST(&Reticle_cur_list); cur_rl != END_OF_LIST(&Reticle_cur_list); cur_rl = GET_NEXT(cur_rl) ) {
690 if ( (cur_rl->objp->type == OBJ_SHIP) || (cur_rl->objp->type == OBJ_JUMP_NODE) ) {
691 ship_in_list = 1;
692 continue;
693 }
694
695 if ( cur_rl->objp->type == OBJ_WEAPON ) {
696 if ( Weapon_info[Weapons[cur_rl->objp->instance].weapon_info_index].subtype == WP_MISSILE ) {
697 ship_in_list = 1;
698 continue;
699 }
700 }
701
702 if ( (cur_rl->objp->type == OBJ_DEBRIS) || (cur_rl->objp->type == OBJ_ASTEROID) ) {
703 debris_in_list = 1;
704 continue;
705 }
706 }
707
708 if ( ship_in_list && debris_in_list ) {
709 // cull debris
710 reticle_list *rl, *next;
711
712 rl = GET_FIRST(&Reticle_cur_list);
713 while ( rl != &Reticle_cur_list ) {
714 next = rl->next;
715 if ( (rl->objp->type == OBJ_DEBRIS) || (rl->objp->type == OBJ_ASTEROID) ){
716 list_remove(&Reticle_cur_list,rl);
717 rl->flags = 0;
718 }
719 rl = next;
720 }
721 }
722
723 for ( cur_rl = GET_FIRST(&Reticle_cur_list); cur_rl != END_OF_LIST(&Reticle_cur_list); cur_rl = GET_NEXT(cur_rl) ) {
724 in_save_list = 0;
725 for ( save_rl = GET_FIRST(&Reticle_save_list); save_rl != END_OF_LIST(&Reticle_save_list); save_rl = GET_NEXT(save_rl) ) {
726 if ( cur_rl->objp == save_rl->objp ) {
727 in_save_list = 1;
728 break;
729 }
730 }
731
732 if ( !in_save_list ) {
733 return_objp = cur_rl->objp;
734 i = hud_reticle_list_find_free();
735 if ( i == -1 )
736 break;
737
738 new_rl = &Reticle_list[i];
739 new_rl->flags |= RL_USED;
740 if ( cur_rl->flags & RL_USE_DOT ) {
741 hud_stuff_reticle_list(new_rl, cur_rl->objp, cur_rl->dot, 1);
742 }
743 else {
744 hud_stuff_reticle_list(new_rl, cur_rl->objp, cur_rl->dist, 0);
745 }
746
747 list_append(&Reticle_save_list, new_rl);
748 break;
749 }
750 } // end for
751
752 if ( return_objp == NULL && !EMPTY(&Reticle_cur_list) ) {
753 i = hud_reticle_list_find_free();
754 if ( i == -1 )
755 return NULL;
756 new_rl = &Reticle_list[i];
757 cur_rl = GET_FIRST(&Reticle_cur_list);
758 *new_rl = *cur_rl;
759 return_objp = cur_rl->objp;
760 hud_reticle_clear_list(&Reticle_save_list);
761 list_append(&Reticle_save_list, new_rl);
762 }
763
764 return return_objp;
765 }
766
767 // hud_target_hotkey_add_remove takes as its parameter which hotkey (1-0) to add/remove the current
768 // target from. This function behaves like the Shift-<selection> does in Windows -- using shift # will toggle
769 // the current target in and out of the selection set.
hud_target_hotkey_add_remove(int k,object * ctarget,int how_to_add)770 void hud_target_hotkey_add_remove( int k, object *ctarget, int how_to_add )
771 {
772 htarget_list *hitem, *plist;
773
774 // don't do anything if a standalone multiplayer server
775 if ( MULTIPLAYER_STANDALONE )
776 return;
777
778 if ( (k < 0) || (k >= MAX_KEYED_TARGETS) ) {
779 nprintf(("Warning", "Bogus hotkey %d sent to hud_target_hotkey_add_remove\n", k));
780 return;
781 }
782
783 plist = &(Players[Player_num].keyed_targets[k]);
784
785 // we must operate only on ships
786 if ( ctarget->type != OBJ_SHIP )
787 return;
788
789 // don't allow player into hotkey set
790 if ( ctarget == Player_obj )
791 return;
792
793 // don't put dying or departing
794 if ( Ships[ctarget->instance].is_dying_or_departing() )
795 return;
796
797 // don't add mission file added hotkey assignments if there are player added assignments
798 // already in the list
799 if ( (how_to_add == HOTKEY_MISSION_FILE_ADDED) && NOT_EMPTY(plist) ) {
800 for ( hitem = GET_FIRST(plist); hitem != END_OF_LIST(plist); hitem = GET_NEXT(hitem) ) {
801 if ( hitem->how_added == HOTKEY_USER_ADDED )
802 return;
803 }
804 }
805
806 // determine if the current target is currently in the set or not
807 for ( hitem = GET_FIRST(plist); hitem != END_OF_LIST(plist); hitem = GET_NEXT(hitem) ) {
808 if ( hitem->objp == ctarget )
809 break;
810 }
811
812 // if hitem == end of the list, then the target should be added, else it should be removed
813 if ( hitem == END_OF_LIST(plist) ) {
814 if ( EMPTY(&htarget_free_list) ) {
815 Int3(); // get Allender -- no more free hotkey target items
816 return;
817 }
818
819 nprintf(("network", "Hotkey: Adding %s\n", Ships[ctarget->instance].ship_name));
820 hitem = GET_FIRST( &htarget_free_list );
821 list_remove( &htarget_free_list, hitem );
822 list_append( plist, hitem );
823 hitem->objp = ctarget;
824 hitem->how_added = how_to_add;
825 } else {
826 nprintf(("network", "Hotkey: Removing %s\n", Ships[ctarget->instance].ship_name));
827 list_remove( plist, hitem );
828 list_append( &htarget_free_list, hitem );
829 hitem->objp = NULL; // for safety
830 }
831 }
832
833 // the following function clears the hotkey set given by parameter passed in
hud_target_hotkey_clear(int k)834 void hud_target_hotkey_clear( int k )
835 {
836 htarget_list *hitem, *plist, *temp;
837
838 plist = &(Players[Player_num].keyed_targets[k]);
839 hitem = GET_FIRST(plist);
840 while ( hitem != END_OF_LIST(plist) ) {
841 temp = GET_NEXT(hitem);
842 list_remove( plist, hitem );
843 list_append( &htarget_free_list, hitem );
844 hitem->objp = NULL;
845 hitem = temp;
846 }
847 if ( Players[Player_num].current_hotkey_set == k ) // clear this variable if we removed the bindings
848 Players[Player_num].current_hotkey_set = -1;
849 }
850
851 // the next function sets the current selected set to be N. If there is just one ship in the selection
852 // set, this ship will become the new target. If there is more than one ship in the selection set,
853 // then the current_target will remain what it was.
hud_target_hotkey_select(int k)854 void hud_target_hotkey_select( int k )
855 {
856 int visible_count = 0;
857 htarget_list *hitem, *plist, *target, *next_target, *first_target;
858 int target_objnum;
859
860 plist = &(Players[Player_num].keyed_targets[k]);
861
862 if ( EMPTY( plist ) ) // no items in list, then do nothing
863 return;
864
865 // a simple walk of the list to get the count
866 for ( hitem = GET_FIRST(plist); hitem != END_OF_LIST(plist); hitem = GET_NEXT(hitem) ){
867 if (awacs_get_level(hitem->objp, Player_ship, 1) > 1) {
868 visible_count++;
869 }
870 }
871
872 // no visible ships in list
873 if (visible_count == 0) {
874 return;
875 }
876
877 // set the current target to be the "next" ship in the list. Scan the list to see if our
878 // current target is in the set. If so, target the next ship in the list, otherwise target
879 // the first
880 // set first_target - first visible item in list
881 // target - item in list that is the player's currently selected target
882 // next_target - next visible item in list following target
883 target_objnum = Player_ai->target_objnum;
884 target = NULL;
885 next_target = NULL;
886 first_target = NULL;
887 for ( hitem = GET_FIRST(plist); hitem != END_OF_LIST(plist); hitem = GET_NEXT(hitem) ) {
888
889 if (awacs_get_level(hitem->objp, Player_ship, 1) > 1) {
890 // get the first valid target
891 if (first_target == NULL) {
892 first_target = hitem;
893 }
894
895 // get the next target in the list following the player currently selected target
896 if (target != NULL) {
897 next_target = hitem;
898 break;
899 }
900 }
901
902 // mark the player currently selected target
903 if ( OBJ_INDEX(hitem->objp) == target_objnum ) {
904 target = hitem;
905 }
906 }
907
908 // if current target is not in list, then target and next_target will be NULL
909 // so we use the first found target
910 if (target == NULL) {
911 Assert(first_target != NULL);
912 if (first_target != NULL) {
913 target = first_target;
914 next_target = first_target;
915 } else {
916 // this should not happen
917 return;
918 }
919 // if current target is in the list but this is not our current selection set,
920 // then we don't want to change target.
921 } else if (Players[Player_num].current_hotkey_set != k) {
922 next_target = target;
923 }
924
925 // update target if more than 1 is visible
926 if (visible_count > 1) {
927 // next already found (after current target in list)
928 if (next_target != NULL) {
929 target = next_target;
930 } else {
931
932 // next is before current target, so search from start of list
933 for ( hitem = GET_FIRST(plist); hitem != END_OF_LIST(plist); hitem = GET_NEXT(hitem) ) {
934 if (awacs_get_level(hitem->objp, Player_ship, 1) > 1) {
935 target = hitem;
936 break;
937 }
938 }
939 }
940 }
941
942 Assert( target != END_OF_LIST(plist) );
943
944 if ( Player_obj != target->objp ){
945 set_target_objnum( Player_ai, OBJ_INDEX(target->objp) );
946 hud_shield_hit_reset(target->objp);
947 }
948
949 Players[Player_num].current_hotkey_set = k;
950 }
951
952 // hud_init_targeting_colors() will initialize the shader and gradient objects used
953 // on the HUD
954 //
955
956 color HUD_color_homing_indicator;
957
hud_make_shader(shader * sh,ubyte r,ubyte,ubyte,float dimmer=1000.0f)958 void hud_make_shader(shader *sh, ubyte r, ubyte /*g*/, ubyte /*b*/, float dimmer = 1000.0f)
959 {
960 // The m matrix converts all colors to shades of green
961 //float tmp = 16.0f*(0.0015625f * i2fl(HUD_color_alpha+1.0f));
962 float tmp = 0.025f * i2fl(HUD_color_alpha+1.0f);
963
964 ubyte R = ubyte(r * tmp);
965 ubyte G = ubyte(r * tmp);
966 ubyte B = ubyte(r * tmp);
967 ubyte A = ubyte((float(r) / dimmer)*(i2fl(HUD_color_alpha) / 15.0f) * 255.0f);
968
969 gr_create_shader( sh, R, G, B, A );
970 }
971
hud_init_targeting_colors()972 void hud_init_targeting_colors()
973 {
974 gr_init_color( &HUD_color_homing_indicator, 0x7f, 0x7f, 0 ); // yellow
975
976 hud_make_shader(&Training_msg_glass, 61, 61, 85, 500.0f);
977
978 hud_init_brackets();
979 }
980
hud_keyed_targets_clear()981 void hud_keyed_targets_clear()
982 {
983 int i;
984
985 // clear out the keyed target list
986 for (i = 0; i < MAX_KEYED_TARGETS; i++ )
987 list_init( &(Players[Player_num].keyed_targets[i]) );
988
989 Players[Player_num].current_hotkey_set = -1;
990
991 // place all of the hoykey target items back onto the free list
992 list_init( &htarget_free_list );
993 for ( i = 0; i < MAX_HOTKEY_TARGET_ITEMS; i++ )
994 list_append( &htarget_free_list, &htarget_items[i] );
995 }
996
997 // Init data used for the weapons display on the HUD
hud_weapons_init()998 void hud_weapons_init()
999 {
1000 Weapon_flash_info.is_bright = 0;
1001 for ( int i = 0; i < MAX_WEAPON_FLASH_LINES; i++ ) {
1002 Weapon_flash_info.flash_duration[i] = 1;
1003 Weapon_flash_info.flash_next[i] = 1;
1004 }
1005
1006 // The E: There used to be a number of checks here. They are no longer needed, as the new HUD code handles it on its own.
1007 Weapon_gauges_loaded = 1;
1008 }
1009
1010 // init data used to play the homing "proximity warning" sound
hud_init_homing_beep()1011 void hud_init_homing_beep()
1012 {
1013 Homing_beep.snd_handle = sound_handle::invalid();
1014 Homing_beep.last_time_played = 0;
1015 Homing_beep.precalced_interp = (Homing_beep.max_cycle_dist-Homing_beep.min_cycle_dist) / (Homing_beep.max_cycle_time - Homing_beep.min_cycle_time );
1016 }
1017
hud_init_ballistic_index()1018 void hud_init_ballistic_index()
1019 {
1020 int i;
1021
1022 // decide whether to realign HUD for ballistic primaries
1023 ballistic_hud_index = 0;
1024 for (i = 0; i < Player_ship->weapons.num_primary_banks; i++)
1025 {
1026 if (Weapon_info[Player_ship->weapons.primary_bank_weapons[i]].wi_flags[Weapon::Info_Flags::Ballistic])
1027 {
1028 ballistic_hud_index = 1;
1029 break;
1030 }
1031 }
1032 }
1033
1034 // hud_init_targeting() will set the current target to point to the dummy node
1035 // in the object used list
1036 //
hud_init_targeting()1037 void hud_init_targeting()
1038 {
1039 Assert(Player_ai != NULL);
1040
1041 // make sure there is no current target
1042 set_target_objnum( Player_ai, -1 );
1043 Player_ai->last_target = -1;
1044 Player_ai->last_subsys_target = NULL;
1045 Player_ai->last_dist = 0.0f;
1046 Player_ai->last_speed = 0.0f;
1047
1048 hud_keyed_targets_clear();
1049 hud_init_missile_lock();
1050 hud_init_artillery();
1051
1052 // Init the lists that hold targets in reticle (to allow cycling of targets in reticle)
1053 hud_reticle_list_init();
1054 hud_init_homing_beep();
1055
1056 hud_weapons_init();
1057
1058 Min_warning_missile_dist = 2.5f*Player_obj->radius;
1059 Max_warning_missile_dist = 1500.0f;
1060
1061 Tl_hostile_reset_timestamp = timestamp(0);
1062 Tl_friendly_reset_timestamp = timestamp(0);
1063 Target_next_uninspected_object_timestamp = timestamp(0);
1064 Target_newest_ship_timestamp = timestamp(0);
1065 Target_next_turret_timestamp = timestamp(0);
1066
1067 if(The_mission.flags[Mission::Mission_Flags::Fullneb]) {
1068 Toggle_text_alpha = TOGGLE_TEXT_NEBULA_ALPHA;
1069 } else {
1070 Toggle_text_alpha = TOGGLE_TEXT_NORMAL_ALPHA;
1071 }
1072 }
1073
1074 // Target the next or previous subobject on the currently selected ship, based on next_flag.
hud_target_subobject_common(int next_flag)1075 void hud_target_subobject_common(int next_flag)
1076 {
1077 if (Player_ai->target_objnum == -1) {
1078 HUD_sourced_printf(HUD_SOURCE_HIDDEN, "%s", XSTR( "No target selected.", 322));
1079 snd_play( gamesnd_get_game_sound(GameSounds::TARGET_FAIL) );
1080 return;
1081 }
1082
1083 if (Objects[Player_ai->target_objnum].type != OBJ_SHIP) {
1084 snd_play( gamesnd_get_game_sound(GameSounds::TARGET_FAIL));
1085 return;
1086 }
1087
1088 ship_subsys *start, *start2, *A;
1089 ship_subsys *subsys_to_target=NULL;
1090 ship *target_shipp;
1091
1092 target_shipp = &Ships[Objects[Player_ai->target_objnum].instance];
1093
1094 if (!Player_ai->targeted_subsys) {
1095 start = END_OF_LIST(&target_shipp->subsys_list);
1096 } else {
1097 start = Player_ai->targeted_subsys;
1098 }
1099
1100 start2 = advance_subsys(start, next_flag);
1101
1102 for ( A = start2; A != start; A = advance_subsys(A, next_flag) ) {
1103
1104 if ( A == &target_shipp->subsys_list ) {
1105 continue;
1106 }
1107
1108 // ignore turrets
1109 if ( A->system_info->type == SUBSYSTEM_TURRET ) {
1110 continue;
1111 }
1112
1113 if ( A->flags[Ship::Subsystem_Flags::Untargetable] ) {
1114 continue;
1115 }
1116
1117 subsys_to_target = A;
1118 break;
1119
1120 } // end for
1121
1122 if ( subsys_to_target == NULL ) {
1123 snd_play( gamesnd_get_game_sound(GameSounds::TARGET_FAIL));
1124 } else {
1125 set_targeted_subsys(Player_ai, subsys_to_target, Player_ai->target_objnum);
1126 target_shipp->last_targeted_subobject[Player_num] = Player_ai->targeted_subsys;
1127 }
1128 }
1129
advance_fb(object * objp,int next_flag)1130 object *advance_fb(object *objp, int next_flag)
1131 {
1132 if (next_flag)
1133 return GET_NEXT(objp);
1134 else
1135 return GET_LAST(objp);
1136 }
1137
1138 // Target the previous subobject on the currently selected ship.
1139 //
1140
hud_target_prev_subobject()1141 void hud_target_prev_subobject()
1142 {
1143 hud_target_subobject_common(0);
1144 }
1145
hud_target_next_subobject()1146 void hud_target_next_subobject()
1147 {
1148 hud_target_subobject_common(1);
1149 }
1150
1151 // hud_target_next() will set the Players[Player_num].current_target to the next target in the object
1152 // used list whose team matches the team parameter. The player is NOT included in the target list.
1153 //
1154 // parameters: team_mask => team(s) of ship to target next
1155 //
1156
hud_target_common(int team_mask,int next_flag)1157 void hud_target_common(int team_mask, int next_flag)
1158 {
1159 object *A, *start, *start2;
1160 ship *shipp;
1161 int is_ship, target_found = FALSE;
1162 SCP_list<CJumpNode>::iterator jnp;
1163
1164 if (Player_ai->target_objnum == -1)
1165 start = &obj_used_list;
1166 else
1167 start = &Objects[Player_ai->target_objnum];
1168
1169 start2 = advance_fb(start, next_flag);
1170
1171 for ( A = start2; A != start; A = advance_fb(A, next_flag) ) {
1172 is_ship = 0;
1173
1174 if (A == &obj_used_list)
1175 continue;
1176
1177 if ( (A == Player_obj) || ((A->type != OBJ_SHIP) && (A->type != OBJ_WEAPON) && (A->type != OBJ_JUMP_NODE)) )
1178 continue;
1179
1180 if ( hud_target_invalid_awacs(A) )
1181 continue;
1182
1183 if (A->type == OBJ_WEAPON) {
1184 if ( !(Weapon_info[Weapons[A->instance].weapon_info_index].wi_flags[Weapon::Info_Flags::Can_be_targeted]) )
1185 if ( !(Weapon_info[Weapons[A->instance].weapon_info_index].wi_flags[Weapon::Info_Flags::Bomb]) )
1186 continue;
1187
1188 if (Weapons[A->instance].lssm_stage == 3)
1189 continue;
1190 }
1191
1192 if (A->type == OBJ_SHIP) {
1193 if (should_be_ignored(&Ships[A->instance]))
1194 continue;
1195
1196 is_ship = 1;
1197 }
1198
1199 if (A->type == OBJ_JUMP_NODE) {
1200 for (jnp = Jump_nodes.begin(); jnp != Jump_nodes.end(); ++jnp) {
1201 if( jnp->GetSCPObject() == A )
1202 break;
1203 }
1204
1205 Assertion(jnp != Jump_nodes.end(), "Failed to find jump node with object index %d; trace out and fix!\n", OBJ_INDEX(A));
1206
1207 if( jnp->IsHidden() )
1208 continue;
1209 }
1210
1211 if ( vm_vec_same(&A->pos, &Eye_position) )
1212 continue;
1213
1214 if ( is_ship ) {
1215 shipp = &Ships[A->instance]; // get a pointer to the ship information
1216
1217 if (!iff_matches_mask(shipp->team, team_mask)) {
1218 continue;
1219 }
1220
1221 if ( A == Player_obj ) {
1222 continue;
1223 }
1224
1225 // if we've reached here, it is a valid next target
1226 if ( Player_ai->target_objnum != OBJ_INDEX(A) ) {
1227 target_found = TRUE;
1228 set_target_objnum( Player_ai, OBJ_INDEX(A) );
1229 hud_shield_hit_reset(A);
1230 // if ship is BIG|HUGE and last subsys is NULL, get turret
1231 hud_maybe_set_sorted_turret_subsys(shipp);
1232 hud_restore_subsystem_target(shipp);
1233 }
1234 } else {
1235 target_found = TRUE;
1236 set_target_objnum( Player_ai, OBJ_INDEX(A) );
1237 hud_shield_hit_reset(A);
1238 }
1239
1240 break;
1241 }
1242
1243 if ( target_found == FALSE ) {
1244 snd_play( gamesnd_get_game_sound(GameSounds::TARGET_FAIL) );
1245 }
1246 }
1247
hud_target_next(int team_mask)1248 void hud_target_next(int team_mask)
1249 {
1250 hud_target_common(team_mask, 1);
1251 }
1252
hud_target_prev(int team_mask)1253 void hud_target_prev(int team_mask)
1254 {
1255 hud_target_common(team_mask, 0);
1256 }
1257
1258 // -------------------------------------------------------------------
1259 // advance_missile_obj()
1260 //
advance_missile_obj(missile_obj * mo,int next_flag)1261 missile_obj *advance_missile_obj(missile_obj *mo, int next_flag)
1262 {
1263 if (next_flag){
1264 return GET_NEXT(mo);
1265 }
1266
1267 return GET_LAST(mo);
1268 }
1269
advance_ship(ship_obj * so,int next_flag)1270 ship_obj *advance_ship(ship_obj *so, int next_flag)
1271 {
1272 if (next_flag){
1273 return GET_NEXT(so);
1274 }
1275
1276 return GET_LAST(so);
1277 }
1278
1279 /// \brief Iterates down to and selects the next target in a linked list
1280 /// fashion ordered from closest to farthest from the
1281 /// attacked_object_number, returning the next valid target.
1282 ///
1283 ///
1284 /// \param targeting_from_closest_to_farthest[in] targets the closest object
1285 /// if true. Targets the farthest
1286 /// away object if false.
1287 /// \param valid_team_mask[in] A bit mask that defines the desired
1288 /// victim team.
1289 /// \param attacked_object_number[in] The objectid that is under attack.
1290 /// Defaults to -1.
1291 /// \param target_filters Applies a bit filter to exclude certain
1292 /// classes of objects from being targeted.
1293 /// Defaults to (SIF_CARGO | SIF_NAVBUOY)
1294 ///
1295 /// \returns The next object to target if targeting was successful.
1296 /// Returns NULL if targeting was unsuccessful.
select_next_target_by_distance(const bool targeting_from_closest_to_farthest,const int valid_team_mask,const int attacked_object_number=-1,flagset<Ship::Info_Flags> * target_filters=NULL)1297 static object* select_next_target_by_distance(const bool targeting_from_closest_to_farthest, const int valid_team_mask, const int attacked_object_number = -1, flagset<Ship::Info_Flags>* target_filters = NULL) {
1298 object *minimum_object_ptr, *maximum_object_ptr, *nearest_object_ptr;
1299 minimum_object_ptr = maximum_object_ptr = nearest_object_ptr = NULL;
1300 float current_distance = hud_find_target_distance(&Objects[Player_ai->target_objnum], Player_obj);
1301 float minimum_distance = 1e20f;
1302 float maximum_distance = 0.0f;
1303 int player_object_index = OBJ_INDEX(Player_obj);
1304 flagset<Ship::Info_Flags> filter;
1305
1306 if (target_filters != NULL) {
1307 filter = *target_filters;
1308 } else {
1309 filter.set(Ship::Info_Flags::Cargo);
1310 filter.set(Ship::Info_Flags::Navbuoy);
1311 }
1312
1313 float nearest_distance;
1314 if (targeting_from_closest_to_farthest) {
1315 nearest_distance = 1e20f;
1316 }
1317 else {
1318 nearest_distance = 0.0f;
1319 }
1320
1321 ship_obj *ship_object_ptr;
1322 object *prospective_victim_ptr;
1323 ship *prospective_victim_ship_ptr;
1324 for (ship_object_ptr = GET_FIRST(&Ship_obj_list); ship_object_ptr != END_OF_LIST(&Ship_obj_list); ship_object_ptr = GET_NEXT(ship_object_ptr)) {
1325 prospective_victim_ptr = &Objects[ship_object_ptr->objnum];
1326 // get a pointer to the ship information
1327 prospective_victim_ship_ptr = &Ships[prospective_victim_ptr->instance];
1328
1329 float new_distance;
1330 if ((prospective_victim_ptr == Player_obj) || (should_be_ignored(prospective_victim_ship_ptr))) {
1331 continue;
1332 }
1333
1334 // choose from the correct team
1335 if (!iff_matches_mask(prospective_victim_ship_ptr->team, valid_team_mask)) {
1336 continue;
1337 }
1338
1339 // don't use object if it is already a target
1340 if (OBJ_INDEX(prospective_victim_ptr) == Player_ai->target_objnum) {
1341 continue;
1342 }
1343
1344
1345 if (attacked_object_number == -1) {
1346 // always ignore navbuoys and cargo
1347 if ((Ship_info[prospective_victim_ship_ptr->ship_info_index].flags & filter).any_set()) {
1348 continue;
1349 }
1350
1351 if (hud_target_invalid_awacs(prospective_victim_ptr)) {
1352 continue;
1353 }
1354
1355 new_distance = hud_find_target_distance(prospective_victim_ptr, Player_obj);
1356 }
1357 else {
1358 // Filter out any target that is not targeting the player --Mastadon
1359 if ((attacked_object_number == player_object_index) && (Ai_info[prospective_victim_ship_ptr->ai_index].target_objnum != player_object_index)) {
1360 continue;
1361 }
1362 esct eval_ship_as_closest_target_args;
1363 eval_ship_as_closest_target_args.attacked_objnum = attacked_object_number;
1364 eval_ship_as_closest_target_args.check_all_turrets = (attacked_object_number == player_object_index);
1365 eval_ship_as_closest_target_args.check_nearest_turret = FALSE;
1366 // We don't ever filter our target selection to just bombers or fighters
1367 // because the select next attacker logic doesn't. --Mastadon
1368 eval_ship_as_closest_target_args.filter = 0;
1369 eval_ship_as_closest_target_args.team_mask = valid_team_mask;
1370 // We always get the turret attacking, since that's how the select next
1371 // attacker logic does it. --Mastadon
1372 eval_ship_as_closest_target_args.turret_attacking_target = 1;
1373 eval_ship_as_closest_target_args.shipp = prospective_victim_ship_ptr;
1374 evaluate_ship_as_closest_target(&eval_ship_as_closest_target_args);
1375
1376 new_distance = eval_ship_as_closest_target_args.min_distance;
1377 }
1378
1379
1380 if (new_distance <= minimum_distance) {
1381 minimum_distance = new_distance;
1382 minimum_object_ptr = prospective_victim_ptr;
1383 }
1384
1385 if (new_distance >= maximum_distance) {
1386 maximum_distance = new_distance;
1387 maximum_object_ptr = prospective_victim_ptr;
1388 }
1389
1390 float diff = 0.0f;
1391 if (targeting_from_closest_to_farthest) {
1392 diff = new_distance - current_distance;
1393 if (diff > 0.0f) {
1394 if (diff < (nearest_distance - current_distance)) {
1395 nearest_distance = new_distance;
1396 nearest_object_ptr = prospective_victim_ptr;
1397 }
1398 }
1399 }
1400 else {
1401 diff = current_distance - new_distance;
1402 if (diff > 0.0f) {
1403 if (diff < (current_distance - nearest_distance)) {
1404 nearest_distance = new_distance;
1405 nearest_object_ptr = prospective_victim_ptr;
1406 }
1407 }
1408 }
1409 }
1410
1411 if (nearest_object_ptr == NULL) {
1412
1413 if (targeting_from_closest_to_farthest) {
1414 if (minimum_object_ptr != NULL) {
1415 nearest_object_ptr = minimum_object_ptr;
1416 }
1417 }
1418 else {
1419 if (maximum_object_ptr != NULL) {
1420 nearest_object_ptr = maximum_object_ptr;
1421 }
1422 }
1423 }
1424
1425 return nearest_object_ptr;
1426 }
1427
1428 ship_obj *get_ship_obj_ptr_from_index(int index);
1429 // -------------------------------------------------------------------
1430 // hud_target_missile()
1431 //
1432 // Target the closest locked missile that is locked on locked_obj
1433 //
1434 // input: source_obj => pointer to object that fired weapon
1435 // next_flag => 0 -> previous 1 -> next
1436 //
1437 // NOTE: this function is only allows targeting bombs
hud_target_missile(object * source_obj,int next_flag)1438 void hud_target_missile(object *source_obj, int next_flag)
1439 {
1440 missile_obj *end, *start, *mo;
1441 object *A, *target_objp;
1442 ai_info *aip;
1443 weapon *wp;
1444 weapon_info *wip;
1445 int target_found = 0;
1446
1447 if ( source_obj->type != OBJ_SHIP )
1448 return;
1449
1450 Assert( Ships[source_obj->instance].ai_index != -1 );
1451 aip = &Ai_info[Ships[source_obj->instance].ai_index];
1452
1453 end = &Missile_obj_list;
1454 if (aip->target_objnum != -1) {
1455 target_objp = &Objects[aip->target_objnum];
1456 if ( target_objp->type == OBJ_WEAPON && Weapon_info[Weapons[target_objp->instance].weapon_info_index].subtype == WP_MISSILE ) { // must be a missile
1457 end = missile_obj_return_address(Weapons[target_objp->instance].missile_list_index);
1458 }
1459 }
1460
1461 start = advance_missile_obj(end, next_flag);
1462
1463 for ( mo = start; mo != end; mo = advance_missile_obj(mo, next_flag) ) {
1464 if ( mo == &Missile_obj_list ){
1465 continue;
1466 }
1467
1468 Assert(mo->objnum >= 0 && mo->objnum < MAX_OBJECTS);
1469 A = &Objects[mo->objnum];
1470
1471 Assert(A->type == OBJ_WEAPON);
1472 Assert((A->instance >= 0) && (A->instance < MAX_WEAPONS));
1473 wp = &Weapons[A->instance];
1474 wip = &Weapon_info[wp->weapon_info_index];
1475
1476 // only allow targeting of bombs
1477 if ( !(wip->wi_flags[Weapon::Info_Flags::Can_be_targeted]) ) {
1478 if ( !(wip->wi_flags[Weapon::Info_Flags::Bomb]) ) {
1479 continue;
1480 }
1481 }
1482
1483 if (wp->lssm_stage==3){
1484 continue;
1485 }
1486
1487 // only allow targeting of hostile bombs
1488 if (!iff_x_attacks_y(Player_ship->team, obj_team(A))) {
1489 continue;
1490 }
1491
1492 if(hud_target_invalid_awacs(A)){
1493 continue;
1494 }
1495
1496 // if we've reached here, got a new target
1497 target_found = TRUE;
1498 set_target_objnum( aip, OBJ_INDEX(A) );
1499 hud_shield_hit_reset(A);
1500 break;
1501 } // end for
1502
1503 if ( !target_found ) {
1504 // if no bomb is found, search for bombers
1505 ship_obj *startShip, *so;
1506
1507 if ( (aip->target_objnum != -1)
1508 && (Objects[aip->target_objnum].type == OBJ_SHIP)
1509 && ((Ship_info[Ships[Objects[aip->target_objnum].instance].ship_info_index].flags[Ship::Info_Flags::Bomber])
1510 || (Objects[aip->target_objnum].flags[Object::Object_Flags::Targetable_as_bomb]))) {
1511 int index = Ships[Objects[aip->target_objnum].instance].ship_list_index;
1512 startShip = get_ship_obj_ptr_from_index(index);
1513 } else {
1514 startShip = GET_FIRST(&Ship_obj_list);
1515 }
1516
1517 for (so=advance_ship(startShip, next_flag); so!=startShip; so=advance_ship(so, next_flag)) {
1518 A = &Objects[so->objnum];
1519
1520 // don't look at header
1521 if (so == &Ship_obj_list) {
1522 continue;
1523 }
1524
1525 // only allow targeting of hostile bombs
1526 if (!iff_x_attacks_y(Player_ship->team, obj_team(A))) {
1527 continue;
1528 }
1529
1530 if(hud_target_invalid_awacs(A)){
1531 continue;
1532 }
1533
1534 // check if ship type is bomber
1535 if ( !(Ship_info[Ships[A->instance].ship_info_index].flags[Ship::Info_Flags::Bomber]) && !(A->flags[Object::Object_Flags::Targetable_as_bomb]) ) {
1536 continue;
1537 }
1538
1539 // check if ignore
1540 if ( should_be_ignored(&Ships[A->instance]) ){
1541 continue;
1542 }
1543
1544 // found a good one
1545 target_found = TRUE;
1546 set_target_objnum( aip, OBJ_INDEX(A) );
1547 hud_shield_hit_reset(A);
1548 break;
1549 }
1550 }
1551
1552 if ( !target_found ) {
1553 snd_play( gamesnd_get_game_sound(GameSounds::TARGET_FAIL), 0.0f );
1554 }
1555 }
1556
1557 // Return !0 if shipp can be scanned, otherwise return 0
hud_target_ship_can_be_scanned(ship * shipp)1558 int hud_target_ship_can_be_scanned(ship *shipp)
1559 {
1560 ship_info *sip;
1561
1562 sip = &Ship_info[shipp->ship_info_index];
1563
1564 // ignore cargo that has already been scanned
1565 if (shipp->flags[Ship::Ship_Flags::Cargo_revealed])
1566 return 0;
1567
1568 // allow ships with scannable flag set
1569 if (shipp->flags[Ship::Ship_Flags::Scannable])
1570 return 1;
1571
1572 // ignore ships that don't carry cargo
1573 if ((sip->class_type < 0) || !(Ship_types[sip->class_type].flags[Ship::Type_Info_Flags::Scannable]))
1574 return 0;
1575
1576 return 1;
1577 }
1578
1579 // target the next/prev uninspected cargo container
hud_target_uninspected_cargo(int next_flag)1580 void hud_target_uninspected_cargo(int next_flag)
1581 {
1582 object *A, *start, *start2;
1583 ship *shipp;
1584 int target_found = 0;
1585
1586 if (Player_ai->target_objnum == -1) {
1587 start = &obj_used_list;
1588 } else {
1589 start = &Objects[Player_ai->target_objnum];
1590 }
1591
1592 start2 = advance_fb(start, next_flag);
1593
1594 for ( A = start2; A != start; A = advance_fb(A, next_flag) ) {
1595 if ( A == &obj_used_list ) {
1596 continue;
1597 }
1598
1599 if (A == Player_obj || (A->type != OBJ_SHIP) ) {
1600 continue;
1601 }
1602
1603 shipp = &Ships[A->instance]; // get a pointer to the ship information
1604
1605 if ( should_be_ignored(shipp) ) {
1606 continue;
1607 }
1608
1609 // ignore all non-cargo carrying craft
1610 if ( !hud_target_ship_can_be_scanned(shipp) ) {
1611 continue;
1612 }
1613
1614 if(hud_target_invalid_awacs(A)){
1615 continue;
1616 }
1617
1618 // if we've reached here, it is a valid next target
1619 if ( Player_ai->target_objnum != OBJ_INDEX(A) ) {
1620 target_found = TRUE;
1621 set_target_objnum( Player_ai, OBJ_INDEX(A) );
1622 hud_shield_hit_reset(A);
1623 }
1624 }
1625
1626 if ( target_found == FALSE ) {
1627 snd_play( gamesnd_get_game_sound(GameSounds::TARGET_FAIL));
1628 }
1629 }
1630
1631 // target the newest ship in the area
hud_target_newest_ship()1632 void hud_target_newest_ship()
1633 {
1634 object *A, *player_target_objp;
1635 object *newest_obj=NULL;
1636 ship *shipp;
1637 ship_obj *so;
1638 uint current_target_arrived_time = 0xffffffff, newest_time = 0;
1639
1640 if ( Player_ai->target_objnum >= 0 ) {
1641 player_target_objp = &Objects[Player_ai->target_objnum];
1642 if ( player_target_objp->type == OBJ_SHIP ) {
1643 current_target_arrived_time = Ships[player_target_objp->instance].create_time;
1644 }
1645 } else {
1646 player_target_objp = NULL;
1647 }
1648
1649 // If no target is selected, then simply target the closest uninspected cargo
1650 if ( Player_ai->target_objnum == -1 || timestamp_elapsed(Target_newest_ship_timestamp) ) {
1651 current_target_arrived_time = 0xffffffff;
1652 }
1653
1654 Target_newest_ship_timestamp = timestamp(TL_RESET);
1655
1656 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
1657 A = &Objects[so->objnum];
1658 shipp = &Ships[A->instance]; // get a pointer to the ship information
1659
1660 if ( (A == Player_obj) || (should_be_ignored(shipp)) )
1661 continue;
1662
1663 // ignore navbuoys
1664 if ( Ship_info[shipp->ship_info_index].flags[Ship::Info_Flags::Navbuoy] ) {
1665 continue;
1666 }
1667
1668 if ( A == player_target_objp ) {
1669 continue;
1670 }
1671
1672 if(hud_target_invalid_awacs(A)){
1673 continue;
1674 }
1675
1676 if ( (shipp->create_time >= newest_time) && (shipp->create_time <= current_target_arrived_time) ) {
1677 newest_time = shipp->create_time;
1678 newest_obj = A;
1679 }
1680 }
1681
1682 if (newest_obj) {
1683 set_target_objnum( Player_ai, OBJ_INDEX(newest_obj) );
1684 hud_shield_hit_reset(newest_obj);
1685 // if BIG|HUGE and no selected subsystem, get sorted turret
1686 hud_maybe_set_sorted_turret_subsys(&Ships[newest_obj->instance]);
1687 hud_restore_subsystem_target(&Ships[newest_obj->instance]);
1688 }
1689 else {
1690 snd_play( gamesnd_get_game_sound(GameSounds::TARGET_FAIL));
1691 }
1692 }
1693
1694 #define TYPE_NONE 0
1695 #define TYPE_FACING_BEAM 1
1696 #define TYPE_FACING_FLAK 2
1697 #define TYPE_FACING_MISSILE 3
1698 #define TYPE_FACING_LASER 4
1699 #define TYPE_NONFACING_BEAM 5
1700 #define TYPE_NONFACING_FLAK 6
1701 #define TYPE_NONFACING_MISSILE 7
1702 #define TYPE_NONFACING_LASER 8
1703 #define TYPE_NONFACING_INC 4
1704
1705 typedef struct eval_next_turret {
1706 ship_subsys *ss;
1707 int type;
1708 float dist;
1709 } eval_next_turret;
1710
turret_compare_func(const void * e1,const void * e2)1711 int turret_compare_func(const void *e1, const void *e2)
1712 {
1713 eval_next_turret *p1 = (eval_next_turret*)e1;
1714 eval_next_turret *p2 = (eval_next_turret*)e2;
1715
1716 Assert(p1->type != TYPE_NONE);
1717 Assert(p2->type != TYPE_NONE);
1718
1719 if (p1->type != p2->type) {
1720 return (p1->type - p2->type);
1721 } else {
1722 float delta_dist = p1->dist - p2->dist;
1723 if (delta_dist < 0) {
1724 return -1;
1725 } else if (delta_dist > 0) {
1726 return 1;
1727 } else {
1728 return 0;
1729 }
1730 }
1731 }
1732
1733 extern bool turret_weapon_has_flags(ship_weapon *swp, Weapon::Info_Flags flags);
1734 extern bool turret_weapon_has_subtype(ship_weapon *swp, int subtype);
1735 // target the next/prev live turret on the current target
1736 // auto_advance from hud_update_closest_turret
hud_target_live_turret(int next_flag,int auto_advance,int only_player_target)1737 void hud_target_live_turret(int next_flag, int auto_advance, int only_player_target)
1738 {
1739 ship_subsys *A;
1740 ship_subsys *live_turret=NULL;
1741 ship *target_shipp;
1742 object *objp;
1743 eval_next_turret ent[MAX_MODEL_SUBSYSTEMS];
1744 int num_live_turrets = 0;
1745
1746 // make sure we're targeting a ship
1747 if (Player_ai->target_objnum == -1 && !auto_advance) {
1748 snd_play(gamesnd_get_game_sound(GameSounds::TARGET_FAIL));
1749 return;
1750 }
1751
1752 // only targeting subsystems on ship
1753 if ((Objects[Player_ai->target_objnum].type != OBJ_SHIP) && (!auto_advance)) {
1754 snd_play( gamesnd_get_game_sound(GameSounds::TARGET_FAIL));
1755 return;
1756 }
1757
1758 // set some pointers
1759 objp = &Objects[Player_ai->target_objnum];
1760 target_shipp = &Ships[objp->instance];
1761
1762 // set timestamp
1763 int timestamp_val = 0;
1764 if (!auto_advance) {
1765 timestamp_val = Target_next_turret_timestamp;
1766 Target_next_turret_timestamp = timestamp(TURRET_RESET);
1767 }
1768
1769 // If no target is selected, then simply target the closest (or facing) turret
1770 int last_subsys_turret = FALSE;
1771 if (Player_ai->targeted_subsys != NULL) {
1772 if (Player_ai->targeted_subsys->system_info->type == SUBSYSTEM_TURRET) {
1773 if (Player_ai->targeted_subsys->weapons.num_primary_banks > 0 || Player_ai->targeted_subsys->weapons.num_secondary_banks > 0) {
1774 last_subsys_turret = TRUE;
1775 }
1776 }
1777 }
1778
1779 // do we want the closest turret (or the one our ship is pointing at)
1780 int get_closest_turret = (auto_advance || !last_subsys_turret || timestamp_elapsed(timestamp_val));
1781
1782 // initialize eval struct
1783 memset(ent,0, sizeof(ent));
1784 int use_straight_ahead_turret = FALSE;
1785
1786 // go through list of turrets
1787 for (A=GET_FIRST(&target_shipp->subsys_list); A!=END_OF_LIST(&target_shipp->subsys_list); A=GET_NEXT(A)) {
1788 // get a turret
1789 if (A->system_info->type == SUBSYSTEM_TURRET) {
1790 // niffiwan: ignore untargetable turrets
1791 if ( A->flags[Ship::Subsystem_Flags::Untargetable] ) {
1792 continue;
1793 }
1794 // check turret has hit points and has a weapon
1795 if ( (A->current_hits > 0) && (A->weapons.num_primary_banks > 0 || A->weapons.num_secondary_banks > 0) ) {
1796 if ( !only_player_target || (A->turret_enemy_objnum == OBJ_INDEX(Player_obj)) ) {
1797 vec3d gsubpos, vec_to_subsys;
1798 float distance, dot;
1799 // get world pos of subsystem and its distance
1800 get_subsystem_world_pos(objp, A, &gsubpos);
1801 distance = vm_vec_normalized_dir(&vec_to_subsys, &gsubpos, &View_position);
1802
1803 // check if facing and in view
1804 int facing = ship_subsystem_in_sight(objp, A, &View_position, &gsubpos, 0);
1805
1806 if (!auto_advance && get_closest_turret && !only_player_target) {
1807 // if within 3 degrees and not previous subsys, use subsys in front
1808 dot = vm_vec_dot(&vec_to_subsys, &Player_obj->orient.vec.fvec);
1809 if ((dot > 0.9986) && facing) {
1810 use_straight_ahead_turret = TRUE;
1811 break;
1812 }
1813 }
1814
1815 // set weapon_type to allow sort of ent on type
1816 if (turret_weapon_has_flags(&A->weapons, Weapon::Info_Flags::Beam)) {
1817 ent[num_live_turrets].type = TYPE_FACING_BEAM;
1818 } else if (turret_weapon_has_flags(&A->weapons, Weapon::Info_Flags::Flak)) {
1819 ent[num_live_turrets].type = TYPE_FACING_FLAK;
1820 } else {
1821 if (turret_weapon_has_subtype(&A->weapons, WP_MISSILE)) {
1822 ent[num_live_turrets].type = TYPE_FACING_MISSILE;
1823 } else if (turret_weapon_has_subtype(&A->weapons, WP_LASER)) {
1824 ent[num_live_turrets].type = TYPE_FACING_LASER;
1825 } else {
1826 //Turret not live, bail
1827 continue;
1828 }
1829 }
1830
1831 // fill out ent struct
1832 ent[num_live_turrets].ss = A;
1833 ent[num_live_turrets].dist = distance;
1834 if (!facing) {
1835 ent[num_live_turrets].type += TYPE_NONFACING_INC;
1836 }
1837 num_live_turrets++;
1838 }
1839 }
1840 }
1841 }
1842
1843 // sort the list if we're not using turret straight ahead of us
1844 if (!use_straight_ahead_turret) {
1845 insertion_sort(ent, num_live_turrets, sizeof(eval_next_turret), turret_compare_func);
1846 }
1847
1848 if (use_straight_ahead_turret) {
1849 // use the straight ahead turret
1850 live_turret = A;
1851 } else {
1852 // check if we have a currently targeted turret and find its position after the sort
1853 int i, start_index, next_index;
1854 if (get_closest_turret) {
1855 start_index = 0;
1856 } else {
1857 start_index = -1;
1858 for (i=0; i<num_live_turrets; i++) {
1859 if (ent[i].ss == Player_ai->targeted_subsys) {
1860 start_index = i;
1861 break;
1862 }
1863 }
1864 // check that we started with a turret
1865 if (start_index == -1) {
1866 start_index = 0;
1867 }
1868 }
1869
1870 // set next live turret
1871 if (num_live_turrets == 0) {
1872 // no live turrets
1873 live_turret = NULL;
1874 } else if (num_live_turrets == 1 || get_closest_turret) {
1875 // only 1 live turret, so set it
1876 live_turret = ent[0].ss;
1877 } else {
1878 if (next_flag) {
1879 // advance to next closest turret
1880 next_index = start_index + 1;
1881 if (next_index == num_live_turrets) {
1882 next_index = 0;
1883 }
1884 } else {
1885 // go to next farther turret
1886 next_index = start_index - 1;
1887 if (next_index == -1) {
1888 next_index = num_live_turrets - 1;
1889 }
1890 }
1891
1892 // set the next turret to be targeted based on next_index
1893 live_turret = ent[next_index].ss;
1894 }
1895
1896 //if (live_turret) {
1897 // debug info
1898 // mprintf(("name %s, index: %d, type: %d\n", live_turret->system_info->subobj_name, next_index, ent[next_index].type));
1899 //}
1900 }
1901
1902 if ( live_turret != NULL ) {
1903 set_targeted_subsys(Player_ai, live_turret, Player_ai->target_objnum);
1904 target_shipp->last_targeted_subobject[Player_num] = Player_ai->targeted_subsys;
1905 } else {
1906 if (!auto_advance) {
1907 snd_play( gamesnd_get_game_sound(GameSounds::TARGET_FAIL));
1908 }
1909 }
1910 }
1911
1912 // select a new target, by auto-targeting
hud_target_auto_target_next()1913 void hud_target_auto_target_next()
1914 {
1915 if ( Framecount < 2 ) {
1916 return;
1917 }
1918
1919 // No auto-targeting after dead.
1920 if (Game_mode & (GM_DEAD | GM_DEAD_BLEW_UP))
1921 return;
1922
1923 // try target next ship in hotkey set, if any -- Backslash
1924 if ( Player->current_hotkey_set != -1 ) {
1925 hud_target_hotkey_select(Player->current_hotkey_set);
1926 }
1927
1928 int valid_team_mask = iff_get_attackee_mask(Player_ship->team);
1929
1930 // if none, try targeting closest hostile fighter/bomber
1931 if ( Player_ai->target_objnum == -1 ) { //-V581
1932 hud_target_closest(valid_team_mask, -1, FALSE, TRUE);
1933 }
1934
1935 // No fighter/bombers exists, so go ahead an target the closest hostile
1936 if ( Player_ai->target_objnum == -1 ) { //-V581
1937 hud_target_closest(valid_team_mask, -1, FALSE);
1938 }
1939
1940 // um, ok. Try targeting asteroids that are on a collision course for an escort ship
1941 if ( Player_ai->target_objnum == -1 ) { //-V581
1942 asteroid_target_closest_danger();
1943 }
1944 }
1945
1946
1947 // Given that object 'targeter' is targeting object 'targetee',
1948 // how far are they? This uses the point closest to the targeter
1949 // object on the targetee's bounding box. So if targeter is inside
1950 // targtee's bounding box, the distance is 0.
hud_find_target_distance(object * targetee,const vec3d * targeter_pos)1951 float hud_find_target_distance( object *targetee, const vec3d *targeter_pos )
1952 {
1953 vec3d tmp_pnt;
1954
1955 int model_num = -1;
1956
1957 // Which model is it?
1958 switch( targetee->type ) {
1959 case OBJ_SHIP:
1960 model_num = Ship_info[Ships[targetee->instance].ship_info_index].model_num;
1961 break;
1962 case OBJ_DEBRIS:
1963 // model_num = Debris[targetee->instance].model_num;
1964 break;
1965 case OBJ_WEAPON:
1966 // Don't find model_num since circles would work better
1967 //model_num = Weapon_info[Weapons[targetee->instance].weapon_info_index].model_num;
1968 break;
1969 case OBJ_ASTEROID:
1970 // Don't find model_num since circles would work better
1971 //model_num = Asteroid_info[Asteroids[targetee->instance].type].model_num;
1972 break;
1973 case OBJ_JUMP_NODE:
1974 // Don't find model_num since circles would work better
1975 //model_num = Jump_nodes[targetee->instance].modelnum;
1976 break;
1977 }
1978
1979 float dist = 0.0f;
1980
1981 // New way, that uses bounding box.
1982 if ( model_num > -1 ) {
1983 dist = model_find_closest_point( &tmp_pnt, model_num, -1, &targetee->orient, &targetee->pos, targeter_pos );
1984 } else {
1985 // Old way, that uses radius.
1986 dist = vm_vec_dist_quick(&targetee->pos, targeter_pos) - targetee->radius;
1987 if ( dist < 0.0f ) {
1988 dist = 0.0f;
1989 }
1990 }
1991 return dist;
1992 }
1993
hud_find_target_distance(object * targetee,object * targeter)1994 float hud_find_target_distance( object *targetee, object *targeter )
1995 {
1996 return hud_find_target_distance(targetee, &targeter->pos);
1997 }
1998
1999 //
2000
2001 /// \brief evaluate a ship (and maybe turrets) as a potential target
2002 ///
2003 /// Check if shipp (or its turrets) is attacking attacked_objnum
2004 /// Provides a special case for player trying to select target (don't check if
2005 /// turrets are aimed at player)
2006 ///
2007 /// \param[in, out] *esct The Evaluate Ship as Closest Target (esct) that will
2008 /// be used to determine if a target is a valid, harmful
2009 /// target and, if so, sets the min_distance attribute
2010 /// to the distance of either the attacker or the
2011 /// closest attacker's turret. Otherwise, min_distance
2012 /// is set to FLT_MAX
2013 ///
2014 /// \return true if either the ship or one of it's turrets are attacking the
2015 /// player. Otherwise, returns false.
evaluate_ship_as_closest_target(esct * esct_p)2016 bool evaluate_ship_as_closest_target(esct *esct_p)
2017 {
2018 int targeting_player, turret_is_attacking;
2019 ship_subsys *ss;
2020 float new_distance;
2021
2022 // initialize
2023 esct_p->min_distance = FLT_MAX;
2024 esct_p->check_nearest_turret = FALSE;
2025 turret_is_attacking = FALSE;
2026
2027
2028 object *objp = &Objects[esct_p->shipp->objnum];
2029 Assert(objp->type == OBJ_SHIP);
2030 if (objp->type != OBJ_SHIP) {
2031 return false;
2032 }
2033
2034 // player being targeted, so we will want closest distance from player
2035 targeting_player = (esct_p->attacked_objnum == OBJ_INDEX(Player_obj));
2036
2037 // filter on team
2038 if ( !iff_matches_mask(esct_p->shipp->team, esct_p->team_mask) ) {
2039 return false;
2040 }
2041
2042 // check if player or ignore ship
2043 if ( (esct_p->shipp->objnum == OBJ_INDEX(Player_obj)) || (should_be_ignored(esct_p->shipp)) ) {
2044 return false;
2045 }
2046
2047 // bail if harmless
2048 if ( Ship_info[esct_p->shipp->ship_info_index].class_type > -1 && !(Ship_types[Ship_info[esct_p->shipp->ship_info_index].class_type].flags[Ship::Type_Info_Flags::Target_as_threat])) {
2049 return false;
2050 }
2051
2052 // only look at targets that are AWACS valid
2053 if (hud_target_invalid_awacs(&Objects[esct_p->shipp->objnum])) {
2054 return false;
2055 }
2056
2057 // If filter is set, only target fighters and bombers
2058 if ( esct_p->filter ) {
2059 if ( !(Ship_info[esct_p->shipp->ship_info_index].is_fighter_bomber()) ) {
2060 return false;
2061 }
2062 }
2063
2064 // find closest turret to player if BIG or HUGE ship
2065 if (Ship_info[esct_p->shipp->ship_info_index].is_big_or_huge()) {
2066 for (ss=GET_FIRST(&esct_p->shipp->subsys_list); ss!=END_OF_LIST(&esct_p->shipp->subsys_list); ss=GET_NEXT(ss)) {
2067
2068 if (ss->flags[Ship::Subsystem_Flags::Untargetable])
2069 continue;
2070
2071 if ( (ss->system_info->type == SUBSYSTEM_TURRET) && (ss->current_hits > 0) ) {
2072
2073 if (esct_p->check_all_turrets || (ss->turret_enemy_objnum == esct_p->attacked_objnum)) {
2074 turret_is_attacking = 1;
2075 esct_p->check_nearest_turret = TRUE;
2076
2077 if ( !esct_p->turret_attacking_target || (esct_p->turret_attacking_target && (ss->turret_enemy_objnum == esct_p->attacked_objnum)) ) {
2078 vec3d gsubpos;
2079 // get world pos of subsystem
2080 vm_vec_unrotate(&gsubpos, &ss->system_info->pnt, &objp->orient);
2081 vm_vec_add2(&gsubpos, &objp->pos);
2082 new_distance = vm_vec_dist_quick(&gsubpos, &Player_obj->pos);
2083
2084 /*
2085 // GET TURRET TYPE, FAVOR BEAM, FLAK, OTHER
2086 int turret_type = ss->system_info->turret_weapon_type;
2087 if (Weapon_info[turret_type].wi_flags[Weapon::Info_Flags::Beam]) {
2088 new_distance *= 0.3f;
2089 } else if (Weapon_info[turret_type].wi_flags[Weapon::Info_Flags::Flak]) {
2090 new_distance *= 0.6f;
2091 } */
2092
2093 // get the closest distance
2094 if (new_distance <= esct_p->min_distance) {
2095 esct_p->min_distance = new_distance;
2096 }
2097 }
2098 }
2099 }
2100 }
2101 }
2102
2103 // If no turret is attacking, check if objp is actually targeting attacked_objnum
2104 // don't bail if targeting is for player
2105 if ( !targeting_player && !turret_is_attacking ) {
2106 ai_info *aip = &Ai_info[esct_p->shipp->ai_index];
2107
2108 if (aip->target_objnum != esct_p->attacked_objnum) {
2109 return false;
2110 }
2111
2112 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)) {
2113 return false;
2114 }
2115 }
2116
2117 // consider the ship alone if there are no attacking turrets
2118 if ( !turret_is_attacking ) {
2119 //new_distance = hud_find_target_distance(objp, Player_obj);
2120 new_distance = vm_vec_dist_quick(&objp->pos, &Player_obj->pos);
2121
2122 if (new_distance <= esct_p->min_distance) {
2123 esct_p->min_distance = new_distance;
2124 esct_p->check_nearest_turret = FALSE;
2125 }
2126 }
2127
2128 return true;
2129 }
2130
2131 /// \brief Sets the Players[Player_num].current_target to the closest ship to
2132 /// the player that matches the team passed as a paramater.
2133 ///
2134 /// The current algorithm is to simply iterate through the objects and
2135 /// calculate the magnitude of the vector that connects the player to the
2136 /// target. The smallest magnitude is tracked, and then used to locate the
2137 /// closest hostile ship. Note only the square of the magnitude is required,
2138 /// since we are only comparing magnitudes.
2139 ///
2140 /// \param[in] team_mask team of closest ship that should be targeted.
2141 /// Default value is -1, if team doesn't matter.
2142 ///
2143 /// \param[in] attacked_objnum object number of ship that is being attacked
2144 /// \param[in] play_fail_snd boolean, whether to play SND_TARGET_FAIL
2145 /// (needed, since function called repeatedly when
2146 /// auto-targeting is enabled, and we don't want a
2147 /// string of fail sounds playing). This is a
2148 /// default parameter with a value of TRUE.
2149 /// \param[in] filter OPTIONAL parameter (default value 0): when set
2150 /// to TRUE, only fighters and bombers are
2151 /// considered for new targets.
2152 /// \param[in] get_closest_turret_attacking_player Finds the closest turret
2153 /// attacking the player if true. Otherwise, only
2154 /// finds the closest attacking ship, targeting the
2155 /// turret closest to the player.
2156 ///
2157 /// \return: true (non-zero) if a target was acquired. Returns false (zero) if
2158 /// no target was acquired.
hud_target_closest(int team_mask,int attacked_objnum,int play_fail_snd,int filter,int get_closest_turret_attacking_player)2159 int hud_target_closest(int team_mask, int attacked_objnum, int play_fail_snd, int filter, int get_closest_turret_attacking_player)
2160 {
2161 object *A;
2162 object *nearest_obj = &obj_used_list;
2163 ship *shipp;
2164 ship_obj *so;
2165 int check_nearest_turret = FALSE;
2166
2167 // evaluate ship closest target struct
2168 esct eval_ship_as_closest_target_args;
2169
2170 float min_distance = FLT_MAX;
2171 int target_found = FALSE;
2172
2173 int player_obj_index = OBJ_INDEX(Player_obj);
2174 ship_subsys *ss;
2175
2176 int initial_attacked_objnum = attacked_objnum;
2177
2178 if ( (attacked_objnum >= 0) && (attacked_objnum != player_obj_index) ) {
2179 // bail if player does not have target
2180 if ( Player_ai->target_objnum == -1 ) {
2181 goto Target_closest_done;
2182 }
2183
2184 if ( Objects[attacked_objnum].type != OBJ_SHIP ) {
2185 goto Target_closest_done;
2186 }
2187
2188 // bail if ship is to be ignored
2189 if (should_be_ignored(&Ships[Objects[attacked_objnum].instance])) {
2190 goto Target_closest_done;
2191 }
2192 }
2193
2194 if (attacked_objnum == -1) {
2195 attacked_objnum = player_obj_index;
2196 }
2197
2198 // check all turrets if for player.
2199 eval_ship_as_closest_target_args.check_all_turrets = (attacked_objnum == player_obj_index);
2200 eval_ship_as_closest_target_args.filter = filter;
2201 eval_ship_as_closest_target_args.team_mask = team_mask;
2202 eval_ship_as_closest_target_args.attacked_objnum = attacked_objnum;
2203 eval_ship_as_closest_target_args.turret_attacking_target = get_closest_turret_attacking_player;
2204
2205 for ( so=GET_FIRST(&Ship_obj_list); so!=END_OF_LIST(&Ship_obj_list); so=GET_NEXT(so) ) {
2206
2207 A = &Objects[so->objnum];
2208 shipp = &Ships[A->instance]; // get a pointer to the ship information
2209
2210 // fill in rest of eval_ship_as_closest_target_args
2211 eval_ship_as_closest_target_args.shipp = shipp;
2212
2213 // Filter out any target that is not targeting the player --Mastadon
2214 if ( (initial_attacked_objnum == player_obj_index) && (Ai_info[shipp->ai_index].target_objnum != player_obj_index) ) {
2215 continue;
2216 }
2217 // check each shipp on list and update nearest obj and subsys
2218 evaluate_ship_as_closest_target(&eval_ship_as_closest_target_args);
2219 if (eval_ship_as_closest_target_args.min_distance < min_distance) {
2220 target_found = TRUE;
2221 min_distance = eval_ship_as_closest_target_args.min_distance;
2222 nearest_obj = A;
2223 check_nearest_turret = eval_ship_as_closest_target_args.check_nearest_turret;
2224 }
2225 }
2226
2227 Target_closest_done:
2228
2229 // maybe ignore target if too far away
2230 // DKA 9/8/99 Remove distance check
2231 /*
2232 if (target_found) {
2233 // get distance to nearest attacker
2234 float dist = vm_vec_dist_quick(&Objects[attacked_objnum].pos, &nearest_obj->pos);
2235
2236 // no distance limit for player obj
2237 if ((attacked_objnum != player_obj_index) && (dist > MIN_DISTANCE_TO_CONSIDER_THREAT)) {
2238 target_found = FALSE;
2239 }
2240 } */
2241
2242 if (target_found) {
2243 set_target_objnum(Player_ai, OBJ_INDEX(nearest_obj));
2244 hud_shield_hit_reset(nearest_obj);
2245 if ( check_nearest_turret ) {
2246
2247 // if former subobject was not a turret do, not change subsystem
2248 ss = Ships[nearest_obj->instance].last_targeted_subobject[Player_num];
2249 if (ss == NULL || get_closest_turret_attacking_player) {
2250 // update nearest turret with later func
2251 hud_target_live_turret(1, 1, get_closest_turret_attacking_player);
2252 Ships[nearest_obj->instance].last_targeted_subobject[Player_num] = Player_ai->targeted_subsys;
2253 }
2254 } else {
2255 hud_restore_subsystem_target(&Ships[nearest_obj->instance]);
2256 }
2257 } else {
2258 // no target found, maybe play fail sound
2259 if (play_fail_snd == TRUE) {
2260 snd_play(gamesnd_get_game_sound(GameSounds::TARGET_FAIL));
2261 }
2262 }
2263
2264 return target_found;
2265 }
2266
2267 // auto update closest turret to attack on big or huge ships
hud_update_closest_turret()2268 void hud_update_closest_turret()
2269 {
2270 hud_target_live_turret(1, 1);
2271
2272 /*
2273 float nearest_distance, new_distance;
2274 ship_subsys *ss, *closest_subsys;
2275 ship *shipp;
2276 object *objp;
2277
2278 nearest_distance = FLT_MAX;
2279 objp = &Objects[Player_ai->target_objnum];
2280 shipp = &Ships[objp->instance];
2281 closest_subsys = NULL;
2282
2283
2284 Assert(Ship_info[shipp->ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP));
2285
2286 for (ss=GET_FIRST(&shipp->subsys_list); ss!=END_OF_LIST(&shipp->subsys_list); ss=GET_NEXT(ss)) {
2287 if ( (ss->system_info->type == SUBSYSTEM_TURRET) && (ss->current_hits > 0) ) {
2288 // make sure turret is not "unused"
2289 if (ss->system_info->turret_weapon_type >= 0) {
2290 vec3d gsubpos;
2291 // get world pos of subsystem
2292 vm_vec_unrotate(&gsubpos, &ss->system_info->pnt, &objp->orient);
2293 vm_vec_add2(&gsubpos, &objp->pos);
2294 new_distance = vm_vec_dist_quick(&gsubpos, &Player_obj->pos);
2295
2296 // GET TURRET TYPE, FAVOR BEAM, FLAK, OTHER
2297 int turret_type = ss->system_info->turret_weapon_type;
2298 if (Weapon_info[turret_type].wi_flags[Weapon::Info_Flags::Beam]) {
2299 new_distance *= 0.3f;
2300 } else if (Weapon_info[turret_type].wi_flags[Weapon::Info_Flags::Flak]) {
2301 new_distance *= 0.6f;
2302 }
2303
2304 // check if facing and in view
2305 int facing = ship_subsystem_in_sight(objp, ss, &View_position, &gsubpos, 0);
2306
2307 if (facing) {
2308 new_distance *= 0.5f;
2309 }
2310
2311 // get the closest distance
2312 if (new_distance <= nearest_distance) {
2313 nearest_distance = new_distance;
2314 closest_subsys = ss;
2315 }
2316 }
2317 }
2318 }
2319
2320 // check if new subsys to target
2321 if (Player_ai->targeted_subsys != NULL) {
2322 set_targeted_subsys(Player_ai, closest_subsys, Player_ai->target_objnum);
2323 shipp->last_targeted_subobject[Player_num] = Player_ai->targeted_subsys;
2324 }
2325 */
2326 }
2327
2328
2329 // --------------------------------------------------------------------
2330 // hud_target_targets_target()
2331 //
2332 // Target your target's target. Your target is specified by objnum passed
2333 // as a parameter.
2334 //
hud_target_targets_target()2335 void hud_target_targets_target()
2336 {
2337 object *objp = NULL;
2338 object *tt_objp = NULL;
2339 int tt_objnum;
2340
2341 if ( Player_ai->target_objnum < 0 || Player_ai->target_objnum >= MAX_OBJECTS ) {
2342 goto ttt_fail;
2343 }
2344
2345 objp = &Objects[Player_ai->target_objnum];
2346 if ( objp->type != OBJ_SHIP ) {
2347 goto ttt_fail;
2348 }
2349
2350 tt_objnum = Ai_info[Ships[objp->instance].ai_index].target_objnum;
2351 if ( tt_objnum < 0 || tt_objnum >= MAX_OBJECTS ) {
2352 goto ttt_fail;
2353 }
2354
2355 if ( tt_objnum == OBJ_INDEX(Player_obj) ) {
2356 goto ttt_fail;
2357 }
2358
2359 tt_objp = &Objects[tt_objnum];
2360
2361 if (hud_target_invalid_awacs(tt_objp)) {
2362 goto ttt_fail;
2363 }
2364
2365 if ( tt_objp->type != OBJ_SHIP ) {
2366 goto ttt_fail;
2367 }
2368
2369 if ( should_be_ignored(&Ships[tt_objp->instance]) ) {
2370 goto ttt_fail;
2371 }
2372
2373 // if we've reached here, found player target's target
2374 set_target_objnum( Player_ai, tt_objnum );
2375 hud_shield_hit_reset(&Objects[tt_objnum]);
2376 if (Objects[tt_objnum].type == OBJ_SHIP) {
2377 hud_maybe_set_sorted_turret_subsys(&Ships[Objects[tt_objnum].instance]);
2378 }
2379 hud_restore_subsystem_target(&Ships[Objects[tt_objnum].instance]);
2380 return;
2381
2382 ttt_fail:
2383 snd_play( gamesnd_get_game_sound(GameSounds::TARGET_FAIL), 0.0f );
2384 }
2385
2386 // Return !0 if target_objp is a valid object type for targeting in reticle, otherwise return 0
object_targetable_in_reticle(object * target_objp)2387 int object_targetable_in_reticle(object *target_objp)
2388 {
2389 int obj_type;
2390 SCP_list<CJumpNode>::iterator jnp;
2391
2392 if (target_objp == Player_obj ) {
2393 return 0;
2394 }
2395
2396 obj_type = target_objp->type;
2397
2398 if ( (obj_type == OBJ_SHIP) || (obj_type == OBJ_DEBRIS) || (obj_type == OBJ_WEAPON) || (obj_type == OBJ_ASTEROID) )
2399 {
2400 return 1;
2401 } else if ( obj_type == OBJ_JUMP_NODE )
2402 {
2403 for (jnp = Jump_nodes.begin(); jnp != Jump_nodes.end(); ++jnp) {
2404 if(jnp->GetSCPObject() == target_objp)
2405 break;
2406 }
2407
2408 if (!jnp->IsHidden())
2409 return 1;
2410 }
2411
2412 return 0;
2413 }
2414
2415 // hud_target_in_reticle_new() will target the object that is closest to the player, and who is
2416 // intersected by a ray passed from the center of the reticle out along the forward vector of the
2417 // player.
2418 //
2419 // targeting of objects of type OBJ_SHIP and OBJ_DEBRIS are supported
2420 //
2421 // Method: A ray is cast from the center of the reticle, and we keep track of any eligible object
2422 // the ray intersects. We take the ship closest to us that intersects an object.
2423 //
2424 // Since this method may work poorly with objects that are far away, hud_target_in_reticle_old()
2425 // is called if no intersections are found.
2426 //
2427 //
2428 #define TARGET_IN_RETICLE_DISTANCE 10000.0f
2429
hud_target_in_reticle_new()2430 void hud_target_in_reticle_new()
2431 {
2432 vec3d terminus;
2433 object *A;
2434 mc_info mc;
2435 float dist;
2436 SCP_list<CJumpNode>::iterator jnp;
2437
2438 hud_reticle_clear_list(&Reticle_cur_list);
2439 Reticle_save_timestamp = timestamp(RESET_TARGET_IN_RETICLE);
2440
2441 // Get 3d vector through center of reticle
2442 vm_vec_scale_add(&terminus, &Eye_position, &Player_obj->orient.vec.fvec, TARGET_IN_RETICLE_DISTANCE);
2443
2444 mc_info_init(&mc);
2445 mc.model_instance_num = -1;
2446 mc.model_num = 0;
2447 for ( A = GET_FIRST(&obj_used_list); A !=END_OF_LIST(&obj_used_list); A = GET_NEXT(A) ) {
2448 if ( !object_targetable_in_reticle(A) ) {
2449 continue;
2450 }
2451
2452 if ( A->type == OBJ_WEAPON ) {
2453 if ( !(Weapon_info[Weapons[A->instance].weapon_info_index].wi_flags[Weapon::Info_Flags::Can_be_targeted]) ) {
2454 if ( !(Weapon_info[Weapons[A->instance].weapon_info_index].wi_flags[Weapon::Info_Flags::Bomb]) ){
2455 continue;
2456 }
2457 }
2458 if (Weapons[A->instance].lssm_stage==3){
2459 continue;
2460 }
2461 }
2462
2463
2464 if ( A->type == OBJ_SHIP ) {
2465 if ( should_be_ignored(&Ships[A->instance]) ){
2466 continue;
2467 }
2468 }
2469
2470 if(hud_target_invalid_awacs(A)){
2471 continue;
2472 }
2473
2474 switch (A->type) {
2475 case OBJ_SHIP:
2476 mc.model_num = Ship_info[Ships[A->instance].ship_info_index].model_num;
2477 break;
2478 case OBJ_DEBRIS:
2479 mc.model_num = Debris[A->instance].model_num;
2480 break;
2481 case OBJ_WEAPON:
2482 mc.model_num = Weapon_info[Weapons[A->instance].weapon_info_index].model_num;
2483 break;
2484 case OBJ_ASTEROID:
2485 {
2486 int pof = 0;
2487 pof = Asteroids[A->instance].asteroid_subtype;
2488 mc.model_num = Asteroid_info[Asteroids[A->instance].asteroid_type].model_num[pof];
2489 }
2490 break;
2491 case OBJ_JUMP_NODE:
2492 for (jnp = Jump_nodes.begin(); jnp != Jump_nodes.end(); ++jnp) {
2493 if(jnp->GetSCPObject() == A)
2494 break;
2495 }
2496
2497 mc.model_num = jnp->GetModelNumber();
2498 break;
2499 default:
2500 Int3(); // Illegal object type.
2501 }
2502
2503 if (mc.model_num == -1) {
2504 // so just check distance of a point
2505 vec3d temp_v;
2506 float angle;
2507 vm_vec_sub(&temp_v, &A->pos, &Eye_position);
2508 vm_vec_normalize(&temp_v);
2509 angle = vm_vec_dot(&Player_obj->orient.vec.fvec, &temp_v);
2510 if (angle > 0.99f) {
2511 dist = vm_vec_mag_squared(&temp_v);
2512 hud_reticle_list_update(A, dist, 0);
2513 }
2514
2515 } else {
2516 mc.orient = &A->orient; // The object's orient
2517 mc.pos = &A->pos; // The object's position
2518 mc.p0 = &Eye_position; // Point 1 of ray to check
2519 mc.p1 = &terminus; // Point 2 of ray to check
2520 mc.flags = MC_CHECK_MODEL; // | MC_ONLY_BOUND_BOX; // check the model, but only its bounding box
2521
2522 model_collide(&mc);
2523 if ( mc.num_hits ) {
2524 dist = vm_vec_dist_squared(&mc.hit_point_world, &Eye_position);
2525 hud_reticle_list_update(A, dist, 0);
2526 }
2527 }
2528 } // end for (go to next object)
2529
2530 hud_target_in_reticle_old(); // try the old method (works well with ships far away)
2531 }
2532
2533 // hud_target_in_reticle_old() will target the object that is closest to the reticle center and inside
2534 // the reticle
2535 //
2536 // targeting of objects of type OBJ_SHIP and OBJ_DEBRIS are supported
2537 //
2538 //
2539 // Method: take the dot product of the foward vector and the vector to target. Take
2540 // the one that is closest to 1 and at least MIN_DOT_FOR_TARGET
2541 //
2542 // IMPORTANT: The MIN_DOT_FOR_TARGET value was arrived at by trial and error and
2543 // is only valid for the HUD reticle in use at that time.
2544
2545 #define MIN_DOT_FOR_TARGET 0.9726// fov for targeting in reticle
2546
hud_target_in_reticle_old()2547 void hud_target_in_reticle_old()
2548 {
2549 object *A, *target_obj;
2550 float dot;
2551 vec3d vec_to_target;
2552
2553 for ( A = GET_FIRST(&obj_used_list); A !=END_OF_LIST(&obj_used_list); A = GET_NEXT(A) ) {
2554 if ( !object_targetable_in_reticle(A) ) {
2555 continue;
2556 }
2557
2558 if ( A->type == OBJ_WEAPON ) {
2559 if ( !(Weapon_info[Weapons[A->instance].weapon_info_index].wi_flags[Weapon::Info_Flags::Can_be_targeted]) ){
2560 if ( !(Weapon_info[Weapons[A->instance].weapon_info_index].wi_flags[Weapon::Info_Flags::Bomb]) ){
2561 continue;
2562 }
2563 }
2564
2565 if (Weapons[A->instance].lssm_stage==3){
2566 continue;
2567 }
2568 }
2569
2570 if ( A->type == OBJ_SHIP ) {
2571 if ( should_be_ignored(&Ships[A->instance]) ){
2572 continue;
2573 }
2574 }
2575
2576 if(hud_target_invalid_awacs(A)){
2577 continue;
2578 }
2579
2580 if ( vm_vec_same( &A->pos, &Eye_position ) ) {
2581 continue;
2582 }
2583
2584 vm_vec_normalized_dir(&vec_to_target, &A->pos, &Eye_position);
2585 dot = vm_vec_dot(&Player_obj->orient.vec.fvec, &vec_to_target);
2586
2587 if ( dot > MIN_DOT_FOR_TARGET ) {
2588 hud_reticle_list_update(A, dot, 1);
2589 }
2590 }
2591
2592 target_obj = hud_reticle_pick_target();
2593 if ( target_obj != NULL ) {
2594 set_target_objnum( Player_ai, OBJ_INDEX(target_obj) );
2595 hud_shield_hit_reset(target_obj);
2596 if ( target_obj->type == OBJ_SHIP ) {
2597 // if BIG|HUGE, maybe set subsys to turret
2598 hud_maybe_set_sorted_turret_subsys(&Ships[target_obj->instance]);
2599 hud_restore_subsystem_target(&Ships[target_obj->instance]);
2600 }
2601 }
2602 else {
2603 snd_play( gamesnd_get_game_sound(GameSounds::TARGET_FAIL), 0.0f );
2604 }
2605 }
2606
2607 // hud_target_subsystem_in_reticle() will target the subsystem that is within the reticle and
2608 // is closest to the reticle center. The current target is the only object that is searched for
2609 // subsystems
2610 //
2611 // Method: take the dot product of the foward vector and the vector to target. Take
2612 // the one that is closest to 1 and at least MIN_DOT_FOR_TARGET
2613 //
2614 // IMPORTANT: The MIN_DOT_FOR_TARGET value was arrived at by trial and error and
2615 // is only valid for the HUD reticle in use at that time.
2616 //
2617
hud_target_subsystem_in_reticle()2618 void hud_target_subsystem_in_reticle()
2619 {
2620 object* targetp;
2621 ship_subsys *subsys;
2622 ship_subsys *nearest_subsys = NULL;
2623 vec3d subobj_pos;
2624
2625 float dot, best_dot;
2626 vec3d vec_to_target;
2627 best_dot = -1.0f;
2628
2629 if ( Player_ai->target_objnum == -1){
2630 hud_target_in_reticle_old();
2631 }
2632
2633 if ( Player_ai->target_objnum == -1) { //-V581
2634 snd_play( gamesnd_get_game_sound(GameSounds::TARGET_FAIL));
2635 return;
2636 }
2637
2638 targetp = &Objects[Player_ai->target_objnum];
2639
2640 if ( targetp->type != OBJ_SHIP ){ // only targeting subsystems on ship
2641 return;
2642 }
2643
2644 int shipnum = targetp->instance;
2645
2646 if ( targetp->type == OBJ_SHIP ) {
2647 if ( should_be_ignored(&Ships[shipnum]) ) {
2648 return;
2649 }
2650 }
2651
2652 for (subsys = GET_FIRST(&Ships[shipnum].subsys_list); subsys != END_OF_LIST(&Ships[shipnum].subsys_list) ; subsys = GET_NEXT( subsys ) ) {
2653
2654 //if the subsystem isn't targetable, skip it
2655 if (subsys->flags[Ship::Subsystem_Flags::Untargetable])
2656 continue;
2657
2658 get_subsystem_world_pos(targetp, subsys, &subobj_pos);
2659
2660 vm_vec_normalized_dir(&vec_to_target, &subobj_pos, &Eye_position);
2661 dot = vm_vec_dot(&Player_obj->orient.vec.fvec, &vec_to_target);
2662
2663 if ( dot > best_dot ) {
2664 best_dot = dot;
2665 if ( best_dot > MIN_DOT_FOR_TARGET )
2666 nearest_subsys = subsys;
2667 }
2668 } // end for
2669
2670 if ( nearest_subsys != NULL ) {
2671 set_targeted_subsys(Player_ai, nearest_subsys, Player_ai->target_objnum);
2672 char r_name[NAME_LENGTH];
2673 int i;
2674 strcpy_s(r_name, ship_subsys_get_name(Player_ai->targeted_subsys));
2675 for (i = 0; r_name[i] > 0; i++) {
2676 if (r_name[i] == '|')
2677 r_name[i] = ' ';
2678 }
2679 HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Targeting subsystem %s.", 323), r_name);
2680 Ships[shipnum].last_targeted_subobject[Player_num] = Player_ai->targeted_subsys;
2681 }
2682 else {
2683 snd_play( gamesnd_get_game_sound(GameSounds::TARGET_FAIL));
2684 }
2685 }
2686
2687 #define T_LENGTH 8
2688 #define T_OFFSET_FROM_CIRCLE -13
2689 #define T_BASE_LENGTH 4
2690
2691 // On entry:
2692 // color set
renderOrientation(object * from_objp,object * to_objp,matrix * from_orientp)2693 void HudGaugeOrientationTee::renderOrientation(object *from_objp, object *to_objp, matrix *from_orientp)
2694 {
2695 float dot_product;
2696 vec3d target_to_obj;
2697 float x1,y1,x2,y2,x3,y3,x4,y4;
2698
2699 vm_vec_sub(&target_to_obj, &from_objp->pos, &to_objp->pos);
2700
2701 vm_vec_normalize(&target_to_obj);
2702
2703 // calculate the dot product between the target_to_player vector and the targets forward vector
2704 //
2705 // 0 - vectors are perpendicular
2706 // 1 - vectors are collinear and in the same direction (target is facing player)
2707 // -1 - vectors are collinear and in the opposite direction (target is facing away from player)
2708 dot_product = vm_vec_dot(&from_orientp->vec.fvec, &target_to_obj);
2709
2710 if (vm_vec_dot(&from_orientp->vec.rvec, &target_to_obj) >= 0) {
2711 if (dot_product >= 0){
2712 dot_product = -PI_2*dot_product + PI;
2713 } else {
2714 dot_product = -PI_2*dot_product - PI;
2715 }
2716 } else {
2717 dot_product *= PI_2; //(range is now -PI/2 => PI/2)
2718 }
2719
2720 y1 = sinf(dot_product) * (Radius - T_OFFSET_FROM_CIRCLE);
2721 x1 = cosf(dot_product) * (Radius - T_OFFSET_FROM_CIRCLE);
2722
2723 y1 += position[1];
2724 x1 += position[0];
2725
2726 x1 += HUD_offset_x;
2727 y1 += HUD_offset_y;
2728
2729 y2 = sinf(dot_product) * (Radius - T_OFFSET_FROM_CIRCLE - T_LENGTH);
2730 x2 = cosf(dot_product) * (Radius - T_OFFSET_FROM_CIRCLE - T_LENGTH);
2731
2732 y2 += position[1];
2733 x2 += position[0];
2734
2735 x2 += HUD_offset_x;
2736 y2 += HUD_offset_y;
2737
2738 x3 = x1 - T_BASE_LENGTH * sinf(dot_product);
2739 y3 = y1 + T_BASE_LENGTH * cosf(dot_product);
2740 x4 = x1 + T_BASE_LENGTH * sinf(dot_product);
2741 y4 = y1 - T_BASE_LENGTH * cosf(dot_product);
2742
2743 // HACK! Should be antialiased!
2744 renderLine(fl2i(x3),fl2i(y3),fl2i(x4),fl2i(y4)); // bottom of T
2745 renderLine(fl2i(x1),fl2i(y1),fl2i(x2),fl2i(y2)); // part of T pointing towards center
2746 }
2747
hud_tri(float x1,float y1,float x2,float y2,float x3,float y3)2748 void hud_tri(float x1,float y1,float x2,float y2,float x3,float y3)
2749 {
2750 int i;
2751
2752 // Make the triangle always be the correct handiness so
2753 // the tmapper won't think its back-facing and throw it out.
2754 float det = (y2-y1)*(x3-x1) - (x2-x1)*(y3-y1);
2755 if ( det >= 0.0f ) {
2756 float tmp;
2757
2758 // swap y1 & y3
2759 tmp = y1;
2760 y1 = y3;
2761 y3 = tmp;
2762
2763 // swap x1 & x3
2764 tmp = x1;
2765 x1 = x3;
2766 x3 = tmp;
2767 }
2768
2769 vertex verts[3];
2770
2771 // zero verts[] out, this is a faster way (nods to Kazan) to make sure that
2772 // the specular colors are set to 0 to avoid rendering problems - taylor
2773 memset(verts, 0, sizeof(verts));
2774
2775
2776 verts[0].screen.xyw.x = x1;
2777 verts[0].screen.xyw.y = y1;
2778 verts[0].screen.xyw.w = 0.0f;
2779 verts[0].texture_position.u = 0.0f;
2780 verts[0].texture_position.v = 0.0f;
2781 verts[0].flags = PF_PROJECTED;
2782 verts[0].codes = 0;
2783 verts[0].r = (ubyte)gr_screen.current_color.red;
2784 verts[0].g = (ubyte)gr_screen.current_color.green;
2785 verts[0].b = (ubyte)gr_screen.current_color.blue;
2786 verts[0].a = (ubyte)gr_screen.current_color.alpha;
2787
2788 verts[1].screen.xyw.x = x2;
2789 verts[1].screen.xyw.y = y2;
2790 verts[1].screen.xyw.w = 0.0f;
2791 verts[1].texture_position.u = 0.0f;
2792 verts[1].texture_position.v = 0.0f;
2793 verts[1].flags = PF_PROJECTED;
2794 verts[1].codes = 0;
2795 verts[1].r = (ubyte)gr_screen.current_color.red;
2796 verts[1].g = (ubyte)gr_screen.current_color.green;
2797 verts[1].b = (ubyte)gr_screen.current_color.blue;
2798 verts[1].a = (ubyte)gr_screen.current_color.alpha;
2799
2800 verts[2].screen.xyw.x = x3;
2801 verts[2].screen.xyw.y = y3;
2802 verts[2].screen.xyw.w = 0.0f;
2803 verts[2].texture_position.u = 0.0f;
2804 verts[2].texture_position.v = 0.0f;
2805 verts[2].flags = PF_PROJECTED;
2806 verts[2].codes = 0;
2807 verts[2].r = (ubyte)gr_screen.current_color.red;
2808 verts[2].g = (ubyte)gr_screen.current_color.green;
2809 verts[2].b = (ubyte)gr_screen.current_color.blue;
2810 verts[2].a = (ubyte)gr_screen.current_color.alpha;
2811
2812 for (i=0; i<3; i++)
2813 gr_resize_screen_posf(&verts[i].screen.xyw.x, &verts[i].screen.xyw.y);
2814
2815 //uint saved_mode = gr_zbuffer_get();
2816 //int cull = gr_set_cull(0);
2817
2818 //gr_zbuffer_set( GR_ZBUFF_NONE );
2819
2820 material material_def;
2821
2822 material_def.set_blend_mode(ALPHA_BLEND_NONE);
2823 material_def.set_depth_mode(ZBUFFER_TYPE_NONE);
2824 material_def.set_cull_mode(false);
2825
2826 g3_render_primitives_colored(&material_def, verts, 3, PRIM_TYPE_TRIFAN, true);
2827
2828 //g3_draw_poly_constant_sw(3, vertlist, TMAP_FLAG_GOURAUD | TMAP_FLAG_RGB | TMAP_FLAG_ALPHA, 0.1f);
2829
2830 //gr_zbuffer_set( saved_mode );
2831 //gr_set_cull(cull);
2832 }
2833
2834
hud_tri_empty(float x1,float y1,float x2,float y2,float x3,float y3)2835 void hud_tri_empty(float x1,float y1,float x2,float y2,float x3,float y3)
2836 {
2837 gr_line(fl2i(x1),fl2i(y1),fl2i(x2),fl2i(y2));
2838 gr_line(fl2i(x2),fl2i(y2),fl2i(x3),fl2i(y3));
2839 gr_line(fl2i(x3),fl2i(y3),fl2i(x1),fl2i(y1));
2840 }
2841
HudGaugeReticleTriangle()2842 HudGaugeReticleTriangle::HudGaugeReticleTriangle():
2843 HudGauge(HUD_OBJECT_HOSTILE_TRI, HUD_HOSTILE_TRIANGLE, false, false, VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY | VM_OTHER_SHIP, 255, 255, 255)
2844 {
2845
2846 }
2847
HudGaugeReticleTriangle(int _gauge_object,int _gauge_config)2848 HudGaugeReticleTriangle::HudGaugeReticleTriangle(int _gauge_object, int _gauge_config):
2849 HudGauge(_gauge_object, _gauge_config, true, false, VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY | VM_OTHER_SHIP, 255, 255, 255)
2850 {
2851
2852 }
2853
initRadius(int length)2854 void HudGaugeReticleTriangle::initRadius(int length)
2855 {
2856 Radius = length;
2857 }
2858
initTriBase(float length)2859 void HudGaugeReticleTriangle::initTriBase(float length)
2860 {
2861 Target_triangle_base = length;
2862 }
2863
initTriHeight(float h)2864 void HudGaugeReticleTriangle::initTriHeight(float h)
2865 {
2866 Target_triangle_height = h;
2867 }
2868
render(float)2869 void HudGaugeReticleTriangle::render(float /*frametime*/)
2870 {
2871 }
2872
2873 // Render a missile warning triangle that has a tail on it to indicate distance
renderTriangleMissileTail(float ang,float xpos,float ypos,float cur_dist,int draw_solid,int draw_inside)2874 void HudGaugeReticleTriangle::renderTriangleMissileTail(float ang, float xpos, float ypos, float cur_dist, int draw_solid, int draw_inside)
2875 {
2876 float x1=0.0f;
2877 float x2=0.0f;
2878 float y1=0.0f;
2879 float y2=0.0f;
2880 float xtail=0.0f;
2881 float ytail=0.0f;
2882
2883 float sin_ang, cos_ang, tail_len;
2884
2885 float max_tail_len=20.0f;
2886
2887 sin_ang=sinf(ang);
2888 cos_ang=cosf(ang);
2889
2890 if ( cur_dist < Min_warning_missile_dist ) {
2891 tail_len = 0.0f;
2892 } else if ( cur_dist > Max_warning_missile_dist ) {
2893 tail_len = max_tail_len;
2894 } else {
2895 tail_len = cur_dist/Max_warning_missile_dist * max_tail_len;
2896 }
2897
2898 if ( draw_inside ) {
2899 x1 = xpos - Target_triangle_base * -sin_ang;
2900 y1 = ypos + Target_triangle_base * cos_ang;
2901 x2 = xpos + Target_triangle_base * -sin_ang;
2902 y2 = ypos - Target_triangle_base * cos_ang;
2903
2904 xpos -= Target_triangle_height * cos_ang;
2905 ypos += Target_triangle_height * sin_ang;
2906
2907 if ( tail_len > 0 ) {
2908 xtail = xpos - tail_len * cos_ang;
2909 ytail = ypos + tail_len * sin_ang;
2910 }
2911
2912 } else {
2913 x1 = xpos - Target_triangle_base * -sin_ang;
2914 y1 = ypos + Target_triangle_base * cos_ang;
2915 x2 = xpos + Target_triangle_base * -sin_ang;
2916 y2 = ypos - Target_triangle_base * cos_ang;
2917
2918 xpos += Target_triangle_height * cos_ang;
2919 ypos -= Target_triangle_height * sin_ang;
2920
2921 if ( tail_len > 0 ) {
2922 xtail = xpos + tail_len * cos_ang;
2923 ytail = ypos - tail_len * sin_ang;
2924 }
2925 }
2926
2927 gr_set_screen_scale(base_w, base_h);
2928 if (draw_solid) {
2929 hud_tri(xpos,ypos,x1,y1,x2,y2);
2930 } else {
2931 hud_tri_empty(xpos,ypos,x1,y1,x2,y2);
2932 }
2933
2934 // draw the tail indicating length
2935 if ( tail_len > 0 ) {
2936 gr_line(fl2i(xpos), fl2i(ypos), fl2i(xtail), fl2i(ytail));
2937 }
2938 gr_reset_screen_scale();
2939 }
2940
2941 // Render a triangle on the outside of the targeting circle.
2942 // Must be inside a g3_start_frame().
2943 // If aspect_flag !0, then render filled, indicating aspect lock.
2944 // If show_interior !0, then point inwards to positions inside reticle
renderTriangle(vec3d * hostile_pos,int aspect_flag,int show_interior,int split_tri)2945 void HudGaugeReticleTriangle::renderTriangle(vec3d *hostile_pos, int aspect_flag, int show_interior, int split_tri)
2946 {
2947 vertex hostile_vertex;
2948 float ang;
2949 float xpos,ypos,cur_dist,sin_ang,cos_ang;
2950 int draw_inside=0;
2951
2952 // determine if the given object is within the targeting reticle
2953 // (which means the triangle is not drawn)
2954
2955 cur_dist = vm_vec_dist_quick(&Player_obj->pos, hostile_pos);
2956
2957 g3_rotate_vertex(&hostile_vertex, hostile_pos);
2958 g3_project_vertex(&hostile_vertex);
2959
2960 if (hostile_vertex.codes == 0) { // on screen
2961 int projected_x, projected_y;
2962
2963 if (!(hostile_vertex.flags & PF_OVERFLOW)) { // make sure point projected
2964 int mag_squared;
2965
2966 projected_x = fl2i(hostile_vertex.screen.xyw.x);
2967 projected_y = fl2i(hostile_vertex.screen.xyw.y);
2968
2969 unsize(&projected_x, &projected_y);
2970
2971 mag_squared = (projected_x - position[0]) * (projected_x - position[0]) +
2972 (projected_y - position[1]) * (projected_y - position[1]);
2973
2974 if ( mag_squared < Radius*Radius ) {
2975 if ( show_interior ) {
2976 draw_inside=1;
2977 } else {
2978 return;
2979 }
2980 }
2981 }
2982 }
2983
2984 int HUD_nose_scaled_x = HUD_nose_x;
2985 int HUD_nose_scaled_y = HUD_nose_y;
2986
2987 gr_resize_screen_pos(&HUD_nose_scaled_x, &HUD_nose_scaled_y);
2988
2989 unsize( &hostile_vertex.world.xyz.x, &hostile_vertex.world.xyz.y );
2990
2991 ang = atan2_safe(hostile_vertex.world.xyz.y,hostile_vertex.world.xyz.x);
2992 sin_ang=sinf(ang);
2993 cos_ang=cosf(ang);
2994
2995 if ( draw_inside ) {
2996 xpos = position[0] + cos_ang*(Radius-7);
2997 ypos = position[1] - sin_ang*(Radius-7);
2998 } else {
2999 xpos = position[0] + cos_ang*(Radius+4);
3000 ypos = position[1] - sin_ang*(Radius+4);
3001 }
3002
3003 xpos += HUD_offset_x + HUD_nose_x;
3004 ypos += HUD_offset_y + HUD_nose_y;
3005
3006 if ( split_tri ) {
3007 // renderTriangleMissileSplit(ang, xpos, ypos, cur_dist, aspect_flag, draw_inside);
3008 renderTriangleMissileTail(ang, xpos, ypos, cur_dist, aspect_flag, draw_inside);
3009 } else {
3010 float x1=0.0f;
3011 float x2=0.0f;
3012 float y1=0.0f;
3013 float y2=0.0f;
3014
3015 if ( draw_inside ) {
3016 x1 = xpos - Target_triangle_base * -sin_ang;
3017 y1 = ypos + Target_triangle_base * cos_ang;
3018 x2 = xpos + Target_triangle_base * -sin_ang;
3019 y2 = ypos - Target_triangle_base * cos_ang;
3020
3021 xpos -= Target_triangle_height * cos_ang;
3022 ypos += Target_triangle_height * sin_ang;
3023
3024 } else {
3025 x1 = xpos - Target_triangle_base * -sin_ang;
3026 y1 = ypos + Target_triangle_base * cos_ang;
3027 x2 = xpos + Target_triangle_base * -sin_ang;
3028 y2 = ypos - Target_triangle_base * cos_ang;
3029
3030 xpos += Target_triangle_height * cos_ang;
3031 ypos -= Target_triangle_height * sin_ang;
3032 }
3033
3034 //renderPrintf(position[0], position[1], "%d", fl2i((360*ang)/(2*PI)));
3035 gr_set_screen_scale(base_w, base_h);
3036 if (aspect_flag) {
3037 hud_tri(xpos,ypos,x1,y1,x2,y2);
3038 } else {
3039 hud_tri_empty(xpos,ypos,x1,y1,x2,y2);
3040 }
3041 gr_reset_screen_scale();
3042 }
3043 }
3044
3045 // Show all homing missiles locked onto the player.
3046 // Also, play the beep!
hud_process_homing_missiles()3047 void hud_process_homing_missiles()
3048 {
3049 object *A;
3050 missile_obj *mo;
3051 weapon *wp;
3052 float dist, nearest_dist;
3053 int closest_is_aspect=0;
3054
3055 nearest_dist = Homing_beep.max_cycle_dist;
3056
3057 for ( mo = GET_NEXT(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
3058 A = &Objects[mo->objnum];
3059 Assert((A->instance >= 0) && (A->instance < MAX_WEAPONS));
3060
3061 wp = &Weapons[A->instance];
3062
3063 if (wp->homing_object == Player_obj) {
3064 dist = vm_vec_dist_quick(&A->pos, &Player_obj->pos);
3065
3066 if (dist < nearest_dist) {
3067 nearest_dist = dist;
3068 if ( Weapon_info[wp->weapon_info_index].is_locked_homing() ) {
3069 closest_is_aspect=1;
3070 } else {
3071 closest_is_aspect=0;
3072 }
3073 }
3074 }
3075 }
3076
3077 // See if need to play warning beep.
3078 if (nearest_dist < Homing_beep.max_cycle_dist ) {
3079 float delta_time;
3080 float cycle_time;
3081
3082 delta_time = f2fl(Missiontime - Homing_beep.last_time_played);
3083
3084 // figure out the cycle time by doing a linear interpretation
3085 cycle_time = Homing_beep.min_cycle_time + (nearest_dist-Homing_beep.min_cycle_dist) * Homing_beep.precalced_interp;
3086
3087 // play a new 'beep' if cycle time has elapsed
3088 if ( (delta_time*1000) > cycle_time ) {
3089 Homing_beep.last_time_played = Missiontime;
3090 if ( snd_is_playing(Homing_beep.snd_handle) ) {
3091 snd_stop(Homing_beep.snd_handle);
3092 }
3093
3094 if ( closest_is_aspect ) {
3095 Homing_beep.snd_handle = snd_play(gamesnd_get_game_sound(ship_get_sound(Player_obj, GameSounds::PROXIMITY_ASPECT_WARNING)));
3096 } else {
3097 Homing_beep.snd_handle = snd_play(gamesnd_get_game_sound(ship_get_sound(Player_obj, GameSounds::PROXIMITY_WARNING)));
3098 }
3099 }
3100 }
3101 }
3102
HudGaugeMissileTriangles()3103 HudGaugeMissileTriangles::HudGaugeMissileTriangles():
3104 HudGaugeReticleTriangle(HUD_OBJECT_MISSILE_TRI, HUD_MISSILE_WARNING_ARROW)
3105 {
3106 }
3107
render(float)3108 void HudGaugeMissileTriangles::render(float /*frametime*/)
3109 {
3110 object *A;
3111 missile_obj *mo;
3112 weapon *wp;
3113
3114 bool in_frame = g3_in_frame() > 0;
3115 if(!in_frame)
3116 g3_start_frame(0);
3117 gr_set_screen_scale(base_w, base_h);
3118
3119 gr_set_color_fast(&HUD_color_homing_indicator);
3120
3121 for ( mo = GET_NEXT(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
3122 A = &Objects[mo->objnum];
3123 Assert((A->instance >= 0) && (A->instance < MAX_WEAPONS));
3124
3125 wp = &Weapons[A->instance];
3126
3127 if (wp->homing_object == Player_obj) {
3128 renderTriangle(&A->pos, Weapon_info[wp->weapon_info_index].is_locked_homing(), 1, 1);
3129 }
3130 }
3131
3132 gr_reset_screen_scale();
3133 if(!in_frame)
3134 g3_end_frame();
3135 }
3136
HudGaugeOrientationTee()3137 HudGaugeOrientationTee::HudGaugeOrientationTee():
3138 HudGauge(HUD_OBJECT_ORIENTATION_TEE, HUD_ORIENTATION_TEE, true, false, VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY | VM_OTHER_SHIP, 255, 255, 255)
3139 {
3140 }
3141
initRadius(int length)3142 void HudGaugeOrientationTee::initRadius(int length)
3143 {
3144 Radius = length;
3145 }
3146
pageIn()3147 void HudGaugeOrientationTee::pageIn()
3148 {
3149 }
3150
3151 // hud_show_orientation_tee() will draw the orientation gauge that orbits the inside of the
3152 // outer reticle ring. If the T is at 12 o'clock, the target is facing the player, if the T
3153 // is at 6 o'clock the target is facing away from the player. If the T is at 3 or 9 o'clock
3154 // the target is facing 90 away from the player.
render(float)3155 void HudGaugeOrientationTee::render(float /*frametime*/)
3156 {
3157 object* targetp;
3158
3159 if (Player_ai->target_objnum == -1 || Player->target_is_dying)
3160 return;
3161
3162 targetp = &Objects[Player_ai->target_objnum];
3163
3164 if ( maybeFlashSexp() == 1 ) {
3165 hud_set_iff_color( targetp );
3166 } else {
3167 hud_set_iff_color( targetp, 1);
3168 }
3169 renderOrientation(targetp, Player_obj, &targetp->orient);
3170 }
3171
3172 // routine to draw a bounding box around a remote detonate missile and distance to
hud_process_remote_detonate_missile()3173 void hud_process_remote_detonate_missile()
3174 {
3175 missile_obj *mo;
3176 object *mobjp;
3177 vertex target_point;
3178
3179 memset(&target_point, 0, sizeof(target_point));
3180
3181 // check for currently locked missiles (highest precedence)
3182 for ( mo = GET_FIRST(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
3183 Assert(mo->objnum >= 0 && mo->objnum < MAX_OBJECTS);
3184 mobjp = &Objects[mo->objnum];
3185
3186 if ((Player_obj != NULL) && (mobjp->parent_sig == Player_obj->parent_sig)) {
3187 if (Weapon_info[Weapons[mobjp->instance].weapon_info_index].wi_flags[Weapon::Info_Flags::Remote]) {
3188 // get box center point
3189 g3_rotate_vertex(&target_point,&mobjp->pos);
3190
3191 // project vertex
3192 g3_project_vertex(&target_point);
3193
3194 if (!(target_point.flags & PF_OVERFLOW)) { // make sure point projected
3195 switch ( mobjp->type ) {
3196 case OBJ_WEAPON:
3197 hud_target_add_display_list(mobjp, &target_point, &mobjp->pos, 0, iff_get_color(IFF_COLOR_MESSAGE, 1), NULL, TARGET_DISPLAY_DIST);
3198 break;
3199
3200 default:
3201 Int3(); // should never happen
3202 return;
3203 }
3204 }
3205 }
3206 }
3207 }
3208 }
3209
3210 // routine to possibly draw a bounding box around a ship sending a message to the player
hud_show_message_sender()3211 void hud_show_message_sender()
3212 {
3213 object *targetp;
3214 vertex target_point; // temp vertex used to find screen position for 3-D object;
3215
3216 // don't draw brackets if no ship sending a message
3217 if ( Message_shipnum == -1 )
3218 return;
3219
3220 targetp = &Objects[Ships[Message_shipnum].objnum];
3221 Assert ( targetp != NULL );
3222
3223 if (targetp->type != OBJ_SHIP) {
3224 // if it's not a ship (maybe it got ship-vanished in the middle of talking) then clear Message_shipnum
3225 Message_shipnum = -1;
3226 return;
3227 }
3228
3229 // Don't do this for the ship you're flying!
3230 if ( targetp == Player_obj ) {
3231 return;
3232 }
3233
3234 // Goober5000 - don't draw if primitive sensors
3235 if ( Ships[Player_obj->instance].flags[Ship::Ship_Flags::Primitive_sensors] ) {
3236 return;
3237 }
3238
3239 // Karajorma - If we've gone to all the trouble to make our friendly ships stealthed they shouldn't then give away
3240 // their position cause they're feeling chatty
3241 // MageKing17 - Make the check see if they're actually stealthed at the time, and may as well include a check for
3242 // being hidden from sensors, too; logic copied from a similar check in hudescort.cpp
3243 if ( (Ships[Message_shipnum].flags[Ship::Ship_Flags::Hidden_from_sensors])
3244 || ((Ships[Message_shipnum].flags[Ship::Ship_Flags::Stealth]) && ((Ships[Message_shipnum].team != Player_ship->team) || (Ships[Message_shipnum].flags[Ship::Ship_Flags::Friendly_stealth_invis])))
3245 ) {
3246 return;
3247 }
3248
3249 Assert ( targetp->instance >=0 && targetp->instance < MAX_SHIPS );
3250
3251 // check the object flags to see if this ship is gone. If so, then don't do this stuff anymore
3252 if ( targetp->flags[Object::Object_Flags::Should_be_dead] ) {
3253 Message_shipnum = -1;
3254 return;
3255 }
3256
3257 memset(&target_point, 0, sizeof(target_point));
3258
3259 // find the current target vertex
3260 g3_rotate_vertex(&target_point, &targetp->pos);
3261 g3_project_vertex(&target_point);
3262
3263 if (!(target_point.flags & PF_OVERFLOW)) { // make sure point projected
3264 hud_target_add_display_list(targetp, &target_point, &targetp->pos, 10, iff_get_color(IFF_COLOR_MESSAGE, 1), NULL, 0);
3265 } else if (target_point.codes != 0) { // target center is not on screen
3266 // draw the offscreen indicator at the edge of the screen where the target is closest to
3267 // AL 11-19-97: only show offscreen indicator if player sensors are functioning
3268 if ( (OBJ_INDEX(targetp) != Player_ai->target_objnum) || (Message_shipnum == Objects[Player_ai->target_objnum].instance) ) {
3269 if ( hud_sensors_ok(Player_ship, 0) ) {
3270 hud_target_add_display_list(targetp, &target_point, &targetp->pos, 0, iff_get_color(IFF_COLOR_MESSAGE, 1), NULL, TARGET_DISPLAY_DIST);
3271 }
3272 }
3273 }
3274 }
3275
3276 // hud_prune_hotkeys()
3277 //
3278 // Check for ships that are dying, departed or dead. These should be removed from the player's
3279 // hotkey lists.
hud_prune_hotkeys()3280 void hud_prune_hotkeys()
3281 {
3282 int i;
3283 htarget_list *hitem, *plist;
3284 object *objp;
3285 ship *sp;
3286
3287 for ( i = 0; i < MAX_KEYED_TARGETS; i++ ) {
3288 plist = &(Players[Player_num].keyed_targets[i]);
3289 if ( EMPTY( plist ) ) // no items in list, then do nothing
3290 continue;
3291
3292 hitem = GET_FIRST(plist);
3293 while ( hitem != END_OF_LIST(plist) ) {
3294 int remove_item;
3295
3296 remove_item = 0;
3297
3298 objp = hitem->objp;
3299 Assert ( objp != NULL );
3300 if ( objp->type == OBJ_SHIP ) {
3301 Assert ( objp->instance >=0 && objp->instance < MAX_SHIPS );
3302 sp = &Ships[objp->instance];
3303 } else {
3304 // if the object isn't a ship, it shouldn't be on the list, so remove it without question
3305 remove_item = 1;
3306 sp = NULL;
3307 }
3308
3309 // check to see if the object is dying -- if so, remove it from the list
3310 // check to see if the ship is departing -- if so, remove it from the list
3311 if ( remove_item || (objp->flags[Object::Object_Flags::Should_be_dead]) || (sp->is_dying_or_departing()) ) {
3312 if ( sp != NULL ) {
3313 nprintf(("Network", "Hotkey: Pruning %s\n", sp->ship_name));
3314 }
3315
3316 htarget_list *temp;
3317 temp = GET_NEXT(hitem);
3318 list_remove( plist, hitem );
3319 list_append( &htarget_free_list, hitem );
3320 hitem->objp = NULL;
3321 hitem = temp;
3322 continue;
3323 }
3324 hitem = GET_NEXT( hitem );
3325 } // end while
3326 } // end for
3327
3328 // save the hotkey sets with mission time reaches a certain point. Code was put here because this
3329 // function always called for both single/multiplayer. Maybe not the best location, but whatever.
3330 mission_hotkey_maybe_save_sets();
3331 }
3332
3333 int HUD_drew_selection_bracket_on_target;
3334
3335 // hud_show_selection_set draws some indicator around all the ships in the current selection set. No
3336 // indicators will be drawn if there is only 1 ship in the set.
hud_show_selection_set()3337 void hud_show_selection_set()
3338 {
3339 htarget_list *hitem, *plist;
3340 object *targetp;
3341 int set, count;
3342 vertex target_point; // temp vertex used to find screen position for 3-D object;
3343
3344 HUD_drew_selection_bracket_on_target = 0;
3345
3346 set = Players[Player_num].current_hotkey_set;
3347 if ( set == -1 )
3348 return;
3349
3350 Assert ( (set >= 0) && (set < MAX_KEYED_TARGETS) );
3351 plist = &(Players[Player_num].keyed_targets[set]);
3352
3353 count = 0;
3354 for ( hitem = GET_FIRST(plist); hitem != END_OF_LIST(plist); hitem = GET_NEXT(hitem) )
3355 count++;
3356
3357 if ( count == 0 ) { // only one ship, do nothing
3358 Players[Player_num].current_hotkey_set = -1;
3359 return;
3360 }
3361
3362 memset(&target_point, 0, sizeof(target_point));
3363
3364 for ( hitem = GET_FIRST(plist); hitem != END_OF_LIST(plist); hitem = GET_NEXT(hitem) ) {
3365 targetp = hitem->objp;
3366 Assert ( targetp != NULL );
3367
3368 ship *target_shipp = NULL;
3369
3370 Assert ( targetp->type == OBJ_SHIP );
3371 Assert ( targetp->instance >=0 && targetp->instance < MAX_SHIPS );
3372 target_shipp = &Ships[targetp->instance];
3373
3374 if ( (Game_mode & GM_MULTIPLAYER) && (target_shipp == Player_ship) ) {
3375 continue;
3376 }
3377
3378 // Goober5000 - don't draw indicators for non-visible ships, per Mantis #1972
3379 // (the only way we could get here is if the hotkey set contained a mix of visible
3380 // and invisible ships)
3381 if (awacs_get_level(targetp, Player_ship, 1) < 1) {
3382 continue;
3383 }
3384
3385 // find the current target vertex
3386 //
3387 g3_rotate_vertex(&target_point,&targetp->pos);
3388 g3_project_vertex(&target_point);
3389
3390 if (!(target_point.flags & PF_OVERFLOW)) { // make sure point projected
3391
3392 switch ( targetp->type ) {
3393 case OBJ_SHIP:
3394 break;
3395
3396 default:
3397 Int3(); // should never happen
3398 return;
3399 }
3400 if ( OBJ_INDEX(targetp) == Player_ai->target_objnum ) {
3401 hud_target_add_display_list(targetp, &target_point, &targetp->pos, 5, iff_get_color(IFF_COLOR_SELECTION, 1), NULL, 0);
3402 HUD_drew_selection_bracket_on_target = 1;
3403 } else if ( Cmdline_targetinfo ) { //Backslash -- show the distance and a lead indicator
3404 hud_target_add_display_list(targetp, &target_point, &targetp->pos, 5, iff_get_color(IFF_COLOR_SELECTION, 1), NULL, TARGET_DISPLAY_DIST | TARGET_DISPLAY_LEAD);
3405 } else {
3406 hud_target_add_display_list(targetp, &target_point, &targetp->pos, 5, iff_get_color(IFF_COLOR_SELECTION, 1), NULL, 0);
3407 }
3408 }
3409
3410 if (target_point.codes != 0) { // target center is not on screen
3411 // draw the offscreen indicator at the edge of the screen where the target is closest to
3412 // AL 11-19-97: only show offscreen indicator if player sensors are functioning
3413
3414 if ( OBJ_INDEX(targetp) != Player_ai->target_objnum ) {
3415 if ( hud_sensors_ok(Player_ship, 0) ) {
3416 hud_target_add_display_list(targetp, &target_point, &targetp->pos, 5, iff_get_color(IFF_COLOR_SELECTION, 1), NULL, 0);
3417 }
3418 }
3419 }
3420 }
3421 }
3422
3423 // hud_show_targeting_gauges() will display the targeting information on the HUD. Called once per frame.
3424 //
3425 // Must be inside a g3_start_frame()
3426 // input: frametime => time in seconds since last update
3427 // in_cockpit => flag (default value 1) indicating whether viewpoint is from cockpit or external
hud_show_targeting_gauges(float frametime)3428 void hud_show_targeting_gauges(float frametime)
3429 {
3430 vertex target_point; // temp vertex used to find screen position for 3-D object;
3431 vec3d target_pos;
3432
3433 hud_show_hostile_triangle();
3434 hud_do_lock_indicators(frametime);
3435
3436 if (Player_ai->target_objnum == -1)
3437 return;
3438
3439 object * targetp = &Objects[Player_ai->target_objnum];
3440 Players[Player_num].lead_indicator_active = 0;
3441
3442 // check to see if there is even a current target
3443 if ( targetp == &obj_used_list ) {
3444 return;
3445 }
3446
3447 // AL 1/20/97: Point to targted subsystem if one exists
3448 if ( Player_ai->targeted_subsys != NULL ) {
3449 get_subsystem_world_pos(targetp, Player_ai->targeted_subsys, &target_pos);
3450 } else {
3451 target_pos = targetp->pos;
3452 }
3453
3454 // find the current target vertex
3455 //
3456 // The 2D screen pos depends on the current viewer position and orientation.
3457 g3_rotate_vertex(&target_point,&target_pos);
3458
3459 hud_set_iff_color( targetp, 1 );
3460 g3_project_vertex(&target_point);
3461
3462 if (!(target_point.flags & PF_OVERFLOW)) { // make sure point projected
3463 if (target_point.codes == 0) { // target center is not on screen
3464 int target_display_flags;
3465
3466 if(Cmdline_targetinfo) {
3467 target_display_flags = TARGET_DISPLAY_DIST | TARGET_DISPLAY_DOTS | TARGET_DISPLAY_SUBSYS | TARGET_DISPLAY_NAME | TARGET_DISPLAY_CLASS;
3468 } else {
3469 target_display_flags = TARGET_DISPLAY_DIST | TARGET_DISPLAY_DOTS | TARGET_DISPLAY_SUBSYS;
3470 }
3471
3472 hud_target_add_display_list(targetp, &target_point, &targetp->pos, 0, NULL, NULL, target_display_flags);
3473 }
3474 } else {
3475 Hud_target_w = 0;
3476 Hud_target_h = 0;
3477 }
3478
3479 // update cargo scanning
3480 hud_cargo_scan_update(targetp, frametime);
3481
3482 // display the lock indicator
3483 if (!Player->target_is_dying) {
3484 // update and render artillery
3485 hud_artillery_update();
3486 hud_artillery_render();
3487 }
3488
3489 if (target_point.codes != 0) {
3490 // draw the offscreen indicator at the edge of the screen where the target is closest to
3491 Assert(Player_ai->target_objnum >= 0);
3492
3493 // AL 11-11-97: don't draw the indicator if the ship is messaging, the indicator is drawn
3494 // in the message sending color in hud_show_message_sender()
3495 if ( Message_shipnum != Objects[Player_ai->target_objnum].instance ) {
3496 hud_target_add_display_list(targetp, &target_point, &targetp->pos, 0, NULL, NULL, 0);
3497 }
3498 }
3499 }
3500
3501 // hud_show_hostile_triangle() will draw an empty triangle that oribits around the outer
3502 // circle of the reticle. It will point to the closest enemy that is firing on the player.
3503 // Currently, it points to the closest enemy that has the player as its target_objnum and has
3504 // SM_ATTACK or SM_SUPER_ATTACK as its ai submode.
hud_show_hostile_triangle()3505 void hud_show_hostile_triangle()
3506 {
3507 object* A;
3508 float min_distance=1e20f;
3509 float new_distance=0.0f;
3510 object* nearest_obj = &obj_used_list;
3511 ai_info *aip;
3512 ship_obj *so;
3513 ship *sp;
3514 ship_subsys *ss;
3515
3516 int player_obj_index = OBJ_INDEX(Player_obj);
3517 int turret_is_attacking = 0;
3518
3519 hostile_obj = NULL;
3520
3521 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
3522
3523 A = &Objects[so->objnum];
3524 sp = &Ships[A->instance];
3525
3526 // only look at ships who attack us
3527 if ( (A == Player_obj) || !(iff_x_attacks_y(Ships[A->instance].team, Player_ship->team)) ) {
3528 continue;
3529 }
3530
3531 aip = &Ai_info[Ships[A->instance].ai_index];
3532
3533 // don't look at ignore ships
3534 if ( should_be_ignored(sp) ) {
3535 continue;
3536 }
3537
3538 // always ignore cargo containers and navbuoys
3539 if ( Ship_info[sp->ship_info_index].class_type > -1 && !(Ship_types[Ship_info[sp->ship_info_index].class_type].flags[Ship::Type_Info_Flags::Show_attack_direction]) ) {
3540 continue;
3541 }
3542
3543 // check if ship is stealthy
3544 if (awacs_get_level(&Objects[sp->objnum], Player_ship, 1) < 1) {
3545 continue;
3546 }
3547
3548 turret_is_attacking = 0;
3549
3550 // check if any turrets on ship are firing at the player (only on non fighter-bombers)
3551 if ( !(Ship_info[sp->ship_info_index].is_fighter_bomber()) ) {
3552 for (ss = GET_FIRST(&sp->subsys_list); ss != END_OF_LIST(&sp->subsys_list); ss = GET_NEXT(ss) ) {
3553 if (ss->flags[Ship::Subsystem_Flags::Untargetable])
3554 continue;
3555
3556 if ( (ss->system_info->type == SUBSYSTEM_TURRET) && (ss->current_hits > 0) ) {
3557
3558 if ( ss->turret_enemy_objnum == player_obj_index ) {
3559 turret_is_attacking = 1;
3560
3561 vec3d gsubpos;
3562 // get world pos of subsystem
3563 vm_vec_unrotate(&gsubpos, &ss->system_info->pnt, &A->orient);
3564 vm_vec_add2(&gsubpos, &A->pos);
3565 new_distance = vm_vec_dist_quick(&gsubpos, &Player_obj->pos);
3566
3567 if (new_distance <= min_distance) {
3568 min_distance=new_distance;
3569 nearest_obj = A;
3570 }
3571 }
3572 }
3573 }
3574 }
3575
3576 if ( !turret_is_attacking ) {
3577 // check for ships attacking the player
3578 if ( aip->target_objnum != Player_ship->objnum ) {
3579 continue;
3580 }
3581
3582 // ignore enemy if not in chase mode
3583 if ( (Game_mode & GM_NORMAL) && (aip->mode != AIM_CHASE) ) {
3584 continue;
3585 }
3586
3587 new_distance = vm_vec_dist_quick(&A->pos, &Player_obj->pos);
3588
3589 if (new_distance <= min_distance) {
3590 min_distance=new_distance;
3591 nearest_obj = A;
3592 }
3593 }
3594 }
3595
3596 if ( nearest_obj == &obj_used_list ) {
3597 return;
3598 }
3599
3600 if ( min_distance > MIN_DISTANCE_TO_CONSIDER_THREAT ) {
3601 return;
3602 }
3603
3604 hostile_obj = nearest_obj;
3605
3606 // hook to maybe warn player about this attacking ship
3607 ship_maybe_warn_player(&Ships[nearest_obj->instance], min_distance);
3608 }
3609
HudGaugeHostileTriangle()3610 HudGaugeHostileTriangle::HudGaugeHostileTriangle():
3611 HudGaugeReticleTriangle(HUD_OBJECT_HOSTILE_TRI, HUD_HOSTILE_TRIANGLE)
3612 {
3613 }
3614
render(float)3615 void HudGaugeHostileTriangle::render(float /*frametime*/)
3616 {
3617 if (hostile_obj && maybeFlashSexp() != 1) {
3618 bool in_frame = g3_in_frame() > 0;
3619 if(!in_frame)
3620 g3_start_frame(0);
3621
3622 // hud_set_iff_color( TEAM_HOSTILE, 1 ); // Note: This should really be TEAM_HOSTILE, not opposite of Player_ship->team.
3623 hud_set_iff_color( hostile_obj, 1 );
3624 renderTriangle(&hostile_obj->pos, 0, 1, 0);
3625
3626 if(!in_frame)
3627 g3_end_frame();
3628 }
3629 }
3630
hud_calculate_lead_pos(vec3d * lead_target_pos,vec3d * target_pos,object * targetp,weapon_info * wip,float dist_to_target,vec3d * rel_pos)3631 void hud_calculate_lead_pos(vec3d *lead_target_pos, vec3d *target_pos, object *targetp, weapon_info *wip, float dist_to_target, vec3d *rel_pos)
3632 {
3633 vec3d target_moving_direction;
3634 vec3d last_delta_vector;
3635 float time_to_target, target_moved_dist;
3636
3637 if(wip->max_speed != 0) {
3638 time_to_target = dist_to_target / wip->max_speed;
3639 } else {
3640 time_to_target = 0;
3641 }
3642
3643 target_moved_dist = targetp->phys_info.speed * time_to_target;
3644
3645 target_moving_direction = targetp->phys_info.vel;
3646
3647 if(The_mission.ai_profile->flags[AI::Profile_Flags::Use_additive_weapon_velocity])
3648 vm_vec_scale_sub2(&target_moving_direction, &Player_obj->phys_info.vel, wip->vel_inherit_amount);
3649
3650 // test if the target is moving at all
3651 if ( vm_vec_mag_quick(&target_moving_direction) < 0.1f ) { // Find distance!
3652 *lead_target_pos = *target_pos;
3653 } else {
3654 vm_vec_normalize(&target_moving_direction);
3655 vm_vec_scale(&target_moving_direction, target_moved_dist);
3656 vm_vec_add(lead_target_pos, target_pos, &target_moving_direction );
3657 polish_predicted_target_pos(wip, targetp, target_pos, lead_target_pos, dist_to_target, &last_delta_vector, 1); // Not used:, float time_to_enemy)
3658
3659 if(rel_pos) { // needed for quick lead indicators, not needed for normal lead indicators.
3660 vm_vec_add2(lead_target_pos, rel_pos);
3661 }
3662 }
3663 }
3664
3665 // Return the bank number for the primary weapon that can fire the farthest, from
3666 // the number of active primary weapons
3667 // input: range => output parameter... it is the range of the selected bank
hud_get_best_primary_bank(float * range)3668 int hud_get_best_primary_bank(float *range)
3669 {
3670 int i, best_bank, bank_to_fire, num_to_test;
3671 float weapon_range, farthest_weapon_range;
3672 ship_weapon *swp;
3673 weapon_info *wip;
3674
3675 swp = &Player_ship->weapons;
3676
3677 farthest_weapon_range = 0.0f;
3678 best_bank = -1;
3679
3680 if ( Player_ship->flags[Ship::Ship_Flags::Primary_linked] ) {
3681 num_to_test = swp->num_primary_banks;
3682 } else {
3683 num_to_test = MIN(1, swp->num_primary_banks);
3684 }
3685
3686 for ( i = 0; i < num_to_test; i++ )
3687 {
3688 bank_to_fire = (swp->current_primary_bank + i) % swp->num_primary_banks;
3689
3690 // calculate the range of the weapon, and only display the lead target indicator
3691 // if the weapon can actually hit the target
3692 Assert(bank_to_fire >= 0 && bank_to_fire < swp->num_primary_banks);
3693 Assert(swp->primary_bank_weapons[bank_to_fire] < weapon_info_size());
3694
3695 if (swp->primary_bank_weapons[bank_to_fire] < 0)
3696 continue;
3697
3698 wip = &Weapon_info[swp->primary_bank_weapons[bank_to_fire]];
3699 weapon_range = MIN((wip->max_speed * wip->lifetime), wip->weapon_range);
3700
3701 // don't consider this primary if it's a ballistic that's out of ammo - Goober5000
3702 if ( wip->wi_flags[Weapon::Info_Flags::Ballistic] )
3703 {
3704 if ( swp->primary_bank_ammo[bank_to_fire] <= 0)
3705 {
3706 continue;
3707 }
3708 }
3709
3710 if ( weapon_range > farthest_weapon_range )
3711 {
3712 best_bank = bank_to_fire;
3713 farthest_weapon_range = weapon_range;
3714 }
3715 }
3716
3717 *range = farthest_weapon_range;
3718 return best_bank;
3719 }
3720
3721 // -----------------------------------------------------------------------------
3722 // polish_predicted_target_pos()
3723 //
3724 // Called by the draw lead indicator code to predict where the enemy is going to be
3725 //
polish_predicted_target_pos(weapon_info * wip,object * targetp,vec3d * enemy_pos,vec3d * predicted_enemy_pos,float dist_to_enemy,vec3d * last_delta_vec,int num_polish_steps,object * reference_object)3726 void polish_predicted_target_pos(weapon_info *wip, object *targetp, vec3d *enemy_pos, vec3d *predicted_enemy_pos, float dist_to_enemy, vec3d *last_delta_vec, int num_polish_steps, object *reference_object)
3727 {
3728 Assertion(reference_object != nullptr, "polish_predicted_target_pos received a nullptr for its reference ship, this is a coder mistake, please report!");
3729
3730 int iteration;
3731 vec3d reference_pos = reference_object->pos;
3732 float time_to_enemy;
3733 vec3d last_predicted_enemy_pos = *predicted_enemy_pos;
3734
3735 float weapon_speed = wip->max_speed;
3736
3737 vm_vec_zero(last_delta_vec);
3738
3739
3740 vec3d enemy_vel = targetp->phys_info.vel;
3741 vec3d enemy_acc;
3742 if (The_mission.ai_profile->second_order_lead_predict_factor > 0) {
3743 vec3d world_rotvel;
3744 vm_vec_unrotate(&world_rotvel, &targetp->phys_info.rotvel, &targetp->orient);
3745 vm_vec_cross(&enemy_acc, &world_rotvel, &enemy_vel);
3746 }
3747 // additive velocity stuff
3748 // not just the player's main target
3749 if (The_mission.ai_profile->flags[AI::Profile_Flags::Use_additive_weapon_velocity]) {
3750 vm_vec_scale_sub2( &enemy_vel, &reference_object->phys_info.vel, wip->vel_inherit_amount);
3751 }
3752
3753 for (iteration=0; iteration < num_polish_steps; iteration++) {
3754 dist_to_enemy = vm_vec_dist_quick(predicted_enemy_pos, &reference_pos);
3755 time_to_enemy = dist_to_enemy/weapon_speed;
3756 vm_vec_scale_add(predicted_enemy_pos, enemy_pos, &enemy_vel, time_to_enemy);
3757 if (The_mission.ai_profile->second_order_lead_predict_factor > 0) {
3758 vm_vec_scale_add(predicted_enemy_pos, predicted_enemy_pos, &enemy_acc, (time_to_enemy * time_to_enemy / 2) * The_mission.ai_profile->second_order_lead_predict_factor);
3759 }
3760 vm_vec_sub(last_delta_vec, predicted_enemy_pos, &last_predicted_enemy_pos);
3761 last_predicted_enemy_pos= *predicted_enemy_pos;
3762 }
3763 }
3764
HudGaugeLeadIndicator()3765 HudGaugeLeadIndicator::HudGaugeLeadIndicator():
3766 HudGauge(HUD_OBJECT_LEAD, HUD_LEAD_INDICATOR, false, false, VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY | VM_OTHER_SHIP, 255, 255, 255)
3767 {
3768
3769 }
3770
initHalfSize(float w,float h)3771 void HudGaugeLeadIndicator::initHalfSize(float w, float h)
3772 {
3773 Lead_indicator_half[0] = w;
3774 Lead_indicator_half[1] = h;
3775 }
3776
initBitmaps(char * fname)3777 void HudGaugeLeadIndicator::initBitmaps(char *fname)
3778 {
3779 Lead_indicator_gauge.first_frame = bm_load_animation(fname, &Lead_indicator_gauge.num_frames);
3780 if ( Lead_indicator_gauge.first_frame < 0 ) {
3781 Warning(LOCATION,"Cannot load hud ani: %s\n", fname);
3782 }
3783 }
3784
pageIn()3785 void HudGaugeLeadIndicator::pageIn()
3786 {
3787 bm_page_in_aabitmap(Lead_indicator_gauge.first_frame, Lead_indicator_gauge.num_frames);
3788 }
3789
3790 // determine the correct frame to draw for the lead indicator
3791 // 0 -> center only (in secondary range only)
3792 // 1 -> full (in secondary and primary range)
3793 // 2 -> oustide only (in primary range only)
3794 //
3795 // input: prange => range of current primary weapon
3796 // srange => range of current secondary weapon
3797 // dist_to_target => current dist to target
3798 //
3799 // exit: 0-2 => frame offset
3800 // -1 => don't draw anything
pickFrame(float prange,float srange,float dist_to_target)3801 int HudGaugeLeadIndicator::pickFrame(float prange, float srange, float dist_to_target)
3802 {
3803 int frame_offset=-1;
3804 int in_prange=0, in_srange=0;
3805
3806 if ( dist_to_target < prange ) {
3807 in_prange=1;
3808 }
3809
3810 if ( dist_to_target < srange ) {
3811 in_srange=1;
3812 }
3813
3814 if ( in_prange && in_srange ) {
3815 frame_offset=1;
3816 } else if ( in_prange && !in_srange ) {
3817 frame_offset=2;
3818 } else if ( !in_prange && in_srange ) {
3819 frame_offset=0;
3820 } else {
3821 frame_offset=-1;
3822 }
3823
3824 return frame_offset;
3825 }
3826
render(float)3827 void HudGaugeLeadIndicator::render(float /*frametime*/)
3828 {
3829 if(Player->target_is_dying) {
3830 return;
3831 }
3832
3833 bool in_frame = g3_in_frame() > 0;
3834 if(!in_frame)
3835 g3_start_frame(0);
3836
3837 // first render the current target the player has selected.
3838 renderLeadCurrentTarget();
3839
3840 // if extra targeting info is enabled, render lead indicators for objects in the target display list.
3841 for(size_t i = 0; i < target_display_list.size(); i++) {
3842 if ( (target_display_list[i].flags & TARGET_DISPLAY_LEAD) && target_display_list[i].objp ) {
3843
3844 // set the color
3845 if( target_display_list[i].bracket_clr.red && target_display_list[i].bracket_clr.green &&
3846 target_display_list[i].bracket_clr.blue ) {
3847 gr_set_color_fast(&target_display_list[i].bracket_clr);
3848 } else {
3849 // use IFF colors if none defined.
3850 hud_set_iff_color(target_display_list[i].objp, 1);
3851 }
3852
3853 renderLeadQuick(&target_display_list[i].target_pos, target_display_list[i].objp);
3854 }
3855 }
3856
3857 if(!in_frame)
3858 g3_end_frame();
3859 }
3860
renderIndicator(int frame_offset,object * targetp,vec3d * lead_target_pos)3861 void HudGaugeLeadIndicator::renderIndicator(int frame_offset, object *targetp, vec3d *lead_target_pos)
3862 {
3863 vertex lead_target_vertex;
3864 int sx, sy;
3865
3866 g3_rotate_vertex(&lead_target_vertex, lead_target_pos);
3867
3868 if (lead_target_vertex.codes == 0) { // on screen
3869 g3_project_vertex(&lead_target_vertex);
3870
3871 if (!(lead_target_vertex.flags & PF_OVERFLOW)) {
3872 if ( maybeFlashSexp() == 1 ) {
3873 hud_set_iff_color(targetp, 0);
3874 } else {
3875 hud_set_iff_color(targetp, 1);
3876 }
3877
3878 if ( Lead_indicator_gauge.first_frame + frame_offset >= 0 ) {
3879 sx = fl2i(lead_target_vertex.screen.xyw.x);
3880 sy = fl2i(lead_target_vertex.screen.xyw.y);
3881
3882 unsize(&sx, &sy);
3883 renderBitmap(Lead_indicator_gauge.first_frame + frame_offset, fl2i(sx - Lead_indicator_half[0]), fl2i(sy - Lead_indicator_half[1]));
3884 }
3885 }
3886 }
3887 }
3888
3889 // HudGaugeLeadIndicator::renderTargetLead() determine where to draw the lead target box and display it
renderLeadCurrentTarget()3890 void HudGaugeLeadIndicator::renderLeadCurrentTarget()
3891 {
3892 vec3d target_pos;
3893 vec3d source_pos;
3894 vec3d *rel_pos;
3895 vec3d lead_target_pos;
3896 object *targetp;
3897 polymodel *pm;
3898 ship_weapon *swp;
3899 weapon_info *wip;
3900 weapon_info *tmp=NULL;
3901 float dist_to_target, prange, srange;
3902 int bank_to_fire, frame_offset;
3903
3904 if (Player_ai->target_objnum == -1)
3905 return;
3906
3907 targetp = &Objects[Player_ai->target_objnum];
3908 if ( (targetp->type != OBJ_SHIP) && (targetp->type != OBJ_WEAPON) && (targetp->type != OBJ_ASTEROID) ) {
3909 return;
3910 }
3911
3912 // only allow bombs to have lead indicator displayed
3913 if ( targetp->type == OBJ_WEAPON ) {
3914 if ( !(Weapon_info[Weapons[targetp->instance].weapon_info_index].wi_flags[Weapon::Info_Flags::Can_be_targeted]) ) {
3915 if ( !(Weapon_info[Weapons[targetp->instance].weapon_info_index].wi_flags[Weapon::Info_Flags::Bomb]) ) {
3916 return;
3917 }
3918 }
3919 }
3920
3921 // If the target is out of range, then draw the correct frame for the lead indicator
3922 if ( Lead_indicator_gauge.first_frame == -1 ) {
3923 Int3();
3924 return;
3925 }
3926
3927 // AL 1/20/97: Point to targted subsystem if one exists
3928 if ( Player_ai->targeted_subsys != NULL ) {
3929 get_subsystem_world_pos(targetp, Player_ai->targeted_subsys, &target_pos);
3930 } else {
3931 target_pos = targetp->pos;
3932 }
3933
3934 pm = model_get(Ship_info[Player_ship->ship_info_index].model_num);
3935 swp = &Player_ship->weapons;
3936
3937 // Added to take care of situation where there are no primary banks on the player ship
3938 // (this may not be possible, depending on what we decide for the weapons loadout rules)
3939 if ( swp->num_primary_banks == 0 )
3940 return;
3941
3942 bank_to_fire = hud_get_best_primary_bank(&prange);
3943
3944 if ( bank_to_fire < 0 )
3945 return;
3946
3947 wip = &Weapon_info[swp->primary_bank_weapons[bank_to_fire]];
3948
3949 if (pm->n_guns && bank_to_fire != -1 ) {
3950 rel_pos = &pm->gun_banks[bank_to_fire].pnt[0];
3951 } else {
3952 rel_pos = NULL;
3953 }
3954
3955 // source_pos will contain the world coordinate of where to base the lead indicator prediction
3956 // from. Normally, this will be the world pos of the gun turret of the currently selected primary
3957 // weapon.
3958 source_pos = Player_obj->pos;
3959 if (rel_pos != NULL) {
3960 vec3d gun_point;
3961 vm_vec_unrotate(&gun_point, rel_pos, &Player_obj->orient);
3962 vm_vec_add2(&source_pos, &gun_point);
3963 }
3964
3965 // Determine "accurate" distance to target.
3966 // This is the distance from the player ship to:
3967 // (if targeting a subsystem) the distance to the subsystem centre
3968 // (playing it safe, will usually be in range at slightly further away due to subsys radius)
3969 // (otherwise) the closest point on the bounding box of the target
3970 if ( Player_ai->targeted_subsys != NULL ) {
3971 dist_to_target = vm_vec_dist(&target_pos, &Player_obj->pos);
3972 } else {
3973 dist_to_target = hud_find_target_distance(targetp, Player_obj);
3974 }
3975
3976 srange = ship_get_secondary_weapon_range(Player_ship);
3977
3978 if ( (swp->current_secondary_bank >= 0) && (swp->secondary_bank_weapons[swp->current_secondary_bank] >= 0) )
3979 {
3980 int bank = swp->current_secondary_bank;
3981 tmp = &Weapon_info[swp->secondary_bank_weapons[bank]];
3982 if ( !(tmp->is_homing()) && !(tmp->is_locked_homing() && Player->target_in_lock_cone) ) {
3983 //The secondary lead indicator is handled farther below if it is a non-locking type
3984 srange = -1.0f;
3985 }
3986 }
3987
3988 frame_offset = pickFrame(prange, srange, dist_to_target);
3989 if ( frame_offset < 0 && srange != -1.0f ) {
3990 return;
3991 }
3992
3993 if (frame_offset >= 0) {
3994 hud_calculate_lead_pos(&lead_target_pos, &target_pos, targetp, wip, dist_to_target);
3995 renderIndicator(frame_offset, targetp, &lead_target_pos);
3996 }
3997
3998 //do dumbfire lead indicator - color is orange (255,128,0) - bright, (192,96,0) - dim
3999 //phreak changed 9/01/02
4000 if((swp->current_secondary_bank>=0) && (swp->secondary_bank_weapons[swp->current_secondary_bank] >= 0)) {
4001 int bank=swp->current_secondary_bank;
4002 wip=&Weapon_info[swp->secondary_bank_weapons[bank]];
4003
4004 //get out of here if the secondary weapon is a homer or if its out of range
4005 if ( wip->is_homing() )
4006 return;
4007
4008 double max_dist = MIN((wip->lifetime * wip->max_speed), wip->weapon_range);
4009
4010 if (dist_to_target > max_dist)
4011 return;
4012 }
4013
4014 hud_calculate_lead_pos(&lead_target_pos, &target_pos, targetp, wip, dist_to_target);
4015 renderIndicator(0, targetp, &lead_target_pos);
4016 }
4017
4018 //Backslash
4019 // A stripped-down version of the lead indicator, only shows primary weapons
4020 // and works for a specified target (not just the current selected target).
4021 // Ideally I'd like to later turn this into something (or make a new function) that would actually WORK with gun convergence/normals
4022 // instead of the existing code (copied from above) that does some calculations and then is ignored ;-)
4023 // (Go look, what's it actually DO with source_pos?)
4024 // And also, something that could be called for multiple weapons, ITTS style.
renderLeadQuick(vec3d * target_world_pos,object * targetp)4025 void HudGaugeLeadIndicator::renderLeadQuick(vec3d *target_world_pos, object *targetp)
4026 {
4027 vec3d source_pos;
4028 vec3d *rel_pos;
4029 vec3d lead_target_pos;
4030 polymodel *pm;
4031 ship_weapon *swp;
4032 weapon_info *wip;
4033 float dist_to_target, prange;
4034 int bank_to_fire, frame_offset;
4035
4036 if ( (targetp->type != OBJ_SHIP) && (targetp->type != OBJ_WEAPON) && (targetp->type != OBJ_ASTEROID) ) {
4037 return;
4038 }
4039
4040 // only allow bombs to have lead indicator displayed
4041 if ( targetp->type == OBJ_WEAPON ) {
4042 if ( !(Weapon_info[Weapons[targetp->instance].weapon_info_index].wi_flags[Weapon::Info_Flags::Can_be_targeted]) ) {
4043 if ( !(Weapon_info[Weapons[targetp->instance].weapon_info_index].wi_flags[Weapon::Info_Flags::Bomb]) ) {
4044 return;
4045 }
4046 }
4047 }
4048
4049 // If the target is out of range, then draw the correct frame for the lead indicator
4050 if ( Lead_indicator_gauge.first_frame == -1 ) {
4051 Int3();
4052 return;
4053 }
4054
4055 pm = model_get(Ship_info[Player_ship->ship_info_index].model_num);
4056 swp = &Player_ship->weapons;
4057
4058 // Added to take care of situation where there are no primary banks on the player ship
4059 // (this may not be possible, depending on what we decide for the weapons loadout rules)
4060 if ( swp->num_primary_banks == 0 )
4061 return;
4062
4063 bank_to_fire = hud_get_best_primary_bank(&prange); //Backslash note: this!
4064 if ( bank_to_fire < 0 )
4065 return;
4066 wip = &Weapon_info[swp->primary_bank_weapons[bank_to_fire]];
4067
4068 if (pm->n_guns && bank_to_fire != -1 ) {
4069 rel_pos = &pm->gun_banks[bank_to_fire].pnt[0];
4070 } else {
4071 rel_pos = NULL;
4072 }
4073 // vec3d firing_vec;
4074 // vm_vec_unrotate(&firing_vec, &po->gun_banks[bank_to_fire].norm[pt], &obj->orient);
4075 // vm_vector_2_matrix(&firing_orient, &firing_vec, NULL, NULL);
4076
4077 // source_pos will contain the world coordinate of where to base the lead indicator prediction
4078 // from. Normally, this will be the world pos of the gun turret of the currently selected primary
4079 // weapon.
4080 source_pos = Player_obj->pos;
4081 if (rel_pos != NULL) {
4082 vec3d gun_point;
4083 vm_vec_unrotate(&gun_point, rel_pos, &Player_obj->orient);
4084 vm_vec_add2(&source_pos, &gun_point);
4085 }
4086
4087 // Determine "accurate" distance to target. This is the distance from the player ship
4088 // to the closest point on the bounding box of the target
4089 dist_to_target = hud_find_target_distance(targetp, Player_obj);
4090
4091 frame_offset = pickFrame(prange, -1.0f, dist_to_target);
4092 if ( frame_offset < 0 ) {
4093 return;
4094 }
4095
4096 hud_calculate_lead_pos(&lead_target_pos, target_world_pos, targetp, wip, dist_to_target, rel_pos);
4097 renderIndicator(frame_offset, targetp, &lead_target_pos);
4098 }
4099
HudGaugeLeadSight()4100 HudGaugeLeadSight::HudGaugeLeadSight():
4101 HudGauge(HUD_OBJECT_LEAD_SIGHT, HUD_LEAD_INDICATOR, true, false, VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY | VM_OTHER_SHIP, 255, 255, 255)
4102 {
4103 }
4104
initBitmaps(char * fname)4105 void HudGaugeLeadSight::initBitmaps(char *fname)
4106 {
4107 Lead_sight.first_frame = bm_load_animation(fname, &Lead_sight.num_frames);
4108
4109 if ( Lead_sight.first_frame < 0 ) {
4110 Warning(LOCATION,"Cannot load hud ani: %s\n", fname);
4111 } else {
4112 int w, h;
4113
4114 bm_get_info(Lead_sight.first_frame, &w, &h);
4115 Lead_sight_half[0] = fl2i(w * 0.5f);
4116 Lead_sight_half[1] = fl2i(h * 0.5f);
4117 }
4118 }
4119
renderSight(int frame_offset,vec3d * target_pos,vec3d * lead_target_pos)4120 void HudGaugeLeadSight::renderSight(int frame_offset, vec3d *target_pos, vec3d *lead_target_pos)
4121 {
4122 vertex target_vertex;
4123 float target_sx;
4124 float target_sy;
4125
4126 vertex lead_target_vertex;
4127 float target_lead_sx;
4128 float target_lead_sy;
4129
4130 // first see if the lead is on screen
4131 g3_rotate_vertex(&lead_target_vertex, lead_target_pos);
4132
4133 if (lead_target_vertex.codes != 0)
4134 return;
4135
4136 g3_project_vertex(&lead_target_vertex);
4137
4138 if (lead_target_vertex.flags & PF_OVERFLOW)
4139 return;
4140
4141 target_lead_sx = lead_target_vertex.screen.xyw.x;
4142 target_lead_sy = lead_target_vertex.screen.xyw.y;
4143
4144 // now see if the target is on screen
4145 g3_rotate_vertex(&target_vertex, target_pos);
4146
4147 if (target_vertex.codes != 0)
4148 return;
4149
4150 g3_project_vertex(&target_vertex);
4151
4152 if (target_vertex.flags & PF_OVERFLOW)
4153 return;
4154
4155 target_sx = target_vertex.screen.xyw.x;
4156 target_sy = target_vertex.screen.xyw.y;
4157
4158 // render the lead sight
4159 if ( Lead_sight.first_frame >= 0 ) {
4160
4161 unsize(&target_lead_sx, &target_lead_sy);
4162 unsize(&target_sx, &target_sy);
4163
4164 float reticle_target_sx = target_sx - Lead_sight_half[0] - target_lead_sx;
4165 float reticle_target_sy = target_sy - Lead_sight_half[1] - target_lead_sy;
4166
4167 reticle_target_sx += position[0] + 0.5f;
4168 reticle_target_sy += position[1] + 0.5f;
4169
4170 setGaugeColor();
4171 renderBitmap(Lead_sight.first_frame + frame_offset, fl2i(reticle_target_sx) + fl2i(HUD_offset_x), fl2i(reticle_target_sy) + fl2i(HUD_offset_y));
4172 }
4173 }
4174
pageIn()4175 void HudGaugeLeadSight::pageIn()
4176 {
4177 bm_page_in_aabitmap(Lead_sight.first_frame, Lead_sight.num_frames);
4178 }
4179
render(float)4180 void HudGaugeLeadSight::render(float /*frametime*/)
4181 {
4182 vec3d target_pos;
4183 vec3d source_pos;
4184 vec3d *rel_pos;
4185 vec3d lead_target_pos;
4186 object *targetp;
4187 polymodel *pm;
4188 ship_weapon *swp;
4189 weapon_info *wip;
4190 float dist_to_target, prange;
4191 int bank_to_fire;
4192
4193 if (Player_ai->target_objnum == -1)
4194 return;
4195
4196 targetp = &Objects[Player_ai->target_objnum];
4197 if ( (targetp->type != OBJ_SHIP) && (targetp->type != OBJ_WEAPON) && (targetp->type != OBJ_ASTEROID) ) {
4198 return;
4199 }
4200
4201 // only allow bombs to have lead indicator displayed
4202 if ( targetp->type == OBJ_WEAPON ) {
4203 if ( !(Weapon_info[Weapons[targetp->instance].weapon_info_index].wi_flags[Weapon::Info_Flags::Bomb]) ) {
4204 return;
4205 }
4206 }
4207
4208 // If the target is out of range, then draw the correct frame for the lead indicator
4209 if ( Lead_sight.first_frame == -1 ) {
4210 Int3();
4211 return;
4212 }
4213
4214 // AL 1/20/97: Point to targeted subsystem if one exists
4215 if ( Player_ai->targeted_subsys != NULL ) {
4216 get_subsystem_world_pos(targetp, Player_ai->targeted_subsys, &target_pos);
4217 } else {
4218 target_pos = targetp->pos;
4219 }
4220
4221 pm = model_get(Ship_info[Player_ship->ship_info_index].model_num);
4222 swp = &Player_ship->weapons;
4223
4224 // Added to take care of situation where there are no primary banks on the player ship
4225 // (this may not be possible, depending on what we decide for the weapons loadout rules)
4226 if ( swp->num_primary_banks == 0 )
4227 return;
4228
4229 bank_to_fire = hud_get_best_primary_bank(&prange);
4230 if ( bank_to_fire < 0 )
4231 return;
4232 wip = &Weapon_info[swp->primary_bank_weapons[bank_to_fire]];
4233
4234 if (pm->n_guns && bank_to_fire != -1 ) {
4235 rel_pos = &pm->gun_banks[bank_to_fire].pnt[0];
4236 } else {
4237 rel_pos = NULL;
4238 }
4239
4240 // source_pos will contain the world coordinate of where to base the lead indicator prediction
4241 // from. Normally, this will be the world pos of the gun turret of the currently selected primary
4242 // weapon.
4243 source_pos = Player_obj->pos;
4244 if (rel_pos != NULL) {
4245 vec3d gun_point;
4246 vm_vec_unrotate(&gun_point, rel_pos, &Player_obj->orient);
4247 vm_vec_add2(&source_pos, &gun_point);
4248 }
4249
4250 // Determine "accurate" distance to target. This is the distance from the player ship
4251 // to the closest point on the bounding box of the target
4252 dist_to_target = hud_find_target_distance(targetp, Player_obj);
4253
4254 bool in_frame;
4255 if ( dist_to_target < prange ) {
4256 // fire it up
4257 in_frame = g3_in_frame() > 0;
4258 if(!in_frame) {
4259 g3_start_frame(0);
4260 }
4261
4262 hud_calculate_lead_pos(&lead_target_pos, &target_pos, targetp, wip, dist_to_target);
4263 renderSight(1, &target_pos, &lead_target_pos); // render the primary weapon lead sight
4264
4265 if(!in_frame) {
4266 g3_end_frame();
4267 }
4268 }
4269
4270 //do dumbfire lead indicator - color is orange (255,128,0) - bright, (192,96,0) - dim
4271 //phreak changed 9/01/02
4272 if((swp->current_secondary_bank>=0) && (swp->secondary_bank_weapons[swp->current_secondary_bank] >= 0))
4273 {
4274 int bank=swp->current_secondary_bank;
4275 wip=&Weapon_info[swp->secondary_bank_weapons[bank]];
4276
4277 //get out of here if the secondary weapon is a homer or if its out of range
4278 if ( wip->is_homing() ) {
4279 return;
4280 }
4281
4282 double max_dist = MIN((wip->lifetime * wip->max_speed), wip->weapon_range);
4283
4284 if (dist_to_target > max_dist) {
4285 return;
4286 }
4287 } else {
4288 return;
4289 }
4290
4291 // fire it up
4292 in_frame = g3_in_frame() > 0;
4293 if(!in_frame) {
4294 g3_start_frame(0);
4295 }
4296
4297 //give it the "in secondary range frame
4298
4299 hud_calculate_lead_pos(&lead_target_pos, &target_pos, targetp, wip, dist_to_target);
4300 renderSight(0, &target_pos, &lead_target_pos); // now render the secondary weapon lead sight
4301
4302 if(!in_frame) {
4303 g3_end_frame();
4304 }
4305 }
4306
hud_cease_subsystem_targeting(bool print_message)4307 void hud_cease_subsystem_targeting(bool print_message)
4308 {
4309 int ship_index;
4310
4311 Assertion(Player_ai->target_objnum < MAX_OBJECTS, "Invalid player target objnum");
4312
4313 if (Player_ai->target_objnum < 0) {
4314 // Nothing selected
4315 if (print_message) {
4316 HUD_sourced_printf(HUD_SOURCE_HIDDEN, "%s", XSTR("No target selected.", 322));
4317 snd_play(gamesnd_get_game_sound(GameSounds::TARGET_FAIL));
4318 }
4319 return;
4320 }
4321
4322 if (Objects[Player_ai->target_objnum].type != OBJ_SHIP) {
4323 // Object isn't a ship, so it doesn't have any subsystems
4324 if (print_message) {
4325 snd_play(gamesnd_get_game_sound(GameSounds::TARGET_FAIL));
4326 }
4327 return;
4328 }
4329
4330 ship_index = Objects[Player_ai->target_objnum].instance;
4331
4332 Assertion(ship_index >= 0 && ship_index < MAX_SHIPS, "Invalid ship index");
4333 Assertion(Player_num >= 0 && Player_num < MAX_PLAYERS, "Invalid player number");
4334
4335 Ships[ship_index].last_targeted_subobject[Player_num] = NULL;
4336 Player_ai->targeted_subsys = NULL;
4337 Player_ai->targeted_subsys_parent = -1;
4338
4339 if ( print_message ) {
4340 HUD_sourced_printf(HUD_SOURCE_HIDDEN, "%s", XSTR( "Deactivating sub-system targeting", 324));
4341 }
4342
4343 hud_stop_looped_locking_sounds();
4344 hud_lock_reset();
4345 }
4346
hud_cease_targeting(bool deliberate)4347 void hud_cease_targeting(bool deliberate)
4348 {
4349 // Turn off subsys targeting before we invalidate the player target_objnum...
4350 hud_cease_subsystem_targeting(false);
4351 set_target_objnum(Player_ai, -1);
4352
4353 if (deliberate) {
4354 // Player wants to stop targeting, so turn off auto-target so we don't immediately aquire another target
4355 Players[Player_num].flags &= ~PLAYER_FLAGS_AUTO_TARGETING;
4356 HUD_sourced_printf(HUD_SOURCE_HIDDEN, "%s", XSTR("Deactivating targeting system", 325));
4357 }
4358 }
4359
4360 // hud_restore_subsystem_target() will remember the last targeted subsystem
4361 // on a target.
4362 //
hud_restore_subsystem_target(ship * shipp)4363 void hud_restore_subsystem_target(ship* shipp)
4364 {
4365 // check if there was a previously targeted sub-system for this target
4366 if ( shipp->last_targeted_subobject[Player_num] != NULL ) {
4367 Player_ai->targeted_subsys = shipp->last_targeted_subobject[Player_num];
4368 Player_ai->targeted_subsys_parent = Player_ai->target_objnum;
4369 }
4370 else {
4371 Player_ai->targeted_subsys = NULL;
4372 Player_ai->targeted_subsys_parent = -1;
4373 }
4374 }
4375
4376 // --------------------------------------------------------------------------------
4377 // get_subsystem_world_pos() returns the world position for a given subsystem on a ship
4378 //
get_subsystem_world_pos(object * parent_obj,ship_subsys * subsys,vec3d * world_pos)4379 vec3d* get_subsystem_world_pos(object* parent_obj, ship_subsys* subsys, vec3d* world_pos)
4380 {
4381 get_subsystem_pos(world_pos, parent_obj, subsys);
4382
4383 return world_pos;
4384 }
4385
4386 // ----------------------------------------------------------------------------
4387 // hud_target_change_check()
4388 //
4389 // called once per frame to account for when the target changes
4390 //
hud_target_change_check()4391 void hud_target_change_check()
4392 {
4393 float current_speed=0.0f;
4394
4395 // Check if player subsystem target has changed, and reset necessary player flag
4396 if ( Player_ai->targeted_subsys != Player_ai->last_subsys_target ) {
4397 Player->subsys_in_view=-1;
4398 }
4399
4400 // check if the main target has changed
4401 if (Player_ai->last_target != Player_ai->target_objnum) {
4402
4403 if ( Player_ai->target_objnum != -1){
4404 snd_play( gamesnd_get_game_sound(ship_get_sound(Player_obj, GameSounds::TARGET_ACQUIRE)), 0.0f );
4405 }
4406
4407 // if we have a hotkey set active, see if new target is in set. If not in
4408 // set, deselect the current hotkey set.
4409 if ( Player->current_hotkey_set != -1 ) {
4410 htarget_list *hitem, *plist;
4411
4412 plist = &(Player->keyed_targets[Player->current_hotkey_set]);
4413 for ( hitem = GET_FIRST(plist); hitem != END_OF_LIST(plist); hitem = GET_NEXT(hitem) ) {
4414 if ( OBJ_INDEX(hitem->objp) == Player_ai->target_objnum ){
4415 break;
4416 }
4417 }
4418 if ( hitem == END_OF_LIST(plist) ){
4419 Player->current_hotkey_set = -1;
4420 }
4421 }
4422
4423 player_stop_cargo_scan_sound();
4424 if ( (Player_ai->target_objnum >= 0) && (Player_ai->target_objnum < MAX_OBJECTS) ) {
4425 hud_shield_hit_reset(&Objects[Player_ai->target_objnum]);
4426 }
4427 hud_targetbox_init_flash();
4428 hud_targetbox_start_flash(TBOX_FLASH_NAME);
4429 hud_gauge_popup_start(HUD_TARGET_MINI_ICON);
4430 Player->cargo_inspect_time=0;
4431 Player->locking_subsys=NULL;
4432 Player->locking_on_center=0;
4433 Player->locking_subsys_parent=-1;
4434
4435 Player_ai->current_target_dist_trend = NO_CHANGE;
4436 Player_ai->current_target_speed_trend = NO_CHANGE;
4437
4438 if ( Players[Player_num].flags & PLAYER_FLAGS_AUTO_MATCH_SPEED ) {
4439 Players[Player_num].flags &= ~PLAYER_FLAGS_MATCH_TARGET;
4440 player_match_target_speed();
4441 }
4442 else {
4443 if ( Players[Player_num].flags & PLAYER_FLAGS_MATCH_TARGET )
4444 Players[Player_num].flags &= ~PLAYER_FLAGS_MATCH_TARGET; // no more target matching.
4445 }
4446
4447 hud_lock_reset();
4448
4449 if ( (Player_ai->target_objnum >= 0) && (Player_ai->target_objnum < MAX_OBJECTS) ) {
4450 if ( Objects[Player_ai->target_objnum].type == OBJ_SHIP ) {
4451 hud_restore_subsystem_target(&Ships[Objects[Player_ai->target_objnum].instance]);
4452 }
4453 }
4454
4455 // reset external cam distance
4456 if (Viewer_mode & VM_EXTERNAL) {
4457 if (Viewer_mode & VM_OTHER_SHIP)
4458 Viewer_external_info.preferred_distance = 2 * Objects[Player_ai->target_objnum].radius;
4459 }
4460 }
4461 else {
4462 if (Player_ai->current_target_distance < Player_ai->last_dist-0.01){
4463 Player_ai->current_target_dist_trend = DECREASING;
4464 } else if (Player_ai->current_target_distance > Player_ai->last_dist+0.01){
4465 Player_ai->current_target_dist_trend = INCREASING;
4466 } else {
4467 Player_ai->current_target_dist_trend = NO_CHANGE;
4468 }
4469
4470 if ( (Player_ai->target_objnum >= 0) && (Player_ai->target_objnum < MAX_OBJECTS) ) {
4471 current_speed = Objects[Player_ai->target_objnum].phys_info.speed;
4472 }
4473
4474 if (current_speed < Player_ai->last_speed-0.01){
4475 Player_ai->current_target_speed_trend = DECREASING;
4476 } else if (current_speed > Player_ai->last_speed+0.01) {
4477 Player_ai->current_target_speed_trend = INCREASING;
4478 } else {
4479 Player_ai->current_target_speed_trend = NO_CHANGE;
4480 }
4481
4482 if ( Players[Player_num].flags & PLAYER_FLAGS_AUTO_MATCH_SPEED ) {
4483 if ( !(Players[Player_num].flags & PLAYER_FLAGS_MATCH_TARGET) ) {
4484 player_match_target_speed();
4485 }
4486 }
4487 }
4488
4489 Player_ai->last_dist = Player_ai->current_target_distance;
4490 Player_ai->last_speed = current_speed;
4491
4492 Player_ai->last_target = Player_ai->target_objnum;
4493 Player_ai->last_subsys_target = Player_ai->targeted_subsys;
4494 }
4495
HudGaugeTargetTriangle()4496 HudGaugeTargetTriangle::HudGaugeTargetTriangle():
4497 HudGaugeReticleTriangle(HUD_OBJECT_TARGET_TRI, HUD_TARGET_TRIANGLE)
4498 {
4499 }
4500
render(float)4501 void HudGaugeTargetTriangle::render(float /*frametime*/)
4502 {
4503 if ( Player_ai->target_objnum == -1)
4504 return;
4505
4506 bool in_frame = g3_in_frame() > 0;
4507 if(!in_frame)
4508 g3_start_frame(0);
4509
4510 object *targetp = &Objects[Player_ai->target_objnum];
4511
4512 // draw the targeting triangle that orbits the outside of the outer circle of the reticle
4513 if (!Player->target_is_dying && maybeFlashSexp() != 1) {
4514
4515 hud_set_iff_color(targetp, 1);
4516 renderTriangle(&targetp->pos, 1, 0, 0);
4517 }
4518
4519 if(!in_frame)
4520 g3_end_frame();
4521 }
4522
4523 // start the weapon line (on the HUD) flashing
hud_start_flash_weapon(int index)4524 void hud_start_flash_weapon(int index)
4525 {
4526 if ( index >= MAX_WEAPON_FLASH_LINES ) {
4527 Int3(); // Get Alan
4528 return;
4529 }
4530
4531 if ( timestamp_elapsed(Weapon_flash_info.flash_duration[index]) ) {
4532 Weapon_flash_info.flash_next[index] = timestamp(TBOX_FLASH_INTERVAL);
4533 Weapon_flash_info.is_bright &= ~(1<<index);
4534 }
4535
4536 Weapon_flash_info.flash_duration[index] = timestamp(TBOX_FLASH_DURATION);
4537 }
4538
4539 // maybe change the text color for the weapon line indicated by index
hud_maybe_flash_weapon(int index)4540 void hud_maybe_flash_weapon(int index)
4541 {
4542 if ( index >= MAX_WEAPON_FLASH_LINES ) {
4543 Int3(); // Get Alan
4544 return;
4545 }
4546
4547 // hud_set_default_color();
4548 hud_set_gauge_color(HUD_WEAPONS_GAUGE);
4549 if ( !timestamp_elapsed(Weapon_flash_info.flash_duration[index]) ) {
4550 if ( timestamp_elapsed(Weapon_flash_info.flash_next[index]) ) {
4551 Weapon_flash_info.flash_next[index] = timestamp(TBOX_FLASH_INTERVAL);
4552 Weapon_flash_info.is_bright ^= (1<<index);
4553 }
4554
4555 if ( Weapon_flash_info.is_bright & (1<<index) ) {
4556 hud_set_gauge_color(HUD_WEAPONS_GAUGE, HUD_C_BRIGHT);
4557 // hud_set_bright_color();
4558 } else {
4559 hud_set_gauge_color(HUD_WEAPONS_GAUGE, HUD_C_DIM);
4560 // hud_set_dim_color();
4561 }
4562 }
4563 }
4564
4565 /**
4566 * @brief Check if targeting is possible based on sensors strength
4567 */
hud_sensors_ok(ship * sp,int show_msg)4568 int hud_sensors_ok(ship *sp, int show_msg)
4569 {
4570 float sensors_str;
4571
4572 // If playing on lowest skill level, sensors don't affect targeting
4573 // If dead, still allow player to target, despite any subsystem damage
4574 // If i'm a multiplayer observer, allow me to target
4575 if ( (Game_skill_level == 0) || (Game_mode & GM_DEAD) || ((Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_OBSERVER)) ) {
4576 return 1;
4577 }
4578
4579 // if the ship is currently being affected by EMP
4580 if(emp_active_local()){
4581 return 0;
4582 }
4583
4584 // ensure targeting functions are not disabled through damage
4585 sensors_str = ship_get_subsystem_strength( sp, SUBSYSTEM_SENSORS );
4586 if ( (sensors_str < MIN_SENSOR_STR_TO_TARGET) || (ship_subsys_disrupted(sp, SUBSYSTEM_SENSORS)) ) {
4587 if ( show_msg ) {
4588 HUD_sourced_printf(HUD_SOURCE_HIDDEN, "%s", XSTR( "Targeting is disabled due to sensors damage", 330));
4589 snd_play(gamesnd_get_game_sound(GameSounds::TARGET_FAIL));
4590 }
4591 return 0;
4592 } else {
4593 return 1;
4594 }
4595 }
4596
hud_communications_state(ship * sp,bool for_death_scream)4597 int hud_communications_state(ship *sp, bool for_death_scream)
4598 {
4599 float str;
4600
4601 // If playing on the lowest skill level, communications always ok
4602 // If dead, still allow player to communicate, despite any subsystem damage
4603 if ( Game_skill_level == 0 || (Game_mode & GM_DEAD) ) {
4604 return COMM_OK;
4605 }
4606
4607 // Goober5000 - if the ship is the player, and he's dying, return OK (so laments can be played)
4608 if ((sp == Player_ship) && (sp->flags[Ship::Ship_Flags::Dying]))
4609 return COMM_OK;
4610
4611 str = ship_get_subsystem_strength( sp, SUBSYSTEM_COMMUNICATION, for_death_scream );
4612
4613 if ( (str <= 0.01) || ship_subsys_disrupted(sp, SUBSYSTEM_COMMUNICATION) ) {
4614 return COMM_DESTROYED;
4615 } else if ( str < MIN_COMM_STR_TO_MESSAGE ) {
4616 return COMM_DAMAGED;
4617 }
4618
4619 // Goober5000 - check for scrambled communications
4620 if (emp_active_local() || sp->flags[Ship::Ship_Flags::Scramble_messages])
4621 return COMM_SCRAMBLED;
4622
4623 return COMM_OK;
4624 }
4625
4626 // target the next or previous hostile/friendly ship
hud_target_next_list(int hostile,int next_flag,int team_mask,int attacked_objnum,int play_fail_snd,int filter,int get_closest_turret_attacking_player)4627 void hud_target_next_list(int hostile, int next_flag, int team_mask, int attacked_objnum, int play_fail_snd, int filter, int get_closest_turret_attacking_player)
4628 {
4629 int timestamp_val, valid_team_mask;
4630
4631 if ( hostile ) {
4632 timestamp_val = Tl_hostile_reset_timestamp;
4633 Tl_hostile_reset_timestamp = timestamp(TL_RESET);
4634 if ( team_mask == -1 ) {
4635 valid_team_mask = iff_get_attackee_mask(Player_ship->team);
4636 } else {
4637 valid_team_mask = team_mask;
4638 }
4639 } else {
4640 // everyone hates a traitor including other traitors so the friendly target option shouldn't work for them
4641 if (Player_ship->team == Iff_traitor) {
4642 snd_play( gamesnd_get_game_sound(GameSounds::TARGET_FAIL), 0.0f );
4643 return;
4644 }
4645
4646 timestamp_val = Tl_friendly_reset_timestamp;
4647 Tl_friendly_reset_timestamp = timestamp(TL_RESET);
4648 valid_team_mask = iff_get_mask(Player_ship->team);
4649 }
4650
4651 // If no target is selected, then simply target the closest ship
4652 if ( Player_ai->target_objnum == -1 || timestamp_elapsed(timestamp_val) ) {
4653 hud_target_closest(valid_team_mask, attacked_objnum, play_fail_snd, filter, get_closest_turret_attacking_player);
4654 return;
4655 }
4656
4657 object *nearest_object = select_next_target_by_distance((next_flag != 0), valid_team_mask, attacked_objnum);
4658
4659 if (nearest_object != NULL) {
4660 // set new target
4661 set_target_objnum( Player_ai, OBJ_INDEX(nearest_object) );
4662 hud_shield_hit_reset(nearest_object);
4663
4664 // maybe set new turret subsystem
4665 hud_maybe_set_sorted_turret_subsys(&Ships[nearest_object->instance]);
4666 hud_restore_subsystem_target(&Ships[nearest_object->instance]);
4667 }
4668 else {
4669 snd_play( gamesnd_get_game_sound(GameSounds::TARGET_FAIL), 0.0f );
4670 }
4671 }
4672
HudGaugeAutoTarget()4673 HudGaugeAutoTarget::HudGaugeAutoTarget():
4674 HudGauge(HUD_OBJECT_AUTO_TARGET, HUD_AUTO_TARGET, false, false, (VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY | VM_OTHER_SHIP), 255, 255, 255)
4675 {
4676 }
4677
initAutoTextOffsets(int x,int y)4678 void HudGaugeAutoTarget::initAutoTextOffsets(int x, int y)
4679 {
4680 Auto_text_offsets[0] = x;
4681 Auto_text_offsets[1] = y;
4682 }
4683
initTargetTextOffsets(int x,int y)4684 void HudGaugeAutoTarget::initTargetTextOffsets(int x, int y)
4685 {
4686 Target_text_offsets[0] = x;
4687 Target_text_offsets[1] = y;
4688 }
4689
initBitmaps(char * fname)4690 void HudGaugeAutoTarget::initBitmaps(char *fname)
4691 {
4692 Toggle_frame.first_frame = bm_load_animation(fname, &Toggle_frame.num_frames);
4693 if ( Toggle_frame.first_frame < 0 ) {
4694 Warning(LOCATION,"Cannot load hud ani: %s\n", fname);
4695 }
4696 }
4697
initOnColor(int r,int g,int b,int a)4698 void HudGaugeAutoTarget::initOnColor(int r, int g, int b, int a)
4699 {
4700 if ( r == -1 || g == -1 || b == -1 || a == -1 ) {
4701 Use_on_color = false;
4702 gr_init_alphacolor(&On_color, 0, 0, 0, 0);
4703 return;
4704 }
4705
4706 Use_on_color = true;
4707 gr_init_alphacolor(&On_color, r, g, b, a);
4708 }
4709
initOffColor(int r,int g,int b,int a)4710 void HudGaugeAutoTarget::initOffColor(int r, int g, int b, int a)
4711 {
4712 if ( r == -1 || g == -1 || b == -1 || a == -1 ) {
4713 Use_off_color = false;
4714 gr_init_alphacolor(&Off_color, 0, 0, 0, 0);
4715 return;
4716 }
4717
4718 Use_off_color = true;
4719 gr_init_alphacolor(&Off_color, r, g, b, a);
4720 }
4721
render(float)4722 void HudGaugeAutoTarget::render(float /*frametime*/)
4723 {
4724 if (Player_ship->flags[Ship::Ship_Flags::Primitive_sensors])
4725 return;
4726
4727 int frame_offset;
4728
4729 if ( Players[Player_num].flags & PLAYER_FLAGS_AUTO_TARGETING ) {
4730 frame_offset = 1;
4731 } else {
4732 frame_offset = 0;
4733 }
4734
4735 // draw the box background
4736 setGaugeColor();
4737 renderBitmap(Toggle_frame.first_frame+frame_offset, position[0], position[1]);
4738
4739 // draw the text on top
4740 if (frame_offset == 1) {
4741 //static color text_color;
4742 //gr_init_alphacolor(&text_color, 0, 0, 0, Toggle_text_alpha);
4743 if ( Use_on_color ) {
4744 gr_set_color_fast(&On_color);
4745 }
4746 } else if ( Use_off_color ) {
4747 gr_set_color_fast(&Off_color);
4748 }
4749
4750 renderString(position[0] + Auto_text_offsets[0], position[1] + Auto_text_offsets[1], XSTR("auto", 1463));
4751 renderString(position[0] + Target_text_offsets[0], position[1] + Target_text_offsets[1], XSTR("target", 1465));
4752 }
4753
pageIn()4754 void HudGaugeAutoTarget::pageIn()
4755 {
4756 bm_page_in_aabitmap(Toggle_frame.first_frame, Toggle_frame.num_frames);
4757 }
4758
HudGaugeAutoSpeed()4759 HudGaugeAutoSpeed::HudGaugeAutoSpeed():
4760 HudGauge(HUD_OBJECT_AUTO_SPEED, HUD_AUTO_SPEED, false, false, (VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY | VM_OTHER_SHIP), 255, 255, 255)
4761 {
4762 }
4763
initAutoTextOffsets(int x,int y)4764 void HudGaugeAutoSpeed::initAutoTextOffsets(int x, int y)
4765 {
4766 Auto_text_offsets[0] = x;
4767 Auto_text_offsets[1] = y;
4768 }
4769
initSpeedTextOffsets(int x,int y)4770 void HudGaugeAutoSpeed::initSpeedTextOffsets(int x, int y)
4771 {
4772 Speed_text_offsets[0] = x;
4773 Speed_text_offsets[1] = y;
4774 }
4775
initBitmaps(char * fname)4776 void HudGaugeAutoSpeed::initBitmaps(char *fname)
4777 {
4778 Toggle_frame.first_frame = bm_load_animation(fname, &Toggle_frame.num_frames);
4779 if ( Toggle_frame.first_frame < 0 ) {
4780 Warning(LOCATION,"Cannot load hud ani: %s\n", fname);
4781 }
4782 }
4783
initOnColor(int r,int g,int b,int a)4784 void HudGaugeAutoSpeed::initOnColor(int r, int g, int b, int a)
4785 {
4786 if ( r == -1 || g == -1 || b == -1 || a == -1 ) {
4787 Use_on_color = false;
4788 gr_init_alphacolor(&On_color, 0, 0, 0, 0);
4789 return;
4790 }
4791
4792 Use_on_color = true;
4793 gr_init_alphacolor(&On_color, r, g, b, a);
4794 }
4795
initOffColor(int r,int g,int b,int a)4796 void HudGaugeAutoSpeed::initOffColor(int r, int g, int b, int a)
4797 {
4798 if ( r == -1 || g == -1 || b == -1 || a == -1 ) {
4799 Use_off_color = false;
4800 gr_init_alphacolor(&Off_color, 0, 0, 0, 0);
4801 return;
4802 }
4803
4804 Use_off_color = true;
4805 gr_init_alphacolor(&Off_color, r, g, b, a);
4806 }
4807
render(float)4808 void HudGaugeAutoSpeed::render(float /*frametime*/)
4809 {
4810 if (Player_ship->flags[Ship::Ship_Flags::Primitive_sensors])
4811 return;
4812
4813 int frame_offset;
4814
4815 if ( Players[Player_num].flags & PLAYER_FLAGS_AUTO_MATCH_SPEED ) {
4816 frame_offset = 3;
4817 } else {
4818 frame_offset = 2;
4819 }
4820
4821 setGaugeColor();
4822
4823 renderBitmap(Toggle_frame.first_frame+frame_offset, position[0], position[1]);
4824
4825 // draw the text on top
4826 if (frame_offset == 3) {
4827 //static color text_color;
4828 //gr_init_alphacolor(&text_color, 0, 0, 0, Toggle_text_alpha);
4829 if ( Use_on_color ) {
4830 gr_set_color_fast(&On_color);
4831 }
4832 } else if ( Use_off_color ) {
4833 gr_set_color_fast(&Off_color);
4834 }
4835 renderString(position[0] + Auto_text_offsets[0], position[1] + Auto_text_offsets[1], XSTR("auto", 1463));
4836 renderString(position[0] + Speed_text_offsets[0], position[1] + Speed_text_offsets[1], XSTR("speed", 1464));
4837 }
4838
pageIn()4839 void HudGaugeAutoSpeed::pageIn()
4840 {
4841 bm_page_in_aabitmap(Toggle_frame.first_frame, Toggle_frame.num_frames);
4842 }
4843
4844 // Set the player target to the closest friendly repair ship
4845 // input: goal_objnum => Try to find repair ship where aip->goal_objnum matches this
4846 // output: 1 => A repair ship was targeted
4847 // 0 => No targeting change
hud_target_closest_repair_ship(int goal_objnum)4848 int hud_target_closest_repair_ship(int goal_objnum)
4849 {
4850 object *A;
4851 object *nearest_obj=&obj_used_list;
4852 ship *shipp;
4853 ship_obj *so;
4854 float min_distance=1e20f;
4855 float new_distance=0.0f;
4856 int rval=0;
4857
4858 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
4859 A = &Objects[so->objnum];
4860 shipp = &Ships[A->instance]; // get a pointer to the ship information
4861
4862 // ignore all ships that aren't repair ships
4863 if ( !(Ship_info[shipp->ship_info_index].flags[Ship::Info_Flags::Support]) ) {
4864 continue;
4865 }
4866
4867 if ( (A == Player_obj) || (should_be_ignored(shipp)) )
4868 continue;
4869
4870 // only consider friendly ships
4871 if ( !(Player_ship->team == shipp->team)) {
4872 continue;
4873 }
4874
4875 if(hud_target_invalid_awacs(A)){
4876 continue;
4877 }
4878
4879 if ( goal_objnum >= 0 ) {
4880 if ( Ai_info[shipp->ai_index].goal_objnum != goal_objnum ) {
4881 continue;
4882 }
4883 }
4884
4885 new_distance = hud_find_target_distance(A,Player_obj);
4886
4887 if (new_distance <= min_distance) {
4888 min_distance=new_distance;
4889 nearest_obj = A;
4890 }
4891 }
4892
4893 if (nearest_obj != &obj_used_list) {
4894 set_target_objnum( Player_ai, OBJ_INDEX(nearest_obj) );
4895 hud_shield_hit_reset(nearest_obj);
4896 hud_restore_subsystem_target(&Ships[nearest_obj->instance]);
4897 rval=1;
4898 }
4899 else {
4900 // inform player how to get a support ship
4901 if ( goal_objnum == -1 ) {
4902 HUD_sourced_printf(HUD_SOURCE_HIDDEN, "%s", XSTR( "No support ships in area. Use messaging to call one in.", 332));
4903 }
4904 rval=0;
4905 }
4906
4907 return rval;
4908 }
4909
hud_target_toggle_hidden_from_sensors()4910 void hud_target_toggle_hidden_from_sensors()
4911 {
4912 if ( Ignore_List[Ship::Ship_Flags::Hidden_from_sensors] ) {
4913 Ignore_List.remove(Ship::Ship_Flags::Hidden_from_sensors);
4914 HUD_sourced_printf(HUD_SOURCE_HIDDEN, NOX("Target hiding from sensors disabled"));
4915 } else {
4916 Ignore_List.set(Ship::Ship_Flags::Hidden_from_sensors);
4917 HUD_sourced_printf(HUD_SOURCE_HIDDEN, NOX("Target hiding from sensors enabled"));
4918 }
4919 }
4920
4921 // target the closest uninspected object
hud_target_closest_uninspected_object()4922 void hud_target_closest_uninspected_object()
4923 {
4924 object *A, *nearest_obj = NULL;
4925 ship *shipp;
4926 ship_obj *so;
4927 float min_distance = 1e20f;
4928 float new_distance = 0.0f;
4929
4930 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
4931
4932 A = &Objects[so->objnum];
4933 shipp = &Ships[A->instance]; // get a pointer to the ship information
4934
4935 if ( (A == Player_obj) || (should_be_ignored(shipp)) ){
4936 continue;
4937 }
4938
4939 if(hud_target_invalid_awacs(A)){
4940 continue;
4941 }
4942
4943 // ignore all non-cargo carrying craft
4944 if ( !hud_target_ship_can_be_scanned(shipp) ) {
4945 continue;
4946 }
4947
4948 new_distance = hud_find_target_distance(A,Player_obj);
4949
4950 if (new_distance <= min_distance) {
4951 min_distance=new_distance;
4952 nearest_obj = A;
4953 }
4954 }
4955
4956 if (nearest_obj != NULL) {
4957 set_target_objnum( Player_ai, OBJ_INDEX(nearest_obj) );
4958 hud_shield_hit_reset(nearest_obj);
4959 hud_restore_subsystem_target(&Ships[nearest_obj->instance]);
4960 }
4961 else {
4962 snd_play( gamesnd_get_game_sound(GameSounds::TARGET_FAIL) );
4963 }
4964 }
4965
4966 // target the next or previous uninspected/unscanned object
hud_target_uninspected_object(int next_flag)4967 void hud_target_uninspected_object(int next_flag)
4968 {
4969 object *A, *min_obj, *max_obj, *nearest_obj;
4970 ship *shipp;
4971 ship_obj *so;
4972 float cur_dist, min_dist, max_dist, new_dist, nearest_dist, diff;
4973
4974 // If no target is selected, then simply target the closest uninspected cargo
4975 if ( Player_ai->target_objnum == -1 || timestamp_elapsed(Target_next_uninspected_object_timestamp) ) {
4976 Target_next_uninspected_object_timestamp = timestamp(TL_RESET);
4977 hud_target_closest_uninspected_object();
4978 return;
4979 }
4980
4981 Target_next_uninspected_object_timestamp = timestamp(TL_RESET);
4982
4983 cur_dist = hud_find_target_distance(&Objects[Player_ai->target_objnum], Player_obj);
4984
4985 min_obj = max_obj = nearest_obj = NULL;
4986 min_dist = 1e20f;
4987 max_dist = 0.0f;
4988 if ( next_flag ) {
4989 nearest_dist = 1e20f;
4990 } else {
4991 nearest_dist = 0.0f;
4992 }
4993
4994 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
4995 A = &Objects[so->objnum];
4996 shipp = &Ships[A->instance]; // get a pointer to the ship information
4997
4998 if ( (A == Player_obj) || (should_be_ignored(shipp)) )
4999 continue;
5000
5001 // ignore all non-cargo carrying craft
5002 if ( !hud_target_ship_can_be_scanned(shipp) ) {
5003 continue;
5004 }
5005
5006 // don't use object if it is already a target
5007 if ( OBJ_INDEX(A) == Player_ai->target_objnum ) {
5008 continue;
5009 }
5010
5011 if(hud_target_invalid_awacs(A)){
5012 continue;
5013 }
5014
5015 new_dist = hud_find_target_distance(A, Player_obj);
5016
5017 if (new_dist <= min_dist) {
5018 min_dist = new_dist;
5019 min_obj = A;
5020 }
5021
5022 if (new_dist >= max_dist) {
5023 max_dist = new_dist;
5024 max_obj = A;
5025 }
5026
5027 if ( next_flag ) {
5028 diff = new_dist - cur_dist;
5029 if ( diff > 0 ) {
5030 if ( diff < ( nearest_dist - cur_dist ) ) {
5031 nearest_dist = new_dist;
5032 nearest_obj = A;
5033 }
5034 }
5035 } else {
5036 diff = cur_dist - new_dist;
5037 if ( diff > 0 ) {
5038 if ( diff < ( cur_dist - nearest_dist ) ) {
5039 nearest_dist = new_dist;
5040 nearest_obj = A;
5041 }
5042 }
5043 }
5044 }
5045
5046 if ( nearest_obj == NULL ) {
5047
5048 if ( next_flag ) {
5049 if ( min_obj != NULL ) {
5050 nearest_obj = min_obj;
5051 }
5052 } else {
5053 if ( max_obj != NULL ) {
5054 nearest_obj = max_obj;
5055 }
5056 }
5057 }
5058
5059 if (nearest_obj != NULL) {
5060 set_target_objnum( Player_ai, OBJ_INDEX(nearest_obj) );
5061 hud_shield_hit_reset(nearest_obj);
5062 hud_restore_subsystem_target(&Ships[nearest_obj->instance]);
5063 }
5064 else {
5065 snd_play( gamesnd_get_game_sound(GameSounds::TARGET_FAIL) );
5066 }
5067 }
5068
5069 // ----------------------------------------------------------------
5070 //
5071 // Target Last Transmission Sender code START
5072 //
5073 // ----------------------------------------------------------------
5074
5075 typedef struct transmit_target
5076 {
5077 int objnum;
5078 int objsig;
5079 } transmit_target;
5080
5081 static int Transmit_target_next_slot = 0;
5082 static int Transmit_target_current_slot = -1;
5083 static int Transmit_target_reset_timer = timestamp(0);
5084
5085 #define MAX_TRANSMIT_TARGETS 10
5086 static transmit_target Transmit_target_list[MAX_TRANSMIT_TARGETS];
5087
5088 // called once per level to initialize the target last transmission sender list
hud_target_last_transmit_level_init()5089 void hud_target_last_transmit_level_init()
5090 {
5091 int i;
5092
5093 for ( i = 0; i < MAX_TRANSMIT_TARGETS; i++ ) {
5094 Transmit_target_list[i].objnum = -1;
5095 Transmit_target_list[i].objsig = -1;
5096 }
5097
5098 Transmit_target_next_slot = 0;
5099 Transmit_target_current_slot = 0;
5100 Transmit_target_reset_timer = timestamp(0);
5101 }
5102
5103 // internal function only.. used to find index for last recorded ship transmission
hud_target_last_transmit_newest()5104 int hud_target_last_transmit_newest()
5105 {
5106 int latest_slot;
5107
5108 latest_slot = Transmit_target_next_slot - 1;
5109 if ( latest_slot < 0 ) {
5110 latest_slot = MAX_TRANSMIT_TARGETS - 1;
5111 }
5112
5113 return latest_slot;
5114 }
5115
5116 // called externally to set the player target to the last ship which sent a transmission to the player
hud_target_last_transmit()5117 void hud_target_last_transmit()
5118 {
5119 int i;
5120
5121 if ( Transmit_target_current_slot < 0 ) {
5122 Transmit_target_current_slot = hud_target_last_transmit_newest();
5123 }
5124
5125 // If timed out, then simply target the last ship to transmit
5126 if ( timestamp_elapsed(Transmit_target_reset_timer) ) {
5127 Transmit_target_current_slot = hud_target_last_transmit_newest();
5128 }
5129
5130 Transmit_target_reset_timer = timestamp(TL_RESET);
5131
5132 int play_fail_sound = 1;
5133 int transmit_index = Transmit_target_current_slot;
5134 Assert(transmit_index >= 0);
5135 for ( i = 0; i < MAX_TRANSMIT_TARGETS; i++ ) {
5136 if ( Transmit_target_list[transmit_index].objnum >= 0 ) {
5137 int transmit_objnum = Transmit_target_list[transmit_index].objnum;
5138
5139 if ( Player_ai->target_objnum == transmit_objnum ) {
5140 play_fail_sound = 0;
5141 } else {
5142 if ( Transmit_target_list[transmit_index].objsig == Objects[Transmit_target_list[transmit_index].objnum].signature ) {
5143 if ( !(should_be_ignored(&Ships[Objects[transmit_objnum].instance])) ) {
5144 Transmit_target_current_slot = transmit_index-1;
5145 if ( Transmit_target_current_slot < 0 ) {
5146 Transmit_target_current_slot = MAX_TRANSMIT_TARGETS - 1;
5147 }
5148 break;
5149 }
5150 }
5151 }
5152 }
5153
5154 transmit_index--;
5155 if ( transmit_index < 0 ) {
5156 transmit_index = MAX_TRANSMIT_TARGETS - 1;
5157 }
5158 }
5159
5160 if ( i == MAX_TRANSMIT_TARGETS ) {
5161 if ( play_fail_sound ) {
5162 snd_play( gamesnd_get_game_sound(GameSounds::TARGET_FAIL) );
5163 }
5164 Transmit_target_current_slot = -1;
5165 return;
5166 }
5167
5168 if(hud_target_invalid_awacs(&Objects[Transmit_target_list[transmit_index].objnum])){
5169 return;
5170 }
5171
5172 // target new ship!
5173 // Fix bug in targeting due to Alt-Y (target last ship sending transmission).
5174 // Was just bogus code in the call to hud_restore_subsystem_target(). -- MK, 9/15/99, 1:59 pm.
5175 int targeted_objnum;
5176 targeted_objnum = Transmit_target_list[transmit_index].objnum;
5177 Assert((targeted_objnum >= 0) && (targeted_objnum < MAX_OBJECTS));
5178
5179 if ((targeted_objnum >= 0) && (targeted_objnum < MAX_OBJECTS)) {
5180 set_target_objnum( Player_ai, Transmit_target_list[transmit_index].objnum );
5181 hud_shield_hit_reset(&Objects[Transmit_target_list[transmit_index].objnum]);
5182 hud_restore_subsystem_target(&Ships[Objects[Transmit_target_list[transmit_index].objnum].instance]);
5183 }
5184 }
5185
5186 // called externally to add a message sender to the list
hud_target_last_transmit_add(int ship_num)5187 void hud_target_last_transmit_add(int ship_num)
5188 {
5189 object *ship_objp;
5190 int ship_objnum;
5191
5192 ship_objnum = Ships[ship_num].objnum;
5193 Assert(ship_objnum >= 0 && ship_objnum < MAX_OBJECTS);
5194 ship_objp = &Objects[ship_objnum];
5195 Assert(ship_objp->type == OBJ_SHIP);
5196
5197 // don't add ourselves to the list
5198 if (Player_obj == ship_objp) {
5199 return;
5200 }
5201
5202 Transmit_target_list[Transmit_target_next_slot].objnum = ship_objnum;
5203 Transmit_target_list[Transmit_target_next_slot].objsig = ship_objp->signature;
5204 Transmit_target_next_slot++;
5205 if ( Transmit_target_next_slot >= MAX_TRANSMIT_TARGETS ) {
5206 Transmit_target_next_slot = 0;
5207 }
5208 }
5209
5210 // target a random ship (useful for EMP stuff)
hud_target_random_ship()5211 void hud_target_random_ship()
5212 {
5213 int shipnum;
5214 int objnum;
5215
5216 shipnum = ship_get_random_targetable_ship();
5217 if((shipnum < 0) || (Ships[shipnum].objnum < 0)){
5218 return;
5219 }
5220 objnum = Ships[shipnum].objnum;
5221
5222 if((objnum >= 0) && (Player_ai != NULL) && !hud_target_invalid_awacs(&Objects[objnum])){
5223 // never target yourself
5224 if(objnum == OBJ_INDEX(Player_obj)){
5225 set_target_objnum(Player_ai, -1);
5226 } else {
5227 set_target_objnum(Player_ai, objnum);
5228 hud_shield_hit_reset(&Objects[objnum]);
5229 }
5230 }
5231 }
5232
5233 // ----------------------------------------------------------------
5234 //
5235 // Target Last Transmission Sender code END
5236 //
5237 // ----------------------------------------------------------------
5238
hudtarget_page_in()5239 void hudtarget_page_in()
5240 {
5241 int i;
5242
5243 for ( i = 0; i < NUM_WEAPON_GAUGES; i++ ) {
5244 bm_page_in_aabitmap( Weapon_gauges[ballistic_hud_index][i].first_frame, Weapon_gauges[ballistic_hud_index][i].num_frames);
5245 }
5246 bm_page_in_aabitmap( New_weapon.first_frame, New_weapon.num_frames );
5247
5248 for (auto &wi : Weapon_info)
5249 {
5250 if (strlen(wi.hud_filename))
5251 {
5252 wi.hud_image_index = bm_load(wi.hud_filename);
5253 }
5254 }
5255 }
5256
hud_stuff_ship_name(char * ship_name_text,const ship * shipp)5257 void hud_stuff_ship_name(char *ship_name_text, const ship *shipp)
5258 {
5259 strcpy(ship_name_text, hud_get_ship_name(shipp).c_str());
5260 }
5261
hud_get_ship_name(const ship * shipp)5262 SCP_string hud_get_ship_name(const ship *shipp)
5263 {
5264 // print ship name
5265 if ( ((Iff_info[shipp->team].flags & IFFF_WING_NAME_HIDDEN) && (shipp->wingnum != -1)) || (shipp->flags[Ship::Ship_Flags::Hide_ship_name]) ) {
5266 return "";
5267 } else if (!Disable_built_in_translations) {
5268 // handle translation
5269 if (Lcl_gr) {
5270 char buf[128];
5271 strcpy(buf, shipp->get_display_name());
5272 lcl_translate_targetbox_name_gr(buf);
5273 return buf;
5274 } else if (Lcl_pl) {
5275 char buf[128];
5276 strcpy(buf, shipp->get_display_name());
5277 lcl_translate_targetbox_name_pl(buf);
5278 return buf;
5279 }
5280 }
5281
5282 return shipp->get_display_name();
5283 }
5284
hud_stuff_ship_callsign(char * ship_callsign_text,const ship * shipp)5285 void hud_stuff_ship_callsign(char *ship_callsign_text, const ship *shipp)
5286 {
5287 strcpy(ship_callsign_text, hud_get_ship_callsign(shipp).c_str());
5288 }
5289
5290 extern char Fred_callsigns[MAX_SHIPS][NAME_LENGTH+1];
hud_get_ship_callsign(const ship * shipp)5291 SCP_string hud_get_ship_callsign(const ship *shipp)
5292 {
5293 // handle multiplayer callsign
5294 if (Game_mode & GM_MULTIPLAYER) {
5295 // get a player num from the object, then get a callsign from the player structure.
5296 int pn = multi_find_player_by_object( &Objects[shipp->objnum] );
5297
5298 if (pn >= 0) {
5299 return Net_players[pn].m_player->short_callsign;
5300 }
5301 }
5302
5303 SCP_string ship_callsign_text;
5304
5305 // try to get callsign
5306 if (Fred_running) {
5307 ship_callsign_text = Fred_callsigns[shipp-Ships];
5308 } else {
5309 if (shipp->callsign_index >= 0) {
5310 ship_callsign_text = mission_parse_lookup_callsign_index(shipp->callsign_index);
5311 }
5312 }
5313
5314 if (!Disable_built_in_translations) {
5315 // handle translation
5316 if (Lcl_gr) {
5317 char buf[128];
5318 strcpy(buf, ship_callsign_text.c_str());
5319 lcl_translate_targetbox_name_gr(buf);
5320 return buf;
5321 } else if (Lcl_pl) {
5322 char buf[128];
5323 strcpy(buf, ship_callsign_text.c_str());
5324 lcl_translate_targetbox_name_pl(buf);
5325 return buf;
5326 }
5327 }
5328
5329 return ship_callsign_text;
5330 }
5331
hud_stuff_ship_class(char * ship_class_text,const ship * shipp)5332 void hud_stuff_ship_class(char *ship_class_text, const ship *shipp)
5333 {
5334 strcpy(ship_class_text, hud_get_ship_class(shipp).c_str());
5335 }
5336
5337 extern char Fred_alt_names[MAX_SHIPS][NAME_LENGTH + 1];
hud_get_ship_class(const ship * shipp)5338 SCP_string hud_get_ship_class(const ship *shipp)
5339 {
5340 SCP_string ship_class_text;
5341
5342 // try to get alt name
5343 if (Fred_running) {
5344 ship_class_text = Fred_alt_names[shipp-Ships];
5345
5346 if (ship_class_text.empty()) {
5347 ship_class_text = Ship_info[shipp->ship_info_index].get_display_name();
5348 }
5349 } else {
5350 if (shipp->alt_type_index >= 0) {
5351 ship_class_text = mission_parse_lookup_alt_index(shipp->alt_type_index);
5352 } else {
5353 ship_class_text = Ship_info[shipp->ship_info_index].get_display_name();
5354 }
5355 }
5356
5357 if (!Disable_built_in_translations) {
5358 // handle translation
5359 if (Lcl_gr) {
5360 char buf[128];
5361 strcpy(buf, ship_class_text.c_str());
5362 lcl_translate_targetbox_name_gr(buf);
5363 return buf;
5364 } else if (Lcl_pl) {
5365 char buf[128];
5366 strcpy(buf, ship_class_text.c_str());
5367 lcl_translate_targetbox_name_pl(buf);
5368 return buf;
5369 }
5370 }
5371
5372 return ship_class_text;
5373 }
5374
HudGaugeCmeasures()5375 HudGaugeCmeasures::HudGaugeCmeasures():
5376 HudGauge(HUD_OBJECT_CMEASURES, HUD_CMEASURE_GAUGE, false, false, VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY | VM_OTHER_SHIP, 255, 255, 255)
5377 {
5378 }
5379
initCountTextOffsets(int x,int y)5380 void HudGaugeCmeasures::initCountTextOffsets(int x, int y)
5381 {
5382 Cm_text_offsets[0] = x;
5383 Cm_text_offsets[1] = y;
5384 }
5385
initCountValueOffsets(int x,int y)5386 void HudGaugeCmeasures::initCountValueOffsets(int x, int y)
5387 {
5388 Cm_text_val_offsets[0] = x;
5389 Cm_text_val_offsets[1] = y;
5390 }
5391
initBitmaps(char * fname)5392 void HudGaugeCmeasures::initBitmaps(char *fname)
5393 {
5394 Cmeasure_gauge.first_frame = bm_load_animation(fname, &Cmeasure_gauge.num_frames);
5395 if ( Cmeasure_gauge.first_frame < 0 ) {
5396 Warning(LOCATION,"Cannot load hud ani: %s\n", fname);
5397 }
5398 }
5399
pageIn()5400 void HudGaugeCmeasures::pageIn()
5401 {
5402 bm_page_in_aabitmap(Cmeasure_gauge.first_frame, Cmeasure_gauge.num_frames);
5403 }
5404
render(float)5405 void HudGaugeCmeasures::render(float /*frametime*/)
5406 {
5407 if ( Cmeasure_gauge.first_frame == -1) {
5408 Int3(); // failed to load coutermeasure gauge background
5409 return;
5410 }
5411
5412 ship_info *sip = &Ship_info[Player_ship->ship_info_index];
5413 if(sip->cmeasure_max < 0 || sip->cmeasure_type < 0){
5414 return;
5415 }
5416
5417 // hud_set_default_color();
5418 setGaugeColor();
5419
5420 // blit the background
5421 renderBitmap(Cmeasure_gauge.first_frame, position[0], position[1]);
5422
5423 // blit text
5424 renderString(position[0] + Cm_text_offsets[0], position[1] + Cm_text_offsets[1], XSTR( "cm.", 327));
5425 if ( !Player_ship ) {
5426 Int3(); // player ship doesn't exist?
5427 return;
5428 }
5429 renderPrintf(position[0] + Cm_text_val_offsets[0], position[1] + Cm_text_val_offsets[1], NOX("%02d"), Player_ship->cmeasure_count);
5430 }
5431
HudGaugeAfterburner()5432 HudGaugeAfterburner::HudGaugeAfterburner():
5433 HudGauge(HUD_OBJECT_AFTERBURNER, HUD_AFTERBURNER_ENERGY, true, false, (VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY | VM_OTHER_SHIP), 255, 255, 255)
5434 {
5435 }
5436
initEnergyHeight(int h)5437 void HudGaugeAfterburner::initEnergyHeight(int h)
5438 {
5439 Energy_h = h;
5440 }
5441
initBitmaps(char * fname)5442 void HudGaugeAfterburner::initBitmaps(char *fname)
5443 {
5444 Energy_bar.first_frame = bm_load_animation(fname, &Energy_bar.num_frames);
5445
5446 if ( Energy_bar.first_frame < 0 ) {
5447 Warning(LOCATION,"Cannot load hud ani: %s\n", fname);
5448 }
5449 }
5450
5451 // Render the HUD afterburner energy gauge
render(float)5452 void HudGaugeAfterburner::render(float /*frametime*/)
5453 {
5454 float percent_left;
5455 int clip_h,w,h;
5456
5457 if ( Energy_bar.first_frame == -1 ){
5458 return;
5459 }
5460
5461 Assert(Player_ship);
5462 if ( !(Ship_info[Player_ship->ship_info_index].flags[Ship::Info_Flags::Afterburner]) ) {
5463 // Goober5000 - instead of drawing an empty burner gauge, don't draw the gauge at all
5464 return;
5465 } else {
5466 percent_left = Player_ship->afterburner_fuel/Ship_info[Player_ship->ship_info_index].afterburner_fuel_capacity;
5467 }
5468
5469 if ( percent_left > 1 ) {
5470 percent_left = 1.0f;
5471 }
5472
5473 clip_h = (int)std::lround((1.0f - percent_left) * Energy_h);
5474
5475 bm_get_info(Energy_bar.first_frame,&w,&h);
5476
5477 setGaugeColor();
5478
5479 if ( clip_h > 0) {
5480 renderBitmapEx(Energy_bar.first_frame, position[0], position[1],w,clip_h,0,0);
5481 }
5482
5483 if ( clip_h <= Energy_h ) {
5484 renderBitmapEx(Energy_bar.first_frame+1, position[0], position[1] + clip_h,w,h-clip_h,0,clip_h);
5485 }
5486 }
5487
pageIn()5488 void HudGaugeAfterburner::pageIn()
5489 {
5490 bm_page_in_aabitmap( Energy_bar.first_frame, Energy_bar.num_frames);
5491 }
5492
HudGaugeWeaponEnergy()5493 HudGaugeWeaponEnergy::HudGaugeWeaponEnergy():
5494 HudGauge(HUD_OBJECT_WEAPON_ENERGY, HUD_WEAPONS_ENERGY, true, false, (VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY | VM_OTHER_SHIP), 255, 255, 255)
5495 {
5496 }
5497
initTextOffsets(int x,int y)5498 void HudGaugeWeaponEnergy::initTextOffsets(int x, int y)
5499 {
5500 Wenergy_text_offsets[0] = x;
5501 Wenergy_text_offsets[1] = y;
5502 }
5503
initEnergyHeight(int h)5504 void HudGaugeWeaponEnergy::initEnergyHeight(int h)
5505 {
5506 Wenergy_h = h;
5507 }
5508
initAlignments(HudAlignment text_align,HudAlignment armed_align)5509 void HudGaugeWeaponEnergy::initAlignments(HudAlignment text_align, HudAlignment armed_align)
5510 {
5511 Text_alignment = text_align;
5512 Armed_alignment = armed_align;
5513 }
5514
initArmedOffsets(int x,int y,int h,bool show)5515 void HudGaugeWeaponEnergy::initArmedOffsets(int x, int y, int h, bool show)
5516 {
5517 Armed_name_offsets[0] = x;
5518 Armed_name_offsets[1] = y;
5519 Show_armed = show;
5520 Armed_name_h = h;
5521 }
5522
initAlwaysShowText(bool show_text)5523 void HudGaugeWeaponEnergy::initAlwaysShowText(bool show_text)
5524 {
5525 Always_show_text = show_text;
5526 }
5527
initMoveText(bool move_text)5528 void HudGaugeWeaponEnergy::initMoveText(bool move_text)
5529 {
5530 Moving_text = move_text;
5531 }
5532
initShowBallistics(bool show_ballistics)5533 void HudGaugeWeaponEnergy::initShowBallistics(bool show_ballistics)
5534 {
5535 Show_ballistic = show_ballistics;
5536 }
5537
initBitmaps(char * fname)5538 void HudGaugeWeaponEnergy::initBitmaps(char *fname)
5539 {
5540 Energy_bar.first_frame = bm_load_animation(fname, &Energy_bar.num_frames);
5541 if ( Energy_bar.first_frame < 0 ) {
5542 Warning(LOCATION,"Cannot load hud ani: %s\n", fname);
5543 } else {
5544 if (Energy_bar.num_frames != 4) {
5545 error_display(0, "The animation '%s' has %d frames but the energy bar requires 4 frames in the animation! Ignoring animation...",
5546 fname, Energy_bar.num_frames);
5547 // Free the bitmap slot and set the frame handle to -1 so that it's ignored when rendering this gauge
5548 bm_release(Energy_bar.first_frame);
5549
5550 Energy_bar.first_frame = -1;
5551 Energy_bar.num_frames = 0;
5552 }
5553 }
5554 }
5555
pageIn()5556 void HudGaugeWeaponEnergy::pageIn()
5557 {
5558 bm_page_in_aabitmap( Energy_bar.first_frame, Energy_bar.num_frames);
5559 }
5560
render(float)5561 void HudGaugeWeaponEnergy::render(float /*frametime*/)
5562 {
5563 int x;
5564 bool use_new_gauge = false;
5565
5566 // Goober5000 - only check for the new gauge in case of command line + a ballistic-capable ship
5567 if (Cmdline_ballistic_gauge)
5568 {
5569 for (x = 0; x < Player_ship->weapons.num_primary_banks; ++x)
5570 {
5571 if (Weapon_info[Player_ship->weapons.primary_bank_weapons[x]].wi_flags[Weapon::Info_Flags::Ballistic])
5572 {
5573 use_new_gauge = true;
5574 break;
5575 }
5576 }
5577 }
5578
5579 if(use_new_gauge)
5580 {
5581 int currentx, currenty, line_height;
5582 int y;
5583 int max_w = 100;
5584 float remaining;
5585 currentx = position[0] + 10;
5586 currenty = position[1];
5587 line_height = gr_get_font_height() + 1;
5588 if(gr_screen.max_w_unscaled == 640) {
5589 max_w = 60;
5590 }
5591
5592 //*****ENERGY GAUGE
5593 if(Weapon_info[Player_ship->weapons.current_primary_bank].energy_consumed > 0.0f)
5594 setGaugeColor(HUD_C_BRIGHT);
5595
5596 //Draw name
5597 renderString(currentx, currenty, XSTR("Energy", 1616));
5598 currenty += line_height;
5599
5600 //Draw background
5601 setGaugeColor(HUD_C_DIM);
5602 renderRect(currentx, currenty, max_w, 10);
5603
5604 //Draw gauge bar
5605 setGaugeColor(HUD_C_NORMAL);
5606 remaining = max_w * ((float)Player_ship->weapon_energy/(float)Ship_info[Player_ship->ship_info_index].max_weapon_reserve);
5607 if(remaining > 0)
5608 {
5609 setGaugeColor(HUD_C_BRIGHT);
5610 for(y = 0; y < 10; y++) {
5611 renderGradientLine(currentx, currenty + y, currentx + fl2i(remaining), currenty + y);
5612 }
5613 }
5614 currenty += 12;
5615
5616 char shortened_name[NAME_LENGTH];
5617 //*****BALLISTIC GAUGES
5618 for(x = 0; x < Player_ship->weapons.num_primary_banks; x++)
5619 {
5620 //Skip all pure-energy weapons
5621 if(!(Weapon_info[Player_ship->weapons.primary_bank_weapons[x]].wi_flags[Weapon::Info_Flags::Ballistic]))
5622 continue;
5623
5624 //Draw the weapon bright or normal depending if it's active or not.
5625 if(x == Player_ship->weapons.current_primary_bank || (Player_ship->flags[Ship::Ship_Flags::Primary_linked])) {
5626 setGaugeColor(HUD_C_BRIGHT);
5627 } else {
5628 setGaugeColor(HUD_C_NORMAL);
5629 }
5630 if(gr_screen.max_w_unscaled == 640) {
5631 strcpy_s(shortened_name, Weapon_info[Player_ship->weapons.primary_bank_weapons[x]].get_display_name());
5632 font::force_fit_string(shortened_name, NAME_LENGTH, 55);
5633 renderString(currentx, currenty, shortened_name);
5634 } else {
5635 renderString(currentx, currenty, Weapon_info[Player_ship->weapons.primary_bank_weapons[x]].get_display_name());
5636 }
5637
5638 //Next 'line'
5639 currenty += line_height;
5640
5641 //Draw the background for the gauge
5642 setGaugeColor(HUD_C_DIM);
5643 renderRect(currentx, currenty, max_w, 10);
5644
5645 //Reset to normal brightness
5646 setGaugeColor(HUD_C_NORMAL);
5647
5648 //Draw the bar graph
5649 remaining = (max_w - 4) * ((float) Player_ship->weapons.primary_bank_ammo[x] / (float) Player_ship->weapons.primary_bank_start_ammo[x]);
5650 if(remaining > 0) {
5651 renderRect(currentx + 2, currenty + 2, fl2i(remaining), 6);
5652 }
5653 //Increment for next 'line'
5654 currenty += 12;
5655 }
5656 }
5657 else
5658 {
5659 float percent_left;
5660 int ballistic_ammo = 0;
5661 int max_ballistic_ammo = 0;
5662 int clip_h, w, h, i;
5663 weapon_info *wip;
5664 ship_weapon *sw;
5665
5666 if ( Energy_bar.first_frame == -1 ) {
5667 return;
5668 }
5669
5670 if ( Player_ship->weapons.num_primary_banks <= 0 ) {
5671 return;
5672 }
5673
5674 sw = &Player_ship->weapons;
5675
5676 // show ballistic ammunition in energy gauge if need be
5677 if ( Show_ballistic ) {
5678 if ( Player_ship->flags[Ship::Ship_Flags::Primary_linked] ) {
5679
5680 // go through all ballistic primaries and add up their ammunition totals and max capacities
5681 for ( i = 0; i < sw->num_primary_banks; i++ ) {
5682
5683 // skip all pure-energy weapons
5684 if( ! ( Weapon_info[sw->primary_bank_weapons[i]].wi_flags[Weapon::Info_Flags::Ballistic] ) ) {
5685 continue;
5686 }
5687
5688 ballistic_ammo += sw->primary_bank_ammo[i];
5689 max_ballistic_ammo += sw->primary_bank_start_ammo[i];
5690 }
5691 } else {
5692 ballistic_ammo = sw->primary_bank_ammo[sw->current_primary_bank];
5693 max_ballistic_ammo = sw->primary_bank_start_ammo[sw->current_primary_bank];
5694 }
5695
5696 if (max_ballistic_ammo > 0) {
5697 percent_left = i2fl(ballistic_ammo) / i2fl(max_ballistic_ammo);
5698 } else {
5699 percent_left = 1.0f;
5700 }
5701 } else {
5702 // also leave if no energy can be stored for weapons - Goober5000
5703 if (!ship_has_energy_weapons(Player_ship))
5704 return;
5705
5706 percent_left = Player_ship->weapon_energy/Ship_info[Player_ship->ship_info_index].max_weapon_reserve;
5707 if ( percent_left > 1 )
5708 {
5709 percent_left = 1.0f;
5710 }
5711 }
5712
5713 clip_h = (int)std::lround((1.0f - percent_left) * Wenergy_h);
5714
5715 if ( percent_left <= 0.3 || Show_ballistic || Always_show_text ) {
5716 char buf[10];
5717 int delta_y = 0, delta_x = 0;
5718
5719 if ( percent_left < 0.1 ) {
5720 gr_set_color_fast(&Color_bright_red);
5721 } else {
5722 setGaugeColor();
5723 }
5724
5725 if ( Show_ballistic ) {
5726 sprintf(buf, "%d", ballistic_ammo);
5727 } else {
5728 sprintf(buf, XSTR( "%d%%", 326), (int)std::lround(percent_left*100));
5729 }
5730
5731 if ( Moving_text ) {
5732 delta_y = clip_h;
5733 }
5734
5735 hud_num_make_mono(buf, font_num);
5736
5737 if ( Text_alignment == HudAlignment::RIGHT ) {
5738 gr_get_string_size(&w, &h, buf);
5739 delta_x = -w;
5740 }
5741
5742 renderString(position[0] + Wenergy_text_offsets[0] + delta_x, position[1] + Wenergy_text_offsets[1] + delta_y, buf);
5743 }
5744
5745 setGaugeColor();
5746
5747 // list currently armed primary banks if we have to
5748 if ( Show_armed ) {
5749 if ( Player_ship->flags[Ship::Ship_Flags::Primary_linked] ) {
5750 // show all primary banks
5751 for ( i = 0; i < Player_ship->weapons.num_primary_banks; i++ ) {
5752 wip = &Weapon_info[sw->primary_bank_weapons[i]];
5753 auto weapon_name = wip->get_display_name();
5754
5755 if ( Armed_alignment == HudAlignment::RIGHT ) {
5756 gr_get_string_size(&w, &h, weapon_name);
5757 } else {
5758 w = 0;
5759 }
5760
5761 renderString(position[0] + Armed_name_offsets[0] - w, position[1] + Armed_name_offsets[1] + Armed_name_h * i, weapon_name);
5762 }
5763 } else {
5764 // just show the current armed bank
5765 i = Player_ship->weapons.current_primary_bank;
5766 wip = &Weapon_info[sw->primary_bank_weapons[i]];
5767 auto weapon_name = wip->get_display_name();
5768
5769 if ( Armed_alignment == HudAlignment::RIGHT ) {
5770 gr_get_string_size(&w, &h, weapon_name);
5771 } else {
5772 w = 0;
5773 }
5774
5775 renderString(position[0] + Armed_name_offsets[0] - w, position[1] + Armed_name_offsets[1], weapon_name);
5776 }
5777 }
5778
5779 for ( i = 0; i < sw->num_primary_banks; i++ )
5780 {
5781 if ( !timestamp_elapsed(Weapon_flash_info.flash_duration[i]) )
5782 {
5783 if ( Weapon_flash_info.is_bright & (1<<i) )
5784 {
5785 // hud_set_bright_color();
5786 setGaugeColor(HUD_C_BRIGHT);
5787 break;
5788 }
5789 }
5790 }
5791
5792 bm_get_info(Energy_bar.first_frame+2,&w,&h);
5793
5794 if ( clip_h > 0 ) {
5795 renderBitmapEx(Energy_bar.first_frame+2, position[0], position[1], w,clip_h,0,0);
5796 }
5797
5798 if ( clip_h <= Wenergy_h ) {
5799 renderBitmapEx(Energy_bar.first_frame+3, position[0], position[1] + clip_h, w,h-clip_h,0,clip_h);
5800 }
5801
5802 // hud_set_default_color();
5803 }
5804 }
5805
HudGaugeWeapons()5806 HudGaugeWeapons::HudGaugeWeapons():
5807 HudGauge(HUD_OBJECT_WEAPONS, HUD_WEAPONS_GAUGE, false, false, VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY | VM_OTHER_SHIP, 255, 255, 255)
5808 {
5809 }
5810
initLinkIcon()5811 void HudGaugeWeapons::initLinkIcon()
5812 {
5813 ubyte sc = lcl_get_font_index(font_num);
5814 // default to a '>' if the font has no special chars
5815 // seems about the closest normal char to the triangle
5816 if (sc == 0) {
5817 Weapon_link_icon = ubyte ('>');
5818 } else {
5819 Weapon_link_icon = sc + 2;
5820 }
5821 }
5822
initTopOffsetX(int x,int x_b)5823 void HudGaugeWeapons::initTopOffsetX(int x, int x_b)
5824 {
5825 top_offset_x[0] = x;
5826 top_offset_x[1] = x_b;
5827 }
5828
initHeaderOffsets(int x,int y,int x_b,int y_b)5829 void HudGaugeWeapons::initHeaderOffsets(int x, int y, int x_b, int y_b)
5830 {
5831 Weapon_header_offsets[0][0] = x;
5832 Weapon_header_offsets[0][1] = y;
5833 Weapon_header_offsets[1][0] = x_b;
5834 Weapon_header_offsets[1][1] = y_b;
5835 }
5836
initFrameOffsetX(int x,int x_b)5837 void HudGaugeWeapons::initFrameOffsetX(int x, int x_b)
5838 {
5839 frame_offset_x[0] = x;
5840 frame_offset_x[1] = x_b;
5841 }
5842
initPrimaryWeaponOffsets(int link_x,int name_x,int ammo_x)5843 void HudGaugeWeapons::initPrimaryWeaponOffsets(int link_x, int name_x, int ammo_x)
5844 {
5845 Weapon_plink_offset_x = link_x;
5846 Weapon_pname_offset_x = name_x;
5847 Weapon_pammo_offset_x = ammo_x;
5848 }
5849
initSecondaryWeaponOffsets(int ammo_x,int name_x,int reload_x,int linked_x,int unlinked_x)5850 void HudGaugeWeapons::initSecondaryWeaponOffsets(int ammo_x, int name_x, int reload_x, int linked_x, int unlinked_x)
5851 {
5852 Weapon_sammo_offset_x = ammo_x;
5853 Weapon_sname_offset_x = name_x;
5854 Weapon_sreload_offset_x = reload_x;
5855 Weapon_slinked_offset_x = linked_x;
5856 Weapon_sunlinked_offset_x = unlinked_x;
5857 }
5858
initStartNameOffsetsY(int p_y,int s_y)5859 void HudGaugeWeapons::initStartNameOffsetsY(int p_y, int s_y)
5860 {
5861 pname_start_offset_y = p_y;
5862 sname_start_offset_y = s_y;
5863 }
5864
initPrimaryHeights(int top_h,int text_h)5865 void HudGaugeWeapons::initPrimaryHeights(int top_h, int text_h)
5866 {
5867 top_primary_h = top_h;
5868 primary_text_h = text_h;
5869 }
5870
initSecondaryHeights(int top_h,int text_h)5871 void HudGaugeWeapons::initSecondaryHeights(int top_h, int text_h)
5872 {
5873 top_secondary_h = top_h;
5874 secondary_text_h = text_h;
5875 }
5876
initBitmapsPrimaryTop(char * fname,char * fname_ballistic)5877 void HudGaugeWeapons::initBitmapsPrimaryTop(char *fname, char *fname_ballistic)
5878 {
5879 // load the graphics for the top portion of the weapons gauge
5880 primary_top[0].first_frame = bm_load_animation(fname, &primary_top[0].num_frames);
5881 if(primary_top[0].first_frame < 0) {
5882 Warning(LOCATION,"Cannot load hud ani: %s\n", fname);
5883 }
5884
5885 primary_top[1].first_frame = bm_load_animation(fname_ballistic, &primary_top[1].num_frames);
5886 if(primary_top[1].first_frame < 0) {
5887 primary_top[1].first_frame = primary_top[0].first_frame;
5888 primary_top[1].num_frames = primary_top[0].num_frames;
5889 }
5890 }
5891
initBitmapsPrimaryMiddle(char * fname,char * fname_ballistic)5892 void HudGaugeWeapons::initBitmapsPrimaryMiddle(char *fname, char *fname_ballistic)
5893 {
5894 // load the graphics for the middle portion of the primary weapons listing
5895 primary_middle[0].first_frame = bm_load_animation(fname, &primary_middle[0].num_frames);
5896 if(primary_middle[0].first_frame < 0) {
5897 Warning(LOCATION,"Cannot load hud ani: %s\n", fname);
5898 }
5899
5900 primary_middle[1].first_frame = bm_load_animation(fname_ballistic, &primary_middle[1].num_frames);
5901 if(primary_middle[1].first_frame < 0) {
5902 primary_middle[1].first_frame = primary_middle[0].first_frame;
5903 primary_middle[1].num_frames = primary_middle[0].num_frames;
5904 }
5905 }
5906
initBitmapsPrimaryLast(char * fname,char * fname_ballistic)5907 void HudGaugeWeapons::initBitmapsPrimaryLast(char *fname, char *fname_ballistic)
5908 {
5909 // load the graphics for the bottom portion of the primary weapons listing if there is one.
5910 // Don't bother the user if there isn't one since retail doesn't use this.
5911 primary_last[0].first_frame = bm_load_animation(fname, &primary_last[0].num_frames);
5912
5913 primary_last[1].first_frame = bm_load_animation(fname_ballistic, &primary_last[1].num_frames);
5914 if(primary_last[1].first_frame < 0) {
5915 primary_last[1].first_frame = primary_last[0].first_frame;
5916 primary_last[1].num_frames = primary_last[0].num_frames;
5917 }
5918 }
5919
initBitmapsSecondaryTop(char * fname,char * fname_ballistic)5920 void HudGaugeWeapons::initBitmapsSecondaryTop(char *fname, char *fname_ballistic)
5921 {
5922 // top portion of the secondary weapons gauge
5923 secondary_top[0].first_frame = bm_load_animation(fname, &secondary_top[0].num_frames);
5924 if(secondary_top[0].first_frame < 0) {
5925 Warning(LOCATION,"Cannot load hud ani: %s\n", fname);
5926 }
5927
5928 secondary_top[1].first_frame = bm_load_animation(fname_ballistic, &secondary_top[1].num_frames);
5929 if(secondary_top[1].first_frame < 0) {
5930 secondary_top[1].first_frame = secondary_top[0].first_frame;
5931 secondary_top[1].num_frames = secondary_top[0].num_frames;
5932 }
5933 }
5934
initBitmapsSecondaryMiddle(char * fname,char * fname_ballistic)5935 void HudGaugeWeapons::initBitmapsSecondaryMiddle(char *fname, char *fname_ballistic)
5936 {
5937 // middle portion of the secondary weapons gauge
5938 secondary_middle[0].first_frame = bm_load_animation(fname, &secondary_middle[0].num_frames);
5939 if(secondary_middle[0].first_frame < 0) {
5940 Warning(LOCATION,"Cannot load hud ani: %s\n", fname);
5941 }
5942
5943 secondary_middle[1].first_frame = bm_load_animation(fname_ballistic, &secondary_middle[1].num_frames);
5944 if(secondary_middle[1].first_frame < 0) {
5945 secondary_middle[1].first_frame = secondary_middle[0].first_frame;
5946 secondary_middle[1].num_frames = secondary_middle[0].num_frames;
5947 }
5948 }
5949
initBitmapsSecondaryBottom(char * fname,char * fname_ballistic)5950 void HudGaugeWeapons::initBitmapsSecondaryBottom(char *fname, char *fname_ballistic)
5951 {
5952 // bottom portion of the entire weapons gauge
5953 secondary_bottom[0].first_frame = bm_load_animation(fname, &secondary_bottom[0].num_frames);
5954 if(secondary_bottom[0].first_frame < 0) {
5955 Warning(LOCATION,"Cannot load hud ani: %s\n", fname);
5956 }
5957
5958 secondary_bottom[1].first_frame = bm_load_animation(fname_ballistic, &secondary_bottom[1].num_frames);
5959 if(secondary_bottom[1].first_frame < 0) {
5960 secondary_bottom[1].first_frame = secondary_bottom[0].first_frame;
5961 secondary_bottom[1].num_frames = secondary_bottom[0].num_frames;
5962 }
5963 }
5964
pageIn()5965 void HudGaugeWeapons::pageIn()
5966 {
5967 if(primary_top[0].first_frame > -1) {
5968 bm_page_in_aabitmap(primary_top[0].first_frame,primary_top[0].num_frames);
5969 }
5970
5971 if(primary_top[1].first_frame > -1) {
5972 bm_page_in_aabitmap(primary_top[1].first_frame,primary_top[1].num_frames);
5973 }
5974
5975 if(primary_middle[0].first_frame > -1) {
5976 bm_page_in_aabitmap(primary_middle[0].first_frame,primary_middle[0].num_frames);
5977 }
5978
5979 if(primary_middle[1].first_frame > -1) {
5980 bm_page_in_aabitmap(primary_middle[1].first_frame, primary_middle[1].num_frames);
5981 }
5982
5983 if(primary_last[1].first_frame > -1) {
5984 bm_page_in_aabitmap(primary_last[1].first_frame, primary_last[1].num_frames);
5985 }
5986
5987 if(secondary_top[0].first_frame > -1) {
5988 bm_page_in_aabitmap(secondary_top[0].first_frame, secondary_top[0].num_frames);
5989 }
5990
5991 if(secondary_top[1].first_frame > -1) {
5992 bm_page_in_aabitmap(secondary_top[1].first_frame, secondary_top[1].num_frames);
5993 }
5994
5995 if(secondary_middle[0].first_frame > -1) {
5996 bm_page_in_aabitmap(secondary_middle[0].first_frame, secondary_middle[0].num_frames);
5997 }
5998
5999 if(secondary_middle[1].first_frame >-1) {
6000 bm_page_in_aabitmap(secondary_middle[0].first_frame, secondary_middle[0].num_frames);
6001 }
6002
6003 if(secondary_bottom[0].first_frame > -1) {
6004 bm_page_in_aabitmap(secondary_bottom[0].first_frame, secondary_bottom[0].num_frames);
6005 }
6006
6007 if(secondary_bottom[1].first_frame > -1) {
6008 bm_page_in_aabitmap(secondary_bottom[0].first_frame, secondary_bottom[0].num_frames);
6009 }
6010 }
6011
render(float)6012 void HudGaugeWeapons::render(float /*frametime*/)
6013 {
6014 ship_weapon *sw;
6015 int np, ns; // np == num primary, ns == num secondary
6016
6017 if(Player_obj->type == OBJ_OBSERVER)
6018 return;
6019
6020 Assert(Player_obj->type == OBJ_SHIP);
6021 Assert(Player_obj->instance >= 0 && Player_obj->instance < MAX_SHIPS);
6022
6023 sw = &Ships[Player_obj->instance].weapons;
6024 np = sw->num_primary_banks;
6025 ns = sw->num_secondary_banks;
6026
6027 setGaugeColor();
6028
6029 // draw top of primary display
6030 renderBitmap(primary_top[ballistic_hud_index].first_frame, position[0] + top_offset_x[ballistic_hud_index], position[1]);
6031
6032 // render the header of this gauge
6033 renderString(position[0] + Weapon_header_offsets[ballistic_hud_index][0], position[1] + Weapon_header_offsets[ballistic_hud_index][1], EG_WEAPON_TITLE, XSTR( "weapons", 328));
6034
6035 const char *weapon_name;
6036 char ammo_str[32];
6037 int i, w, h;
6038 int y = position[1] + top_primary_h;
6039 int name_y = position[1] + pname_start_offset_y;
6040
6041 // render primaries
6042 for(i = 0; i < np; i++) {
6043 setGaugeColor();
6044
6045 // choose which background to draw for additional primaries.
6046 // Note, we don't draw a background for the first primary.
6047 // It is assumed that the top primary wep frame already has this rendered.
6048 if(i == 1) {
6049 // used to draw the second primary weapon background
6050 renderBitmap(primary_middle[ballistic_hud_index].first_frame, position[0] + frame_offset_x[ballistic_hud_index], y);
6051 } else if(i != 0) {
6052 // used to draw the the third, fourth, fifth, etc...
6053 if(primary_last[ballistic_hud_index].first_frame != -1)
6054 renderBitmap(primary_last[ballistic_hud_index].first_frame, position[0] + frame_offset_x[ballistic_hud_index], y);
6055 }
6056
6057 weapon_name = Weapon_info[sw->primary_bank_weapons[i]].get_display_name();
6058
6059 // maybe modify name here to fit
6060
6061 // do we need to flash the text?
6062 if (HudGauge::maybeFlashSexp() == i ) {
6063 setGaugeColor(HUD_C_BRIGHT);
6064 } else {
6065 maybeFlashWeapon(i);
6066 }
6067
6068 // indicate if this is linked or currently armed
6069 if ( ((sw->current_primary_bank == i) && !(Player_ship->flags[Ship::Ship_Flags::Primary_linked])) || ((Player_ship->flags[Ship::Ship_Flags::Primary_linked]) && !(Weapon_info[sw->primary_bank_weapons[i]].wi_flags[Weapon::Info_Flags::Nolink]))) {
6070 renderPrintf(position[0] + Weapon_plink_offset_x, name_y, EG_NULL, "%c", Weapon_link_icon);
6071 }
6072
6073 // either render this primary's image or its name
6074 if(Weapon_info[sw->primary_bank_weapons[0]].hud_image_index != -1) {
6075 renderBitmap(Weapon_info[sw->primary_bank_weapons[i]].hud_image_index, position[0] + Weapon_pname_offset_x, name_y);
6076 } else {
6077 renderPrintf(position[0] + Weapon_pname_offset_x, name_y, EG_WEAPON_P2, "%s", weapon_name);
6078 }
6079
6080 // if this is a ballistic primary with ammo, render the ammo count
6081 if (Weapon_info[sw->primary_bank_weapons[i]].wi_flags[Weapon::Info_Flags::Ballistic]) {
6082 // print out the ammo right justified
6083 sprintf(ammo_str, "%d", sw->primary_bank_ammo[i]);
6084
6085 hud_num_make_mono(ammo_str, font_num);
6086 gr_get_string_size(&w, &h, ammo_str);
6087
6088 renderString(position[0] + Weapon_pammo_offset_x - w, name_y, EG_NULL, ammo_str);
6089 }
6090
6091 if(i != 0) {
6092 y += primary_text_h;
6093 }
6094 name_y += primary_text_h;
6095 }
6096
6097 weapon_info *wip;
6098
6099 if ( HudGauge::maybeFlashSexp() == i ) {
6100 setGaugeColor(HUD_C_BRIGHT);
6101 }
6102
6103 renderBitmap(secondary_top[ballistic_hud_index].first_frame, position[0] + frame_offset_x[ballistic_hud_index], y);
6104 name_y = y + sname_start_offset_y;
6105 y += top_secondary_h;
6106
6107 for(i = 0; i < ns; i++)
6108 {
6109 setGaugeColor();
6110 wip = &Weapon_info[sw->secondary_bank_weapons[i]];
6111
6112 if(i!=0) {
6113 renderBitmap(secondary_middle[ballistic_hud_index].first_frame, position[0] + frame_offset_x[ballistic_hud_index], y);
6114 }
6115
6116 maybeFlashWeapon(np+i);
6117
6118 if (wip->has_display_name()) {
6119 // Do not apply the cluster bomb hack if we have an alternate name to make translating that name possible
6120 weapon_name = wip->get_display_name();
6121 } else {
6122 // HACK - make Cluster Bomb fit on the HUD.
6123 if(!stricmp(wip->name,"cluster bomb")){
6124 weapon_name = NOX("Cluster");
6125 } else {
6126 weapon_name = wip->get_display_name();
6127 }
6128 }
6129
6130 if ( sw->current_secondary_bank == i ) {
6131 // show that this is the current secondary armed
6132 renderPrintf(position[0] + Weapon_sunlinked_offset_x, name_y, EG_NULL, "%c", Weapon_link_icon);
6133
6134 // indicate if this is linked
6135 // don't draw the link indicator if the fire can't be fired link.
6136 // the link flag is ignored rather than cleared so the player can cycle past a no-doublefire weapon without the setting being cleared
6137 if ( Player_ship->flags[Ship::Ship_Flags::Secondary_dual_fire] && !wip->wi_flags[Weapon::Info_Flags::No_doublefire] &&
6138 !The_mission.ai_profile->flags[AI::Profile_Flags::Disable_player_secondary_doublefire] ) {
6139 renderPrintf(position[0] + Weapon_slinked_offset_x, name_y, EG_NULL, "%c", Weapon_link_icon);
6140 }
6141
6142 // show secondary weapon's image or print its name
6143 if(wip->hud_image_index != -1) {
6144 renderBitmap(wip->hud_image_index, position[0] + Weapon_sname_offset_x, name_y);
6145 } else {
6146 renderString(position[0] + Weapon_sname_offset_x, name_y, i ? EG_WEAPON_S1 : EG_WEAPON_S2, weapon_name);
6147 }
6148
6149 // show the cooldown time
6150 if ( (sw->secondary_bank_ammo[i] > 0) && (sw->current_secondary_bank >= 0) ) {
6151 int ms_till_fire = timestamp_until(sw->next_secondary_fire_stamp[sw->current_secondary_bank]);
6152 if ( (ms_till_fire >= 500) && ((wip->fire_wait >= 1 ) || (ms_till_fire > wip->fire_wait*1000)) ) {
6153 renderPrintf(position[0] + Weapon_sreload_offset_x, name_y, EG_NULL, "%d", (int)std::lround(ms_till_fire/1000.0f));
6154 }
6155 }
6156 } else {
6157 // just print the weapon's name since this isn't armed
6158 renderString(position[0] + Weapon_sname_offset_x, name_y, i ? EG_WEAPON_S1 : EG_WEAPON_S2, weapon_name);
6159 }
6160
6161 int ammo=sw->secondary_bank_ammo[i];
6162
6163 // print out the ammo right justified
6164 sprintf(ammo_str, "%d", ammo);
6165 hud_num_make_mono(ammo_str, font_num);
6166 gr_get_string_size(&w, &h, ammo_str);
6167
6168 renderString(position[0] + Weapon_sammo_offset_x - w, name_y, EG_NULL, ammo_str);
6169
6170 if(i != 0) {
6171 y += secondary_text_h;
6172 }
6173 name_y += secondary_text_h;
6174 }
6175
6176 // a bit lonely here with no secondaries so just print "<none>"
6177 if(ns==0)
6178 {
6179 renderString(position[0] + Weapon_pname_offset_x, name_y, EG_WEAPON_S1, XSTR( "<none>", 329));
6180 }
6181
6182 y -= 0;
6183 // finish drawing the background
6184 renderBitmap(secondary_bottom[ballistic_hud_index].first_frame, position[0] + frame_offset_x[ballistic_hud_index], y);
6185 }
6186
hud_update_weapon_flash()6187 void hud_update_weapon_flash()
6188 {
6189 ship_weapon *sw;
6190 int num_weapons;
6191
6192 sw = &Ships[Player_obj->instance].weapons;
6193 num_weapons = sw->num_primary_banks + sw->num_secondary_banks;
6194
6195 if ( num_weapons > MAX_WEAPON_FLASH_LINES ) {
6196 Int3(); // Get Alan
6197 return;
6198 }
6199
6200 for(int i = 0; i < num_weapons; i++) {
6201 if ( !timestamp_elapsed(Weapon_flash_info.flash_duration[i]) ) {
6202 if ( timestamp_elapsed(Weapon_flash_info.flash_next[i]) ) {
6203 Weapon_flash_info.flash_next[i] = timestamp(TBOX_FLASH_INTERVAL);
6204 Weapon_flash_info.is_bright ^= (1<<i);
6205 }
6206 }
6207 }
6208 }
6209
maybeFlashWeapon(int index)6210 void HudGaugeWeapons::maybeFlashWeapon(int index)
6211 {
6212 if ( index >= MAX_WEAPON_FLASH_LINES ) {
6213 Int3(); // Get Alan
6214 return;
6215 }
6216
6217 // hud_set_default_color();
6218 setGaugeColor();
6219 if ( !timestamp_elapsed(Weapon_flash_info.flash_duration[index]) ) {
6220 if ( Weapon_flash_info.is_bright & (1<<index) ) {
6221 setGaugeColor(HUD_C_BRIGHT);
6222 // hud_set_bright_color();
6223 } else {
6224 setGaugeColor(HUD_C_DIM);
6225 // hud_set_dim_color();
6226 }
6227 }
6228 }
6229
hud_target_add_display_list(object * objp,const vertex * target_point,const vec3d * target_pos,int correction,const color * bracket_clr,const char * name,int flags)6230 void hud_target_add_display_list(object *objp, const vertex *target_point, const vec3d *target_pos, int correction, const color *bracket_clr, const char* name, int flags)
6231 {
6232 target_display_info element;
6233
6234 element.objp = objp;
6235 element.target_point = *target_point;
6236 element.target_pos = *target_pos;
6237 element.correction = correction;
6238 element.flags = flags;
6239
6240 if(bracket_clr) {
6241 element.bracket_clr = *bracket_clr;
6242 } else {
6243 // no color given, so this will tell the target display gauges to use IFF colors.
6244 gr_init_color(&element.bracket_clr, 0, 0, 0);
6245 }
6246
6247 if(name) {
6248 strcpy_s(element.name, name);
6249 } else {
6250 strcpy_s(element.name, "");
6251 }
6252
6253 target_display_list.push_back(element);
6254 }
6255
hud_target_clear_display_list()6256 void hud_target_clear_display_list()
6257 {
6258 target_display_list.clear();
6259 }
6260
HudGaugeOffscreen()6261 HudGaugeOffscreen::HudGaugeOffscreen():
6262 HudGauge(HUD_OBJECT_OFFSCREEN, HUD_OFFSCREEN_INDICATOR, false, true, VM_DEAD_VIEW | VM_OTHER_SHIP, 255, 255, 255)
6263 {
6264 }
6265
initMaxTriSeperation(float length)6266 void HudGaugeOffscreen::initMaxTriSeperation(float length)
6267 {
6268 Max_offscreen_tri_seperation = length;
6269 }
6270
initMaxFrontSeperation(float length)6271 void HudGaugeOffscreen::initMaxFrontSeperation(float length)
6272 {
6273 Max_front_seperation = length;
6274 }
6275
initTriBase(float length)6276 void HudGaugeOffscreen::initTriBase(float length)
6277 {
6278 Offscreen_tri_base = length;
6279 }
6280
initTriHeight(float length)6281 void HudGaugeOffscreen::initTriHeight(float length)
6282 {
6283 Offscreen_tri_height = length;
6284 }
6285
pageIn()6286 void HudGaugeOffscreen::pageIn()
6287 {
6288 }
6289
render(float)6290 void HudGaugeOffscreen::render(float /*frametime*/)
6291 {
6292 // don't show offscreen indicator if we're warping out.
6293 if ( Player->control_mode != PCM_NORMAL ) {
6294 return;
6295 }
6296
6297 for(size_t i = 0; i < target_display_list.size(); i++) {
6298 if(target_display_list[i].target_point.codes != 0) {
6299 float dist = 0.0f;
6300 vec2d coords;
6301 float half_triangle_sep;
6302 int dir;
6303
6304 if(target_display_list[i].objp) {
6305 dist = hud_find_target_distance( target_display_list[i].objp, Player_obj );
6306 } else {
6307 // if we don't have a corresponding object, use given position to figure out distance
6308 dist = vm_vec_dist_quick(&Player_obj->pos, &target_display_list[i].target_pos);
6309 }
6310
6311 if( target_display_list[i].bracket_clr.red && target_display_list[i].bracket_clr.green &&
6312 target_display_list[i].bracket_clr.blue ) {
6313 gr_set_color_fast(&target_display_list[i].bracket_clr);
6314 } else {
6315 // use IFF colors if none defined.
6316 if(target_display_list[i].objp) {
6317 hud_set_iff_color(target_display_list[i].objp, 1);
6318 } else {
6319 // no object so this must mean it's a nav point. but for some odd reason someone forgot to include a color.
6320 gr_set_color_fast(&target_display_list[i].bracket_clr);
6321 }
6322 }
6323
6324 calculatePosition(&target_display_list[i].target_point, &target_display_list[i].target_pos, &coords, &dir, &half_triangle_sep);
6325 renderOffscreenIndicator(&coords, dir, dist, half_triangle_sep, true);
6326 }
6327 }
6328 }
6329
calculatePosition(vertex * target_point,vec3d * tpos,vec2d * outcoords,int * dir,float * half_triangle_sep)6330 void HudGaugeOffscreen::calculatePosition(vertex* target_point, vec3d *tpos, vec2d *outcoords, int *dir, float *half_triangle_sep)
6331 {
6332 float xpos,ypos;
6333 vec3d targ_to_player;
6334 float dist_behind;
6335 float triangle_sep;
6336 float half_gauge_length;
6337
6338 bool in_frame = g3_in_frame() > 0;
6339 if(!in_frame)
6340 g3_start_frame(0);
6341 gr_set_screen_scale(base_w, base_h);
6342
6343 // calculate the dot product between the players forward vector and the vector connecting
6344 // the player to the target. Normalize targ_to_player since we want the dot product
6345 // to range between 0 -> 1.
6346 vm_vec_sub(&targ_to_player, &Player_obj->pos, tpos);
6347 vm_vec_normalize(&targ_to_player);
6348 dist_behind = vm_vec_dot(&Player_obj->orient.vec.fvec, &targ_to_player);
6349
6350 if (dist_behind < 0) { // still in front of player, but not in view
6351 dist_behind = dist_behind + 1.0f;
6352 if (dist_behind > 0.2 ){
6353 triangle_sep = ( dist_behind ) * Max_front_seperation;
6354 } else {
6355 triangle_sep = 0.0f;
6356 }
6357 } else {
6358 triangle_sep = dist_behind * Max_offscreen_tri_seperation + Max_offscreen_tri_seperation;
6359 }
6360
6361 if ( triangle_sep > Max_offscreen_tri_seperation + Max_front_seperation){
6362 triangle_sep = Max_offscreen_tri_seperation + Max_front_seperation;
6363 }
6364
6365 // calculate these values only once, since it will be used in several places
6366 *half_triangle_sep = 0.5f * triangle_sep;
6367 half_gauge_length = *half_triangle_sep + Offscreen_tri_base;
6368
6369 // We need to find the screen (x,y) for where to draw the offscreen indicator
6370 //
6371 // The best way I've found is to draw a line from the eye_pos to the target, and
6372 // then use clip_line() to find the screen (x,y) for where the line hits the edge
6373 // of the screen.
6374 //
6375 // The weird thing about clip_line() is that is flips around the two verticies,
6376 // so I use eye_vertex->sx and eye_vertex->sy for the off-screen indicator (x,y)
6377 //
6378 vertex *eye_vertex = NULL;
6379 vertex real_eye_vertex;
6380 eye_vertex = &real_eye_vertex; // this is needed since clip line takes a **vertex
6381 vec3d eye_pos;
6382 vm_vec_add( &eye_pos, &Eye_position, &View_matrix.vec.fvec);
6383 g3_rotate_vertex(eye_vertex, &eye_pos);
6384
6385 ubyte codes_or;
6386 codes_or = (ubyte)(target_point->codes | eye_vertex->codes);
6387 clip_line(&target_point,&eye_vertex,codes_or,0);
6388
6389 if (!(target_point->flags&PF_PROJECTED))
6390 g3_project_vertex(target_point);
6391
6392 if (!(eye_vertex->flags&PF_PROJECTED))
6393 g3_project_vertex(eye_vertex);
6394
6395 if (eye_vertex->flags&PF_OVERFLOW) {
6396 Int3(); // This is unlikely to happen, but can if a clip goes through the player's eye.
6397 Player_ai->target_objnum = -1;
6398 if (!in_frame)
6399 g3_end_frame();
6400 return;
6401 }
6402
6403 if (target_point->flags & PF_TEMP_POINT)
6404 free_temp_point(target_point);
6405
6406 if (eye_vertex->flags & PF_TEMP_POINT)
6407 free_temp_point(eye_vertex);
6408
6409 xpos = eye_vertex->screen.xyw.x;
6410 ypos = eye_vertex->screen.xyw.y;
6411
6412 // we need it unsized here and it will be fixed when things are acutally drawn
6413 gr_unsize_screen_posf(&xpos, &ypos);
6414
6415 xpos = (xpos<1) ? 0 : xpos;
6416 ypos = (ypos<1) ? 0 : ypos;
6417
6418 if ( xpos >= gr_screen.clip_right_unscaled) {
6419 xpos = i2fl(gr_screen.clip_right_unscaled);
6420 *dir = 0;
6421
6422 if ( ypos < (half_gauge_length - gr_screen.clip_top_unscaled) )
6423 ypos = half_gauge_length;
6424
6425 if ( ypos > (gr_screen.clip_bottom_unscaled - half_gauge_length) )
6426 ypos = gr_screen.clip_bottom_unscaled - half_gauge_length;
6427
6428 } else if ( xpos <= gr_screen.clip_left_unscaled ) {
6429 xpos = i2fl(gr_screen.clip_left_unscaled);
6430 *dir = 1;
6431
6432 if ( ypos < (half_gauge_length - gr_screen.clip_top_unscaled) )
6433 ypos = half_gauge_length;
6434
6435 if ( ypos > (gr_screen.clip_bottom_unscaled - half_gauge_length) )
6436 ypos = gr_screen.clip_bottom_unscaled - half_gauge_length;
6437
6438 } else if ( ypos <= gr_screen.clip_top_unscaled ) {
6439 ypos = i2fl(gr_screen.clip_top_unscaled);
6440 *dir = 2;
6441
6442 if ( xpos < ( half_gauge_length - gr_screen.clip_left_unscaled) )
6443 xpos = half_gauge_length;
6444
6445 if ( xpos > (gr_screen.clip_right_unscaled - half_gauge_length) )
6446 xpos = gr_screen.clip_right_unscaled - half_gauge_length;
6447
6448 } else if ( ypos >= gr_screen.clip_bottom_unscaled ) {
6449 ypos = i2fl(gr_screen.clip_bottom_unscaled);
6450 *dir = 3;
6451
6452 if ( xpos < ( half_gauge_length - gr_screen.clip_left_unscaled) )
6453 xpos = half_gauge_length;
6454
6455 if ( xpos > (gr_screen.clip_right_unscaled - half_gauge_length) )
6456 xpos = gr_screen.clip_right_unscaled - half_gauge_length;
6457
6458 } else {
6459 Int3();
6460 if (!in_frame)
6461 g3_end_frame();
6462 return;
6463 }
6464
6465 // The offscreen target triangles are drawn according the the diagram below
6466 //
6467 //
6468 //
6469 // x3 x3
6470 // / | | \.
6471 // / | | \.
6472 // x1____x2 x2____x1
6473 // | |
6474 // .......|................|............(xpos,ypos)
6475 // | |
6476 // x4____x5 x5____x4
6477 // \ | | /
6478 // \ | | /
6479 // x6 x6
6480 //
6481 //
6482
6483 xpos = (float)floor(xpos);
6484 ypos = (float)floor(ypos);
6485
6486 if (outcoords != NULL) {
6487 outcoords->x = xpos;
6488 outcoords->y = ypos;
6489 }
6490
6491 gr_reset_screen_scale();
6492
6493 if(!in_frame)
6494 g3_end_frame();
6495 }
6496
renderOffscreenIndicator(vec2d * coords,int dir,float distance,float half_triangle_sep,bool draw_solid)6497 void HudGaugeOffscreen::renderOffscreenIndicator(vec2d *coords, int dir, float distance, float half_triangle_sep, bool draw_solid)
6498 {
6499 float xpos, ypos;
6500 float displayed_distance;
6501 char buf[32];
6502 int w = 0, h = 0;
6503
6504 // points to draw triangles
6505 float x1=0.0f;
6506 float y1=0.0f;
6507 float x2=0.0f;
6508 float y2=0.0f;
6509 float x3=0.0f;
6510 float y3=0.0f;
6511 float x4=0.0f;
6512 float y4=0.0f;
6513 float x5=0.0f;
6514 float y5=0.0f;
6515 float x6=0.0f;
6516 float y6=0.0f;
6517
6518 // scale by distance modifier from hud_guages.tbl for display purposes
6519 displayed_distance = distance * Hud_unit_multiplier;
6520
6521 bool in_frame = g3_in_frame() > 0;
6522 if(!in_frame)
6523 g3_start_frame(0);
6524
6525 gr_set_screen_scale(base_w, base_h);
6526
6527 if (displayed_distance > 0.0f) {
6528 sprintf(buf, "%d", (int)std::lround(displayed_distance));
6529 hud_num_make_mono(buf, font_num);
6530 gr_get_string_size(&w, &h, buf);
6531 } else {
6532 buf[0] = 0;
6533 }
6534
6535 xpos = coords->x;
6536 ypos = coords->y;
6537
6538 if (dir == 0) {
6539 x1 = x4 = (xpos+2);
6540
6541 x2 = x3 = x5 = x6 = x1 - Offscreen_tri_height;
6542 y1 = y2 = ypos - half_triangle_sep;
6543 y3 = y2 - Offscreen_tri_base;
6544
6545 y4 = y5 = ypos + half_triangle_sep;
6546 y6 = y5 + Offscreen_tri_base;
6547
6548 if ( buf[0] ) {
6549 gr_string( fl2i(xpos - w - 10), (int)std::lround(ypos - h/2.0f), buf);
6550 }
6551 } else if (dir == 1) {
6552 x1 = x4 = (xpos-1);
6553
6554 x2 = x3 = x5 = x6 = x1 + Offscreen_tri_height;
6555 y1 = y2 = ypos - half_triangle_sep;
6556 y3 = y2 - Offscreen_tri_base;
6557
6558 y4 = y5 = ypos + half_triangle_sep;
6559 y6 = y5 + Offscreen_tri_base;
6560
6561 if ( buf[0] ) {
6562 gr_string(fl2i(xpos + 10), (int)std::lround(ypos - h/2.0f), buf);
6563 }
6564 } else if (dir == 2) {
6565 y1 = y4 = (ypos-1);
6566
6567 y2 = y3 = y5 = y6 = y1 + Offscreen_tri_height;
6568 x1 = x2 = xpos - half_triangle_sep;
6569 x3 = x2 - Offscreen_tri_base;
6570
6571 x4 = x5 = xpos + half_triangle_sep;
6572 x6 = x5 + Offscreen_tri_base;
6573
6574 if ( buf[0] ) {
6575 gr_string((int)std::lround(xpos - w/2.0f), fl2i(ypos+10), buf);
6576 }
6577 } else if (dir == 3) {
6578 y1 = y4 = (ypos+2);
6579
6580 y2 = y3 = y5 = y6 = y1 - Offscreen_tri_height;
6581 x1 = x2 = xpos - half_triangle_sep;
6582 x3 = x2 - Offscreen_tri_base;
6583
6584 x4 = x5 = xpos + half_triangle_sep;
6585 x6 = x5 + Offscreen_tri_base;
6586
6587 if ( buf[0] ) {
6588 gr_string((int)std::lround(xpos - w/2.0f), fl2i(ypos-h-10), buf);
6589 }
6590 }
6591
6592 if (draw_solid) {
6593 hud_tri(x3,y3,x2,y2,x1,y1);
6594 hud_tri(x4,y4,x5,y5,x6,y6);
6595 } else {
6596 hud_tri_empty(x3,y3,x2,y2,x1,y1);
6597 hud_tri_empty(x4,y4,x5,y5,x6,y6);
6598 }
6599
6600 if (dir == 0 || dir == 3){
6601 gr_line(fl2i(x2),fl2i(y2),fl2i(x5),fl2i(y5));
6602 } else if (dir == 1) {
6603 gr_line(fl2i(x2-1),fl2i(y2),fl2i(x5-1),fl2i(y5));
6604 } else {
6605 gr_line(fl2i(x2),fl2i(y2-1),fl2i(x5),fl2i(y5-1));
6606 }
6607
6608 gr_reset_screen_scale();
6609
6610 if(!in_frame)
6611 g3_end_frame();
6612 }
6613
HudGaugeWarheadCount()6614 HudGaugeWarheadCount::HudGaugeWarheadCount():
6615 HudGauge(HUD_OBJECT_WARHEAD_COUNT, HUD_WEAPONS_GAUGE, false, false, VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY, 255, 255, 255)
6616 {
6617 }
6618
initBitmap(char * fname)6619 void HudGaugeWarheadCount::initBitmap(char *fname)
6620 {
6621 Warhead.first_frame = bm_load_animation(fname, &Warhead.num_frames);
6622
6623 if ( Warhead.first_frame < 0 ) {
6624 Warning(LOCATION,"Cannot load hud ani: %s\n", fname);
6625 }
6626 }
6627
initNameOffsets(int x,int y)6628 void HudGaugeWarheadCount::initNameOffsets(int x, int y)
6629 {
6630 Warhead_name_offsets[0] = x;
6631 Warhead_name_offsets[1] = y;
6632 }
6633
initCountOffsets(int x,int y)6634 void HudGaugeWarheadCount::initCountOffsets(int x, int y)
6635 {
6636 Warhead_count_offsets[0] = x;
6637 Warhead_count_offsets[1] = y;
6638 }
6639
initCountSizes(int w,int h)6640 void HudGaugeWarheadCount::initCountSizes(int w, int h)
6641 {
6642 Warhead_count_size[0] = w;
6643 Warhead_count_size[1] = h;
6644 }
6645
initMaxSymbols(int count)6646 void HudGaugeWarheadCount::initMaxSymbols(int count)
6647 {
6648 Max_symbols = count;
6649 }
6650
initMaxColumns(int count)6651 void HudGaugeWarheadCount::initMaxColumns(int count)
6652 {
6653 Max_columns = count;
6654 }
6655
initTextAlign(HudAlignment align)6656 void HudGaugeWarheadCount::initTextAlign(HudAlignment align)
6657 {
6658 Text_align = align;
6659 }
6660
pageIn()6661 void HudGaugeWarheadCount::pageIn()
6662 {
6663 bm_page_in_aabitmap(Warhead.first_frame, Warhead.num_frames);
6664 }
6665
render(float)6666 void HudGaugeWarheadCount::render(float /*frametime*/)
6667 {
6668 if(Player_obj->type == OBJ_OBSERVER) {
6669 return;
6670 }
6671
6672 Assert(Player_obj->type == OBJ_SHIP);
6673 Assert(Player_obj->instance >= 0 && Player_obj->instance < MAX_SHIPS);
6674
6675 ship_weapon *sw = &Ships[Player_obj->instance].weapons;
6676
6677 // don't bother displaying anything if we have no secondaries
6678 if ( sw->num_secondary_banks <= 0 ) {
6679 return;
6680 }
6681
6682 int wep_num = sw->current_secondary_bank;
6683 weapon_info *wip = &Weapon_info[sw->secondary_bank_weapons[wep_num]];
6684 int ammo = sw->secondary_bank_ammo[wep_num];
6685
6686 // don't bother displaying anything if we have no ammo.
6687 if ( ammo <= 0 ) {
6688 return;
6689 }
6690
6691 auto weapon_name = wip->get_display_name();
6692
6693 setGaugeColor();
6694
6695 // display the weapon name
6696 if ( Text_align == HudAlignment::RIGHT ) {
6697 int w, h;
6698
6699 gr_get_string_size(&w, &h, weapon_name);
6700 renderString(position[0] + Warhead_name_offsets[0] - w, position[1] + Warhead_name_offsets[1], weapon_name);
6701 } else if ( Text_align == HudAlignment::LEFT ) {
6702 renderString(position[0] + Warhead_name_offsets[0], position[1] + Warhead_name_offsets[1], weapon_name);
6703 }
6704
6705 setGaugeColor(HUD_C_BRIGHT);
6706
6707 // if ammo is greater than the icon display limit, just show a numeric
6708 if ( ammo > Max_symbols ) {
6709 char ammo_str[32];
6710
6711 sprintf(ammo_str, "%d", ammo);
6712 hud_num_make_mono(ammo_str, font_num);
6713
6714 if ( Text_align == HudAlignment::RIGHT ) {
6715 int w, h;
6716
6717 gr_get_string_size(&w, &h, ammo_str);
6718 renderString(position[0] + Warhead_count_offsets[0] - w, position[1] + Warhead_count_offsets[1], ammo_str);
6719 } else if ( Text_align == HudAlignment::LEFT ) {
6720 renderString(position[0] + Warhead_count_offsets[0], position[1] + Warhead_count_offsets[1], ammo_str);
6721 }
6722
6723 return;
6724 }
6725
6726 int delta_x = 0, delta_y = 0;
6727 if ( Text_align == HudAlignment::RIGHT ) {
6728 delta_x = -Warhead_count_size[0];
6729 } else if ( Text_align == HudAlignment::LEFT ) {
6730 delta_x = Warhead_count_size[0];
6731 }
6732
6733 int i, column;
6734 for ( i = 0; i < ammo; i++ ) {
6735 if ( Max_columns > 0 ) {
6736 delta_y = Warhead_count_size[1] * (i / Max_columns);
6737 column = i % Max_columns;
6738 } else {
6739 column = i;
6740 }
6741
6742 renderBitmap(Warhead.first_frame, position[0] + Warhead_count_offsets[0] + column * delta_x, position[1] + Warhead_count_offsets[1] + delta_y);
6743 }
6744 }
6745
HudGaugeWeaponList(int _gauge_object)6746 HudGaugeWeaponList::HudGaugeWeaponList(int _gauge_object):
6747 HudGauge(_gauge_object, HUD_WEAPONS_GAUGE, false, false, VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY, 255, 255, 255)
6748 {
6749
6750 }
6751
initLinkIcon()6752 void HudGaugeWeaponList::initLinkIcon() {
6753 ubyte sc = lcl_get_font_index(font_num);
6754 // default to a '>' if the font has no special chars
6755 // seems about the closest normal char to the triangle
6756 if (sc == 0) {
6757 Weapon_link_icon = ubyte ('>');
6758 } else {
6759 Weapon_link_icon = sc + 2;
6760 }
6761 }
6762
initBitmaps(char * fname_first,char * fname_entry,char * fname_last)6763 void HudGaugeWeaponList::initBitmaps(char *fname_first, char *fname_entry, char *fname_last)
6764 {
6765 _background_first.first_frame = bm_load_animation(fname_first, &_background_first.num_frames);
6766 if(_background_first.first_frame < 0) {
6767 Warning(LOCATION,"Cannot load hud ani: %s\n", fname_first);
6768 }
6769
6770 _background_entry.first_frame = bm_load_animation(fname_entry, &_background_entry.num_frames);
6771 if(_background_entry.first_frame < 0) {
6772 Warning(LOCATION,"Cannot load hud ani: %s\n", fname_entry);
6773 }
6774
6775 _background_last.first_frame = bm_load_animation(fname_last, &_background_last.num_frames);
6776 if(_background_last.first_frame < 0) {
6777 Warning(LOCATION,"Cannot load hud ani: %s\n", fname_last);
6778 }
6779 }
6780
initBgFirstOffsetX(int x)6781 void HudGaugeWeaponList::initBgFirstOffsetX(int x)
6782 {
6783 _bg_first_offset_x = x;
6784 }
6785
initBgEntryOffsetX(int x)6786 void HudGaugeWeaponList::initBgEntryOffsetX(int x)
6787 {
6788 _bg_entry_offset_x = x;
6789 }
6790
initBgLastOffsetX(int x)6791 void HudGaugeWeaponList::initBgLastOffsetX(int x)
6792 {
6793 _bg_last_offset_x = x;
6794 }
6795
initBgLastOffsetY(int y)6796 void HudGaugeWeaponList::initBgLastOffsetY(int y)
6797 {
6798 _bg_last_offset_y = y;
6799 }
6800
initBgFirstHeight(int h)6801 void HudGaugeWeaponList::initBgFirstHeight(int h)
6802 {
6803 _background_first_h = h;
6804 }
6805
initBgEntryHeight(int h)6806 void HudGaugeWeaponList::initBgEntryHeight(int h)
6807 {
6808 _background_entry_h = h;
6809 }
6810
initHeaderText(char * text)6811 void HudGaugeWeaponList::initHeaderText(char *text)
6812 {
6813 strcpy_s(header_text, text);
6814 }
6815
initHeaderOffsets(int x,int y)6816 void HudGaugeWeaponList::initHeaderOffsets(int x, int y)
6817 {
6818 _header_offsets[0] = x;
6819 _header_offsets[1] = y;
6820 }
6821
initEntryStartY(int y)6822 void HudGaugeWeaponList::initEntryStartY(int y)
6823 {
6824 _entry_start_y = y;
6825 }
6826
initEntryHeight(int h)6827 void HudGaugeWeaponList::initEntryHeight(int h)
6828 {
6829 _entry_h = h;
6830 }
6831
pageIn()6832 void HudGaugeWeaponList::pageIn()
6833 {
6834 if ( _background_first.first_frame >= 0 ) {
6835 bm_page_in_aabitmap(_background_first.first_frame, _background_first.num_frames);
6836 }
6837
6838 if ( _background_entry.first_frame >= 0 ) {
6839 bm_page_in_aabitmap(_background_entry.first_frame, _background_entry.num_frames);
6840 }
6841
6842 if ( _background_last.first_frame >= 0 ) {
6843 bm_page_in_aabitmap(_background_last.first_frame, _background_last.num_frames);
6844 }
6845 }
6846
maybeFlashWeapon(int index)6847 void HudGaugeWeaponList::maybeFlashWeapon(int index)
6848 {
6849 if ( index >= MAX_WEAPON_FLASH_LINES ) {
6850 Int3(); // Get Alan
6851 return;
6852 }
6853
6854 // hud_set_default_color();
6855 setGaugeColor();
6856 if ( !timestamp_elapsed(Weapon_flash_info.flash_duration[index]) ) {
6857 if ( Weapon_flash_info.is_bright & (1<<index) ) {
6858 setGaugeColor(HUD_C_BRIGHT);
6859 // hud_set_bright_color();
6860 } else {
6861 setGaugeColor(HUD_C_DIM);
6862 // hud_set_dim_color();
6863 }
6864 }
6865 }
6866
render(float)6867 void HudGaugeWeaponList::render(float /*frametime*/)
6868 {
6869
6870 }
6871
HudGaugePrimaryWeapons()6872 HudGaugePrimaryWeapons::HudGaugePrimaryWeapons():
6873 HudGaugeWeaponList(HUD_OBJECT_PRIMARY_WEAPONS)
6874 {
6875
6876 }
6877
initPrimaryLinkOffsetX(int x)6878 void HudGaugePrimaryWeapons::initPrimaryLinkOffsetX(int x)
6879 {
6880 _plink_offset_x = x;
6881 }
6882
initPrimaryNameOffsetX(int x)6883 void HudGaugePrimaryWeapons::initPrimaryNameOffsetX(int x)
6884 {
6885 _pname_offset_x = x;
6886 }
6887
initPrimaryAmmoOffsetX(int x)6888 void HudGaugePrimaryWeapons::initPrimaryAmmoOffsetX(int x)
6889 {
6890 _pammo_offset_x = x;
6891 }
6892
render(float)6893 void HudGaugePrimaryWeapons::render(float /*frametime*/)
6894 {
6895 ship_weapon *sw;
6896
6897 int num_primaries; // np == num primary
6898
6899 if(Player_obj->type == OBJ_OBSERVER)
6900 return;
6901
6902 Assert(Player_obj->type == OBJ_SHIP);
6903 Assert(Player_obj->instance >= 0 && Player_obj->instance < MAX_SHIPS);
6904
6905 sw = &Ships[Player_obj->instance].weapons;
6906
6907 num_primaries = sw->num_primary_banks;
6908
6909 setGaugeColor();
6910
6911 renderBitmap(_background_first.first_frame, position[0], position[1]);
6912
6913 // render the header of this gauge
6914 renderString(position[0] + _header_offsets[0], position[1] + _header_offsets[1], EG_WEAPON_TITLE, header_text);
6915
6916 char ammo_str[32];
6917 int i, w, h;
6918 int bg_y_offset = _background_first_h;
6919 int text_y_offset = _entry_start_y;
6920
6921 for ( i = 0; i < num_primaries; ++i ) {
6922 setGaugeColor();
6923
6924 renderBitmap(_background_entry.first_frame, position[0], position[1] + bg_y_offset);
6925
6926 auto weapon_name = Weapon_info[sw->primary_bank_weapons[i]].get_display_name();
6927
6928 if (HudGauge::maybeFlashSexp() == i ) {
6929 setGaugeColor(HUD_C_BRIGHT);
6930 } else {
6931 maybeFlashWeapon(i);
6932 }
6933
6934 // indicate if this is linked or currently armed
6935 if ( (sw->current_primary_bank == i) || (Player_ship->flags[Ship::Ship_Flags::Primary_linked]) ) {
6936 renderPrintf(position[0] + _plink_offset_x, position[1] + text_y_offset, EG_NULL, "%c", Weapon_link_icon);
6937 }
6938
6939 // either render this primary's image or its name
6940 if(Weapon_info[sw->primary_bank_weapons[0]].hud_image_index != -1) {
6941 renderBitmap(Weapon_info[sw->primary_bank_weapons[i]].hud_image_index, position[0] + _pname_offset_x, text_y_offset);
6942 } else {
6943 renderPrintf(position[0] + _pname_offset_x, position[1] + text_y_offset, EG_WEAPON_P2, "%s", weapon_name);
6944 }
6945
6946 // if this is a ballistic primary with ammo, render the ammo count
6947 if (Weapon_info[sw->primary_bank_weapons[i]].wi_flags[Weapon::Info_Flags::Ballistic]) {
6948 // print out the ammo right justified
6949 sprintf(ammo_str, "%d", sw->primary_bank_ammo[i]);
6950
6951 hud_num_make_mono(ammo_str, font_num);
6952 gr_get_string_size(&w, &h, ammo_str);
6953
6954 renderString(position[0] + _pammo_offset_x - w, position[1] + text_y_offset, EG_NULL, ammo_str);
6955 }
6956
6957 text_y_offset += _entry_h;
6958 bg_y_offset += _background_entry_h;
6959 }
6960
6961 if ( num_primaries == 0 ) {
6962 renderBitmap(_background_entry.first_frame, position[0], position[1] + bg_y_offset);
6963 renderString(position[0] + _pname_offset_x, position[1] + text_y_offset, EG_WEAPON_P1, XSTR( "<none>", 329));
6964
6965 bg_y_offset += _background_entry_h;
6966 }
6967
6968 renderBitmap(_background_last.first_frame, position[0], position[1] + bg_y_offset + _bg_last_offset_y);
6969 }
6970
HudGaugeSecondaryWeapons()6971 HudGaugeSecondaryWeapons::HudGaugeSecondaryWeapons():
6972 HudGaugeWeaponList(HUD_OBJECT_SECONDARY_WEAPONS)
6973 {
6974
6975 }
6976
initSecondaryAmmoOffsetX(int x)6977 void HudGaugeSecondaryWeapons::initSecondaryAmmoOffsetX(int x)
6978 {
6979 _sammo_offset_x = x;
6980 }
6981
initSecondaryNameOffsetX(int x)6982 void HudGaugeSecondaryWeapons::initSecondaryNameOffsetX(int x)
6983 {
6984 _sname_offset_x = x;
6985 }
6986
initSecondaryReloadOffsetX(int x)6987 void HudGaugeSecondaryWeapons::initSecondaryReloadOffsetX(int x)
6988 {
6989 _sreload_offset_x = x;
6990 }
6991
initSecondaryLinkedOffsetX(int x)6992 void HudGaugeSecondaryWeapons::initSecondaryLinkedOffsetX(int x)
6993 {
6994 _slinked_offset_x = x;
6995 }
6996
initSecondaryUnlinkedOffsetX(int x)6997 void HudGaugeSecondaryWeapons::initSecondaryUnlinkedOffsetX(int x)
6998 {
6999 _sunlinked_offset_x = x;
7000 }
7001
render(float)7002 void HudGaugeSecondaryWeapons::render(float /*frametime*/)
7003 {
7004 ship_weapon *sw;
7005
7006 int num_primaries, num_secondaries;
7007
7008 Assert(Player_obj->type == OBJ_SHIP);
7009 Assert(Player_obj->instance >= 0 && Player_obj->instance < MAX_SHIPS);
7010
7011 sw = &Ships[Player_obj->instance].weapons;
7012
7013 num_primaries = sw->num_primary_banks;
7014 num_secondaries = sw->num_secondary_banks;
7015
7016 setGaugeColor();
7017
7018 renderBitmap(_background_first.first_frame, position[0], position[1]);
7019
7020 // render the header of this gauge
7021 renderString(position[0] + _header_offsets[0], position[1] + _header_offsets[1], EG_WEAPON_TITLE, header_text);
7022
7023 weapon_info *wip;
7024 char ammo_str[32];
7025 int i, w, h;
7026 int bg_y_offset = _background_first_h;
7027 int text_y_offset = _entry_start_y;
7028
7029 for ( i = 0; i < num_secondaries; ++i ) {
7030 setGaugeColor();
7031 wip = &Weapon_info[sw->secondary_bank_weapons[i]];
7032
7033 renderBitmap(_background_entry.first_frame, position[0], position[1] + bg_y_offset);
7034
7035 maybeFlashWeapon(num_primaries+i);
7036
7037 auto weapon_name = wip->get_display_name();
7038
7039 if ( sw->current_secondary_bank == i ) {
7040 // show that this is the current secondary armed
7041 renderPrintf(position[0] + _sunlinked_offset_x, position[1] + text_y_offset, EG_NULL, "%c", Weapon_link_icon);
7042
7043 // indicate if this is linked
7044 // don't draw the link indicator if the fire can't be fired link.
7045 // the link flag is ignored rather than cleared so the player can cycle past a no-doublefire weapon without the setting being cleared
7046 if ( Player_ship->flags[Ship::Ship_Flags::Secondary_dual_fire] && !wip->wi_flags[Weapon::Info_Flags::No_doublefire] &&
7047 !The_mission.ai_profile->flags[AI::Profile_Flags::Disable_player_secondary_doublefire] ) {
7048 renderPrintf(position[0] + _slinked_offset_x, position[1] + text_y_offset, EG_NULL, "%c", Weapon_link_icon);
7049 }
7050
7051 // show secondary weapon's image or print its name
7052 if(wip->hud_image_index != -1) {
7053 renderBitmap(wip->hud_image_index, position[0] + _sname_offset_x, position[1] + text_y_offset);
7054 } else {
7055 renderString(position[0] + _sname_offset_x, position[1] + text_y_offset, i ? EG_WEAPON_S1 : EG_WEAPON_S2, weapon_name);
7056 }
7057
7058 // show the cooldown time
7059 if ( (sw->secondary_bank_ammo[i] > 0) && (sw->current_secondary_bank >= 0) ) {
7060 int ms_till_fire = timestamp_until(sw->next_secondary_fire_stamp[sw->current_secondary_bank]);
7061 if ( (ms_till_fire >= 500) && ((wip->fire_wait >= 1 ) || (ms_till_fire > wip->fire_wait*1000)) ) {
7062 renderPrintf(position[0] + _sreload_offset_x, position[1] + text_y_offset, EG_NULL, "%d", (int)std::lround(ms_till_fire/1000.0f));
7063 }
7064 }
7065 } else {
7066 renderString(position[0] + _sname_offset_x, position[1] + text_y_offset, i ? EG_WEAPON_S1 : EG_WEAPON_S2, weapon_name);
7067 }
7068
7069 int ammo = sw->secondary_bank_ammo[i];
7070
7071 // print out the ammo right justified
7072 sprintf(ammo_str, "%d", ammo);
7073 hud_num_make_mono(ammo_str, font_num);
7074 gr_get_string_size(&w, &h, ammo_str);
7075
7076 renderString(position[0] + _sammo_offset_x - w, position[1] + text_y_offset, EG_NULL, ammo_str);
7077
7078 bg_y_offset += _background_entry_h;
7079 text_y_offset += _entry_h;
7080 }
7081
7082 if ( num_secondaries == 0 ) {
7083 renderBitmap(_background_entry.first_frame, position[0], position[1] + bg_y_offset);
7084 renderString(position[0] + _sname_offset_x, position[1] + text_y_offset, EG_WEAPON_S1, XSTR( "<none>", 329));
7085
7086 bg_y_offset += _background_entry_h;
7087 }
7088
7089 // finish drawing the background
7090 renderBitmap(_background_last.first_frame, position[0], position[1] + bg_y_offset + _bg_last_offset_y);
7091 }
7092
HudGaugeHardpoints()7093 HudGaugeHardpoints::HudGaugeHardpoints():
7094 HudGauge(HUD_OBJECT_HARDPOINTS, HUD_WEAPONS_GAUGE, false, false, VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY, 255, 255, 255)
7095 {
7096
7097 }
7098
initSizes(int w,int h)7099 void HudGaugeHardpoints::initSizes(int w, int h)
7100 {
7101 _size[0] = w;
7102 _size[1] = h;
7103 }
7104
initLineWidth(float w)7105 void HudGaugeHardpoints::initLineWidth(float w)
7106 {
7107 _line_width = w;
7108 }
7109
initViewDir(int dir)7110 void HudGaugeHardpoints::initViewDir(int dir)
7111 {
7112 _view_direction = dir;
7113 }
7114
initDrawOptions(bool primary_models,bool secondary_models)7115 void HudGaugeHardpoints::initDrawOptions(bool primary_models, bool secondary_models)
7116 {
7117 draw_primary_models = primary_models;
7118 draw_secondary_models = secondary_models;
7119 }
7120
render(float)7121 void HudGaugeHardpoints::render(float /*frametime*/)
7122 {
7123 int sx, sy;
7124 ship *sp;
7125 ship_info *sip;
7126 object *objp = Player_obj;
7127
7128 sp = &Ships[objp->instance];
7129 sip = &Ship_info[sp->ship_info_index];
7130
7131 sx = position[0];
7132 sy = position[1];
7133
7134 bool g3_yourself = !g3_in_frame();
7135 angles top_view = {-PI_2,0.0f,0.0f};
7136 angles front_view = {PI_2*2.0f,PI_2*2.0f,0.0f};
7137 matrix object_orient;
7138
7139 switch ( _view_direction ) {
7140 case TOP:
7141 vm_angles_2_matrix(&object_orient, &top_view);
7142 break;
7143 case FRONT:
7144 vm_angles_2_matrix(&object_orient, &front_view);
7145 break;
7146 }
7147
7148 gr_screen.clip_width = _size[0];
7149 gr_screen.clip_height = _size[1];
7150
7151 //Fire it up
7152 if(g3_yourself)
7153 g3_start_frame(1);
7154 hud_save_restore_camera_data(1);
7155 setClip(sx, sy, _size[0], _size[1]);
7156
7157 g3_set_view_matrix( &sip->closeup_pos, &vmd_identity_matrix, sip->closeup_zoom*1.5f);
7158
7159 gr_set_proj_matrix(Proj_fov, gr_screen.clip_aspect, Min_draw_distance, Max_draw_distance);
7160 gr_set_view_matrix(&Eye_position, &Eye_matrix);
7161
7162 setGaugeColor();
7163
7164 //We're ready to show stuff
7165 model_render_params render_info;
7166
7167 gr_stencil_clear();
7168
7169 const int detail_level_lock = 1;
7170
7171 render_info.set_detail_level_lock(detail_level_lock);
7172 render_info.set_flags(
7173 MR_NO_LIGHTING | MR_AUTOCENTER | MR_NO_FOGGING | MR_NO_TEXTURING | MR_NO_CULL | MR_NO_ZBUFFER | MR_STENCIL_WRITE
7174 | MR_NO_COLOR_WRITES);
7175 render_info.set_object_number(OBJ_INDEX(objp));
7176
7177 model_render_immediate( &render_info, sip->model_num, &object_orient, &vmd_zero_vector);
7178
7179 render_info.set_color(gauge_color);
7180 render_info.set_flags(MR_NO_LIGHTING | MR_AUTOCENTER | MR_NO_FOGGING | MR_NO_TEXTURING | MR_NO_ZBUFFER | MR_NO_CULL | MR_STENCIL_READ);
7181 // Factor was chosen pretty arbitrarily to make outline feature work with existing data
7182 render_info.set_outline_thickness(15.f * _line_width);
7183
7184 model_render_immediate(
7185 &render_info,
7186 sip->model_num,
7187 &object_orient,
7188 &vmd_zero_vector
7189 );
7190
7191 gr_stencil_set(GR_STENCIL_NONE);
7192
7193 // draw weapon models
7194 int i, k;
7195 ship_weapon *swp = &sp->weapons;
7196 vertex draw_point;
7197 vec3d subobj_pos;
7198
7199 int render_flags = MR_NO_LIGHTING | MR_AUTOCENTER | MR_NO_FOGGING | MR_NO_TEXTURING | MR_NO_ZBUFFER;
7200
7201 setGaugeColor();
7202 float alpha = gr_screen.current_color.alpha / 255.0f;
7203
7204 //secondary weapons
7205 int num_secondaries_rendered = 0;
7206 if ( draw_secondary_models ) {
7207 auto ship_pm = model_get(sip->model_num);
7208
7209 for (i = 0; i < swp->num_secondary_banks; i++) {
7210 auto wip = &Weapon_info[swp->secondary_bank_weapons[i]];
7211
7212 if (wip->external_model_num == -1 || !sip->draw_secondary_models[i])
7213 continue;
7214
7215 auto bank = &ship_pm->missile_banks[i];
7216
7217 if (wip->wi_flags[Weapon::Info_Flags::External_weapon_lnch]) {
7218 for(k = 0; k < bank->num_slots; k++) {
7219 model_render_params weapon_render_info;
7220
7221 weapon_render_info.set_detail_level_lock(detail_level_lock);
7222 weapon_render_info.set_flags(render_flags);
7223
7224 // We need to transform the position local to the model to be in "world" space relative to the rendered outline
7225 vec3d world_position;
7226 vm_vec_unrotate(&world_position, &bank->pnt[k], &object_orient);
7227
7228 // "Bank" the external model by the angle offset
7229 angles angs = { 0.0f, bank->external_model_angle_offset[k], 0.0f };
7230 matrix model_orient = object_orient;
7231 vm_rotate_matrix_by_angles(&model_orient, &angs);
7232
7233 model_render_immediate(&weapon_render_info, wip->external_model_num, &model_orient, &bank->pnt[k]);
7234 }
7235 } else {
7236 num_secondaries_rendered = 0;
7237
7238 for(k = 0; k < bank->num_slots; k++)
7239 {
7240 auto secondary_weapon_pos = bank->pnt[k];
7241
7242 if (num_secondaries_rendered >= sp->weapons.secondary_bank_ammo[i])
7243 break;
7244
7245 if(sp->secondary_point_reload_pct.get(i, k) <= 0.0)
7246 continue;
7247
7248 model_render_params weapon_render_info;
7249
7250 if ( swp->current_secondary_bank == i && ( swp->secondary_next_slot[i] == k || ( swp->secondary_next_slot[i]+1 == k && sp->flags[Ship::Ship_Flags::Secondary_dual_fire] ) ) ) {
7251 weapon_render_info.set_color(Color_bright_blue);
7252 } else {
7253 weapon_render_info.set_color(Color_bright_white);
7254 }
7255
7256 num_secondaries_rendered++;
7257
7258 vm_vec_scale_add2(&secondary_weapon_pos, &vmd_z_vector, -(1.0f-sp->secondary_point_reload_pct.get(i, k)) * model_get(wip->external_model_num)->rad);
7259
7260 weapon_render_info.set_detail_level_lock(detail_level_lock);
7261 weapon_render_info.set_flags(render_flags);
7262
7263 // We need to transform the position local to the model to be in "world" space relative to the rendered outline
7264 vec3d world_position;
7265 vm_vec_unrotate(&world_position, &secondary_weapon_pos, &object_orient);
7266
7267 // "Bank" the external model by the angle offset
7268 angles angs = { 0.0f, bank->external_model_angle_offset[k], 0.0f };
7269 matrix model_orient = object_orient;
7270 vm_rotate_matrix_by_angles(&model_orient, &angs);
7271
7272 model_render_immediate(&weapon_render_info, wip->external_model_num, &model_orient, &world_position);
7273 }
7274 }
7275 }
7276 }
7277
7278 resetClip();
7279
7280 setGaugeColor(HUD_C_BRIGHT);
7281
7282 //primary weapons
7283 if ( draw_primary_models ) {
7284 auto ship_pm = model_get(sip->model_num);
7285
7286 for ( i = 0; i < swp->num_primary_banks; i++ ) {
7287 auto wip = &Weapon_info[swp->primary_bank_weapons[i]];
7288 auto bank = &ship_pm->gun_banks[i];
7289
7290 for ( k = 0; k < bank->num_slots; k++ ) {
7291 if ( wip->external_model_num < 0 || !sip->draw_primary_models[i] ) {
7292 vm_vec_unrotate(&subobj_pos, &bank->pnt[k], &object_orient);
7293 //vm_vec_sub(&subobj_pos, &Eye_position, &subobj_pos);
7294 //g3_rotate_vertex(&draw_point, &bank->pnt[k]);
7295
7296 g3_rotate_vertex(&draw_point, &subobj_pos);
7297 g3_project_vertex(&draw_point);
7298
7299 //resize(&width, &height);
7300
7301 //unsize(&xc, &yc);
7302 //unsize(&draw_point.screen.xyw.x, &draw_point.screen.xyw.y);
7303
7304 renderCircle((int)draw_point.screen.xyw.x + position[0], (int)draw_point.screen.xyw.y + position[1], 10);
7305 //renderCircle(xc, yc, 25);
7306 } else {
7307 model_render_params weapon_render_info;
7308 weapon_render_info.set_detail_level_lock(detail_level_lock);
7309 weapon_render_info.set_flags(render_flags);
7310 weapon_render_info.set_alpha(alpha);
7311
7312 // We need to transform the position local to the model to be in "world" space relative to the rendered outline
7313 vec3d world_position;
7314 vm_vec_unrotate(&world_position, &bank->pnt[k], &object_orient);
7315
7316 // "Bank" the external model by the angle offset
7317 angles angs = { 0.0f, bank->external_model_angle_offset[k], 0.0f };
7318 matrix model_orient = object_orient;
7319 vm_rotate_matrix_by_angles(&model_orient, &angs);
7320
7321 model_render_immediate(&weapon_render_info, wip->external_model_num, swp->primary_bank_external_model_instance[i], &model_orient, &world_position);
7322 }
7323 }
7324 }
7325 }
7326
7327 //We're done
7328 gr_end_view_matrix();
7329 gr_end_proj_matrix();
7330 if(g3_yourself)
7331 g3_end_frame();
7332
7333 hud_save_restore_camera_data(0);
7334 }
7335