2 * $Logfile: /Freespace2/code/Hud/HUDWingmanStatus.cpp $
7 * Module for the wingman status gauge
10 * Revision 1.1 2002/05/03 03:28:09 root
14 * 7 6/10/99 3:43p Dave
15 * Do a better job of syncing text colors to HUD gauges.
17 * 6 1/07/99 9:06a Jasen
18 * coords blah blah blah
20 * 5 1/06/99 4:03p Dave
21 * Added better comments to wingman status gauge coords.
23 * 4 12/28/98 3:17p Dave
24 * Support for multiple hud bitmap filenames for hi-res mode.
26 * 3 12/21/98 5:03p Dave
27 * Modified all hud elements to be multi-resolution friendly.
29 * 2 10/07/98 10:53a Dave
32 * 1 10/07/98 10:49a Dave
34 * 17 8/25/98 1:48p Dave
35 * First rev of EMP effect. Player side stuff basically done. Next comes
38 * 16 6/09/98 5:18p Lawrance
39 * French/German localization
41 * 15 6/09/98 10:31a Hoffoss
42 * Created index numbers for all xstr() references. Any new xstr() stuff
43 * added from here on out should be added to the end if the list. The
44 * current list count can be found in FreeSpace.cpp (search for
47 * 14 6/01/98 11:43a John
48 * JAS & MK: Classified all strings for localization.
50 * 13 5/06/98 2:46p Mike
51 * Modify num-ships-attacking system.
53 * 12 4/17/98 1:42p Allender
54 * fixed optimized build warning
56 * 11 4/16/98 2:56p Allender
57 * multiple wings were not working after I added Zeta wing
59 * 10 4/15/98 11:09p Allender
60 * status gaugs works for team v. team
62 * 9 4/14/98 1:35a Allender
63 * (blindly) work on wingman status gauge for team v team multiplayer
65 * 8 4/01/98 9:21p John
66 * Made NDEBUG, optimized build with no warnings or errors.
68 * 7 3/31/98 11:46p Lawrance
69 * Fix several bugs related to departing ships.
71 * 6 3/26/98 5:26p John
72 * added new paging code. nonfunctional.
74 * 5 3/22/98 11:13p Allender
75 * work with respawning -- save parse object so ship can be correctly
76 * restored. Restore wingman status information so gauge updates
79 * 4 3/20/98 10:26a Lawrance
80 * Don't display gauge if Alpha1 is only ship
82 * 3 3/18/98 12:03p John
83 * Marked all the new strings as externalized or not.
85 * 2 3/14/98 4:59p Lawrance
86 * Totally rework HUD wingman status gauge to work with 5 arbitrary wings
88 * 1 3/14/98 8:23a Lawrance
94 #include "hudwingmanstatus.h"
99 #include "hudtargetbox.h"
100 #include "linklist.h"
101 #include "systemvars.h"
105 #define HUD_WINGMAN_STATUS_NUM_FRAMES 5
106 #define BACKGROUND_LEFT 0
107 #define BACKGROUND_MIDDLE 1
108 #define BACKGROUND_RIGHT 2
109 #define WINGMAN_STATUS_DOTS 3
110 #define WINGMAN_STATUS_NAMES 4
112 static char *Wingman_status_filenames[GR_NUM_RESOLUTIONS][HUD_WINGMAN_STATUS_NUM_FRAMES] =
132 static hud_frames Wingman_status_frames[HUD_WINGMAN_STATUS_NUM_FRAMES];
133 static int Wingman_status_gauge_loaded=0;
135 #define HUD_WINGMAN_STATUS_NONE 0 // wingman doesn't exist
136 #define HUD_WINGMAN_STATUS_DEAD 1 // wingman has died
137 #define HUD_WINGMAN_STATUS_ALIVE 2 // wingman is in the mission
138 #define HUD_WINGMAN_STATUS_NOT_HERE 3 // wingman hasn't arrived, or has departed
140 #define HUD_WINGMAN_MAX_WINGS 6 // upped to 6 to hold room for Zeta in team v team.
141 #define HUD_WINGMAN_MAX_SHIPS_PER_WINGS 6
143 typedef struct Wingman_status
145 int ignore; // set to 1 when we should ignore this item -- used in team v. team
147 float hull[HUD_WINGMAN_MAX_SHIPS_PER_WINGS]; // 0.0 -> 1.0
148 int status[HUD_WINGMAN_MAX_SHIPS_PER_WINGS]; // HUD_WINGMAN_STATUS_*
151 wingman_status HUD_wingman_status[HUD_WINGMAN_MAX_WINGS];
153 #define HUD_WINGMAN_UPDATE_STATUS_INTERVAL 200
154 static int HUD_wingman_update_timer;
156 static int HUD_wingman_flash_duration[HUD_WINGMAN_MAX_WINGS][HUD_WINGMAN_MAX_SHIPS_PER_WINGS];
157 static int HUD_wingman_flash_next[HUD_WINGMAN_MAX_WINGS][HUD_WINGMAN_MAX_SHIPS_PER_WINGS];
158 static int HUD_wingman_flash_is_bright;
160 // coords to draw wingman status icons, for 1-5 wings (0-4)
161 int HUD_wingman_left_coords[GR_NUM_RESOLUTIONS][5][2] = {
163 {550, 144}, // where to draw the left part of gauge if we have 1 wing
164 {550, 144}, // "" 2 wings
165 {515, 144}, // "" 3 wings
166 {480, 144}, // "" 4 wings
167 {445, 144} // "" 5 wings
177 int HUD_wingman_middle_coords[GR_NUM_RESOLUTIONS][5][2] = {
179 {0, 0}, // we never draw this for 1 wing
180 {0, 0}, // we never draw this for 2 wings
181 {586, 144}, // where to draw the _first_ middle gauge for 3 wings
182 {551, 144}, // "" 4 wings
183 {516, 144} // "" 5 wings
193 int HUD_wingman_right_coords[GR_NUM_RESOLUTIONS][5][2] = {
195 {621, 144}, // always drawn in the same spot
210 // special coordinates if only one wing is present
211 int HUD_wingman_status_single_coords[GR_NUM_RESOLUTIONS][4][2] =
214 {589,159}, // where to draw dots 1 - 4 if we have only one wing present (special case)
227 int HUD_wingman_status_name_coords[GR_NUM_RESOLUTIONS][HUD_WINGMAN_MAX_WINGS][2] =
230 {459,185}, // duplicated the first item because we only ever display 5 items
238 {841,185}, // duplicated the first item because we only ever display 5 items
247 int HUD_wingman_status_coords[GR_NUM_RESOLUTIONS][HUD_WINGMAN_MAX_WINGS][HUD_WINGMAN_MAX_SHIPS_PER_WINGS][2] =
249 // duplicated first set of data because we will only ever display up to 5 wings
252 {{467,159}, // ship 1
257 {478,175}}, // ship 6
344 int hud_wingman_status_wing_index(char *wing_name)
347 if ( !stricmp("alpha", wing_name) ) {
349 } else if ( !stricmp("beta", wing_name) ) {
351 } else if ( !stricmp("gamma", wing_name) ) {
353 } else if ( !stricmp("delta", wing_name) ) {
355 } else if ( !stricmp("epsilon", wing_name) ) {
357 } else if ( (Game_mode & GM_MULTIPLAYER) && IS_MISSION_MULTI_TEAMS && !stricmp("zeta", wing_name) ) {
365 // flag a player wing ship as destroyed
366 void hud_set_wingman_status_dead(int wing_index, int wing_pos)
368 Assert(wing_index >= 0 && wing_index < HUD_WINGMAN_MAX_WINGS);
369 Assert(wing_pos >= 0 && wing_index < HUD_WINGMAN_MAX_SHIPS_PER_WINGS);
371 HUD_wingman_status[wing_index].status[wing_pos] = HUD_WINGMAN_STATUS_DEAD;
374 // flags a given player wing ship as departed
375 void hud_set_wingman_status_departed(int wing_index, int wing_pos)
377 Assert(wing_index >= 0 && wing_index < HUD_WINGMAN_MAX_WINGS);
378 Assert(wing_pos >= 0 && wing_index < HUD_WINGMAN_MAX_SHIPS_PER_WINGS);
380 HUD_wingman_status[wing_index].status[wing_pos] = HUD_WINGMAN_STATUS_NOT_HERE;
383 // flags a given player wing ship as not existing
384 void hud_set_wingman_status_none( int wing_index, int wing_pos)
388 Assert(wing_index >= 0 && wing_index < HUD_WINGMAN_MAX_WINGS);
389 Assert(wing_pos >= 0 && wing_index < HUD_WINGMAN_MAX_SHIPS_PER_WINGS);
391 HUD_wingman_status[wing_index].status[wing_pos] = HUD_WINGMAN_STATUS_NONE;
394 for ( i = 0; i < HUD_WINGMAN_MAX_SHIPS_PER_WINGS; i++ ) {
395 if ( HUD_wingman_status[wing_index].status[i] != HUD_WINGMAN_STATUS_NONE ) {
401 HUD_wingman_status[wing_index].used = used;
404 // flags a given player wing ship as "alive" (for multiplayer respawns )
405 void hud_set_wingman_status_alive( int wing_index, int wing_pos)
407 Assert(wing_index >= 0 && wing_index < HUD_WINGMAN_MAX_WINGS);
408 Assert(wing_pos >= 0 && wing_index < HUD_WINGMAN_MAX_SHIPS_PER_WINGS);
410 HUD_wingman_status[wing_index].status[wing_pos] = HUD_WINGMAN_STATUS_ALIVE;
413 // get the hull percent for a specific ship, return value 0.0 -> 1.0
414 float hud_get_ship_hull_percent(int ship_index)
419 ship_objp = &Objects[Ships[ship_index].objnum];
420 sip = &Ship_info[Ships[ship_index].ship_info_index];
422 return (ship_objp->hull_strength / sip->initial_hull_strength);
425 void hud_wingman_status_init_late_wings()
428 int i, j, wing_index;
430 for ( i = 0; i < num_wings; i++ ) {
431 wing_index = hud_wingman_status_wing_index(Wings[i].name);
433 if ( (wing_index >= 0) && (Wings[i].total_arrived_count == 0) ) {
434 HUD_wingman_status[wing_index].used = 1;
435 for (j = 0; j < Wings[i].wave_count; j++) {
436 HUD_wingman_status[wing_index].status[j] = HUD_WINGMAN_STATUS_NOT_HERE;
443 // function which marks the other team wing as not used for the wingman status gauge
444 void hud_wingman_kill_multi_teams()
448 // do nothing in single player or non team v. team games
449 if ( Game_mode & GM_NORMAL )
452 if ( !IS_MISSION_MULTI_TEAMS )
456 if ( Net_player->p_info.team == 0 )
457 wing_index = hud_wingman_status_wing_index(NOX("zeta"));
458 else if ( Net_player->p_info.team == 1 )
459 wing_index = hud_wingman_status_wing_index(NOX("alpha"));
461 if ( wing_index == -1 )
464 HUD_wingman_status[wing_index].ignore = 1;
468 // called once per level to init the wingman status gauge. Loads in the frames the first time
469 void hud_init_wingman_status_gauge()
473 if ( !Wingman_status_gauge_loaded ) {
475 for ( i = 0; i < HUD_WINGMAN_STATUS_NUM_FRAMES; i++ ) {
476 Wingman_status_frames[i].first_frame = bm_load_animation(Wingman_status_filenames[gr_screen.res][i], &Wingman_status_frames[i].num_frames);
477 if ( Wingman_status_frames[i].first_frame == -1 ) {
478 Warning(LOCATION, NOX("Error loading Wingman_status_filenames[gr_screen.res][i]'\n"));
482 Wingman_status_gauge_loaded = 1;
485 hud_wingman_status_init_flash();
487 HUD_wingman_update_timer=timestamp(0); // update status right away
489 for (i = 0; i < HUD_WINGMAN_MAX_WINGS; i++) {
490 HUD_wingman_status[i].ignore = 0;
491 HUD_wingman_status[i].used = 0;
492 for ( j = 0; j < HUD_WINGMAN_MAX_SHIPS_PER_WINGS; j++ ) {
493 HUD_wingman_status[i].status[j] = HUD_WINGMAN_STATUS_NONE;
497 hud_wingman_status_init_late_wings();
498 hud_wingman_kill_multi_teams();
499 hud_wingman_status_update();
502 // Update the status of the wingman status
503 void hud_wingman_status_update()
505 if ( timestamp_elapsed(HUD_wingman_update_timer) ) {
506 int wing_index,wing_pos;
511 HUD_wingman_update_timer=timestamp(HUD_WINGMAN_UPDATE_STATUS_INTERVAL);
513 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
514 ship_objp = &Objects[so->objnum];
515 shipp = &Ships[ship_objp->instance];
517 wing_index = shipp->wing_status_wing_index;
518 wing_pos = shipp->wing_status_wing_pos;
520 if ( (wing_index >= 0) && (wing_pos >= 0) ) {
522 HUD_wingman_status[wing_index].used = 1;
523 if (!(shipp->flags & SF_DEPARTING) ) {
524 HUD_wingman_status[wing_index].status[wing_pos] = HUD_WINGMAN_STATUS_ALIVE;
526 HUD_wingman_status[wing_index].hull[wing_pos] = hud_get_ship_hull_percent(ship_objp->instance);
527 if ( HUD_wingman_status[wing_index].hull[wing_pos] <= 0 ) {
528 HUD_wingman_status[wing_index].status[wing_pos] = HUD_WINGMAN_STATUS_DEAD;
535 void hud_wingman_status_blit_left_frame(int num_wings_to_draw)
539 // draw left side of frame
540 if((num_wings_to_draw < 1) || (num_wings_to_draw > 5)){
544 sx = HUD_wingman_left_coords[gr_screen.res][num_wings_to_draw - 1][0];
545 sy = HUD_wingman_left_coords[gr_screen.res][num_wings_to_draw - 1][1];
546 bitmap = Wingman_status_frames[BACKGROUND_LEFT].first_frame;
549 GR_AABITMAP(bitmap, sx, sy);
550 // gr_set_bitmap(bitmap);
551 // gr_aabitmap(sx, sy);
554 // write "wingmen" on gauge
555 gr_string(sx+2, sy+2, XSTR( "wingmen", 352));
558 void hud_wingman_status_blit_middle_frame(int num_wings_to_draw)
563 bitmap = Wingman_status_frames[BACKGROUND_MIDDLE].first_frame;
568 // don't draw for 1 or 2 wings
569 if((num_wings == 1) || (num_wings == 2)){
573 // draw left side of frame
574 if((num_wings_to_draw < 1) || (num_wings_to_draw > 5)){
580 for(idx=num_wings_to_draw; idx>=3; idx--){
581 sx = HUD_wingman_middle_coords[gr_screen.res][idx - 1][0];
582 sy = HUD_wingman_middle_coords[gr_screen.res][idx - 1][1];
583 GR_AABITMAP(bitmap, sx, sy);
587 void hud_wingman_status_blit_right_frame(int num_wings_to_draw)
591 // draw left side of frame
592 if((num_wings_to_draw < 1) || (num_wings_to_draw > 5)){
597 sx = HUD_wingman_right_coords[gr_screen.res][num_wings_to_draw - 1][0];
598 sy = HUD_wingman_right_coords[gr_screen.res][num_wings_to_draw - 1][1];
599 bitmap = Wingman_status_frames[BACKGROUND_RIGHT].first_frame;
602 GR_AABITMAP(bitmap, sx, sy);
606 void hud_wingman_status_blit_dots(int wing_index, int screen_index, int num_wings_to_draw)
608 int i, sx, sy, is_bright, bitmap = -1, screen_pos;
610 Wingman_status_frames[WINGMAN_STATUS_DOTS].first_frame;
612 if ( Wingman_status_frames[WINGMAN_STATUS_DOTS].first_frame < 0 ) {
616 if ( Wingman_status_frames[WINGMAN_STATUS_NAMES].first_frame < 0 ) {
620 screen_pos = screen_index + (HUD_WINGMAN_MAX_WINGS - num_wings_to_draw);
623 for ( i = 0; i < HUD_WINGMAN_MAX_SHIPS_PER_WINGS; i++ ) {
625 if ( hud_wingman_status_maybe_flash(wing_index, i) ) {
631 switch( HUD_wingman_status[wing_index].status[i] ) {
633 case HUD_WINGMAN_STATUS_ALIVE:
634 bitmap = Wingman_status_frames[WINGMAN_STATUS_DOTS].first_frame;
635 if ( HUD_wingman_status[wing_index].hull[i] > 0.5f ) {
636 // gr_set_color_fast(&IFF_colors[IFF_COLOR_FRIENDLY][is_bright]);
638 hud_set_gauge_color(HUD_WINGMEN_STATUS, is_bright ? HUD_C_BRIGHT : HUD_C_NORMAL);
640 gr_set_color_fast(&IFF_colors[IFF_COLOR_HOSTILE][is_bright]);
644 case HUD_WINGMAN_STATUS_DEAD:
645 gr_set_color_fast(&IFF_colors[IFF_COLOR_HOSTILE][0]);
646 bitmap = Wingman_status_frames[WINGMAN_STATUS_DOTS].first_frame+1;
649 case HUD_WINGMAN_STATUS_NOT_HERE:
650 // gr_set_color_fast(&IFF_colors[IFF_COLOR_FRIENDLY][0]);
651 hud_set_gauge_color(HUD_WINGMEN_STATUS, is_bright ? HUD_C_BRIGHT : HUD_C_NORMAL);
652 bitmap = Wingman_status_frames[WINGMAN_STATUS_DOTS].first_frame+1;
661 if ( num_wings_to_draw == 1 ) {
662 sx = HUD_wingman_status_single_coords[gr_screen.res][i][0];
663 sy = HUD_wingman_status_single_coords[gr_screen.res][i][1];
665 sx = HUD_wingman_status_coords[gr_screen.res][screen_pos][i][0];
666 sy = HUD_wingman_status_coords[gr_screen.res][screen_pos][i][1];
670 GR_AABITMAP(bitmap, sx, sy);
675 bitmap = Wingman_status_frames[WINGMAN_STATUS_NAMES].first_frame + wing_index;
677 if ( num_wings_to_draw == 1 ) {
678 sx = HUD_wingman_status_single_coords[gr_screen.res][0][0] - 8;
679 sy = HUD_wingman_status_single_coords[gr_screen.res][0][1] + 26;
681 sx = HUD_wingman_status_name_coords[gr_screen.res][screen_pos][0];
682 sy = HUD_wingman_status_name_coords[gr_screen.res][screen_pos][1];
685 // hud_set_default_color();
686 hud_set_gauge_color(HUD_WINGMEN_STATUS);
688 GR_AABITMAP(bitmap, sx, sy);
689 // gr_set_bitmap(bitmap);
690 // gr_aabitmap(sx, sy);
695 int hud_wingman_status_wingmen_exist(int num_wings_to_draw)
699 switch ( num_wings_to_draw ) {
704 for (i = 0; i < HUD_WINGMAN_MAX_WINGS; i++) {
705 if ( HUD_wingman_status[i].used > 0 ) {
706 for ( j = 0; j < HUD_WINGMAN_MAX_SHIPS_PER_WINGS; j++ ) {
707 if ( HUD_wingman_status[i].status[j] != HUD_WINGMAN_STATUS_NONE ) {
727 // Draw the wingman status gauge
728 void hud_wingman_status_render()
730 int i, count, num_wings_to_draw = 0;
732 for (i = 0; i < HUD_WINGMAN_MAX_WINGS; i++) {
733 if ( (HUD_wingman_status[i].used > 0) && (HUD_wingman_status[i].ignore == 0) ) {
738 if ( !hud_wingman_status_wingmen_exist(num_wings_to_draw) ) {
742 // hud_set_default_color();
743 hud_set_gauge_color(HUD_WINGMEN_STATUS);
745 // blit the background frames
746 hud_wingman_status_blit_left_frame(num_wings_to_draw);
747 hud_wingman_status_blit_middle_frame(num_wings_to_draw);
748 hud_wingman_status_blit_right_frame(num_wings_to_draw);
751 for (i = 0; i < HUD_WINGMAN_MAX_WINGS; i++) {
752 if ( (HUD_wingman_status[i].used <= 0) || (HUD_wingman_status[i].ignore == 1) ) {
756 hud_wingman_status_blit_dots(i, count, num_wings_to_draw);
761 // init the flashing timers for the wingman status gauge
762 void hud_wingman_status_init_flash()
766 for ( i = 0; i < HUD_WINGMAN_MAX_WINGS; i++ ) {
767 for ( j = 0; j < HUD_WINGMAN_MAX_SHIPS_PER_WINGS; j++ ) {
768 HUD_wingman_flash_duration[i][j] = timestamp(0);
769 HUD_wingman_flash_next[i][j] = timestamp(0);
773 HUD_wingman_flash_is_bright = 0;
776 // start the targetbox item flashing for TBOX_FLASH_DURATION
777 void hud_wingman_status_start_flash(int wing_index, int wing_pos)
779 HUD_wingman_flash_duration[wing_index][wing_pos] = timestamp(TBOX_FLASH_DURATION);
782 // set the color for flashing dot
783 // exit: 1 => set bright color
784 // 0 => set default color
785 int hud_wingman_status_maybe_flash(int wing_index, int wing_pos)
787 int index, draw_bright=0;
789 index = wing_index*HUD_WINGMAN_MAX_SHIPS_PER_WINGS + wing_pos;
791 if ( !timestamp_elapsed(HUD_wingman_flash_duration[wing_index][wing_pos]) ) {
792 if ( timestamp_elapsed(HUD_wingman_flash_next[wing_index][wing_pos]) ) {
793 HUD_wingman_flash_next[wing_index][wing_pos] = timestamp(TBOX_FLASH_INTERVAL);
794 HUD_wingman_flash_is_bright ^= (1<<index); // toggle between default and bright frames
797 if ( HUD_wingman_flash_is_bright & (1<<index) ) {
805 int hud_wingman_status_wing_pos(int shipnum, int wing_status_index, wing *wingp)
807 int i, wing_pos = -1;
809 for (i = 0; i < wingp->wave_count; i++) {
810 if ( wingp->ship_index[i] == shipnum ) {
819 void hud_wingman_status_set_index(int shipnum)
821 int wing_index, wing_pos;
829 shipp = &Ships[shipnum];
831 if ( shipp->wingnum < 0 ) {
835 wingp = &Wings[shipp->wingnum];
837 // Check for Alpha, Beta, Gamma, Delta or Epsilon wings
838 wing_index = hud_wingman_status_wing_index(wingp->name);
839 if ( wing_index < 0 ) {
843 shipp->wing_status_wing_index = (char)wing_index;
845 wing_pos = hud_wingman_status_wing_pos(shipnum, wing_index, wingp);
847 shipp->wing_status_wing_pos = (char)wing_pos;
850 void hudwingmanstatus_page_in()
853 for ( i = 0; i < HUD_WINGMAN_STATUS_NUM_FRAMES; i++ ) {
854 bm_page_in_aabitmap( Wingman_status_frames[i].first_frame, Wingman_status_frames[i].num_frames );
858 int get_blip_bitmap()
860 return Wingman_status_frames[WINGMAN_STATUS_DOTS].first_frame+1;