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.4 2004/09/20 01:31:44 theoddone33
19 * Revision 1.3 2002/06/09 04:41:23 relnev
20 * added copyright header
22 * Revision 1.2 2002/05/07 03:16:47 theoddone33
23 * The Great Newline Fix
25 * Revision 1.1.1.1 2002/05/03 03:28:10 root
29 * 11 8/18/99 11:31a Jefff
30 * mission title on kill matrix
32 * 10 8/17/99 9:55a Dave
33 * Fixed dogfight scoring problem.
35 * 9 8/06/99 12:29a Dave
38 * 8 5/03/99 8:32p Dave
39 * New version of multi host options screen.
41 * 7 4/12/99 10:07p Dave
42 * Made network startup more forgiving. Added checkmarks to dogfight
43 * screen for players who hit commit.
45 * 6 4/12/99 2:22p Dave
46 * More checks for dogfight stats.
48 * 5 2/24/99 3:40p Dave
49 * Fixed problem where ships were becoming traitor all the time.
51 * 4 2/24/99 2:25p Dave
52 * Fixed up chatbox bugs. Made squad war reporting better. Fixed a respawn
53 * bug for dogfight more.
55 * 3 2/23/99 8:11p Dave
56 * Tidied up dogfight mode. Fixed TvT ship type problems for alpha wing.
57 * Small pass over todolist items.
59 * 2 2/23/99 2:29p Dave
60 * First run of oldschool dogfight mode.
66 #include "multiutil.h"
67 #include "multi_log.h"
70 #include "systemvars.h"
71 #include "freespace.h"
73 #include "missionscreencommon.h"
74 #include "eventmusic.h"
80 #include "multi_dogfight.h"
81 #include "alphacolors.h"
83 // ----------------------------------------------------------------------------------------------------
84 // MULTI DOGFIGHT DEFINES/VARS
88 UI_WINDOW Multi_df_window;
90 #define NUM_MULTI_DF_BUTTONS 1
91 #define ACCEPT_BUTTON 0
93 ui_button_info Multi_df_buttons[GR_NUM_RESOLUTIONS][NUM_MULTI_DF_BUTTONS] = {
96 ui_button_info("CB_05a", 571, 425, 578, 413, 5),
100 ui_button_info("2_CB_05a", 914, 681, 914, 660, 5),
104 int Multi_df_background_bitmap = -1;
105 const char *Multi_df_background_fname[GR_NUM_RESOLUTIONS] = {
109 const char *Multi_df_mask_fname[GR_NUM_RESOLUTIONS] = {
114 // coord 3 is max width
115 static int Kill_matrix_title_coords[GR_NUM_RESOLUTIONS][3] = {
124 // display area coords
125 int Multi_df_display_coords[GR_NUM_RESOLUTIONS][4] = {
134 #define MULTI_DF_TOTAL_ADJUST 5
136 // "check" icon coords
137 int Multi_df_check_coords[GR_NUM_RESOLUTIONS] = {
145 // players when the screen started - we need to store this explicity so that even after players leave, we can display the full kill matrix
146 typedef struct multi_df_score {
147 char callsign[CALLSIGN_LEN+1]; // callsign for this guy
148 scoring_struct stats; // stats for the guy
149 int np_index; // absolute index into the netplayers array
151 multi_df_score Multi_df_score[MAX_PLAYERS];
152 int Multi_df_score_count = 0;
155 // ----------------------------------------------------------------------------------------------------
156 // MULTI DOGFIGHT FORWARD DECLARATIONS
159 // process button presses
160 void multi_df_process_buttons();
162 // button was pressed
163 void multi_df_button_pressed(int button);
165 // setup kill matrix data
166 void multi_df_setup_kill_matrix();
168 // blit the kill matrix
169 void multi_df_blit_kill_matrix();
171 // stuff a string representing the # of kills, player X had on player Y (where X and Y are indices into Multi_df_score)
172 // returns the # of kills
173 int multi_df_stuff_kills(char *kills, const int max_klen, int player_x, int player_y);
176 // ----------------------------------------------------------------------------------------------------
177 // MULTI DOGFIGHT FUNCTIONS
180 // call once per level just before entering the mission
181 void multi_df_level_pre_enter()
185 // if we're not in dogfight mode, do nothing
186 if(!(Netgame.type_flags & NG_TYPE_DOGFIGHT)){
190 // go through all player ships and make them hostile
191 for(idx=0; idx<MAX_PLAYERS; idx++){
192 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)){
193 Ships[Objects[Net_players[idx].player->objnum].instance].team = TEAM_TRAITOR;
200 // evaluate a kill in dogfight by a netplayer
201 void multi_df_eval_kill(net_player *killer, object *dead_obj)
205 // if we're not in dogfight mode, do nothing
206 if(!(Netgame.type_flags & NG_TYPE_DOGFIGHT)){
211 if((killer == NULL) || (dead_obj == NULL) || (killer->player == NULL)){
215 // try and find the dead player
216 dead_index = multi_find_player_by_object(dead_obj);
220 SDL_assert(dead_index < MAX_PLAYERS);
221 if(dead_index == NET_PLAYER_INDEX(killer)){
226 killer->player->stats.m_dogfight_kills[dead_index]++;
231 void multi_df_debrief_init()
233 // no longer is mission
234 Game_mode &= ~(GM_IN_MISSION);
237 // call scoring level close for my stats. Needed for award_init. The stats will
238 // be backed out if used chooses to replace them.
239 scoring_level_close();
241 // multiplayer debriefing stuff
242 multi_debrief_init();
244 // close down any old instances of the chatbox
247 // create the new one
250 // always play success music
251 common_music_init(SCORE_DEBRIEF_SUCCESS);
254 multi_df_setup_kill_matrix();
260 // load background bitmap
261 Multi_df_background_bitmap = bm_load(Multi_df_background_fname[gr_screen.res]);
262 SDL_assert(Multi_df_background_bitmap);
264 // create the UI window
265 Multi_df_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
266 Multi_df_window.set_mask_bmap(Multi_df_mask_fname[gr_screen.res]);
268 // initialize the control buttons
269 for (idx=0; idx<NUM_MULTI_DF_BUTTONS; idx++) {
270 b = &Multi_df_buttons[gr_screen.res][idx];
273 b->button.create(&Multi_df_window, NULL, b->x, b->y, 60, 30, 1, 1);
275 // set its highlight action
276 b->button.set_highlight_action(common_play_highlight_sound);
278 // set its animation bitmaps
279 b->button.set_bmaps(b->filename);
281 // link the mask hotspot
282 b->button.link_hotspot(b->hotspot);
286 w = &Multi_df_window;
287 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);
291 void multi_df_debrief_do()
296 k = chatbox_process();
297 new_k = Multi_df_window.process(k, 0);
299 // process keypresses
302 multi_debrief_esc_hit();
307 multi_df_process_buttons();
312 // process debriefing details
313 multi_debrief_do_frame();
315 // draw the background
316 GR_MAYBE_CLEAR_RES(Multi_df_background_bitmap);
317 if (Multi_df_background_bitmap >= 0) {
318 gr_set_bitmap(Multi_df_background_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
323 Multi_df_window.draw();
326 multi_df_blit_kill_matrix();
328 // render the chatbox
331 // draw the mission title
332 SDL_strlcpy(buf, The_mission.name, SDL_arraysize(buf));
333 gr_force_fit_string(buf, 255, Kill_matrix_title_coords[gr_screen.res][2]);
334 gr_set_color_fast(&Color_bright_white);
335 gr_string(Kill_matrix_title_coords[gr_screen.res][0], Kill_matrix_title_coords[gr_screen.res][1], buf);
342 void multi_df_debrief_close()
346 // shutdown the chatbox
349 // if stats weren't accepted, backout my own stats
350 if (multi_debrief_stats_accept_code() != 1) {
351 // if stats weren't accepted, backout my own stats
352 if (multi_debrief_stats_accept_code() != 1) {
353 if(MULTIPLAYER_MASTER){
354 for(idx=0; idx<MAX_PLAYERS; idx++){
355 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].player != NULL)){
356 scoring_backout_accept(&Net_players[idx].player->stats);
360 scoring_backout_accept( &Player->stats );
366 common_music_close();
370 // ----------------------------------------------------------------------------------------------------
371 // MULTI DOGFIGHT FORWARD DEFINITIONS
374 // process button presses
375 void multi_df_process_buttons()
379 for(idx=0; idx<NUM_MULTI_DF_BUTTONS; idx++){
380 if(Multi_df_buttons[gr_screen.res][idx].button.pressed()){
381 multi_df_button_pressed(idx);
387 // button was pressed
388 void multi_df_button_pressed(int button)
392 multi_debrief_accept_hit();
397 // setup kill matrix data
398 void multi_df_setup_kill_matrix()
403 Multi_df_score_count = 0;
405 // add players as necessary
406 for(idx=0; idx<MAX_PLAYERS; idx++){
407 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].player != NULL)){
408 // stuff data for this guy
409 s = &Multi_df_score[Multi_df_score_count++];
411 ml_printf("Dogfight debrief stats for %s", Net_players[idx].player->callsign);
412 for(int s_idx=0; s_idx<MAX_PLAYERS; s_idx++){
413 ml_printf("%d", Net_players[idx].player->stats.m_dogfight_kills[s_idx]);
416 s->stats = Net_players[idx].player->stats;
417 SDL_strlcpy(s->callsign, Net_players[idx].player->callsign, SDL_arraysize(s->callsign));
423 // blit the kill matrix
424 void multi_df_blit_kill_matrix()
426 int idx, s_idx, str_len;
428 char squashed_string[CALLSIGN_LEN+1] = "";
430 // max width of an individual item, and the text that can be in that item
431 float max_item_width = ((float)Multi_df_display_coords[gr_screen.res][2] - 40.0f) / (float)(Multi_df_score_count + 1);
432 float max_text_width = max_item_width * 0.8f;
434 // start x for the top bar (one item to the right)
435 int top_x_start = Multi_df_display_coords[gr_screen.res][0] + (int)max_item_width;
436 int top_y_start = Multi_df_display_coords[gr_screen.res][1];
438 // start x for the side bar
439 //int side_x_start = Multi_df_display_coords[gr_screen.res][0];
440 int side_y_start = Multi_df_display_coords[gr_screen.res][1] + 10;
445 for(idx=0; idx<Multi_df_score_count; idx++){
446 // force the string to fit nicely
447 SDL_strlcpy(squashed_string, Multi_df_score[idx].callsign, SDL_arraysize(squashed_string));
448 gr_force_fit_string(squashed_string, CALLSIGN_LEN, (int)max_text_width);
449 gr_get_string_size(&str_len, NULL, squashed_string);
451 // set color and blit the string
452 SDL_assert(Multi_df_score[idx].np_index >= 0);
453 if(Multi_df_score[idx].np_index >= 0){
454 gr_set_color_fast(Color_netplayer[Multi_df_score[idx].np_index]);
456 gr_string(cx + (int)((max_item_width - (float)str_len)/2.0f), cy, squashed_string);
459 cx += (int)max_item_width;
462 // draw the rest of the scoreboard
466 for(idx=0; idx<Multi_df_score_count; idx++){
467 // draw a check if necessary
468 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)){
469 if(Multi_common_icons[MICON_VALID] != -1){
470 gr_set_bitmap(Multi_common_icons[MICON_VALID], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
471 gr_bitmap(Multi_df_check_coords[gr_screen.res], cy);
476 cx = Multi_df_display_coords[gr_screen.res][0];
477 SDL_strlcpy(squashed_string, Multi_df_score[idx].callsign, SDL_arraysize(squashed_string));
478 gr_force_fit_string(squashed_string, CALLSIGN_LEN, (int)max_text_width);
479 gr_get_string_size(&str_len, NULL, squashed_string);
480 SDL_assert(Multi_df_score[idx].np_index >= 0);
481 if(Multi_df_score[idx].np_index >= 0){
482 gr_set_color_fast(Color_netplayer[Multi_df_score[idx].np_index]);
484 gr_string(cx, cy, squashed_string);
488 for(s_idx=0; s_idx<Multi_df_score_count; s_idx++){
489 // stuff the string to be displayed and select the proper display color
491 SDL_strlcpy(squashed_string, "-", SDL_arraysize(squashed_string));
492 gr_set_color_fast(&Color_grey);
494 row_total += multi_df_stuff_kills(squashed_string, SDL_arraysize(squashed_string), idx, s_idx);
495 SDL_assert(Multi_df_score[idx].np_index >= 0);
496 if(Multi_df_score[idx].np_index >= 0){
497 gr_set_color_fast(Color_netplayer[Multi_df_score[idx].np_index]);
502 gr_force_fit_string(squashed_string, CALLSIGN_LEN, (int)max_text_width);
503 gr_get_string_size(&str_len, NULL, squashed_string);
504 gr_string(cx + (int)((max_item_width - (float)str_len)/2.0f), cy, squashed_string);
507 cx += (int)max_item_width;
510 // draw the row total
511 gr_set_color_fast(Color_netplayer[Multi_df_score[idx].np_index]);
512 SDL_snprintf(squashed_string, SDL_arraysize(squashed_string), "(%d)", row_total);
513 gr_get_string_size(&str_len, NULL, squashed_string);
514 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);
524 for(idx=0; idx<Multi_df_score_count; idx++){
525 // add up this column
527 for(s_idx=0; s_idx<Multi_df_score_count; s_idx++){
529 column_total += Multi_df_score[idx].stats.m_dogfight_kills[s_idx];
533 sprintf(squashed_string, "%d", column_total);
534 gr_set_color_fast(Color_netplayer[Multi_df_score[idx].np_index]);
535 gr_string(cx, cy, squashed_string);
538 cx += (int)max_item_width;
543 // stuff a string representing the # of kills, player X had on player Y (where X and Y are indices into Multi_df_score)
544 // returns the # of kills
545 int multi_df_stuff_kills(char *kills, const int max_klen, int player_x, int player_y)
547 SDL_strlcpy(kills, "", max_klen);
550 multi_df_score *s = &Multi_df_score[player_x];
552 SDL_snprintf(kills, max_klen, "%d", s->stats.m_dogfight_kills[Multi_df_score[player_y].np_index]);
554 return s->stats.m_dogfight_kills[Multi_df_score[player_y].np_index];
556 SDL_snprintf(kills, max_klen, "%d", 0);