/* * Copyright (C) Volition, Inc. 1999. All rights reserved. * * All source code herein is the property of Volition, Inc. You may not sell * or otherwise commercially exploit the source or things you created based on the * source. * */ #include "network/multi_dogfight.h" #include "network/multi.h" #include "network/multiutil.h" #include "network/multi_log.h" #include "object/object.h" #include "ship/ship.h" #include "freespace.h" #include "io/key.h" #include "missionui/missionscreencommon.h" #include "gamesnd/eventmusic.h" #include "gamesnd/gamesnd.h" #include "network/multiui.h" #include "missionui/chatbox.h" #include "ui/ui.h" #include "graphics/font.h" #include "globalincs/alphacolors.h" #include "playerman/player.h" #include "stats/scoring.h" #include "mission/missionparse.h" #include "iff_defs/iff_defs.h" #include "pilotfile/pilotfile.h" #include "cfile/cfile.h" // ---------------------------------------------------------------------------------------------------- // MULTI DOGFIGHT DEFINES/VARS // // interface stuff UI_WINDOW Multi_df_window; #define NUM_MULTI_DF_BUTTONS 1 #define ACCEPT_BUTTON 0 ui_button_info Multi_df_buttons[GR_NUM_RESOLUTIONS][NUM_MULTI_DF_BUTTONS] = { { // GR_640 // accept ui_button_info("CB_05a", 571, 425, 578, 413, 5), }, { // GR_1024 // accept ui_button_info("2_CB_05a", 914, 681, 914, 660, 5), } }; int Multi_df_background_bitmap = -1; const char *Multi_df_background_fname[GR_NUM_RESOLUTIONS] = { "KillMatrix", "2_KillMatrix" }; const char *Multi_df_mask_fname[GR_NUM_RESOLUTIONS] = { "KillMatrix-m", "2_KillMatrix-m" }; // coord 3 is max width static int Kill_matrix_title_coords[GR_NUM_RESOLUTIONS][3] = { { // GR_640 19, 118, 172 }, { // GR_1024 33, 194, 272 } }; // display area coords int Multi_df_display_coords[GR_NUM_RESOLUTIONS][4] = { { // GR_640 43, 133, 569, 269 }, { // GR_1024 60, 213, 919, 429 } }; #define MULTI_DF_TOTAL_ADJUST 5 // "check" icon coords int Multi_df_check_coords[GR_NUM_RESOLUTIONS] = { // GR_640 28, // GR_1024 45 }; multi_df_score Multi_df_score[MAX_PLAYERS]; int Multi_df_score_count = 0; // ---------------------------------------------------------------------------------------------------- // MULTI DOGFIGHT FORWARD DECLARATIONS // // process button presses void multi_df_process_buttons(); // button was pressed void multi_df_button_pressed(int button); // setup kill matrix data void multi_df_setup_kill_matrix(); // blit the kill matrix void multi_df_blit_kill_matrix(); // stuff a string representing the # of kills, player X had on player Y (where X and Y are indices into Multi_df_score) // returns the # of kills int multi_df_stuff_kills(char *kills, int player_x, int player_y); // ---------------------------------------------------------------------------------------------------- // MULTI DOGFIGHT FUNCTIONS // // call once per level just before entering the mission void multi_df_level_pre_enter() { int idx; // if we're not in dogfight mode, do nothing if(!(Netgame.type_flags & NG_TYPE_DOGFIGHT)){ return; } // go through all player ships and make them hostile for(idx=0; idxobjnum >= 0) && (Objects[Net_players[idx].m_player->objnum].type == OBJ_SHIP)){ Ships[Objects[Net_players[idx].m_player->objnum].instance].team = Iff_traitor; } } // } // evaluate a kill in dogfight by a netplayer void multi_df_eval_kill(const net_player *killer, const object *dead_obj) { int dead_index = -1; // if we're not in dogfight mode, do nothing if(!(Netgame.type_flags & NG_TYPE_DOGFIGHT)){ return; } // sanity checks if((killer == NULL) || (dead_obj == NULL) || (killer->m_player == NULL)){ return; } // try and find the dead player dead_index = multi_find_player_by_object(dead_obj); if(dead_index < 0){ return; } Assert(dead_index < MAX_PLAYERS); if(dead_index == NET_PLAYER_INDEX(killer)){ return; } // update his kills killer->m_player->stats.m_dogfight_kills[dead_index]++; } // debrief void multi_df_debrief_init(bool API_Access) { // no longer is mission Game_mode &= ~(GM_IN_MISSION); game_flush(); // call scoring level close for my stats. Needed for award_init. The stats will // be backed out if used chooses to replace them. scoring_level_close(); // multiplayer debriefing stuff multi_debrief_init(); // close down any old instances of the chatbox chatbox_close(); // create the new one chatbox_create(); // always play success music if (!API_Access) { common_music_init(SCORE_DEBRIEFING_SUCCESS); } // setup kill matrix multi_df_setup_kill_matrix(); // The rest is UI code we don't need for the API, so just return here if (API_Access) { return; } UI_WINDOW *w; ui_button_info *b; int idx; // load background bitmap Multi_df_background_bitmap = bm_load(Multi_df_background_fname[gr_screen.res]); Assert(Multi_df_background_bitmap); // create the UI window Multi_df_window.create(0, 0, gr_screen.max_w_unscaled, gr_screen.max_h_unscaled, 0); Multi_df_window.set_mask_bmap(Multi_df_mask_fname[gr_screen.res]); // initialize the control buttons for (idx=0; idxbutton.create(&Multi_df_window, NULL, b->x, b->y, 60, 30, 1, 1); // set its highlight action b->button.set_highlight_action(common_play_highlight_sound); // set its animation bitmaps b->button.set_bmaps(b->filename); // link the mask hotspot b->button.link_hotspot(b->hotspot); } // add some text w = &Multi_df_window; 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); } // do frame void multi_df_debrief_do(bool API_Access) { int k, new_k; const size_t max_buf_len = 255; char buf[max_buf_len+1]; k = chatbox_process(); if (!API_Access) { new_k = Multi_df_window.process(k, 0); // process keypresses switch (new_k) { case KEY_ESC: multi_debrief_esc_hit(); break; } // process buttons multi_df_process_buttons(); // music stuff common_music_do(); } // process debriefing details multi_debrief_do_frame(); if (!API_Access) { // draw the background GR_MAYBE_CLEAR_RES(Multi_df_background_bitmap); if (Multi_df_background_bitmap >= 0) { gr_set_bitmap(Multi_df_background_bitmap); gr_bitmap(0, 0, GR_RESIZE_MENU); } // draw the window Multi_df_window.draw(); // kill matrix multi_df_blit_kill_matrix(); // render the chatbox chatbox_render(); // draw the mission title strncpy(buf, The_mission.name.c_str(), max_buf_len); buf[max_buf_len] = '\0'; font::force_fit_string(buf, max_buf_len, Kill_matrix_title_coords[gr_screen.res][2]); gr_set_color_fast(&Color_bright_white); gr_string(Kill_matrix_title_coords[gr_screen.res][0], Kill_matrix_title_coords[gr_screen.res][1], buf, GR_RESIZE_MENU); // flip gr_flip(); } } // close void multi_df_debrief_close(bool API_Access) { int idx; scoring_struct *sc; // shutdown the chatbox chatbox_close(); // if stats weren't accepted, backout my own stats if (multi_debrief_stats_accept_code() != 1) { // if stats weren't accepted, backout my own stats if (multi_debrief_stats_accept_code() != 1) { if(MULTIPLAYER_MASTER){ for(idx=0; idxstats; scoring_backout_accept(sc); if (Net_player == &Net_players[idx]) { Pilot.update_stats_backout( sc ); } } } } else { scoring_backout_accept( &Player->stats ); Pilot.update_stats_backout( &Player->stats ); } } } if (!API_Access) { // music stuff common_music_close(); } } // ---------------------------------------------------------------------------------------------------- // MULTI DOGFIGHT FORWARD DEFINITIONS // // process button presses void multi_df_process_buttons() { int idx; for(idx=0; idxcallsign); for(s_idx=0; s_idxstats.m_dogfight_kills[s_idx]); } s->stats = Net_players[idx].m_player->stats; strcpy_s(s->callsign, Net_players[idx].m_player->callsign); s->np_index = idx; } } } // blit the kill matrix void multi_df_blit_kill_matrix() { int idx, s_idx; int cx, cy; char squashed_string[CALLSIGN_LEN+1] = ""; int dy = gr_get_font_height() + 1; // max width of an individual item, and the text that can be in that item float max_item_width = ((float)Multi_df_display_coords[gr_screen.res][2] - 40.0f) / (float)(Multi_df_score_count + 1); float max_text_width = max_item_width * 0.8f; // start x for the top bar (one item to the right) int top_x_start = Multi_df_display_coords[gr_screen.res][0] + (int)max_item_width; int top_y_start = Multi_df_display_coords[gr_screen.res][1]; // start x for the side bar int side_x_start = Multi_df_display_coords[gr_screen.res][0]; int side_y_start = Multi_df_display_coords[gr_screen.res][1] + dy; // draw the top bar cx = top_x_start; cy = top_y_start; for(idx=0; idx= 0); if(Multi_df_score[idx].np_index >= 0){ gr_set_color_fast(Color_netplayer[Multi_df_score[idx].np_index]); } gr_string(cx + (int)((max_item_width - (float)w)/2.0f), cy, squashed_string, GR_RESIZE_MENU); // next spot cx += (int)max_item_width; } // draw the rest of the scoreboard cx = side_x_start; cy = side_y_start; int row_total; for(idx=0; idx= 0); if(Multi_df_score[idx].np_index >= 0){ gr_set_color_fast(Color_netplayer[Multi_df_score[idx].np_index]); } gr_string(cx, cy, squashed_string, GR_RESIZE_MENU); cx = top_x_start; row_total = 0; for(s_idx=0; s_idx= 0); if(Multi_df_score[idx].np_index >= 0){ gr_set_color_fast(Color_netplayer[Multi_df_score[idx].np_index]); } } // draw the string int w = font::force_fit_string(squashed_string, CALLSIGN_LEN, (int)max_text_width); gr_string(cx + (int)((max_item_width - (float)w)/2.0f), cy, squashed_string, GR_RESIZE_MENU); // next spot cx += (int)max_item_width; } // draw the row total gr_set_color_fast(Color_netplayer[Multi_df_score[idx].np_index]); sprintf(squashed_string, "(%d)", row_total); int w; gr_get_string_size(&w, nullptr, squashed_string); gr_string(Multi_df_display_coords[gr_screen.res][0] + Multi_df_display_coords[gr_screen.res][2] - (MULTI_DF_TOTAL_ADJUST + w), cy, squashed_string, GR_RESIZE_MENU); cy += dy; } } // stuff a string representing the # of kills, player X had on player Y (where X and Y are indices into Multi_df_score) // returns the # of kills int multi_df_stuff_kills(char *kills, int player_x, int player_y) { multi_df_score *s = &Multi_df_score[player_x]; strcpy(kills, ""); sprintf(kills, "%d", s->stats.m_dogfight_kills[Multi_df_score[player_y].np_index]); return s->stats.m_dogfight_kills[Multi_df_score[player_y].np_index]; }