]> icculus.org git repositories - taylor/freespace2.git/blob - src/network/multi_dogfight.cpp
port needs to be in host byte order
[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.4  2004/09/20 01:31:44  theoddone33
17  * GCC 3.4 fixes.
18  *
19  * Revision 1.3  2002/06/09 04:41:23  relnev
20  * added copyright header
21  *
22  * Revision 1.2  2002/05/07 03:16:47  theoddone33
23  * The Great Newline Fix
24  *
25  * Revision 1.1.1.1  2002/05/03 03:28:10  root
26  * Initial import.
27  *
28  * 
29  * 11    8/18/99 11:31a Jefff
30  * mission title on kill matrix
31  * 
32  * 10    8/17/99 9:55a Dave
33  * Fixed dogfight scoring problem.
34  * 
35  * 9     8/06/99 12:29a Dave
36  * Multiple bug fixes.
37  * 
38  * 8     5/03/99 8:32p Dave
39  * New version of multi host options screen.
40  * 
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.
44  * 
45  * 6     4/12/99 2:22p Dave
46  * More checks for dogfight stats.
47  * 
48  * 5     2/24/99 3:40p Dave
49  * Fixed problem where ships were becoming traitor all the time.
50  * 
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.
54  * 
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.
58  * 
59  * 2     2/23/99 2:29p Dave
60  * First run of oldschool dogfight mode. 
61  *   
62  * $NoKeywords: $
63  */
64
65 #include "multi.h"
66 #include "multiutil.h"
67 #include "multi_log.h"
68 #include "bmpman.h"
69 #include "object.h"
70 #include "systemvars.h"
71 #include "freespace.h"
72 #include "key.h"
73 #include "missionscreencommon.h"
74 #include "eventmusic.h"
75 #include "gamesnd.h"
76 #include "multiui.h"
77 #include "chatbox.h"
78 #include "ui.h"
79 #include "font.h"
80 #include "multi_dogfight.h"
81 #include "alphacolors.h"
82
83 // ----------------------------------------------------------------------------------------------------
84 // MULTI DOGFIGHT DEFINES/VARS
85 //
86
87 // interface stuff
88 UI_WINDOW Multi_df_window;
89
90 #define NUM_MULTI_DF_BUTTONS                    1
91 #define ACCEPT_BUTTON                                   0
92
93 ui_button_info Multi_df_buttons[GR_NUM_RESOLUTIONS][NUM_MULTI_DF_BUTTONS] = {
94         { // GR_640
95                 // accept
96                 ui_button_info("CB_05a",        571,    425,    578,    413,    5),
97         },      
98         { // GR_1024
99                 // accept
100                 ui_button_info("2_CB_05a",      914,    681,    914,    660,    5),
101         }
102 };
103
104 int Multi_df_background_bitmap = -1;
105 const char *Multi_df_background_fname[GR_NUM_RESOLUTIONS] = {
106         "KillMatrix",
107         "2_KillMatrix"
108 };
109 const char *Multi_df_mask_fname[GR_NUM_RESOLUTIONS] = {
110         "KillMatrix-m",
111         "2_KillMatrix-m"
112 };
113
114 // coord 3 is max width
115 static int Kill_matrix_title_coords[GR_NUM_RESOLUTIONS][3] = {
116         {       // GR_640
117                 19, 118, 172
118         },
119         {       // GR_1024
120                 33, 194, 272
121         }
122 };
123
124 // display area coords
125 int Multi_df_display_coords[GR_NUM_RESOLUTIONS][4] = {
126         { // GR_640
127                 43, 133, 569, 269
128         },
129         { // GR_1024
130                 60, 213, 919, 429
131         }
132 };
133
134 #define MULTI_DF_TOTAL_ADJUST                           5
135
136 // "check" icon coords
137 int Multi_df_check_coords[GR_NUM_RESOLUTIONS] = {
138         // GR_640
139         28,     
140
141         // GR_1024
142         45      
143 };
144
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
150 } multi_df_score;
151 multi_df_score Multi_df_score[MAX_PLAYERS];
152 int Multi_df_score_count = 0;
153
154
155 // ----------------------------------------------------------------------------------------------------
156 // MULTI DOGFIGHT FORWARD DECLARATIONS
157 //
158
159 // process button presses
160 void multi_df_process_buttons();
161
162 // button was pressed
163 void multi_df_button_pressed(int button);
164
165 // setup kill matrix data
166 void multi_df_setup_kill_matrix();
167
168 // blit the kill matrix
169 void multi_df_blit_kill_matrix();
170
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);
174
175
176 // ----------------------------------------------------------------------------------------------------
177 // MULTI DOGFIGHT FUNCTIONS
178 //
179
180 // call once per level just before entering the mission
181 void multi_df_level_pre_enter()
182 {                       
183         int idx;
184
185         // if we're not in dogfight mode, do nothing
186         if(!(Netgame.type_flags & NG_TYPE_DOGFIGHT)){
187                 return;
188         }
189
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;
194                 }
195         }
196
197         // 
198 }
199
200 // evaluate a kill in dogfight by a netplayer
201 void multi_df_eval_kill(net_player *killer, object *dead_obj)
202 {
203         int dead_index = -1;
204         
205         // if we're not in dogfight mode, do nothing
206         if(!(Netgame.type_flags & NG_TYPE_DOGFIGHT)){
207                 return;
208         }
209
210         // sanity checks
211         if((killer == NULL) || (dead_obj == NULL) || (killer->player == NULL)){
212                 return;
213         }
214         
215         // try and find the dead player
216         dead_index = multi_find_player_by_object(dead_obj);
217         if(dead_index < 0){
218                 return;
219         }
220         SDL_assert(dead_index < MAX_PLAYERS);
221         if(dead_index == NET_PLAYER_INDEX(killer)){
222                 return;
223         }
224 #ifndef MAKE_FS1
225         // update his kills
226         killer->player->stats.m_dogfight_kills[dead_index]++;
227 #endif
228 }
229
230 // debrief
231 void multi_df_debrief_init()
232 {
233         // no longer is mission
234         Game_mode &= ~(GM_IN_MISSION);  
235         game_flush();
236
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();
240
241         // multiplayer debriefing stuff
242         multi_debrief_init();
243
244         // close down any old instances of the chatbox
245         chatbox_close();
246
247         // create the new one
248         chatbox_create();
249
250         // always play success music
251         common_music_init(SCORE_DEBRIEF_SUCCESS);
252
253         // setup kill matrix
254         multi_df_setup_kill_matrix();
255
256         UI_WINDOW *w;
257         ui_button_info *b;
258         int idx;
259
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);
263
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]);
267         
268         // initialize the control buttons
269         for (idx=0; idx<NUM_MULTI_DF_BUTTONS; idx++) {
270                 b = &Multi_df_buttons[gr_screen.res][idx];
271
272                 // create the button
273                 b->button.create(&Multi_df_window, NULL, b->x, b->y, 60, 30, 1, 1);
274                 
275                 // set its highlight action
276                 b->button.set_highlight_action(common_play_highlight_sound);
277
278                 // set its animation bitmaps
279                 b->button.set_bmaps(b->filename);
280
281                 // link the mask hotspot
282                 b->button.link_hotspot(b->hotspot);
283         }               
284
285         // add some text
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);
288 }
289
290 // do frame
291 void multi_df_debrief_do()
292 {
293         int k, new_k;
294         char buf[256];
295         
296         k = chatbox_process();  
297         new_k = Multi_df_window.process(k, 0);  
298
299         // process keypresses
300         switch(new_k){
301         case SDLK_ESCAPE:
302                 multi_debrief_esc_hit();
303                 break;
304         }
305
306         // process buttons      
307         multi_df_process_buttons();
308
309         // music stuff
310         common_music_do();
311
312         // process debriefing details
313         multi_debrief_do_frame();
314
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);
319                 gr_bitmap(0, 0);
320         } 
321
322         // draw the window
323         Multi_df_window.draw(); 
324
325         // kill matrix
326         multi_df_blit_kill_matrix();
327
328         // render the chatbox
329         chatbox_render();
330
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);
336
337         // flip
338         gr_flip();
339 }
340
341 // close
342 void multi_df_debrief_close()
343 {
344         int idx;
345
346         // shutdown the chatbox
347         chatbox_close();
348
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);
357                                         }
358                                 }
359                         } else {
360                                 scoring_backout_accept( &Player->stats );
361                         }
362                 }
363         }
364
365         // music stuff
366         common_music_close();
367 }
368
369
370 // ----------------------------------------------------------------------------------------------------
371 // MULTI DOGFIGHT FORWARD DEFINITIONS
372 //
373
374 // process button presses
375 void multi_df_process_buttons()
376 {
377         int idx;
378
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);
382                         break;
383                 }
384         }
385 }
386
387 // button was pressed
388 void multi_df_button_pressed(int button)
389 {
390         switch(button){
391         case ACCEPT_BUTTON:
392                 multi_debrief_accept_hit();
393                 break;
394         }
395 }
396
397 // setup kill matrix data
398 void multi_df_setup_kill_matrix()
399 {
400         int idx;
401         multi_df_score *s;
402
403         Multi_df_score_count = 0;
404
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++];
410 #ifndef MAKE_FS1
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]);
414                         }
415 #endif
416                         s->stats = Net_players[idx].player->stats;
417                         SDL_strlcpy(s->callsign, Net_players[idx].player->callsign, SDL_arraysize(s->callsign));
418                         s->np_index = idx;
419                 }
420         }
421 }
422
423 // blit the kill matrix
424 void multi_df_blit_kill_matrix()
425 {
426         int idx, s_idx, str_len;
427         int cx, cy;
428         char squashed_string[CALLSIGN_LEN+1] = "";
429
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;
433
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];    
437
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;
441
442         // draw the top bar
443         cx = top_x_start;
444         cy = top_y_start;
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);
450
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]);
455                 }
456                 gr_string(cx + (int)((max_item_width - (float)str_len)/2.0f), cy, squashed_string);
457
458                 // next spot
459                 cx += (int)max_item_width;
460         }
461
462         // draw the rest of the scoreboard      
463         //cx = side_x_start;
464         cy = side_y_start;
465         int row_total;
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);
472                         }
473                 }
474
475                 // draw the name
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]);
483                 }
484                 gr_string(cx, cy, squashed_string);
485
486                 cx = top_x_start;
487                 row_total = 0;
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
490                         if(s_idx == idx){
491                                 SDL_strlcpy(squashed_string, "-", SDL_arraysize(squashed_string));
492                                 gr_set_color_fast(&Color_grey);
493                         } else {
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]);
498                                 }                               
499                         }                                               
500
501                         // draw the string
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);
505
506                         // next spot
507                         cx += (int)max_item_width;
508                 }
509
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);
515
516                 cy += 10;
517         }
518
519         /*
520         // blit totals
521         int column_total;
522         cx = top_x_start;
523         cy += 3;
524         for(idx=0; idx<Multi_df_score_count; idx++){
525                 // add up this column
526                 column_total = 0;
527                 for(s_idx=0; s_idx<Multi_df_score_count; s_idx++){
528                         // add                  
529                         column_total += Multi_df_score[idx].stats.m_dogfight_kills[s_idx];
530                 }
531
532                 // draw
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);
536
537                 // next item
538                 cx += (int)max_item_width;
539         }
540         */
541 }
542
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)
546 {
547         SDL_strlcpy(kills, "", max_klen);
548
549 #ifndef MAKE_FS1
550         multi_df_score *s = &Multi_df_score[player_x];
551
552         SDL_snprintf(kills, max_klen, "%d", s->stats.m_dogfight_kills[Multi_df_score[player_y].np_index]);
553
554         return s->stats.m_dogfight_kills[Multi_df_score[player_y].np_index];
555 #else
556         SDL_snprintf(kills, max_klen, "%d", 0);
557
558         return 0;
559 #endif
560 }