]> icculus.org git repositories - taylor/freespace2.git/blob - src/network/multi_dogfight.cpp
Initial revision
[taylor/freespace2.git] / src / network / multi_dogfight.cpp
1 /*
2  * $Logfile: /Freespace2/code/Network/multi_dogfight.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  * 
7  * $Log$
8  * Revision 1.1  2002/05/03 03:28:10  root
9  * Initial revision
10  *
11  * 
12  * 11    8/18/99 11:31a Jefff
13  * mission title on kill matrix
14  * 
15  * 10    8/17/99 9:55a Dave
16  * Fixed dogfight scoring problem.
17  * 
18  * 9     8/06/99 12:29a Dave
19  * Multiple bug fixes.
20  * 
21  * 8     5/03/99 8:32p Dave
22  * New version of multi host options screen.
23  * 
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.
27  * 
28  * 6     4/12/99 2:22p Dave
29  * More checks for dogfight stats.
30  * 
31  * 5     2/24/99 3:40p Dave
32  * Fixed problem where ships were becoming traitor all the time.
33  * 
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.
37  * 
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.
41  * 
42  * 2     2/23/99 2:29p Dave
43  * First run of oldschool dogfight mode. 
44  *   
45  * $NoKeywords: $
46  */
47
48 #include "multi.h"
49 #include "multiutil.h"
50 #include "multi_log.h"
51 #include "bmpman.h"
52 #include "object.h"
53 #include "systemvars.h"
54 #include "freespace.h"
55 #include "key.h"
56 #include "missionscreencommon.h"
57 #include "eventmusic.h"
58 #include "gamesnd.h"
59 #include "multiui.h"
60 #include "chatbox.h"
61 #include "ui.h"
62 #include "font.h"
63 #include "multi_dogfight.h"
64 #include "alphacolors.h"
65
66 // ----------------------------------------------------------------------------------------------------
67 // MULTI DOGFIGHT DEFINES/VARS
68 //
69
70 // interface stuff
71 UI_WINDOW Multi_df_window;
72
73 #define NUM_MULTI_DF_BUTTONS                    1
74 #define ACCEPT_BUTTON                                   0
75
76 ui_button_info Multi_df_buttons[GR_NUM_RESOLUTIONS][NUM_MULTI_DF_BUTTONS] = {
77         { // GR_640
78                 // accept
79                 ui_button_info("CB_05a",        571,    425,    578,    413,    5),
80         },      
81         { // GR_1024
82                 // accept
83                 ui_button_info("2_CB_05a",      914,    681,    914,    660,    5),
84         }
85 };
86
87 int Multi_df_background_bitmap = -1;
88 char *Multi_df_background_fname[GR_NUM_RESOLUTIONS] = {
89         "KillMatrix",
90         "2_KillMatrix"
91 };
92 char *Multi_df_mask_fname[GR_NUM_RESOLUTIONS] = {
93         "KillMatrix-m",
94         "2_KillMatrix-m"
95 };
96
97 // coord 3 is max width
98 static int Kill_matrix_title_coords[GR_NUM_RESOLUTIONS][3] = {
99         {       // GR_640
100                 19, 118, 172
101         },
102         {       // GR_1024
103                 33, 194, 272
104         }
105 };
106
107 // display area coords
108 int Multi_df_display_coords[GR_NUM_RESOLUTIONS][4] = {
109         { // GR_640
110                 43, 133, 569, 269
111         },
112         { // GR_1024
113                 60, 213, 919, 429
114         }
115 };
116
117 #define MULTI_DF_TOTAL_ADJUST                           5
118
119 // "check" icon coords
120 int Multi_df_check_coords[GR_NUM_RESOLUTIONS] = {
121         // GR_640
122         28,     
123
124         // GR_1024
125         45      
126 };
127
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
133 } multi_df_score;
134 multi_df_score Multi_df_score[MAX_PLAYERS];
135 int Multi_df_score_count = 0;
136
137
138 // ----------------------------------------------------------------------------------------------------
139 // MULTI DOGFIGHT FORWARD DECLARATIONS
140 //
141
142 // process button presses
143 void multi_df_process_buttons();
144
145 // button was pressed
146 void multi_df_button_pressed(int button);
147
148 // setup kill matrix data
149 void multi_df_setup_kill_matrix();
150
151 // blit the kill matrix
152 void multi_df_blit_kill_matrix();
153
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);
157
158
159 // ----------------------------------------------------------------------------------------------------
160 // MULTI DOGFIGHT FUNCTIONS
161 //
162
163 // call once per level just before entering the mission
164 void multi_df_level_pre_enter()
165 {                       
166         int idx;
167
168         // if we're not in dogfight mode, do nothing
169         if(!(Netgame.type_flags & NG_TYPE_DOGFIGHT)){
170                 return;
171         }
172
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;
177                 }
178         }
179
180         // 
181 }
182
183 // evaluate a kill in dogfight by a netplayer
184 void multi_df_eval_kill(net_player *killer, object *dead_obj)
185 {
186         int dead_index = -1;
187         
188         // if we're not in dogfight mode, do nothing
189         if(!(Netgame.type_flags & NG_TYPE_DOGFIGHT)){
190                 return;
191         }
192
193         // sanity checks
194         if((killer == NULL) || (dead_obj == NULL) || (killer->player == NULL)){
195                 return;
196         }
197         
198         // try and find the dead player
199         dead_index = multi_find_player_by_object(dead_obj);
200         if(dead_index < 0){
201                 return;
202         }
203         Assert(dead_index < MAX_PLAYERS);
204         if(dead_index == NET_PLAYER_INDEX(killer)){
205                 return;
206         }
207
208         // update his kills
209         killer->player->stats.m_dogfight_kills[dead_index]++;
210 }
211
212 // debrief
213 void multi_df_debrief_init()
214 {
215         // no longer is mission
216         Game_mode &= ~(GM_IN_MISSION);  
217         game_flush();
218
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();
222
223         // multiplayer debriefing stuff
224         multi_debrief_init();
225
226         // close down any old instances of the chatbox
227         chatbox_close();
228
229         // create the new one
230         chatbox_create();
231
232         // always play success music
233         common_music_init(SCORE_DEBRIEF_SUCCESS);
234
235         // setup kill matrix
236         multi_df_setup_kill_matrix();
237
238         UI_WINDOW *w;
239         ui_button_info *b;
240         int idx;
241
242         // load background bitmap
243         Multi_df_background_bitmap = bm_load(Multi_df_background_fname[gr_screen.res]);
244         Assert(Multi_df_background_bitmap);
245
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]);
249         
250         // initialize the control buttons
251         for (idx=0; idx<NUM_MULTI_DF_BUTTONS; idx++) {
252                 b = &Multi_df_buttons[gr_screen.res][idx];
253
254                 // create the button
255                 b->button.create(&Multi_df_window, NULL, b->x, b->y, 60, 30, 1, 1);
256                 
257                 // set its highlight action
258                 b->button.set_highlight_action(common_play_highlight_sound);
259
260                 // set its animation bitmaps
261                 b->button.set_bmaps(b->filename);
262
263                 // link the mask hotspot
264                 b->button.link_hotspot(b->hotspot);
265         }               
266
267         // add some text
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);
270 }
271
272 // do frame
273 void multi_df_debrief_do()
274 {
275         int k, new_k;
276         char buf[256];
277         
278         k = chatbox_process();  
279         new_k = Multi_df_window.process(k, 0);  
280
281         // process keypresses
282         switch(new_k){
283         case KEY_ESC:
284                 multi_debrief_esc_hit();
285                 break;
286         }
287
288         // process buttons      
289         multi_df_process_buttons();
290
291         // music stuff
292         common_music_do();
293
294         // process debriefing details
295         multi_debrief_do_frame();
296
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);
301                 gr_bitmap(0, 0);
302         } 
303
304         // draw the window
305         Multi_df_window.draw(); 
306
307         // kill matrix
308         multi_df_blit_kill_matrix();
309
310         // render the chatbox
311         chatbox_render();
312
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);
318
319         // flip
320         gr_flip();
321 }
322
323 // close
324 void multi_df_debrief_close()
325 {
326         int idx;
327
328         // shutdown the chatbox
329         chatbox_close();
330
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);
339                                         }
340                                 }
341                         } else {
342                                 scoring_backout_accept( &Player->stats );
343                         }
344                 }
345         }
346
347         // music stuff
348         common_music_close();
349 }
350
351
352 // ----------------------------------------------------------------------------------------------------
353 // MULTI DOGFIGHT FORWARD DEFINITIONS
354 //
355
356 // process button presses
357 void multi_df_process_buttons()
358 {
359         int idx;
360
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);
364                         break;
365                 }
366         }
367 }
368
369 // button was pressed
370 void multi_df_button_pressed(int button)
371 {
372         switch(button){
373         case ACCEPT_BUTTON:
374                 multi_debrief_accept_hit();
375                 break;
376         }
377 }
378
379 // setup kill matrix data
380 void multi_df_setup_kill_matrix()
381 {
382         int idx, s_idx;
383         multi_df_score *s;
384
385         Multi_df_score_count = 0;
386
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++];
392
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]);
396                         }
397
398                         s->stats = Net_players[idx].player->stats;
399                         strcpy(s->callsign, Net_players[idx].player->callsign);                 
400                         s->np_index = idx;
401                 }
402         }
403 }
404
405 // blit the kill matrix
406 void multi_df_blit_kill_matrix()
407 {
408         int idx, s_idx, str_len;
409         int cx, cy;
410         char squashed_string[CALLSIGN_LEN+1] = "";
411
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;
415
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];    
419
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;
423
424         // draw the top bar
425         cx = top_x_start;
426         cy = top_y_start;
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);
432
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]);
437                 }
438                 gr_string(cx + (int)((max_item_width - (float)str_len)/2.0f), cy, squashed_string);
439
440                 // next spot
441                 cx += (int)max_item_width;
442         }
443
444         // draw the rest of the scoreboard      
445         cx = side_x_start;
446         cy = side_y_start;
447         int row_total;
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);
454                         }
455                 }
456
457                 // draw the name
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]);
465                 }
466                 gr_string(cx, cy, squashed_string);
467
468                 cx = top_x_start;
469                 row_total = 0;
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
472                         if(s_idx == idx){
473                                 strcpy(squashed_string, "-");
474                                 gr_set_color_fast(&Color_grey);
475                         } else {
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]);
480                                 }                               
481                         }                                               
482
483                         // draw the string
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);
487
488                         // next spot
489                         cx += (int)max_item_width;
490                 }
491
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);
497
498                 cy += 10;
499         }
500
501         /*
502         // blit totals
503         int column_total;
504         cx = top_x_start;
505         cy += 3;
506         for(idx=0; idx<Multi_df_score_count; idx++){
507                 // add up this column
508                 column_total = 0;
509                 for(s_idx=0; s_idx<Multi_df_score_count; s_idx++){
510                         // add                  
511                         column_total += Multi_df_score[idx].stats.m_dogfight_kills[s_idx];
512                 }
513
514                 // draw
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);
518
519                 // next item
520                 cx += (int)max_item_width;
521         }
522         */
523 }
524
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)
528 {
529         multi_df_score *s = &Multi_df_score[player_x];
530         strcpy(kills, "");
531         
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];
534 }