]> icculus.org git repositories - taylor/freespace2.git/blob - src/network/multi_dogfight.cpp
added copyright header
[taylor/freespace2.git] / src / network / multi_dogfight.cpp
1 /*
2  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3  *
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
6  * the source.
7  */
8
9 /*
10  * $Logfile: /Freespace2/code/Network/multi_dogfight.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  * 
15  * $Log$
16  * Revision 1.3  2002/06/09 04:41:23  relnev
17  * added copyright header
18  *
19  * Revision 1.2  2002/05/07 03:16:47  theoddone33
20  * The Great Newline Fix
21  *
22  * Revision 1.1.1.1  2002/05/03 03:28:10  root
23  * Initial import.
24  *
25  * 
26  * 11    8/18/99 11:31a Jefff
27  * mission title on kill matrix
28  * 
29  * 10    8/17/99 9:55a Dave
30  * Fixed dogfight scoring problem.
31  * 
32  * 9     8/06/99 12:29a Dave
33  * Multiple bug fixes.
34  * 
35  * 8     5/03/99 8:32p Dave
36  * New version of multi host options screen.
37  * 
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.
41  * 
42  * 6     4/12/99 2:22p Dave
43  * More checks for dogfight stats.
44  * 
45  * 5     2/24/99 3:40p Dave
46  * Fixed problem where ships were becoming traitor all the time.
47  * 
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.
51  * 
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.
55  * 
56  * 2     2/23/99 2:29p Dave
57  * First run of oldschool dogfight mode. 
58  *   
59  * $NoKeywords: $
60  */
61
62 #include "multi.h"
63 #include "multiutil.h"
64 #include "multi_log.h"
65 #include "bmpman.h"
66 #include "object.h"
67 #include "systemvars.h"
68 #include "freespace.h"
69 #include "key.h"
70 #include "missionscreencommon.h"
71 #include "eventmusic.h"
72 #include "gamesnd.h"
73 #include "multiui.h"
74 #include "chatbox.h"
75 #include "ui.h"
76 #include "font.h"
77 #include "multi_dogfight.h"
78 #include "alphacolors.h"
79
80 // ----------------------------------------------------------------------------------------------------
81 // MULTI DOGFIGHT DEFINES/VARS
82 //
83
84 // interface stuff
85 UI_WINDOW Multi_df_window;
86
87 #define NUM_MULTI_DF_BUTTONS                    1
88 #define ACCEPT_BUTTON                                   0
89
90 ui_button_info Multi_df_buttons[GR_NUM_RESOLUTIONS][NUM_MULTI_DF_BUTTONS] = {
91         { // GR_640
92                 // accept
93                 ui_button_info("CB_05a",        571,    425,    578,    413,    5),
94         },      
95         { // GR_1024
96                 // accept
97                 ui_button_info("2_CB_05a",      914,    681,    914,    660,    5),
98         }
99 };
100
101 int Multi_df_background_bitmap = -1;
102 char *Multi_df_background_fname[GR_NUM_RESOLUTIONS] = {
103         "KillMatrix",
104         "2_KillMatrix"
105 };
106 char *Multi_df_mask_fname[GR_NUM_RESOLUTIONS] = {
107         "KillMatrix-m",
108         "2_KillMatrix-m"
109 };
110
111 // coord 3 is max width
112 static int Kill_matrix_title_coords[GR_NUM_RESOLUTIONS][3] = {
113         {       // GR_640
114                 19, 118, 172
115         },
116         {       // GR_1024
117                 33, 194, 272
118         }
119 };
120
121 // display area coords
122 int Multi_df_display_coords[GR_NUM_RESOLUTIONS][4] = {
123         { // GR_640
124                 43, 133, 569, 269
125         },
126         { // GR_1024
127                 60, 213, 919, 429
128         }
129 };
130
131 #define MULTI_DF_TOTAL_ADJUST                           5
132
133 // "check" icon coords
134 int Multi_df_check_coords[GR_NUM_RESOLUTIONS] = {
135         // GR_640
136         28,     
137
138         // GR_1024
139         45      
140 };
141
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
147 } multi_df_score;
148 multi_df_score Multi_df_score[MAX_PLAYERS];
149 int Multi_df_score_count = 0;
150
151
152 // ----------------------------------------------------------------------------------------------------
153 // MULTI DOGFIGHT FORWARD DECLARATIONS
154 //
155
156 // process button presses
157 void multi_df_process_buttons();
158
159 // button was pressed
160 void multi_df_button_pressed(int button);
161
162 // setup kill matrix data
163 void multi_df_setup_kill_matrix();
164
165 // blit the kill matrix
166 void multi_df_blit_kill_matrix();
167
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);
171
172
173 // ----------------------------------------------------------------------------------------------------
174 // MULTI DOGFIGHT FUNCTIONS
175 //
176
177 // call once per level just before entering the mission
178 void multi_df_level_pre_enter()
179 {                       
180         int idx;
181
182         // if we're not in dogfight mode, do nothing
183         if(!(Netgame.type_flags & NG_TYPE_DOGFIGHT)){
184                 return;
185         }
186
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;
191                 }
192         }
193
194         // 
195 }
196
197 // evaluate a kill in dogfight by a netplayer
198 void multi_df_eval_kill(net_player *killer, object *dead_obj)
199 {
200         int dead_index = -1;
201         
202         // if we're not in dogfight mode, do nothing
203         if(!(Netgame.type_flags & NG_TYPE_DOGFIGHT)){
204                 return;
205         }
206
207         // sanity checks
208         if((killer == NULL) || (dead_obj == NULL) || (killer->player == NULL)){
209                 return;
210         }
211         
212         // try and find the dead player
213         dead_index = multi_find_player_by_object(dead_obj);
214         if(dead_index < 0){
215                 return;
216         }
217         Assert(dead_index < MAX_PLAYERS);
218         if(dead_index == NET_PLAYER_INDEX(killer)){
219                 return;
220         }
221
222         // update his kills
223         killer->player->stats.m_dogfight_kills[dead_index]++;
224 }
225
226 // debrief
227 void multi_df_debrief_init()
228 {
229         // no longer is mission
230         Game_mode &= ~(GM_IN_MISSION);  
231         game_flush();
232
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();
236
237         // multiplayer debriefing stuff
238         multi_debrief_init();
239
240         // close down any old instances of the chatbox
241         chatbox_close();
242
243         // create the new one
244         chatbox_create();
245
246         // always play success music
247         common_music_init(SCORE_DEBRIEF_SUCCESS);
248
249         // setup kill matrix
250         multi_df_setup_kill_matrix();
251
252         UI_WINDOW *w;
253         ui_button_info *b;
254         int idx;
255
256         // load background bitmap
257         Multi_df_background_bitmap = bm_load(Multi_df_background_fname[gr_screen.res]);
258         Assert(Multi_df_background_bitmap);
259
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]);
263         
264         // initialize the control buttons
265         for (idx=0; idx<NUM_MULTI_DF_BUTTONS; idx++) {
266                 b = &Multi_df_buttons[gr_screen.res][idx];
267
268                 // create the button
269                 b->button.create(&Multi_df_window, NULL, b->x, b->y, 60, 30, 1, 1);
270                 
271                 // set its highlight action
272                 b->button.set_highlight_action(common_play_highlight_sound);
273
274                 // set its animation bitmaps
275                 b->button.set_bmaps(b->filename);
276
277                 // link the mask hotspot
278                 b->button.link_hotspot(b->hotspot);
279         }               
280
281         // add some text
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);
284 }
285
286 // do frame
287 void multi_df_debrief_do()
288 {
289         int k, new_k;
290         char buf[256];
291         
292         k = chatbox_process();  
293         new_k = Multi_df_window.process(k, 0);  
294
295         // process keypresses
296         switch(new_k){
297         case KEY_ESC:
298                 multi_debrief_esc_hit();
299                 break;
300         }
301
302         // process buttons      
303         multi_df_process_buttons();
304
305         // music stuff
306         common_music_do();
307
308         // process debriefing details
309         multi_debrief_do_frame();
310
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);
315                 gr_bitmap(0, 0);
316         } 
317
318         // draw the window
319         Multi_df_window.draw(); 
320
321         // kill matrix
322         multi_df_blit_kill_matrix();
323
324         // render the chatbox
325         chatbox_render();
326
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);
332
333         // flip
334         gr_flip();
335 }
336
337 // close
338 void multi_df_debrief_close()
339 {
340         int idx;
341
342         // shutdown the chatbox
343         chatbox_close();
344
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);
353                                         }
354                                 }
355                         } else {
356                                 scoring_backout_accept( &Player->stats );
357                         }
358                 }
359         }
360
361         // music stuff
362         common_music_close();
363 }
364
365
366 // ----------------------------------------------------------------------------------------------------
367 // MULTI DOGFIGHT FORWARD DEFINITIONS
368 //
369
370 // process button presses
371 void multi_df_process_buttons()
372 {
373         int idx;
374
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);
378                         break;
379                 }
380         }
381 }
382
383 // button was pressed
384 void multi_df_button_pressed(int button)
385 {
386         switch(button){
387         case ACCEPT_BUTTON:
388                 multi_debrief_accept_hit();
389                 break;
390         }
391 }
392
393 // setup kill matrix data
394 void multi_df_setup_kill_matrix()
395 {
396         int idx, s_idx;
397         multi_df_score *s;
398
399         Multi_df_score_count = 0;
400
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++];
406
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]);
410                         }
411
412                         s->stats = Net_players[idx].player->stats;
413                         strcpy(s->callsign, Net_players[idx].player->callsign);                 
414                         s->np_index = idx;
415                 }
416         }
417 }
418
419 // blit the kill matrix
420 void multi_df_blit_kill_matrix()
421 {
422         int idx, s_idx, str_len;
423         int cx, cy;
424         char squashed_string[CALLSIGN_LEN+1] = "";
425
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;
429
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];    
433
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;
437
438         // draw the top bar
439         cx = top_x_start;
440         cy = top_y_start;
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);
446
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]);
451                 }
452                 gr_string(cx + (int)((max_item_width - (float)str_len)/2.0f), cy, squashed_string);
453
454                 // next spot
455                 cx += (int)max_item_width;
456         }
457
458         // draw the rest of the scoreboard      
459         cx = side_x_start;
460         cy = side_y_start;
461         int row_total;
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);
468                         }
469                 }
470
471                 // draw the name
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]);
479                 }
480                 gr_string(cx, cy, squashed_string);
481
482                 cx = top_x_start;
483                 row_total = 0;
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
486                         if(s_idx == idx){
487                                 strcpy(squashed_string, "-");
488                                 gr_set_color_fast(&Color_grey);
489                         } else {
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]);
494                                 }                               
495                         }                                               
496
497                         // draw the string
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);
501
502                         // next spot
503                         cx += (int)max_item_width;
504                 }
505
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);
511
512                 cy += 10;
513         }
514
515         /*
516         // blit totals
517         int column_total;
518         cx = top_x_start;
519         cy += 3;
520         for(idx=0; idx<Multi_df_score_count; idx++){
521                 // add up this column
522                 column_total = 0;
523                 for(s_idx=0; s_idx<Multi_df_score_count; s_idx++){
524                         // add                  
525                         column_total += Multi_df_score[idx].stats.m_dogfight_kills[s_idx];
526                 }
527
528                 // draw
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);
532
533                 // next item
534                 cx += (int)max_item_width;
535         }
536         */
537 }
538
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)
542 {
543         multi_df_score *s = &Multi_df_score[player_x];
544         strcpy(kills, "");
545         
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];
548 }