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/Network/multi_dogfight.cpp $
16 * Revision 1.3 2002/06/09 04:41:23 relnev
17 * added copyright header
19 * Revision 1.2 2002/05/07 03:16:47 theoddone33
20 * The Great Newline Fix
22 * Revision 1.1.1.1 2002/05/03 03:28:10 root
26 * 11 8/18/99 11:31a Jefff
27 * mission title on kill matrix
29 * 10 8/17/99 9:55a Dave
30 * Fixed dogfight scoring problem.
32 * 9 8/06/99 12:29a Dave
35 * 8 5/03/99 8:32p Dave
36 * New version of multi host options screen.
38 * 7 4/12/99 10:07p Dave
39 * Made network startup more forgiving. Added checkmarks to dogfight
40 * screen for players who hit commit.
42 * 6 4/12/99 2:22p Dave
43 * More checks for dogfight stats.
45 * 5 2/24/99 3:40p Dave
46 * Fixed problem where ships were becoming traitor all the time.
48 * 4 2/24/99 2:25p Dave
49 * Fixed up chatbox bugs. Made squad war reporting better. Fixed a respawn
50 * bug for dogfight more.
52 * 3 2/23/99 8:11p Dave
53 * Tidied up dogfight mode. Fixed TvT ship type problems for alpha wing.
54 * Small pass over todolist items.
56 * 2 2/23/99 2:29p Dave
57 * First run of oldschool dogfight mode.
63 #include "multiutil.h"
64 #include "multi_log.h"
67 #include "systemvars.h"
68 #include "freespace.h"
70 #include "missionscreencommon.h"
71 #include "eventmusic.h"
77 #include "multi_dogfight.h"
78 #include "alphacolors.h"
80 // ----------------------------------------------------------------------------------------------------
81 // MULTI DOGFIGHT DEFINES/VARS
85 UI_WINDOW Multi_df_window;
87 #define NUM_MULTI_DF_BUTTONS 1
88 #define ACCEPT_BUTTON 0
90 ui_button_info Multi_df_buttons[GR_NUM_RESOLUTIONS][NUM_MULTI_DF_BUTTONS] = {
93 ui_button_info("CB_05a", 571, 425, 578, 413, 5),
97 ui_button_info("2_CB_05a", 914, 681, 914, 660, 5),
101 int Multi_df_background_bitmap = -1;
102 char *Multi_df_background_fname[GR_NUM_RESOLUTIONS] = {
106 char *Multi_df_mask_fname[GR_NUM_RESOLUTIONS] = {
111 // coord 3 is max width
112 static int Kill_matrix_title_coords[GR_NUM_RESOLUTIONS][3] = {
121 // display area coords
122 int Multi_df_display_coords[GR_NUM_RESOLUTIONS][4] = {
131 #define MULTI_DF_TOTAL_ADJUST 5
133 // "check" icon coords
134 int Multi_df_check_coords[GR_NUM_RESOLUTIONS] = {
142 // players when the screen started - we need to store this explicity so that even after players leave, we can display the full kill matrix
143 typedef struct multi_df_score {
144 char callsign[CALLSIGN_LEN+1]; // callsign for this guy
145 scoring_struct stats; // stats for the guy
146 int np_index; // absolute index into the netplayers array
148 multi_df_score Multi_df_score[MAX_PLAYERS];
149 int Multi_df_score_count = 0;
152 // ----------------------------------------------------------------------------------------------------
153 // MULTI DOGFIGHT FORWARD DECLARATIONS
156 // process button presses
157 void multi_df_process_buttons();
159 // button was pressed
160 void multi_df_button_pressed(int button);
162 // setup kill matrix data
163 void multi_df_setup_kill_matrix();
165 // blit the kill matrix
166 void multi_df_blit_kill_matrix();
168 // stuff a string representing the # of kills, player X had on player Y (where X and Y are indices into Multi_df_score)
169 // returns the # of kills
170 int multi_df_stuff_kills(char *kills, int player_x, int player_y);
173 // ----------------------------------------------------------------------------------------------------
174 // MULTI DOGFIGHT FUNCTIONS
177 // call once per level just before entering the mission
178 void multi_df_level_pre_enter()
182 // if we're not in dogfight mode, do nothing
183 if(!(Netgame.type_flags & NG_TYPE_DOGFIGHT)){
187 // go through all player ships and make them hostile
188 for(idx=0; idx<MAX_PLAYERS; idx++){
189 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)){
190 Ships[Objects[Net_players[idx].player->objnum].instance].team = TEAM_TRAITOR;
197 // evaluate a kill in dogfight by a netplayer
198 void multi_df_eval_kill(net_player *killer, object *dead_obj)
202 // if we're not in dogfight mode, do nothing
203 if(!(Netgame.type_flags & NG_TYPE_DOGFIGHT)){
208 if((killer == NULL) || (dead_obj == NULL) || (killer->player == NULL)){
212 // try and find the dead player
213 dead_index = multi_find_player_by_object(dead_obj);
217 Assert(dead_index < MAX_PLAYERS);
218 if(dead_index == NET_PLAYER_INDEX(killer)){
223 killer->player->stats.m_dogfight_kills[dead_index]++;
227 void multi_df_debrief_init()
229 // no longer is mission
230 Game_mode &= ~(GM_IN_MISSION);
233 // call scoring level close for my stats. Needed for award_init. The stats will
234 // be backed out if used chooses to replace them.
235 scoring_level_close();
237 // multiplayer debriefing stuff
238 multi_debrief_init();
240 // close down any old instances of the chatbox
243 // create the new one
246 // always play success music
247 common_music_init(SCORE_DEBRIEF_SUCCESS);
250 multi_df_setup_kill_matrix();
256 // load background bitmap
257 Multi_df_background_bitmap = bm_load(Multi_df_background_fname[gr_screen.res]);
258 Assert(Multi_df_background_bitmap);
260 // create the UI window
261 Multi_df_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
262 Multi_df_window.set_mask_bmap(Multi_df_mask_fname[gr_screen.res]);
264 // initialize the control buttons
265 for (idx=0; idx<NUM_MULTI_DF_BUTTONS; idx++) {
266 b = &Multi_df_buttons[gr_screen.res][idx];
269 b->button.create(&Multi_df_window, NULL, b->x, b->y, 60, 30, 1, 1);
271 // set its highlight action
272 b->button.set_highlight_action(common_play_highlight_sound);
274 // set its animation bitmaps
275 b->button.set_bmaps(b->filename);
277 // link the mask hotspot
278 b->button.link_hotspot(b->hotspot);
282 w = &Multi_df_window;
283 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);
287 void multi_df_debrief_do()
292 k = chatbox_process();
293 new_k = Multi_df_window.process(k, 0);
295 // process keypresses
298 multi_debrief_esc_hit();
303 multi_df_process_buttons();
308 // process debriefing details
309 multi_debrief_do_frame();
311 // draw the background
312 GR_MAYBE_CLEAR_RES(Multi_df_background_bitmap);
313 if (Multi_df_background_bitmap >= 0) {
314 gr_set_bitmap(Multi_df_background_bitmap);
319 Multi_df_window.draw();
322 multi_df_blit_kill_matrix();
324 // render the chatbox
327 // draw the mission title
328 strcpy(buf, The_mission.name);
329 gr_force_fit_string(buf, 255, Kill_matrix_title_coords[gr_screen.res][2]);
330 gr_set_color_fast(&Color_bright_white);
331 gr_string(Kill_matrix_title_coords[gr_screen.res][0], Kill_matrix_title_coords[gr_screen.res][1], buf);
338 void multi_df_debrief_close()
342 // shutdown the chatbox
345 // if stats weren't accepted, backout my own stats
346 if (multi_debrief_stats_accept_code() != 1) {
347 // if stats weren't accepted, backout my own stats
348 if (multi_debrief_stats_accept_code() != 1) {
349 if(MULTIPLAYER_MASTER){
350 for(idx=0; idx<MAX_PLAYERS; idx++){
351 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].player != NULL)){
352 scoring_backout_accept(&Net_players[idx].player->stats);
356 scoring_backout_accept( &Player->stats );
362 common_music_close();
366 // ----------------------------------------------------------------------------------------------------
367 // MULTI DOGFIGHT FORWARD DEFINITIONS
370 // process button presses
371 void multi_df_process_buttons()
375 for(idx=0; idx<NUM_MULTI_DF_BUTTONS; idx++){
376 if(Multi_df_buttons[gr_screen.res][idx].button.pressed()){
377 multi_df_button_pressed(idx);
383 // button was pressed
384 void multi_df_button_pressed(int button)
388 multi_debrief_accept_hit();
393 // setup kill matrix data
394 void multi_df_setup_kill_matrix()
399 Multi_df_score_count = 0;
401 // add players as necessary
402 for(idx=0; idx<MAX_PLAYERS; idx++){
403 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].player != NULL)){
404 // stuff data for this guy
405 s = &Multi_df_score[Multi_df_score_count++];
407 ml_printf("Dogfight debrief stats for %s", Net_players[idx].player->callsign);
408 for(s_idx=0; s_idx<MAX_PLAYERS; s_idx++){
409 ml_printf("%d", Net_players[idx].player->stats.m_dogfight_kills[s_idx]);
412 s->stats = Net_players[idx].player->stats;
413 strcpy(s->callsign, Net_players[idx].player->callsign);
419 // blit the kill matrix
420 void multi_df_blit_kill_matrix()
422 int idx, s_idx, str_len;
424 char squashed_string[CALLSIGN_LEN+1] = "";
426 // max width of an individual item, and the text that can be in that item
427 float max_item_width = ((float)Multi_df_display_coords[gr_screen.res][2] - 40.0f) / (float)(Multi_df_score_count + 1);
428 float max_text_width = max_item_width * 0.8f;
430 // start x for the top bar (one item to the right)
431 int top_x_start = Multi_df_display_coords[gr_screen.res][0] + (int)max_item_width;
432 int top_y_start = Multi_df_display_coords[gr_screen.res][1];
434 // start x for the side bar
435 int side_x_start = Multi_df_display_coords[gr_screen.res][0];
436 int side_y_start = Multi_df_display_coords[gr_screen.res][1] + 10;
441 for(idx=0; idx<Multi_df_score_count; idx++){
442 // force the string to fit nicely
443 strcpy(squashed_string, Multi_df_score[idx].callsign);
444 gr_force_fit_string(squashed_string, CALLSIGN_LEN, (int)max_text_width);
445 gr_get_string_size(&str_len, NULL, squashed_string);
447 // set color and blit the string
448 Assert(Multi_df_score[idx].np_index >= 0);
449 if(Multi_df_score[idx].np_index >= 0){
450 gr_set_color_fast(Color_netplayer[Multi_df_score[idx].np_index]);
452 gr_string(cx + (int)((max_item_width - (float)str_len)/2.0f), cy, squashed_string);
455 cx += (int)max_item_width;
458 // draw the rest of the scoreboard
462 for(idx=0; idx<Multi_df_score_count; idx++){
463 // draw a check if necessary
464 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)){
465 if(Multi_common_icons[MICON_VALID] != -1){
466 gr_set_bitmap(Multi_common_icons[MICON_VALID]);
467 gr_bitmap(Multi_df_check_coords[gr_screen.res], cy);
472 cx = Multi_df_display_coords[gr_screen.res][0];
473 strcpy(squashed_string, Multi_df_score[idx].callsign);
474 gr_force_fit_string(squashed_string, CALLSIGN_LEN, (int)max_text_width);
475 gr_get_string_size(&str_len, NULL, squashed_string);
476 Assert(Multi_df_score[idx].np_index >= 0);
477 if(Multi_df_score[idx].np_index >= 0){
478 gr_set_color_fast(Color_netplayer[Multi_df_score[idx].np_index]);
480 gr_string(cx, cy, squashed_string);
484 for(s_idx=0; s_idx<Multi_df_score_count; s_idx++){
485 // stuff the string to be displayed and select the proper display color
487 strcpy(squashed_string, "-");
488 gr_set_color_fast(&Color_grey);
490 row_total += multi_df_stuff_kills(squashed_string, idx, s_idx);
491 Assert(Multi_df_score[idx].np_index >= 0);
492 if(Multi_df_score[idx].np_index >= 0){
493 gr_set_color_fast(Color_netplayer[Multi_df_score[idx].np_index]);
498 gr_force_fit_string(squashed_string, CALLSIGN_LEN, (int)max_text_width);
499 gr_get_string_size(&str_len, NULL, squashed_string);
500 gr_string(cx + (int)((max_item_width - (float)str_len)/2.0f), cy, squashed_string);
503 cx += (int)max_item_width;
506 // draw the row total
507 gr_set_color_fast(Color_netplayer[Multi_df_score[idx].np_index]);
508 sprintf(squashed_string, "(%d)", row_total);
509 gr_get_string_size(&str_len, NULL, squashed_string);
510 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);
520 for(idx=0; idx<Multi_df_score_count; idx++){
521 // add up this column
523 for(s_idx=0; s_idx<Multi_df_score_count; s_idx++){
525 column_total += Multi_df_score[idx].stats.m_dogfight_kills[s_idx];
529 sprintf(squashed_string, "%d", column_total);
530 gr_set_color_fast(Color_netplayer[Multi_df_score[idx].np_index]);
531 gr_string(cx, cy, squashed_string);
534 cx += (int)max_item_width;
539 // stuff a string representing the # of kills, player X had on player Y (where X and Y are indices into Multi_df_score)
540 // returns the # of kills
541 int multi_df_stuff_kills(char *kills, int player_x, int player_y)
543 multi_df_score *s = &Multi_df_score[player_x];
546 sprintf(kills, "%d", s->stats.m_dogfight_kills[Multi_df_score[player_y].np_index]);
547 return s->stats.m_dogfight_kills[Multi_df_score[player_y].np_index];