2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
4 * All source code herein is the property of Volition, Inc. You may not sell
5 * or otherwise commercially exploit the source or things you created based on
10 * $Logfile: /Freespace2/code/Hud/HUDWingmanStatus.cpp $
15 * Module for the wingman status gauge
18 * Revision 1.3 2002/07/13 06:46:48 theoddone33
21 * Revision 1.2 2002/06/09 04:41:21 relnev
22 * added copyright header
24 * Revision 1.1.1.1 2002/05/03 03:28:09 root
28 * 7 6/10/99 3:43p Dave
29 * Do a better job of syncing text colors to HUD gauges.
31 * 6 1/07/99 9:06a Jasen
32 * coords blah blah blah
34 * 5 1/06/99 4:03p Dave
35 * Added better comments to wingman status gauge coords.
37 * 4 12/28/98 3:17p Dave
38 * Support for multiple hud bitmap filenames for hi-res mode.
40 * 3 12/21/98 5:03p Dave
41 * Modified all hud elements to be multi-resolution friendly.
43 * 2 10/07/98 10:53a Dave
46 * 1 10/07/98 10:49a Dave
48 * 17 8/25/98 1:48p Dave
49 * First rev of EMP effect. Player side stuff basically done. Next comes
52 * 16 6/09/98 5:18p Lawrance
53 * French/German localization
55 * 15 6/09/98 10:31a Hoffoss
56 * Created index numbers for all xstr() references. Any new xstr() stuff
57 * added from here on out should be added to the end if the list. The
58 * current list count can be found in FreeSpace.cpp (search for
61 * 14 6/01/98 11:43a John
62 * JAS & MK: Classified all strings for localization.
64 * 13 5/06/98 2:46p Mike
65 * Modify num-ships-attacking system.
67 * 12 4/17/98 1:42p Allender
68 * fixed optimized build warning
70 * 11 4/16/98 2:56p Allender
71 * multiple wings were not working after I added Zeta wing
73 * 10 4/15/98 11:09p Allender
74 * status gaugs works for team v. team
76 * 9 4/14/98 1:35a Allender
77 * (blindly) work on wingman status gauge for team v team multiplayer
79 * 8 4/01/98 9:21p John
80 * Made NDEBUG, optimized build with no warnings or errors.
82 * 7 3/31/98 11:46p Lawrance
83 * Fix several bugs related to departing ships.
85 * 6 3/26/98 5:26p John
86 * added new paging code. nonfunctional.
88 * 5 3/22/98 11:13p Allender
89 * work with respawning -- save parse object so ship can be correctly
90 * restored. Restore wingman status information so gauge updates
93 * 4 3/20/98 10:26a Lawrance
94 * Don't display gauge if Alpha1 is only ship
96 * 3 3/18/98 12:03p John
97 * Marked all the new strings as externalized or not.
99 * 2 3/14/98 4:59p Lawrance
100 * Totally rework HUD wingman status gauge to work with 5 arbitrary wings
102 * 1 3/14/98 8:23a Lawrance
108 #include "hudwingmanstatus.h"
113 #include "hudtargetbox.h"
114 #include "linklist.h"
115 #include "systemvars.h"
119 #define HUD_WINGMAN_STATUS_NUM_FRAMES 5
120 #define BACKGROUND_LEFT 0
121 #define BACKGROUND_MIDDLE 1
122 #define BACKGROUND_RIGHT 2
123 #define WINGMAN_STATUS_DOTS 3
124 #define WINGMAN_STATUS_NAMES 4
126 static const char *Wingman_status_filenames[GR_NUM_RESOLUTIONS][HUD_WINGMAN_STATUS_NUM_FRAMES] =
146 static hud_frames Wingman_status_frames[HUD_WINGMAN_STATUS_NUM_FRAMES];
147 static int Wingman_status_gauge_loaded=0;
149 #define HUD_WINGMAN_STATUS_NONE 0 // wingman doesn't exist
150 #define HUD_WINGMAN_STATUS_DEAD 1 // wingman has died
151 #define HUD_WINGMAN_STATUS_ALIVE 2 // wingman is in the mission
152 #define HUD_WINGMAN_STATUS_NOT_HERE 3 // wingman hasn't arrived, or has departed
154 #define HUD_WINGMAN_MAX_WINGS 6 // upped to 6 to hold room for Zeta in team v team.
155 #define HUD_WINGMAN_MAX_SHIPS_PER_WINGS 6
157 typedef struct Wingman_status
159 int ignore; // set to 1 when we should ignore this item -- used in team v. team
161 float hull[HUD_WINGMAN_MAX_SHIPS_PER_WINGS]; // 0.0 -> 1.0
162 int status[HUD_WINGMAN_MAX_SHIPS_PER_WINGS]; // HUD_WINGMAN_STATUS_*
165 wingman_status HUD_wingman_status[HUD_WINGMAN_MAX_WINGS];
167 #define HUD_WINGMAN_UPDATE_STATUS_INTERVAL 200
168 static int HUD_wingman_update_timer;
170 static int HUD_wingman_flash_duration[HUD_WINGMAN_MAX_WINGS][HUD_WINGMAN_MAX_SHIPS_PER_WINGS];
171 static int HUD_wingman_flash_next[HUD_WINGMAN_MAX_WINGS][HUD_WINGMAN_MAX_SHIPS_PER_WINGS];
172 static int HUD_wingman_flash_is_bright;
174 // coords to draw wingman status icons, for 1-5 wings (0-4)
175 int HUD_wingman_left_coords[GR_NUM_RESOLUTIONS][5][2] = {
177 {550, 144}, // where to draw the left part of gauge if we have 1 wing
178 {550, 144}, // "" 2 wings
179 {515, 144}, // "" 3 wings
180 {480, 144}, // "" 4 wings
181 {445, 144} // "" 5 wings
191 int HUD_wingman_middle_coords[GR_NUM_RESOLUTIONS][5][2] = {
193 {0, 0}, // we never draw this for 1 wing
194 {0, 0}, // we never draw this for 2 wings
195 {586, 144}, // where to draw the _first_ middle gauge for 3 wings
196 {551, 144}, // "" 4 wings
197 {516, 144} // "" 5 wings
207 int HUD_wingman_right_coords[GR_NUM_RESOLUTIONS][5][2] = {
209 {621, 144}, // always drawn in the same spot
224 // special coordinates if only one wing is present
225 int HUD_wingman_status_single_coords[GR_NUM_RESOLUTIONS][4][2] =
228 {589,159}, // where to draw dots 1 - 4 if we have only one wing present (special case)
241 int HUD_wingman_status_name_coords[GR_NUM_RESOLUTIONS][HUD_WINGMAN_MAX_WINGS][2] =
244 {459,185}, // duplicated the first item because we only ever display 5 items
252 {841,185}, // duplicated the first item because we only ever display 5 items
261 int HUD_wingman_status_coords[GR_NUM_RESOLUTIONS][HUD_WINGMAN_MAX_WINGS][HUD_WINGMAN_MAX_SHIPS_PER_WINGS][2] =
263 // duplicated first set of data because we will only ever display up to 5 wings
266 {{467,159}, // ship 1
271 {478,175}}, // ship 6
358 int hud_wingman_status_wing_index(const char *wing_name)
361 if ( !stricmp("alpha", wing_name) ) {
363 } else if ( !stricmp("beta", wing_name) ) {
365 } else if ( !stricmp("gamma", wing_name) ) {
367 } else if ( !stricmp("delta", wing_name) ) {
369 } else if ( !stricmp("epsilon", wing_name) ) {
371 } else if ( (Game_mode & GM_MULTIPLAYER) && IS_MISSION_MULTI_TEAMS && !stricmp("zeta", wing_name) ) {
379 // flag a player wing ship as destroyed
380 void hud_set_wingman_status_dead(int wing_index, int wing_pos)
382 Assert(wing_index >= 0 && wing_index < HUD_WINGMAN_MAX_WINGS);
383 Assert(wing_pos >= 0 && wing_index < HUD_WINGMAN_MAX_SHIPS_PER_WINGS);
385 HUD_wingman_status[wing_index].status[wing_pos] = HUD_WINGMAN_STATUS_DEAD;
388 // flags a given player wing ship as departed
389 void hud_set_wingman_status_departed(int wing_index, int wing_pos)
391 Assert(wing_index >= 0 && wing_index < HUD_WINGMAN_MAX_WINGS);
392 Assert(wing_pos >= 0 && wing_index < HUD_WINGMAN_MAX_SHIPS_PER_WINGS);
394 HUD_wingman_status[wing_index].status[wing_pos] = HUD_WINGMAN_STATUS_NOT_HERE;
397 // flags a given player wing ship as not existing
398 void hud_set_wingman_status_none( int wing_index, int wing_pos)
402 Assert(wing_index >= 0 && wing_index < HUD_WINGMAN_MAX_WINGS);
403 Assert(wing_pos >= 0 && wing_index < HUD_WINGMAN_MAX_SHIPS_PER_WINGS);
405 HUD_wingman_status[wing_index].status[wing_pos] = HUD_WINGMAN_STATUS_NONE;
408 for ( i = 0; i < HUD_WINGMAN_MAX_SHIPS_PER_WINGS; i++ ) {
409 if ( HUD_wingman_status[wing_index].status[i] != HUD_WINGMAN_STATUS_NONE ) {
415 HUD_wingman_status[wing_index].used = used;
418 // flags a given player wing ship as "alive" (for multiplayer respawns )
419 void hud_set_wingman_status_alive( int wing_index, int wing_pos)
421 Assert(wing_index >= 0 && wing_index < HUD_WINGMAN_MAX_WINGS);
422 Assert(wing_pos >= 0 && wing_index < HUD_WINGMAN_MAX_SHIPS_PER_WINGS);
424 HUD_wingman_status[wing_index].status[wing_pos] = HUD_WINGMAN_STATUS_ALIVE;
427 // get the hull percent for a specific ship, return value 0.0 -> 1.0
428 float hud_get_ship_hull_percent(int ship_index)
433 ship_objp = &Objects[Ships[ship_index].objnum];
434 sip = &Ship_info[Ships[ship_index].ship_info_index];
436 return (ship_objp->hull_strength / sip->initial_hull_strength);
439 void hud_wingman_status_init_late_wings()
442 int i, j, wing_index;
444 for ( i = 0; i < num_wings; i++ ) {
445 wing_index = hud_wingman_status_wing_index(Wings[i].name);
447 if ( (wing_index >= 0) && (Wings[i].total_arrived_count == 0) ) {
448 HUD_wingman_status[wing_index].used = 1;
449 for (j = 0; j < Wings[i].wave_count; j++) {
450 HUD_wingman_status[wing_index].status[j] = HUD_WINGMAN_STATUS_NOT_HERE;
457 // function which marks the other team wing as not used for the wingman status gauge
458 void hud_wingman_kill_multi_teams()
462 // do nothing in single player or non team v. team games
463 if ( Game_mode & GM_NORMAL )
466 if ( !IS_MISSION_MULTI_TEAMS )
470 if ( Net_player->p_info.team == 0 )
471 wing_index = hud_wingman_status_wing_index(NOX("zeta"));
472 else if ( Net_player->p_info.team == 1 )
473 wing_index = hud_wingman_status_wing_index(NOX("alpha"));
475 if ( wing_index == -1 )
478 HUD_wingman_status[wing_index].ignore = 1;
482 // called once per level to init the wingman status gauge. Loads in the frames the first time
483 void hud_init_wingman_status_gauge()
487 if ( !Wingman_status_gauge_loaded ) {
489 for ( i = 0; i < HUD_WINGMAN_STATUS_NUM_FRAMES; i++ ) {
490 Wingman_status_frames[i].first_frame = bm_load_animation(Wingman_status_filenames[gr_screen.res][i], &Wingman_status_frames[i].num_frames);
491 if ( Wingman_status_frames[i].first_frame == -1 ) {
492 Warning(LOCATION, NOX("Error loading Wingman_status_filenames[gr_screen.res][i]'\n"));
496 Wingman_status_gauge_loaded = 1;
499 hud_wingman_status_init_flash();
501 HUD_wingman_update_timer=timestamp(0); // update status right away
503 for (i = 0; i < HUD_WINGMAN_MAX_WINGS; i++) {
504 HUD_wingman_status[i].ignore = 0;
505 HUD_wingman_status[i].used = 0;
506 for ( j = 0; j < HUD_WINGMAN_MAX_SHIPS_PER_WINGS; j++ ) {
507 HUD_wingman_status[i].status[j] = HUD_WINGMAN_STATUS_NONE;
511 hud_wingman_status_init_late_wings();
512 hud_wingman_kill_multi_teams();
513 hud_wingman_status_update();
516 // Update the status of the wingman status
517 void hud_wingman_status_update()
519 if ( timestamp_elapsed(HUD_wingman_update_timer) ) {
520 int wing_index,wing_pos;
525 HUD_wingman_update_timer=timestamp(HUD_WINGMAN_UPDATE_STATUS_INTERVAL);
527 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
528 ship_objp = &Objects[so->objnum];
529 shipp = &Ships[ship_objp->instance];
531 wing_index = shipp->wing_status_wing_index;
532 wing_pos = shipp->wing_status_wing_pos;
534 if ( (wing_index >= 0) && (wing_pos >= 0) ) {
536 HUD_wingman_status[wing_index].used = 1;
537 if (!(shipp->flags & SF_DEPARTING) ) {
538 HUD_wingman_status[wing_index].status[wing_pos] = HUD_WINGMAN_STATUS_ALIVE;
540 HUD_wingman_status[wing_index].hull[wing_pos] = hud_get_ship_hull_percent(ship_objp->instance);
541 if ( HUD_wingman_status[wing_index].hull[wing_pos] <= 0 ) {
542 HUD_wingman_status[wing_index].status[wing_pos] = HUD_WINGMAN_STATUS_DEAD;
549 void hud_wingman_status_blit_left_frame(int num_wings_to_draw)
553 // draw left side of frame
554 if((num_wings_to_draw < 1) || (num_wings_to_draw > 5)){
558 sx = HUD_wingman_left_coords[gr_screen.res][num_wings_to_draw - 1][0];
559 sy = HUD_wingman_left_coords[gr_screen.res][num_wings_to_draw - 1][1];
560 bitmap = Wingman_status_frames[BACKGROUND_LEFT].first_frame;
563 GR_AABITMAP(bitmap, sx, sy);
564 // gr_set_bitmap(bitmap);
565 // gr_aabitmap(sx, sy);
568 // write "wingmen" on gauge
569 gr_string(sx+2, sy+2, XSTR( "wingmen", 352));
572 void hud_wingman_status_blit_middle_frame(int num_wings_to_draw)
577 bitmap = Wingman_status_frames[BACKGROUND_MIDDLE].first_frame;
582 // don't draw for 1 or 2 wings
583 if((num_wings == 1) || (num_wings == 2)){
587 // draw left side of frame
588 if((num_wings_to_draw < 1) || (num_wings_to_draw > 5)){
594 for(idx=num_wings_to_draw; idx>=3; idx--){
595 sx = HUD_wingman_middle_coords[gr_screen.res][idx - 1][0];
596 sy = HUD_wingman_middle_coords[gr_screen.res][idx - 1][1];
597 GR_AABITMAP(bitmap, sx, sy);
601 void hud_wingman_status_blit_right_frame(int num_wings_to_draw)
605 // draw left side of frame
606 if((num_wings_to_draw < 1) || (num_wings_to_draw > 5)){
611 sx = HUD_wingman_right_coords[gr_screen.res][num_wings_to_draw - 1][0];
612 sy = HUD_wingman_right_coords[gr_screen.res][num_wings_to_draw - 1][1];
613 bitmap = Wingman_status_frames[BACKGROUND_RIGHT].first_frame;
616 GR_AABITMAP(bitmap, sx, sy);
620 void hud_wingman_status_blit_dots(int wing_index, int screen_index, int num_wings_to_draw)
622 int i, sx, sy, is_bright, bitmap = -1, screen_pos;
625 Wingman_status_frames[WINGMAN_STATUS_DOTS].first_frame;
628 if ( Wingman_status_frames[WINGMAN_STATUS_DOTS].first_frame < 0 ) {
632 if ( Wingman_status_frames[WINGMAN_STATUS_NAMES].first_frame < 0 ) {
636 screen_pos = screen_index + (HUD_WINGMAN_MAX_WINGS - num_wings_to_draw);
639 for ( i = 0; i < HUD_WINGMAN_MAX_SHIPS_PER_WINGS; i++ ) {
641 if ( hud_wingman_status_maybe_flash(wing_index, i) ) {
647 switch( HUD_wingman_status[wing_index].status[i] ) {
649 case HUD_WINGMAN_STATUS_ALIVE:
650 bitmap = Wingman_status_frames[WINGMAN_STATUS_DOTS].first_frame;
651 if ( HUD_wingman_status[wing_index].hull[i] > 0.5f ) {
652 // gr_set_color_fast(&IFF_colors[IFF_COLOR_FRIENDLY][is_bright]);
654 hud_set_gauge_color(HUD_WINGMEN_STATUS, is_bright ? HUD_C_BRIGHT : HUD_C_NORMAL);
656 gr_set_color_fast(&IFF_colors[IFF_COLOR_HOSTILE][is_bright]);
660 case HUD_WINGMAN_STATUS_DEAD:
661 gr_set_color_fast(&IFF_colors[IFF_COLOR_HOSTILE][0]);
662 bitmap = Wingman_status_frames[WINGMAN_STATUS_DOTS].first_frame+1;
665 case HUD_WINGMAN_STATUS_NOT_HERE:
666 // gr_set_color_fast(&IFF_colors[IFF_COLOR_FRIENDLY][0]);
667 hud_set_gauge_color(HUD_WINGMEN_STATUS, is_bright ? HUD_C_BRIGHT : HUD_C_NORMAL);
668 bitmap = Wingman_status_frames[WINGMAN_STATUS_DOTS].first_frame+1;
677 if ( num_wings_to_draw == 1 ) {
678 sx = HUD_wingman_status_single_coords[gr_screen.res][i][0];
679 sy = HUD_wingman_status_single_coords[gr_screen.res][i][1];
681 sx = HUD_wingman_status_coords[gr_screen.res][screen_pos][i][0];
682 sy = HUD_wingman_status_coords[gr_screen.res][screen_pos][i][1];
686 GR_AABITMAP(bitmap, sx, sy);
691 bitmap = Wingman_status_frames[WINGMAN_STATUS_NAMES].first_frame + wing_index;
693 if ( num_wings_to_draw == 1 ) {
694 sx = HUD_wingman_status_single_coords[gr_screen.res][0][0] - 8;
695 sy = HUD_wingman_status_single_coords[gr_screen.res][0][1] + 26;
697 sx = HUD_wingman_status_name_coords[gr_screen.res][screen_pos][0];
698 sy = HUD_wingman_status_name_coords[gr_screen.res][screen_pos][1];
701 // hud_set_default_color();
702 hud_set_gauge_color(HUD_WINGMEN_STATUS);
704 GR_AABITMAP(bitmap, sx, sy);
705 // gr_set_bitmap(bitmap);
706 // gr_aabitmap(sx, sy);
711 int hud_wingman_status_wingmen_exist(int num_wings_to_draw)
715 switch ( num_wings_to_draw ) {
720 for (i = 0; i < HUD_WINGMAN_MAX_WINGS; i++) {
721 if ( HUD_wingman_status[i].used > 0 ) {
722 for ( j = 0; j < HUD_WINGMAN_MAX_SHIPS_PER_WINGS; j++ ) {
723 if ( HUD_wingman_status[i].status[j] != HUD_WINGMAN_STATUS_NONE ) {
743 // Draw the wingman status gauge
744 void hud_wingman_status_render()
746 int i, count, num_wings_to_draw = 0;
748 for (i = 0; i < HUD_WINGMAN_MAX_WINGS; i++) {
749 if ( (HUD_wingman_status[i].used > 0) && (HUD_wingman_status[i].ignore == 0) ) {
754 if ( !hud_wingman_status_wingmen_exist(num_wings_to_draw) ) {
758 // hud_set_default_color();
759 hud_set_gauge_color(HUD_WINGMEN_STATUS);
761 // blit the background frames
762 hud_wingman_status_blit_left_frame(num_wings_to_draw);
763 hud_wingman_status_blit_middle_frame(num_wings_to_draw);
764 hud_wingman_status_blit_right_frame(num_wings_to_draw);
767 for (i = 0; i < HUD_WINGMAN_MAX_WINGS; i++) {
768 if ( (HUD_wingman_status[i].used <= 0) || (HUD_wingman_status[i].ignore == 1) ) {
772 hud_wingman_status_blit_dots(i, count, num_wings_to_draw);
777 // init the flashing timers for the wingman status gauge
778 void hud_wingman_status_init_flash()
782 for ( i = 0; i < HUD_WINGMAN_MAX_WINGS; i++ ) {
783 for ( j = 0; j < HUD_WINGMAN_MAX_SHIPS_PER_WINGS; j++ ) {
784 HUD_wingman_flash_duration[i][j] = timestamp(0);
785 HUD_wingman_flash_next[i][j] = timestamp(0);
789 HUD_wingman_flash_is_bright = 0;
792 // start the targetbox item flashing for TBOX_FLASH_DURATION
793 void hud_wingman_status_start_flash(int wing_index, int wing_pos)
795 HUD_wingman_flash_duration[wing_index][wing_pos] = timestamp(TBOX_FLASH_DURATION);
798 // set the color for flashing dot
799 // exit: 1 => set bright color
800 // 0 => set default color
801 int hud_wingman_status_maybe_flash(int wing_index, int wing_pos)
803 int index, draw_bright=0;
805 index = wing_index*HUD_WINGMAN_MAX_SHIPS_PER_WINGS + wing_pos;
807 if ( !timestamp_elapsed(HUD_wingman_flash_duration[wing_index][wing_pos]) ) {
808 if ( timestamp_elapsed(HUD_wingman_flash_next[wing_index][wing_pos]) ) {
809 HUD_wingman_flash_next[wing_index][wing_pos] = timestamp(TBOX_FLASH_INTERVAL);
810 HUD_wingman_flash_is_bright ^= (1<<index); // toggle between default and bright frames
813 if ( HUD_wingman_flash_is_bright & (1<<index) ) {
821 int hud_wingman_status_wing_pos(int shipnum, int wing_status_index, wing *wingp)
823 int i, wing_pos = -1;
825 for (i = 0; i < wingp->wave_count; i++) {
826 if ( wingp->ship_index[i] == shipnum ) {
835 void hud_wingman_status_set_index(int shipnum)
837 int wing_index, wing_pos;
845 shipp = &Ships[shipnum];
847 if ( shipp->wingnum < 0 ) {
851 wingp = &Wings[shipp->wingnum];
853 // Check for Alpha, Beta, Gamma, Delta or Epsilon wings
854 wing_index = hud_wingman_status_wing_index(wingp->name);
855 if ( wing_index < 0 ) {
859 shipp->wing_status_wing_index = (char)wing_index;
861 wing_pos = hud_wingman_status_wing_pos(shipnum, wing_index, wingp);
863 shipp->wing_status_wing_pos = (char)wing_pos;
866 void hudwingmanstatus_page_in()
869 for ( i = 0; i < HUD_WINGMAN_STATUS_NUM_FRAMES; i++ ) {
870 bm_page_in_aabitmap( Wingman_status_frames[i].first_frame, Wingman_status_frames[i].num_frames );
874 int get_blip_bitmap()
876 return Wingman_status_frames[WINGMAN_STATUS_DOTS].first_frame+1;