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.2 2002/06/09 04:41:21 relnev
19 * added copyright header
21 * Revision 1.1.1.1 2002/05/03 03:28:09 root
25 * 7 6/10/99 3:43p Dave
26 * Do a better job of syncing text colors to HUD gauges.
28 * 6 1/07/99 9:06a Jasen
29 * coords blah blah blah
31 * 5 1/06/99 4:03p Dave
32 * Added better comments to wingman status gauge coords.
34 * 4 12/28/98 3:17p Dave
35 * Support for multiple hud bitmap filenames for hi-res mode.
37 * 3 12/21/98 5:03p Dave
38 * Modified all hud elements to be multi-resolution friendly.
40 * 2 10/07/98 10:53a Dave
43 * 1 10/07/98 10:49a Dave
45 * 17 8/25/98 1:48p Dave
46 * First rev of EMP effect. Player side stuff basically done. Next comes
49 * 16 6/09/98 5:18p Lawrance
50 * French/German localization
52 * 15 6/09/98 10:31a Hoffoss
53 * Created index numbers for all xstr() references. Any new xstr() stuff
54 * added from here on out should be added to the end if the list. The
55 * current list count can be found in FreeSpace.cpp (search for
58 * 14 6/01/98 11:43a John
59 * JAS & MK: Classified all strings for localization.
61 * 13 5/06/98 2:46p Mike
62 * Modify num-ships-attacking system.
64 * 12 4/17/98 1:42p Allender
65 * fixed optimized build warning
67 * 11 4/16/98 2:56p Allender
68 * multiple wings were not working after I added Zeta wing
70 * 10 4/15/98 11:09p Allender
71 * status gaugs works for team v. team
73 * 9 4/14/98 1:35a Allender
74 * (blindly) work on wingman status gauge for team v team multiplayer
76 * 8 4/01/98 9:21p John
77 * Made NDEBUG, optimized build with no warnings or errors.
79 * 7 3/31/98 11:46p Lawrance
80 * Fix several bugs related to departing ships.
82 * 6 3/26/98 5:26p John
83 * added new paging code. nonfunctional.
85 * 5 3/22/98 11:13p Allender
86 * work with respawning -- save parse object so ship can be correctly
87 * restored. Restore wingman status information so gauge updates
90 * 4 3/20/98 10:26a Lawrance
91 * Don't display gauge if Alpha1 is only ship
93 * 3 3/18/98 12:03p John
94 * Marked all the new strings as externalized or not.
96 * 2 3/14/98 4:59p Lawrance
97 * Totally rework HUD wingman status gauge to work with 5 arbitrary wings
99 * 1 3/14/98 8:23a Lawrance
105 #include "hudwingmanstatus.h"
110 #include "hudtargetbox.h"
111 #include "linklist.h"
112 #include "systemvars.h"
116 #define HUD_WINGMAN_STATUS_NUM_FRAMES 5
117 #define BACKGROUND_LEFT 0
118 #define BACKGROUND_MIDDLE 1
119 #define BACKGROUND_RIGHT 2
120 #define WINGMAN_STATUS_DOTS 3
121 #define WINGMAN_STATUS_NAMES 4
123 static char *Wingman_status_filenames[GR_NUM_RESOLUTIONS][HUD_WINGMAN_STATUS_NUM_FRAMES] =
143 static hud_frames Wingman_status_frames[HUD_WINGMAN_STATUS_NUM_FRAMES];
144 static int Wingman_status_gauge_loaded=0;
146 #define HUD_WINGMAN_STATUS_NONE 0 // wingman doesn't exist
147 #define HUD_WINGMAN_STATUS_DEAD 1 // wingman has died
148 #define HUD_WINGMAN_STATUS_ALIVE 2 // wingman is in the mission
149 #define HUD_WINGMAN_STATUS_NOT_HERE 3 // wingman hasn't arrived, or has departed
151 #define HUD_WINGMAN_MAX_WINGS 6 // upped to 6 to hold room for Zeta in team v team.
152 #define HUD_WINGMAN_MAX_SHIPS_PER_WINGS 6
154 typedef struct Wingman_status
156 int ignore; // set to 1 when we should ignore this item -- used in team v. team
158 float hull[HUD_WINGMAN_MAX_SHIPS_PER_WINGS]; // 0.0 -> 1.0
159 int status[HUD_WINGMAN_MAX_SHIPS_PER_WINGS]; // HUD_WINGMAN_STATUS_*
162 wingman_status HUD_wingman_status[HUD_WINGMAN_MAX_WINGS];
164 #define HUD_WINGMAN_UPDATE_STATUS_INTERVAL 200
165 static int HUD_wingman_update_timer;
167 static int HUD_wingman_flash_duration[HUD_WINGMAN_MAX_WINGS][HUD_WINGMAN_MAX_SHIPS_PER_WINGS];
168 static int HUD_wingman_flash_next[HUD_WINGMAN_MAX_WINGS][HUD_WINGMAN_MAX_SHIPS_PER_WINGS];
169 static int HUD_wingman_flash_is_bright;
171 // coords to draw wingman status icons, for 1-5 wings (0-4)
172 int HUD_wingman_left_coords[GR_NUM_RESOLUTIONS][5][2] = {
174 {550, 144}, // where to draw the left part of gauge if we have 1 wing
175 {550, 144}, // "" 2 wings
176 {515, 144}, // "" 3 wings
177 {480, 144}, // "" 4 wings
178 {445, 144} // "" 5 wings
188 int HUD_wingman_middle_coords[GR_NUM_RESOLUTIONS][5][2] = {
190 {0, 0}, // we never draw this for 1 wing
191 {0, 0}, // we never draw this for 2 wings
192 {586, 144}, // where to draw the _first_ middle gauge for 3 wings
193 {551, 144}, // "" 4 wings
194 {516, 144} // "" 5 wings
204 int HUD_wingman_right_coords[GR_NUM_RESOLUTIONS][5][2] = {
206 {621, 144}, // always drawn in the same spot
221 // special coordinates if only one wing is present
222 int HUD_wingman_status_single_coords[GR_NUM_RESOLUTIONS][4][2] =
225 {589,159}, // where to draw dots 1 - 4 if we have only one wing present (special case)
238 int HUD_wingman_status_name_coords[GR_NUM_RESOLUTIONS][HUD_WINGMAN_MAX_WINGS][2] =
241 {459,185}, // duplicated the first item because we only ever display 5 items
249 {841,185}, // duplicated the first item because we only ever display 5 items
258 int HUD_wingman_status_coords[GR_NUM_RESOLUTIONS][HUD_WINGMAN_MAX_WINGS][HUD_WINGMAN_MAX_SHIPS_PER_WINGS][2] =
260 // duplicated first set of data because we will only ever display up to 5 wings
263 {{467,159}, // ship 1
268 {478,175}}, // ship 6
355 int hud_wingman_status_wing_index(char *wing_name)
358 if ( !stricmp("alpha", wing_name) ) {
360 } else if ( !stricmp("beta", wing_name) ) {
362 } else if ( !stricmp("gamma", wing_name) ) {
364 } else if ( !stricmp("delta", wing_name) ) {
366 } else if ( !stricmp("epsilon", wing_name) ) {
368 } else if ( (Game_mode & GM_MULTIPLAYER) && IS_MISSION_MULTI_TEAMS && !stricmp("zeta", wing_name) ) {
376 // flag a player wing ship as destroyed
377 void hud_set_wingman_status_dead(int wing_index, int wing_pos)
379 Assert(wing_index >= 0 && wing_index < HUD_WINGMAN_MAX_WINGS);
380 Assert(wing_pos >= 0 && wing_index < HUD_WINGMAN_MAX_SHIPS_PER_WINGS);
382 HUD_wingman_status[wing_index].status[wing_pos] = HUD_WINGMAN_STATUS_DEAD;
385 // flags a given player wing ship as departed
386 void hud_set_wingman_status_departed(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_NOT_HERE;
394 // flags a given player wing ship as not existing
395 void hud_set_wingman_status_none( int wing_index, int wing_pos)
399 Assert(wing_index >= 0 && wing_index < HUD_WINGMAN_MAX_WINGS);
400 Assert(wing_pos >= 0 && wing_index < HUD_WINGMAN_MAX_SHIPS_PER_WINGS);
402 HUD_wingman_status[wing_index].status[wing_pos] = HUD_WINGMAN_STATUS_NONE;
405 for ( i = 0; i < HUD_WINGMAN_MAX_SHIPS_PER_WINGS; i++ ) {
406 if ( HUD_wingman_status[wing_index].status[i] != HUD_WINGMAN_STATUS_NONE ) {
412 HUD_wingman_status[wing_index].used = used;
415 // flags a given player wing ship as "alive" (for multiplayer respawns )
416 void hud_set_wingman_status_alive( int wing_index, int wing_pos)
418 Assert(wing_index >= 0 && wing_index < HUD_WINGMAN_MAX_WINGS);
419 Assert(wing_pos >= 0 && wing_index < HUD_WINGMAN_MAX_SHIPS_PER_WINGS);
421 HUD_wingman_status[wing_index].status[wing_pos] = HUD_WINGMAN_STATUS_ALIVE;
424 // get the hull percent for a specific ship, return value 0.0 -> 1.0
425 float hud_get_ship_hull_percent(int ship_index)
430 ship_objp = &Objects[Ships[ship_index].objnum];
431 sip = &Ship_info[Ships[ship_index].ship_info_index];
433 return (ship_objp->hull_strength / sip->initial_hull_strength);
436 void hud_wingman_status_init_late_wings()
439 int i, j, wing_index;
441 for ( i = 0; i < num_wings; i++ ) {
442 wing_index = hud_wingman_status_wing_index(Wings[i].name);
444 if ( (wing_index >= 0) && (Wings[i].total_arrived_count == 0) ) {
445 HUD_wingman_status[wing_index].used = 1;
446 for (j = 0; j < Wings[i].wave_count; j++) {
447 HUD_wingman_status[wing_index].status[j] = HUD_WINGMAN_STATUS_NOT_HERE;
454 // function which marks the other team wing as not used for the wingman status gauge
455 void hud_wingman_kill_multi_teams()
459 // do nothing in single player or non team v. team games
460 if ( Game_mode & GM_NORMAL )
463 if ( !IS_MISSION_MULTI_TEAMS )
467 if ( Net_player->p_info.team == 0 )
468 wing_index = hud_wingman_status_wing_index(NOX("zeta"));
469 else if ( Net_player->p_info.team == 1 )
470 wing_index = hud_wingman_status_wing_index(NOX("alpha"));
472 if ( wing_index == -1 )
475 HUD_wingman_status[wing_index].ignore = 1;
479 // called once per level to init the wingman status gauge. Loads in the frames the first time
480 void hud_init_wingman_status_gauge()
484 if ( !Wingman_status_gauge_loaded ) {
486 for ( i = 0; i < HUD_WINGMAN_STATUS_NUM_FRAMES; i++ ) {
487 Wingman_status_frames[i].first_frame = bm_load_animation(Wingman_status_filenames[gr_screen.res][i], &Wingman_status_frames[i].num_frames);
488 if ( Wingman_status_frames[i].first_frame == -1 ) {
489 Warning(LOCATION, NOX("Error loading Wingman_status_filenames[gr_screen.res][i]'\n"));
493 Wingman_status_gauge_loaded = 1;
496 hud_wingman_status_init_flash();
498 HUD_wingman_update_timer=timestamp(0); // update status right away
500 for (i = 0; i < HUD_WINGMAN_MAX_WINGS; i++) {
501 HUD_wingman_status[i].ignore = 0;
502 HUD_wingman_status[i].used = 0;
503 for ( j = 0; j < HUD_WINGMAN_MAX_SHIPS_PER_WINGS; j++ ) {
504 HUD_wingman_status[i].status[j] = HUD_WINGMAN_STATUS_NONE;
508 hud_wingman_status_init_late_wings();
509 hud_wingman_kill_multi_teams();
510 hud_wingman_status_update();
513 // Update the status of the wingman status
514 void hud_wingman_status_update()
516 if ( timestamp_elapsed(HUD_wingman_update_timer) ) {
517 int wing_index,wing_pos;
522 HUD_wingman_update_timer=timestamp(HUD_WINGMAN_UPDATE_STATUS_INTERVAL);
524 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
525 ship_objp = &Objects[so->objnum];
526 shipp = &Ships[ship_objp->instance];
528 wing_index = shipp->wing_status_wing_index;
529 wing_pos = shipp->wing_status_wing_pos;
531 if ( (wing_index >= 0) && (wing_pos >= 0) ) {
533 HUD_wingman_status[wing_index].used = 1;
534 if (!(shipp->flags & SF_DEPARTING) ) {
535 HUD_wingman_status[wing_index].status[wing_pos] = HUD_WINGMAN_STATUS_ALIVE;
537 HUD_wingman_status[wing_index].hull[wing_pos] = hud_get_ship_hull_percent(ship_objp->instance);
538 if ( HUD_wingman_status[wing_index].hull[wing_pos] <= 0 ) {
539 HUD_wingman_status[wing_index].status[wing_pos] = HUD_WINGMAN_STATUS_DEAD;
546 void hud_wingman_status_blit_left_frame(int num_wings_to_draw)
550 // draw left side of frame
551 if((num_wings_to_draw < 1) || (num_wings_to_draw > 5)){
555 sx = HUD_wingman_left_coords[gr_screen.res][num_wings_to_draw - 1][0];
556 sy = HUD_wingman_left_coords[gr_screen.res][num_wings_to_draw - 1][1];
557 bitmap = Wingman_status_frames[BACKGROUND_LEFT].first_frame;
560 GR_AABITMAP(bitmap, sx, sy);
561 // gr_set_bitmap(bitmap);
562 // gr_aabitmap(sx, sy);
565 // write "wingmen" on gauge
566 gr_string(sx+2, sy+2, XSTR( "wingmen", 352));
569 void hud_wingman_status_blit_middle_frame(int num_wings_to_draw)
574 bitmap = Wingman_status_frames[BACKGROUND_MIDDLE].first_frame;
579 // don't draw for 1 or 2 wings
580 if((num_wings == 1) || (num_wings == 2)){
584 // draw left side of frame
585 if((num_wings_to_draw < 1) || (num_wings_to_draw > 5)){
591 for(idx=num_wings_to_draw; idx>=3; idx--){
592 sx = HUD_wingman_middle_coords[gr_screen.res][idx - 1][0];
593 sy = HUD_wingman_middle_coords[gr_screen.res][idx - 1][1];
594 GR_AABITMAP(bitmap, sx, sy);
598 void hud_wingman_status_blit_right_frame(int num_wings_to_draw)
602 // draw left side of frame
603 if((num_wings_to_draw < 1) || (num_wings_to_draw > 5)){
608 sx = HUD_wingman_right_coords[gr_screen.res][num_wings_to_draw - 1][0];
609 sy = HUD_wingman_right_coords[gr_screen.res][num_wings_to_draw - 1][1];
610 bitmap = Wingman_status_frames[BACKGROUND_RIGHT].first_frame;
613 GR_AABITMAP(bitmap, sx, sy);
617 void hud_wingman_status_blit_dots(int wing_index, int screen_index, int num_wings_to_draw)
619 int i, sx, sy, is_bright, bitmap = -1, screen_pos;
621 Wingman_status_frames[WINGMAN_STATUS_DOTS].first_frame;
623 if ( Wingman_status_frames[WINGMAN_STATUS_DOTS].first_frame < 0 ) {
627 if ( Wingman_status_frames[WINGMAN_STATUS_NAMES].first_frame < 0 ) {
631 screen_pos = screen_index + (HUD_WINGMAN_MAX_WINGS - num_wings_to_draw);
634 for ( i = 0; i < HUD_WINGMAN_MAX_SHIPS_PER_WINGS; i++ ) {
636 if ( hud_wingman_status_maybe_flash(wing_index, i) ) {
642 switch( HUD_wingman_status[wing_index].status[i] ) {
644 case HUD_WINGMAN_STATUS_ALIVE:
645 bitmap = Wingman_status_frames[WINGMAN_STATUS_DOTS].first_frame;
646 if ( HUD_wingman_status[wing_index].hull[i] > 0.5f ) {
647 // gr_set_color_fast(&IFF_colors[IFF_COLOR_FRIENDLY][is_bright]);
649 hud_set_gauge_color(HUD_WINGMEN_STATUS, is_bright ? HUD_C_BRIGHT : HUD_C_NORMAL);
651 gr_set_color_fast(&IFF_colors[IFF_COLOR_HOSTILE][is_bright]);
655 case HUD_WINGMAN_STATUS_DEAD:
656 gr_set_color_fast(&IFF_colors[IFF_COLOR_HOSTILE][0]);
657 bitmap = Wingman_status_frames[WINGMAN_STATUS_DOTS].first_frame+1;
660 case HUD_WINGMAN_STATUS_NOT_HERE:
661 // gr_set_color_fast(&IFF_colors[IFF_COLOR_FRIENDLY][0]);
662 hud_set_gauge_color(HUD_WINGMEN_STATUS, is_bright ? HUD_C_BRIGHT : HUD_C_NORMAL);
663 bitmap = Wingman_status_frames[WINGMAN_STATUS_DOTS].first_frame+1;
672 if ( num_wings_to_draw == 1 ) {
673 sx = HUD_wingman_status_single_coords[gr_screen.res][i][0];
674 sy = HUD_wingman_status_single_coords[gr_screen.res][i][1];
676 sx = HUD_wingman_status_coords[gr_screen.res][screen_pos][i][0];
677 sy = HUD_wingman_status_coords[gr_screen.res][screen_pos][i][1];
681 GR_AABITMAP(bitmap, sx, sy);
686 bitmap = Wingman_status_frames[WINGMAN_STATUS_NAMES].first_frame + wing_index;
688 if ( num_wings_to_draw == 1 ) {
689 sx = HUD_wingman_status_single_coords[gr_screen.res][0][0] - 8;
690 sy = HUD_wingman_status_single_coords[gr_screen.res][0][1] + 26;
692 sx = HUD_wingman_status_name_coords[gr_screen.res][screen_pos][0];
693 sy = HUD_wingman_status_name_coords[gr_screen.res][screen_pos][1];
696 // hud_set_default_color();
697 hud_set_gauge_color(HUD_WINGMEN_STATUS);
699 GR_AABITMAP(bitmap, sx, sy);
700 // gr_set_bitmap(bitmap);
701 // gr_aabitmap(sx, sy);
706 int hud_wingman_status_wingmen_exist(int num_wings_to_draw)
710 switch ( num_wings_to_draw ) {
715 for (i = 0; i < HUD_WINGMAN_MAX_WINGS; i++) {
716 if ( HUD_wingman_status[i].used > 0 ) {
717 for ( j = 0; j < HUD_WINGMAN_MAX_SHIPS_PER_WINGS; j++ ) {
718 if ( HUD_wingman_status[i].status[j] != HUD_WINGMAN_STATUS_NONE ) {
738 // Draw the wingman status gauge
739 void hud_wingman_status_render()
741 int i, count, num_wings_to_draw = 0;
743 for (i = 0; i < HUD_WINGMAN_MAX_WINGS; i++) {
744 if ( (HUD_wingman_status[i].used > 0) && (HUD_wingman_status[i].ignore == 0) ) {
749 if ( !hud_wingman_status_wingmen_exist(num_wings_to_draw) ) {
753 // hud_set_default_color();
754 hud_set_gauge_color(HUD_WINGMEN_STATUS);
756 // blit the background frames
757 hud_wingman_status_blit_left_frame(num_wings_to_draw);
758 hud_wingman_status_blit_middle_frame(num_wings_to_draw);
759 hud_wingman_status_blit_right_frame(num_wings_to_draw);
762 for (i = 0; i < HUD_WINGMAN_MAX_WINGS; i++) {
763 if ( (HUD_wingman_status[i].used <= 0) || (HUD_wingman_status[i].ignore == 1) ) {
767 hud_wingman_status_blit_dots(i, count, num_wings_to_draw);
772 // init the flashing timers for the wingman status gauge
773 void hud_wingman_status_init_flash()
777 for ( i = 0; i < HUD_WINGMAN_MAX_WINGS; i++ ) {
778 for ( j = 0; j < HUD_WINGMAN_MAX_SHIPS_PER_WINGS; j++ ) {
779 HUD_wingman_flash_duration[i][j] = timestamp(0);
780 HUD_wingman_flash_next[i][j] = timestamp(0);
784 HUD_wingman_flash_is_bright = 0;
787 // start the targetbox item flashing for TBOX_FLASH_DURATION
788 void hud_wingman_status_start_flash(int wing_index, int wing_pos)
790 HUD_wingman_flash_duration[wing_index][wing_pos] = timestamp(TBOX_FLASH_DURATION);
793 // set the color for flashing dot
794 // exit: 1 => set bright color
795 // 0 => set default color
796 int hud_wingman_status_maybe_flash(int wing_index, int wing_pos)
798 int index, draw_bright=0;
800 index = wing_index*HUD_WINGMAN_MAX_SHIPS_PER_WINGS + wing_pos;
802 if ( !timestamp_elapsed(HUD_wingman_flash_duration[wing_index][wing_pos]) ) {
803 if ( timestamp_elapsed(HUD_wingman_flash_next[wing_index][wing_pos]) ) {
804 HUD_wingman_flash_next[wing_index][wing_pos] = timestamp(TBOX_FLASH_INTERVAL);
805 HUD_wingman_flash_is_bright ^= (1<<index); // toggle between default and bright frames
808 if ( HUD_wingman_flash_is_bright & (1<<index) ) {
816 int hud_wingman_status_wing_pos(int shipnum, int wing_status_index, wing *wingp)
818 int i, wing_pos = -1;
820 for (i = 0; i < wingp->wave_count; i++) {
821 if ( wingp->ship_index[i] == shipnum ) {
830 void hud_wingman_status_set_index(int shipnum)
832 int wing_index, wing_pos;
840 shipp = &Ships[shipnum];
842 if ( shipp->wingnum < 0 ) {
846 wingp = &Wings[shipp->wingnum];
848 // Check for Alpha, Beta, Gamma, Delta or Epsilon wings
849 wing_index = hud_wingman_status_wing_index(wingp->name);
850 if ( wing_index < 0 ) {
854 shipp->wing_status_wing_index = (char)wing_index;
856 wing_pos = hud_wingman_status_wing_pos(shipnum, wing_index, wingp);
858 shipp->wing_status_wing_pos = (char)wing_pos;
861 void hudwingmanstatus_page_in()
864 for ( i = 0; i < HUD_WINGMAN_STATUS_NUM_FRAMES; i++ ) {
865 bm_page_in_aabitmap( Wingman_status_frames[i].first_frame, Wingman_status_frames[i].num_frames );
869 int get_blip_bitmap()
871 return Wingman_status_frames[WINGMAN_STATUS_DOTS].first_frame+1;