2 * $Logfile: /Freespace2/code/Network/multi_dogfight.cpp $
8 * Revision 1.1 2002/05/03 03:28:10 root
12 * 11 8/18/99 11:31a Jefff
13 * mission title on kill matrix
15 * 10 8/17/99 9:55a Dave
16 * Fixed dogfight scoring problem.
18 * 9 8/06/99 12:29a Dave
21 * 8 5/03/99 8:32p Dave
22 * New version of multi host options screen.
24 * 7 4/12/99 10:07p Dave
25 * Made network startup more forgiving. Added checkmarks to dogfight
26 * screen for players who hit commit.
28 * 6 4/12/99 2:22p Dave
29 * More checks for dogfight stats.
31 * 5 2/24/99 3:40p Dave
32 * Fixed problem where ships were becoming traitor all the time.
34 * 4 2/24/99 2:25p Dave
35 * Fixed up chatbox bugs. Made squad war reporting better. Fixed a respawn
36 * bug for dogfight more.
38 * 3 2/23/99 8:11p Dave
39 * Tidied up dogfight mode. Fixed TvT ship type problems for alpha wing.
40 * Small pass over todolist items.
42 * 2 2/23/99 2:29p Dave
43 * First run of oldschool dogfight mode.
49 #include "multiutil.h"
50 #include "multi_log.h"
53 #include "systemvars.h"
54 #include "freespace.h"
56 #include "missionscreencommon.h"
57 #include "eventmusic.h"
63 #include "multi_dogfight.h"
64 #include "alphacolors.h"
66 // ----------------------------------------------------------------------------------------------------
67 // MULTI DOGFIGHT DEFINES/VARS
71 UI_WINDOW Multi_df_window;
73 #define NUM_MULTI_DF_BUTTONS 1
74 #define ACCEPT_BUTTON 0
76 ui_button_info Multi_df_buttons[GR_NUM_RESOLUTIONS][NUM_MULTI_DF_BUTTONS] = {
79 ui_button_info("CB_05a", 571, 425, 578, 413, 5),
83 ui_button_info("2_CB_05a", 914, 681, 914, 660, 5),
87 int Multi_df_background_bitmap = -1;
88 char *Multi_df_background_fname[GR_NUM_RESOLUTIONS] = {
92 char *Multi_df_mask_fname[GR_NUM_RESOLUTIONS] = {
97 // coord 3 is max width
98 static int Kill_matrix_title_coords[GR_NUM_RESOLUTIONS][3] = {
107 // display area coords
108 int Multi_df_display_coords[GR_NUM_RESOLUTIONS][4] = {
117 #define MULTI_DF_TOTAL_ADJUST 5
119 // "check" icon coords
120 int Multi_df_check_coords[GR_NUM_RESOLUTIONS] = {
128 // players when the screen started - we need to store this explicity so that even after players leave, we can display the full kill matrix
129 typedef struct multi_df_score {
130 char callsign[CALLSIGN_LEN+1]; // callsign for this guy
131 scoring_struct stats; // stats for the guy
132 int np_index; // absolute index into the netplayers array
134 multi_df_score Multi_df_score[MAX_PLAYERS];
135 int Multi_df_score_count = 0;
138 // ----------------------------------------------------------------------------------------------------
139 // MULTI DOGFIGHT FORWARD DECLARATIONS
142 // process button presses
143 void multi_df_process_buttons();
145 // button was pressed
146 void multi_df_button_pressed(int button);
148 // setup kill matrix data
149 void multi_df_setup_kill_matrix();
151 // blit the kill matrix
152 void multi_df_blit_kill_matrix();
154 // stuff a string representing the # of kills, player X had on player Y (where X and Y are indices into Multi_df_score)
155 // returns the # of kills
156 int multi_df_stuff_kills(char *kills, int player_x, int player_y);
159 // ----------------------------------------------------------------------------------------------------
160 // MULTI DOGFIGHT FUNCTIONS
163 // call once per level just before entering the mission
164 void multi_df_level_pre_enter()
168 // if we're not in dogfight mode, do nothing
169 if(!(Netgame.type_flags & NG_TYPE_DOGFIGHT)){
173 // go through all player ships and make them hostile
174 for(idx=0; idx<MAX_PLAYERS; idx++){
175 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_OBSERVER(Net_players[idx]) && (Net_players[idx].player != NULL) && (Net_players[idx].player->objnum >= 0) && (Objects[Net_players[idx].player->objnum].type == OBJ_SHIP)){
176 Ships[Objects[Net_players[idx].player->objnum].instance].team = TEAM_TRAITOR;
183 // evaluate a kill in dogfight by a netplayer
184 void multi_df_eval_kill(net_player *killer, object *dead_obj)
188 // if we're not in dogfight mode, do nothing
189 if(!(Netgame.type_flags & NG_TYPE_DOGFIGHT)){
194 if((killer == NULL) || (dead_obj == NULL) || (killer->player == NULL)){
198 // try and find the dead player
199 dead_index = multi_find_player_by_object(dead_obj);
203 Assert(dead_index < MAX_PLAYERS);
204 if(dead_index == NET_PLAYER_INDEX(killer)){
209 killer->player->stats.m_dogfight_kills[dead_index]++;
213 void multi_df_debrief_init()
215 // no longer is mission
216 Game_mode &= ~(GM_IN_MISSION);
219 // call scoring level close for my stats. Needed for award_init. The stats will
220 // be backed out if used chooses to replace them.
221 scoring_level_close();
223 // multiplayer debriefing stuff
224 multi_debrief_init();
226 // close down any old instances of the chatbox
229 // create the new one
232 // always play success music
233 common_music_init(SCORE_DEBRIEF_SUCCESS);
236 multi_df_setup_kill_matrix();
242 // load background bitmap
243 Multi_df_background_bitmap = bm_load(Multi_df_background_fname[gr_screen.res]);
244 Assert(Multi_df_background_bitmap);
246 // create the UI window
247 Multi_df_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
248 Multi_df_window.set_mask_bmap(Multi_df_mask_fname[gr_screen.res]);
250 // initialize the control buttons
251 for (idx=0; idx<NUM_MULTI_DF_BUTTONS; idx++) {
252 b = &Multi_df_buttons[gr_screen.res][idx];
255 b->button.create(&Multi_df_window, NULL, b->x, b->y, 60, 30, 1, 1);
257 // set its highlight action
258 b->button.set_highlight_action(common_play_highlight_sound);
260 // set its animation bitmaps
261 b->button.set_bmaps(b->filename);
263 // link the mask hotspot
264 b->button.link_hotspot(b->hotspot);
268 w = &Multi_df_window;
269 w->add_XSTR("Accept", 1035, Multi_df_buttons[gr_screen.res][ACCEPT_BUTTON].xt, Multi_df_buttons[gr_screen.res][ACCEPT_BUTTON].yt, &Multi_df_buttons[gr_screen.res][ACCEPT_BUTTON].button, UI_XSTR_COLOR_PINK);
273 void multi_df_debrief_do()
278 k = chatbox_process();
279 new_k = Multi_df_window.process(k, 0);
281 // process keypresses
284 multi_debrief_esc_hit();
289 multi_df_process_buttons();
294 // process debriefing details
295 multi_debrief_do_frame();
297 // draw the background
298 GR_MAYBE_CLEAR_RES(Multi_df_background_bitmap);
299 if (Multi_df_background_bitmap >= 0) {
300 gr_set_bitmap(Multi_df_background_bitmap);
305 Multi_df_window.draw();
308 multi_df_blit_kill_matrix();
310 // render the chatbox
313 // draw the mission title
314 strcpy(buf, The_mission.name);
315 gr_force_fit_string(buf, 255, Kill_matrix_title_coords[gr_screen.res][2]);
316 gr_set_color_fast(&Color_bright_white);
317 gr_string(Kill_matrix_title_coords[gr_screen.res][0], Kill_matrix_title_coords[gr_screen.res][1], buf);
324 void multi_df_debrief_close()
328 // shutdown the chatbox
331 // if stats weren't accepted, backout my own stats
332 if (multi_debrief_stats_accept_code() != 1) {
333 // if stats weren't accepted, backout my own stats
334 if (multi_debrief_stats_accept_code() != 1) {
335 if(MULTIPLAYER_MASTER){
336 for(idx=0; idx<MAX_PLAYERS; idx++){
337 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].player != NULL)){
338 scoring_backout_accept(&Net_players[idx].player->stats);
342 scoring_backout_accept( &Player->stats );
348 common_music_close();
352 // ----------------------------------------------------------------------------------------------------
353 // MULTI DOGFIGHT FORWARD DEFINITIONS
356 // process button presses
357 void multi_df_process_buttons()
361 for(idx=0; idx<NUM_MULTI_DF_BUTTONS; idx++){
362 if(Multi_df_buttons[gr_screen.res][idx].button.pressed()){
363 multi_df_button_pressed(idx);
369 // button was pressed
370 void multi_df_button_pressed(int button)
374 multi_debrief_accept_hit();
379 // setup kill matrix data
380 void multi_df_setup_kill_matrix()
385 Multi_df_score_count = 0;
387 // add players as necessary
388 for(idx=0; idx<MAX_PLAYERS; idx++){
389 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].player != NULL)){
390 // stuff data for this guy
391 s = &Multi_df_score[Multi_df_score_count++];
393 ml_printf("Dogfight debrief stats for %s", Net_players[idx].player->callsign);
394 for(s_idx=0; s_idx<MAX_PLAYERS; s_idx++){
395 ml_printf("%d", Net_players[idx].player->stats.m_dogfight_kills[s_idx]);
398 s->stats = Net_players[idx].player->stats;
399 strcpy(s->callsign, Net_players[idx].player->callsign);
405 // blit the kill matrix
406 void multi_df_blit_kill_matrix()
408 int idx, s_idx, str_len;
410 char squashed_string[CALLSIGN_LEN+1] = "";
412 // max width of an individual item, and the text that can be in that item
413 float max_item_width = ((float)Multi_df_display_coords[gr_screen.res][2] - 40.0f) / (float)(Multi_df_score_count + 1);
414 float max_text_width = max_item_width * 0.8f;
416 // start x for the top bar (one item to the right)
417 int top_x_start = Multi_df_display_coords[gr_screen.res][0] + (int)max_item_width;
418 int top_y_start = Multi_df_display_coords[gr_screen.res][1];
420 // start x for the side bar
421 int side_x_start = Multi_df_display_coords[gr_screen.res][0];
422 int side_y_start = Multi_df_display_coords[gr_screen.res][1] + 10;
427 for(idx=0; idx<Multi_df_score_count; idx++){
428 // force the string to fit nicely
429 strcpy(squashed_string, Multi_df_score[idx].callsign);
430 gr_force_fit_string(squashed_string, CALLSIGN_LEN, (int)max_text_width);
431 gr_get_string_size(&str_len, NULL, squashed_string);
433 // set color and blit the string
434 Assert(Multi_df_score[idx].np_index >= 0);
435 if(Multi_df_score[idx].np_index >= 0){
436 gr_set_color_fast(Color_netplayer[Multi_df_score[idx].np_index]);
438 gr_string(cx + (int)((max_item_width - (float)str_len)/2.0f), cy, squashed_string);
441 cx += (int)max_item_width;
444 // draw the rest of the scoreboard
448 for(idx=0; idx<Multi_df_score_count; idx++){
449 // draw a check if necessary
450 if(!MULTI_CONNECTED(Net_players[Multi_df_score[idx].np_index]) || (Net_players[Multi_df_score[idx].np_index].state == NETPLAYER_STATE_DEBRIEF_ACCEPT) || (Net_players[Multi_df_score[idx].np_index].state == NETPLAYER_STATE_DEBRIEF_REPLAY)){
451 if(Multi_common_icons[MICON_VALID] != -1){
452 gr_set_bitmap(Multi_common_icons[MICON_VALID]);
453 gr_bitmap(Multi_df_check_coords[gr_screen.res], cy);
458 cx = Multi_df_display_coords[gr_screen.res][0];
459 strcpy(squashed_string, Multi_df_score[idx].callsign);
460 gr_force_fit_string(squashed_string, CALLSIGN_LEN, (int)max_text_width);
461 gr_get_string_size(&str_len, NULL, squashed_string);
462 Assert(Multi_df_score[idx].np_index >= 0);
463 if(Multi_df_score[idx].np_index >= 0){
464 gr_set_color_fast(Color_netplayer[Multi_df_score[idx].np_index]);
466 gr_string(cx, cy, squashed_string);
470 for(s_idx=0; s_idx<Multi_df_score_count; s_idx++){
471 // stuff the string to be displayed and select the proper display color
473 strcpy(squashed_string, "-");
474 gr_set_color_fast(&Color_grey);
476 row_total += multi_df_stuff_kills(squashed_string, idx, s_idx);
477 Assert(Multi_df_score[idx].np_index >= 0);
478 if(Multi_df_score[idx].np_index >= 0){
479 gr_set_color_fast(Color_netplayer[Multi_df_score[idx].np_index]);
484 gr_force_fit_string(squashed_string, CALLSIGN_LEN, (int)max_text_width);
485 gr_get_string_size(&str_len, NULL, squashed_string);
486 gr_string(cx + (int)((max_item_width - (float)str_len)/2.0f), cy, squashed_string);
489 cx += (int)max_item_width;
492 // draw the row total
493 gr_set_color_fast(Color_netplayer[Multi_df_score[idx].np_index]);
494 sprintf(squashed_string, "(%d)", row_total);
495 gr_get_string_size(&str_len, NULL, squashed_string);
496 gr_string(Multi_df_display_coords[gr_screen.res][0] + Multi_df_display_coords[gr_screen.res][2] - (MULTI_DF_TOTAL_ADJUST + str_len), cy, squashed_string);
506 for(idx=0; idx<Multi_df_score_count; idx++){
507 // add up this column
509 for(s_idx=0; s_idx<Multi_df_score_count; s_idx++){
511 column_total += Multi_df_score[idx].stats.m_dogfight_kills[s_idx];
515 sprintf(squashed_string, "%d", column_total);
516 gr_set_color_fast(Color_netplayer[Multi_df_score[idx].np_index]);
517 gr_string(cx, cy, squashed_string);
520 cx += (int)max_item_width;
525 // stuff a string representing the # of kills, player X had on player Y (where X and Y are indices into Multi_df_score)
526 // returns the # of kills
527 int multi_df_stuff_kills(char *kills, int player_x, int player_y)
529 multi_df_score *s = &Multi_df_score[player_x];
532 sprintf(kills, "%d", s->stats.m_dogfight_kills[Multi_df_score[player_y].np_index]);
533 return s->stats.m_dogfight_kills[Multi_df_score[player_y].np_index];