]> icculus.org git repositories - taylor/freespace2.git/blob - src/network/multi_dogfight.cpp
The Great Newline Fix
[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.2  2002/05/07 03:16:47  theoddone33
9  * The Great Newline Fix
10  *
11  * Revision 1.1.1.1  2002/05/03 03:28:10  root
12  * Initial import.
13  *
14  * 
15  * 11    8/18/99 11:31a Jefff
16  * mission title on kill matrix
17  * 
18  * 10    8/17/99 9:55a Dave
19  * Fixed dogfight scoring problem.
20  * 
21  * 9     8/06/99 12:29a Dave
22  * Multiple bug fixes.
23  * 
24  * 8     5/03/99 8:32p Dave
25  * New version of multi host options screen.
26  * 
27  * 7     4/12/99 10:07p Dave
28  * Made network startup more forgiving. Added checkmarks to dogfight
29  * screen for players who hit commit.
30  * 
31  * 6     4/12/99 2:22p Dave
32  * More checks for dogfight stats.
33  * 
34  * 5     2/24/99 3:40p Dave
35  * Fixed problem where ships were becoming traitor all the time.
36  * 
37  * 4     2/24/99 2:25p Dave
38  * Fixed up chatbox bugs. Made squad war reporting better. Fixed a respawn
39  * bug for dogfight more.
40  * 
41  * 3     2/23/99 8:11p Dave
42  * Tidied up dogfight mode. Fixed TvT ship type problems for alpha wing.
43  * Small pass over todolist items.
44  * 
45  * 2     2/23/99 2:29p Dave
46  * First run of oldschool dogfight mode. 
47  *   
48  * $NoKeywords: $
49  */
50
51 #include "multi.h"
52 #include "multiutil.h"
53 #include "multi_log.h"
54 #include "bmpman.h"
55 #include "object.h"
56 #include "systemvars.h"
57 #include "freespace.h"
58 #include "key.h"
59 #include "missionscreencommon.h"
60 #include "eventmusic.h"
61 #include "gamesnd.h"
62 #include "multiui.h"
63 #include "chatbox.h"
64 #include "ui.h"
65 #include "font.h"
66 #include "multi_dogfight.h"
67 #include "alphacolors.h"
68
69 // ----------------------------------------------------------------------------------------------------
70 // MULTI DOGFIGHT DEFINES/VARS
71 //
72
73 // interface stuff
74 UI_WINDOW Multi_df_window;
75
76 #define NUM_MULTI_DF_BUTTONS                    1
77 #define ACCEPT_BUTTON                                   0
78
79 ui_button_info Multi_df_buttons[GR_NUM_RESOLUTIONS][NUM_MULTI_DF_BUTTONS] = {
80         { // GR_640
81                 // accept
82                 ui_button_info("CB_05a",        571,    425,    578,    413,    5),
83         },      
84         { // GR_1024
85                 // accept
86                 ui_button_info("2_CB_05a",      914,    681,    914,    660,    5),
87         }
88 };
89
90 int Multi_df_background_bitmap = -1;
91 char *Multi_df_background_fname[GR_NUM_RESOLUTIONS] = {
92         "KillMatrix",
93         "2_KillMatrix"
94 };
95 char *Multi_df_mask_fname[GR_NUM_RESOLUTIONS] = {
96         "KillMatrix-m",
97         "2_KillMatrix-m"
98 };
99
100 // coord 3 is max width
101 static int Kill_matrix_title_coords[GR_NUM_RESOLUTIONS][3] = {
102         {       // GR_640
103                 19, 118, 172
104         },
105         {       // GR_1024
106                 33, 194, 272
107         }
108 };
109
110 // display area coords
111 int Multi_df_display_coords[GR_NUM_RESOLUTIONS][4] = {
112         { // GR_640
113                 43, 133, 569, 269
114         },
115         { // GR_1024
116                 60, 213, 919, 429
117         }
118 };
119
120 #define MULTI_DF_TOTAL_ADJUST                           5
121
122 // "check" icon coords
123 int Multi_df_check_coords[GR_NUM_RESOLUTIONS] = {
124         // GR_640
125         28,     
126
127         // GR_1024
128         45      
129 };
130
131 // players when the screen started - we need to store this explicity so that even after players leave, we can display the full kill matrix
132 typedef struct multi_df_score {
133         char callsign[CALLSIGN_LEN+1];          // callsign for this guy        
134         scoring_struct stats;                                   // stats for the guy    
135         int np_index;                                                           // absolute index into the netplayers array
136 } multi_df_score;
137 multi_df_score Multi_df_score[MAX_PLAYERS];
138 int Multi_df_score_count = 0;
139
140
141 // ----------------------------------------------------------------------------------------------------
142 // MULTI DOGFIGHT FORWARD DECLARATIONS
143 //
144
145 // process button presses
146 void multi_df_process_buttons();
147
148 // button was pressed
149 void multi_df_button_pressed(int button);
150
151 // setup kill matrix data
152 void multi_df_setup_kill_matrix();
153
154 // blit the kill matrix
155 void multi_df_blit_kill_matrix();
156
157 // stuff a string representing the # of kills, player X had on player Y (where X and Y are indices into Multi_df_score)
158 // returns the # of kills
159 int multi_df_stuff_kills(char *kills, int player_x, int player_y);
160
161
162 // ----------------------------------------------------------------------------------------------------
163 // MULTI DOGFIGHT FUNCTIONS
164 //
165
166 // call once per level just before entering the mission
167 void multi_df_level_pre_enter()
168 {                       
169         int idx;
170
171         // if we're not in dogfight mode, do nothing
172         if(!(Netgame.type_flags & NG_TYPE_DOGFIGHT)){
173                 return;
174         }
175
176         // go through all player ships and make them hostile
177         for(idx=0; idx<MAX_PLAYERS; idx++){
178                 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)){
179                         Ships[Objects[Net_players[idx].player->objnum].instance].team = TEAM_TRAITOR;
180                 }
181         }
182
183         // 
184 }
185
186 // evaluate a kill in dogfight by a netplayer
187 void multi_df_eval_kill(net_player *killer, object *dead_obj)
188 {
189         int dead_index = -1;
190         
191         // if we're not in dogfight mode, do nothing
192         if(!(Netgame.type_flags & NG_TYPE_DOGFIGHT)){
193                 return;
194         }
195
196         // sanity checks
197         if((killer == NULL) || (dead_obj == NULL) || (killer->player == NULL)){
198                 return;
199         }
200         
201         // try and find the dead player
202         dead_index = multi_find_player_by_object(dead_obj);
203         if(dead_index < 0){
204                 return;
205         }
206         Assert(dead_index < MAX_PLAYERS);
207         if(dead_index == NET_PLAYER_INDEX(killer)){
208                 return;
209         }
210
211         // update his kills
212         killer->player->stats.m_dogfight_kills[dead_index]++;
213 }
214
215 // debrief
216 void multi_df_debrief_init()
217 {
218         // no longer is mission
219         Game_mode &= ~(GM_IN_MISSION);  
220         game_flush();
221
222         // call scoring level close for my stats.  Needed for award_init.  The stats will
223         // be backed out if used chooses to replace them.
224         scoring_level_close();
225
226         // multiplayer debriefing stuff
227         multi_debrief_init();
228
229         // close down any old instances of the chatbox
230         chatbox_close();
231
232         // create the new one
233         chatbox_create();
234
235         // always play success music
236         common_music_init(SCORE_DEBRIEF_SUCCESS);
237
238         // setup kill matrix
239         multi_df_setup_kill_matrix();
240
241         UI_WINDOW *w;
242         ui_button_info *b;
243         int idx;
244
245         // load background bitmap
246         Multi_df_background_bitmap = bm_load(Multi_df_background_fname[gr_screen.res]);
247         Assert(Multi_df_background_bitmap);
248
249         // create the UI window
250         Multi_df_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
251         Multi_df_window.set_mask_bmap(Multi_df_mask_fname[gr_screen.res]);
252         
253         // initialize the control buttons
254         for (idx=0; idx<NUM_MULTI_DF_BUTTONS; idx++) {
255                 b = &Multi_df_buttons[gr_screen.res][idx];
256
257                 // create the button
258                 b->button.create(&Multi_df_window, NULL, b->x, b->y, 60, 30, 1, 1);
259                 
260                 // set its highlight action
261                 b->button.set_highlight_action(common_play_highlight_sound);
262
263                 // set its animation bitmaps
264                 b->button.set_bmaps(b->filename);
265
266                 // link the mask hotspot
267                 b->button.link_hotspot(b->hotspot);
268         }               
269
270         // add some text
271         w = &Multi_df_window;   
272         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);
273 }
274
275 // do frame
276 void multi_df_debrief_do()
277 {
278         int k, new_k;
279         char buf[256];
280         
281         k = chatbox_process();  
282         new_k = Multi_df_window.process(k, 0);  
283
284         // process keypresses
285         switch(new_k){
286         case KEY_ESC:
287                 multi_debrief_esc_hit();
288                 break;
289         }
290
291         // process buttons      
292         multi_df_process_buttons();
293
294         // music stuff
295         common_music_do();
296
297         // process debriefing details
298         multi_debrief_do_frame();
299
300         // draw the background
301         GR_MAYBE_CLEAR_RES(Multi_df_background_bitmap);
302         if (Multi_df_background_bitmap >= 0) {
303                 gr_set_bitmap(Multi_df_background_bitmap);
304                 gr_bitmap(0, 0);
305         } 
306
307         // draw the window
308         Multi_df_window.draw(); 
309
310         // kill matrix
311         multi_df_blit_kill_matrix();
312
313         // render the chatbox
314         chatbox_render();
315
316         // draw the mission title
317         strcpy(buf, The_mission.name);
318         gr_force_fit_string(buf, 255, Kill_matrix_title_coords[gr_screen.res][2]);
319         gr_set_color_fast(&Color_bright_white);
320         gr_string(Kill_matrix_title_coords[gr_screen.res][0], Kill_matrix_title_coords[gr_screen.res][1], buf);
321
322         // flip
323         gr_flip();
324 }
325
326 // close
327 void multi_df_debrief_close()
328 {
329         int idx;
330
331         // shutdown the chatbox
332         chatbox_close();
333
334         // if stats weren't accepted, backout my own stats
335         if (multi_debrief_stats_accept_code() != 1) {           
336                 // if stats weren't accepted, backout my own stats
337                 if (multi_debrief_stats_accept_code() != 1) {
338                         if(MULTIPLAYER_MASTER){
339                                 for(idx=0; idx<MAX_PLAYERS; idx++){
340                                         if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].player != NULL)){
341                                                 scoring_backout_accept(&Net_players[idx].player->stats);
342                                         }
343                                 }
344                         } else {
345                                 scoring_backout_accept( &Player->stats );
346                         }
347                 }
348         }
349
350         // music stuff
351         common_music_close();
352 }
353
354
355 // ----------------------------------------------------------------------------------------------------
356 // MULTI DOGFIGHT FORWARD DEFINITIONS
357 //
358
359 // process button presses
360 void multi_df_process_buttons()
361 {
362         int idx;
363
364         for(idx=0; idx<NUM_MULTI_DF_BUTTONS; idx++){
365                 if(Multi_df_buttons[gr_screen.res][idx].button.pressed()){
366                         multi_df_button_pressed(idx);
367                         break;
368                 }
369         }
370 }
371
372 // button was pressed
373 void multi_df_button_pressed(int button)
374 {
375         switch(button){
376         case ACCEPT_BUTTON:
377                 multi_debrief_accept_hit();
378                 break;
379         }
380 }
381
382 // setup kill matrix data
383 void multi_df_setup_kill_matrix()
384 {
385         int idx, s_idx;
386         multi_df_score *s;
387
388         Multi_df_score_count = 0;
389
390         // add players as necessary
391         for(idx=0; idx<MAX_PLAYERS; idx++){
392                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].player != NULL)){
393                         // stuff data for this guy
394                         s = &Multi_df_score[Multi_df_score_count++];
395
396                         ml_printf("Dogfight debrief stats for %s", Net_players[idx].player->callsign);
397                         for(s_idx=0; s_idx<MAX_PLAYERS; s_idx++){
398                                 ml_printf("%d", Net_players[idx].player->stats.m_dogfight_kills[s_idx]);
399                         }
400
401                         s->stats = Net_players[idx].player->stats;
402                         strcpy(s->callsign, Net_players[idx].player->callsign);                 
403                         s->np_index = idx;
404                 }
405         }
406 }
407
408 // blit the kill matrix
409 void multi_df_blit_kill_matrix()
410 {
411         int idx, s_idx, str_len;
412         int cx, cy;
413         char squashed_string[CALLSIGN_LEN+1] = "";
414
415         // max width of an individual item, and the text that can be in that item
416         float max_item_width = ((float)Multi_df_display_coords[gr_screen.res][2] - 40.0f) / (float)(Multi_df_score_count + 1);
417         float max_text_width = max_item_width * 0.8f;
418
419         // start x for the top bar (one item to the right)
420         int top_x_start = Multi_df_display_coords[gr_screen.res][0] + (int)max_item_width;
421         int top_y_start = Multi_df_display_coords[gr_screen.res][1];    
422
423         // start x for the side bar
424         int side_x_start = Multi_df_display_coords[gr_screen.res][0];
425         int side_y_start = Multi_df_display_coords[gr_screen.res][1] + 10;
426
427         // draw the top bar
428         cx = top_x_start;
429         cy = top_y_start;
430         for(idx=0; idx<Multi_df_score_count; idx++){            
431                 // force the string to fit nicely
432                 strcpy(squashed_string, Multi_df_score[idx].callsign);
433                 gr_force_fit_string(squashed_string, CALLSIGN_LEN, (int)max_text_width);
434                 gr_get_string_size(&str_len, NULL, squashed_string);
435
436                 // set color and blit the string                
437                 Assert(Multi_df_score[idx].np_index >= 0);
438                 if(Multi_df_score[idx].np_index >= 0){
439                         gr_set_color_fast(Color_netplayer[Multi_df_score[idx].np_index]);
440                 }
441                 gr_string(cx + (int)((max_item_width - (float)str_len)/2.0f), cy, squashed_string);
442
443                 // next spot
444                 cx += (int)max_item_width;
445         }
446
447         // draw the rest of the scoreboard      
448         cx = side_x_start;
449         cy = side_y_start;
450         int row_total;
451         for(idx=0; idx<Multi_df_score_count; idx++){            
452                 // draw a check if necessary
453                 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)){
454                         if(Multi_common_icons[MICON_VALID] != -1){
455                                 gr_set_bitmap(Multi_common_icons[MICON_VALID]);
456                                 gr_bitmap(Multi_df_check_coords[gr_screen.res], cy);
457                         }
458                 }
459
460                 // draw the name
461                 cx = Multi_df_display_coords[gr_screen.res][0];
462                 strcpy(squashed_string, Multi_df_score[idx].callsign);
463                 gr_force_fit_string(squashed_string, CALLSIGN_LEN, (int)max_text_width);
464                 gr_get_string_size(&str_len, NULL, squashed_string);            
465                 Assert(Multi_df_score[idx].np_index >= 0);
466                 if(Multi_df_score[idx].np_index >= 0){
467                         gr_set_color_fast(Color_netplayer[Multi_df_score[idx].np_index]);
468                 }
469                 gr_string(cx, cy, squashed_string);
470
471                 cx = top_x_start;
472                 row_total = 0;
473                 for(s_idx=0; s_idx<Multi_df_score_count; s_idx++){
474                         // stuff the string to be displayed and select the proper display color
475                         if(s_idx == idx){
476                                 strcpy(squashed_string, "-");
477                                 gr_set_color_fast(&Color_grey);
478                         } else {
479                                 row_total += multi_df_stuff_kills(squashed_string, idx, s_idx);
480                                 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]);
483                                 }                               
484                         }                                               
485
486                         // draw the string
487                         gr_force_fit_string(squashed_string, CALLSIGN_LEN, (int)max_text_width);
488                         gr_get_string_size(&str_len, NULL, squashed_string);
489                         gr_string(cx + (int)((max_item_width - (float)str_len)/2.0f), cy, squashed_string);
490
491                         // next spot
492                         cx += (int)max_item_width;
493                 }
494
495                 // draw the row total
496                 gr_set_color_fast(Color_netplayer[Multi_df_score[idx].np_index]);
497                 sprintf(squashed_string, "(%d)", row_total);
498                 gr_get_string_size(&str_len, NULL, squashed_string);
499                 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);
500
501                 cy += 10;
502         }
503
504         /*
505         // blit totals
506         int column_total;
507         cx = top_x_start;
508         cy += 3;
509         for(idx=0; idx<Multi_df_score_count; idx++){
510                 // add up this column
511                 column_total = 0;
512                 for(s_idx=0; s_idx<Multi_df_score_count; s_idx++){
513                         // add                  
514                         column_total += Multi_df_score[idx].stats.m_dogfight_kills[s_idx];
515                 }
516
517                 // draw
518                 sprintf(squashed_string, "%d", column_total);
519                 gr_set_color_fast(Color_netplayer[Multi_df_score[idx].np_index]);
520                 gr_string(cx, cy, squashed_string);
521
522                 // next item
523                 cx += (int)max_item_width;
524         }
525         */
526 }
527
528 // stuff a string representing the # of kills, player X had on player Y (where X and Y are indices into Multi_df_score)
529 // returns the # of kills
530 int multi_df_stuff_kills(char *kills, int player_x, int player_y)
531 {
532         multi_df_score *s = &Multi_df_score[player_x];
533         strcpy(kills, "");
534         
535         sprintf(kills, "%d", s->stats.m_dogfight_kills[Multi_df_score[player_y].np_index]);
536         return s->stats.m_dogfight_kills[Multi_df_score[player_y].np_index];
537 }