]> icculus.org git repositories - taylor/freespace2.git/blob - src/network/multi_pinfo.cpp
Initial revision
[taylor/freespace2.git] / src / network / multi_pinfo.cpp
1 /*
2  * $Logfile: /Freespace2/code/Network/multi_pinfo.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  * 15    9/10/99 9:44p Dave
13  * Bumped version # up. Make server reliable connects not have such a huge
14  * timeout. 
15  * 
16  * 14    8/30/99 5:01p Dave
17  * Made d3d do less state changing in the nebula. Use new chat server for
18  * PXO.
19  * 
20  * 13    8/22/99 5:53p Dave
21  * Scoring fixes. Added self destruct key. Put callsigns in the logfile
22  * instead of ship designations for multiplayer players.
23  * 
24  * 12    8/03/99 11:43a Dave
25  * Don't allow medals button in demo.
26  * 
27  * 11    7/26/99 11:14a Andsager
28  * Disable medals in demo multiplayer
29  * 
30  * 10    6/16/99 4:06p Dave
31  * New pilot info popup. Added new draw-bitmap-as-poly function.
32  * 
33  * 9     1/30/99 1:29a Dave
34  * Fixed nebula thumbnail problem. Full support for 1024x768 choose pilot
35  * screen.  Fixed beam weapon death messages.
36  * 
37  * 8     1/14/99 6:06p Dave
38  * 100% full squad logo support for single player and multiplayer.
39  * 
40  * 7     1/12/99 4:07a Dave
41  * Put in barracks code support for selecting squad logos. Properly
42  * distribute squad logos in a multiplayer game.
43  * 
44  * 6     12/14/98 12:13p Dave
45  * Spiffed up xfer system a bit. Put in support for squad logo file xfer.
46  * Need to test now.
47  * 
48  * 5     11/30/98 1:07p Dave
49  * 16 bit conversion, first run.
50  * 
51  * 4     11/05/98 4:18p Dave
52  * First run nebula support. Beefed up localization a bit. Removed all
53  * conditional compiles for foreign versions. Modified mission file
54  * format.
55  * 
56  * 3     10/13/98 9:29a Dave
57  * Started neatening up freespace.h. Many variables renamed and
58  * reorganized. Added AlphaColors.[h,cpp]
59  * 
60  * 2     10/07/98 10:53a Dave
61  * Initial checkin.
62  * 
63  * 1     10/07/98 10:50a Dave
64  * 
65  * 
66  * $NoKeywords: $
67  */
68
69 #include "ui.h"
70 #include "bmpman.h"
71 #include "gamesnd.h"
72 #include "freespace.h"
73 #include "gamesequence.h"
74 #include "key.h"
75 #include "medals.h"
76 #include "palman.h"
77 #include "mouse.h"
78 #include "multi.h"
79 #include "multi_xfer.h"
80 #include "multi_pinfo.h"
81 #include "alphacolors.h"
82 #include "localize.h"
83 #include "3d.h"
84
85 // ---------------------------------------------------------------------------------------
86 // MULTI PLAYER INFO DEFINES/VARS
87 //
88
89 //XSTR:OFF
90
91 #define MULTI_PINFO_NUM_BUTTONS         4
92
93 // bitmaps defs
94 char *Multi_pinfo_bitmap_name[GR_NUM_RESOLUTIONS] = {
95         "PilotInfo",
96         "2_PilotInfo"
97 };
98 char *Multi_pinfo_bitmap_mask[GR_NUM_RESOLUTIONS] = {
99         "PilotInfo-M",
100         "2_PilotInfo-M"
101 };
102
103 // button defs
104 #define MPI_SCROLL_STATS_UP                     0
105 #define MPI_SCROLL_STATS_DOWN                   1
106 #define MPI_MEDALS                                              2
107 #define MPI_EXIT                                                        3
108
109 // pilot image area defs
110 int Multi_pinfo_pilot_coords[GR_NUM_RESOLUTIONS][4] = {
111         { // GR_640
112                 22, 159, 160, 120
113         },
114         { // GR_1024
115                 35, 254, 256, 192
116         }
117 };
118 int Multi_pinfo_squad_coords[GR_NUM_RESOLUTIONS][4] = {
119         { // GR_640
120                 22, 299, 128, 128
121         },
122         { // GR_1024
123                 35, 479, 205, 205
124         }
125 };
126
127 // pilot bitmaps
128 typedef struct np_bitmap {
129         int bitmap;                                                                     // bitmap id
130         char filename[MAX_FILENAME_LEN];                // filename
131 } np_bitmap;
132 np_bitmap Mp_pilot;                     // pilot pic
133 np_bitmap Mp_squad;                     // squad logo
134
135 UI_WINDOW Multi_pinfo_window;                                                                                   // the window object for the join screen
136 UI_BUTTON Multi_pinfo_select_button;                                                            // for selecting list items
137 int Multi_pinfo_bitmap;                                                                                                 // the background bitmap
138 ui_button_info Multi_pinfo_buttons[GR_NUM_RESOLUTIONS][MULTI_PINFO_NUM_BUTTONS] = {
139         { // GR_640
140                 ui_button_info("PIB_00",        617,    256,    -1,     -1,     0),
141                 ui_button_info("PIB_01",        617,    298,    -1,     -1,     1),
142                 ui_button_info("PIB_02",        172,    322,    -1,     -1,     2),
143                 ui_button_info("PIB_03",        219,    332,    217,    318,    3)
144         },
145         { // GR_1024
146                 ui_button_info("2_PIB_00",      988,    410,    -1,     -1,     0),
147                 ui_button_info("2_PIB_01",      988,    477,    -1,     -1,     1),
148                 ui_button_info("2_PIB_02",      276,    516,    -1,     -1,     2),
149                 ui_button_info("2_PIB_03",      350,    532,    348,    510,    3)
150         }
151 };
152
153 #define MULTI_PINFO_NUM_TEXT                    1
154 UI_XSTR Multi_pinfo_text[GR_NUM_RESOLUTIONS][MULTI_PINFO_NUM_TEXT] = {
155         { // GR_640
156                 { "Close",              428,    217,    318,    UI_XSTR_COLOR_PINK, -1, &Multi_pinfo_buttons[0][MPI_EXIT].button },             
157         },
158         { // GR_1024
159                 { "Close",              428,    348,    510,    UI_XSTR_COLOR_PINK, -1, &Multi_pinfo_buttons[1][MPI_EXIT].button },             
160         }
161 };
162
163 //XSTR:ON
164
165 // stats labels
166 #define MULTI_PINFO_NUM_STATS_LABELS            9
167 #define MPI_RANK                                                                        0
168 #define MPI_MISSIONS_FLOWN                                              1
169 #define MPI_FLIGHT_TIME                                                 2
170 #define MPI_LAST_FLOWN                                                  3
171 #define MPI_FIGHTER_KILLS                                               4
172 // #define MPI_OTHER_KILLS                                                      5
173 #define MPI_PSHOTS_FIRED                                                5
174 //#define MPI_PSHOTS_HIT                                                        6
175  #define MPI_PSHOTS_PCT                                                 6
176 #define MPI_SSHOTS_FIRED                                                7
177 // #define MPI_SSHOTS_HIT                                                       10
178 #define MPI_SSHOTS_PCT                                                  8
179
180 char *Multi_pinfo_stats_labels[MULTI_PINFO_NUM_STATS_LABELS]; 
181
182 #define MAX_LABEL_TEXT          50
183 char Multi_pinfo_stats_vals[MULTI_PINFO_NUM_STATS_LABELS][MAX_LABEL_TEXT];
184 int Multi_pinfo_stats_label_offsets[MULTI_PINFO_NUM_STATS_LABELS] = {
185         20,10,10,20,20,10,20,10,10,
186 };
187
188 // stats area defs
189 int Multi_pinfo_stats_area_coords[GR_NUM_RESOLUTIONS][4] = {
190         { // GR_640
191                 215, 163, 414, 155
192         },
193         { // GR_1024
194                 335, 261, 662, 248
195         }
196 };
197 int Multi_pinfo_stats_x[GR_NUM_RESOLUTIONS] = {
198         460,            // GR_640
199         650             // GR_1024
200 };
201
202 // is the popup already running
203 int Multi_pinfo_popup_running = 0;
204
205 // background bitmap to be blitted
206 int Multi_pinfo_screen_save = -1;
207
208 // flag indicating if the popup has gotten messed up somewhere and should bail
209 int Multi_pinfo_popup_error = 0;
210
211 // flag indicating if the popup should be done
212 int Multi_pinfo_popup_done = 0;
213
214 // player this popup is being used for
215 net_player *Multi_pinfo_popup_player = NULL;
216
217 // screen shader
218 extern shader Grey_shader;
219
220 // hardware textures backup
221 int Multi_pinfo_hardware_texture_backup;
222
223
224 // ---------------------------------------------------------------------------------------
225 // MULTI PLAYER INFO FORWARD DECLARATIONS
226 //
227
228 // initialize all popup details (graphics, etc)
229 void multi_pinfo_popup_init(net_player *pl);
230
231 // run the popup in a tight loop (no states)
232 void multi_pinfo_popup_do();
233
234 // close the popup
235 void multi_pinfo_popup_close();
236
237 // blit the pilot image
238 void multi_pinfo_blit_pilot_image();
239
240 // blit the pilot squadron logo
241 void multi_pinfo_blit_squadron_logo();
242
243 // blit the player statistics
244 void multi_pinfo_blit_player_stats();
245
246 // check for button presses
247 void multi_pinfo_popup_check_buttons();
248
249 // act on a button press
250 void multi_pinfo_popup_button_pressed(int n);
251
252 // display the medals screen for this player
253 void multi_pinfo_do_medals();
254
255 // load up and use the proper palette
256 void multi_pinfo_set_palette();
257
258 // build the stats value strings for this player
259 void multi_pinfo_build_stats();
260
261 // if the pilot's image was currently loading when we started the popup, load it up now if its finished
262 void multi_pinfo_maybe_reload_pic(np_bitmap *b);
263
264 // reset the player infomation for this popup
265 void multi_pinfo_reset_player(net_player *np);
266
267 // lookup the "previous" player in the netplayer list, return null if not found
268 net_player *multi_pinfo_get_prev_player(net_player *np);
269
270 // lookup the "next" player in the netplayer list, return null if not found
271 net_player *multi_pinfo_get_next_player(net_player *np);
272
273
274 // ---------------------------------------------------------------------------------------
275 // MULTI PLAYER INFO FUNCTIONS
276 //
277
278 // fire up the player info popup, select first available pilot if np == NULL
279 void multi_pinfo_popup(net_player *np)
280 {
281         // if the popup is already running, don't do anything
282         if(Multi_pinfo_popup_running){
283                 return;
284         }
285
286         // set the player for informational purposes
287         Assert(np != NULL);     
288
289         // play the popup appear sound
290         gamesnd_play_iface(SND_POPUP_APPEAR);
291
292         // initialize the popup
293         multi_pinfo_popup_init(np);
294
295         // mark the popup as running
296         Multi_pinfo_popup_running = 1;
297
298         // run the popup
299         multi_pinfo_popup_do();
300
301         // close the popup
302         multi_pinfo_popup_close();
303
304         // play the popup disappear sound
305         gamesnd_play_iface(SND_POPUP_DISAPPEAR);
306 }
307
308 // notify the popup that a player has left
309 void multi_pinfo_notify_drop(net_player *np)
310 {
311         net_player *reset;
312
313         // if we're no active, bail
314         if(!Multi_pinfo_popup_running){
315                 return;
316         }
317
318         // if this is not the player we're currently displaying, bail
319         if(np != Multi_pinfo_popup_player){
320                 return;
321         }
322
323         // otherwise we need to switch to someone else
324         reset = multi_pinfo_get_prev_player(np);
325         if(reset != NULL){
326                 multi_pinfo_reset_player(reset);
327                 return;
328         }
329         reset = multi_pinfo_get_next_player(np);
330         if(reset != NULL){
331                 multi_pinfo_reset_player(reset);
332                 return;
333         }
334
335         // bail, since there's no one else
336         Int3();
337         Multi_pinfo_popup_done = 1;
338 }
339
340
341 // ---------------------------------------------------------------------------------------
342 // MULTI PLAYER INFO FORWARD DEFINITIONS
343 //
344
345 // initialize all popup details (graphics, etc)
346 void multi_pinfo_popup_init(net_player *np)
347 {
348         int idx;
349         
350         // no errors to start with
351         Multi_pinfo_popup_error = 0;
352
353         // shouldn't be done
354         Multi_pinfo_popup_done = 0;
355
356         // store the background as it currently is
357         Multi_pinfo_screen_save = gr_save_screen();
358         if(Multi_pinfo_screen_save == -1){
359                 Multi_pinfo_popup_error = 1;
360                 return;
361         }
362
363         // create the interface window
364         Multi_pinfo_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
365         Multi_pinfo_window.set_mask_bmap(Multi_pinfo_bitmap_mask[gr_screen.res]);
366
367         // load the background bitmap
368         Multi_pinfo_bitmap = bm_load(Multi_pinfo_bitmap_name[gr_screen.res]);
369         if(Multi_pinfo_bitmap < 0){
370                 Multi_pinfo_popup_error = 1;
371                 return; 
372         }
373
374         // backup hardware textures setting and bash to max
375         Multi_pinfo_hardware_texture_backup = Detail.hardware_textures;
376         Detail.hardware_textures = MAX_DETAIL_LEVEL;
377
378         // zero bitmap info
379         Mp_pilot.bitmap = -1;
380         strcpy(Mp_pilot.filename, "");
381         Mp_squad.bitmap = -1;
382         strcpy(Mp_squad.filename, "");
383
384         // set the player status
385         multi_pinfo_reset_player(np);   
386         
387         // create the interface buttons
388         for(idx=0;idx<MULTI_PINFO_NUM_BUTTONS;idx++){
389                 // create the object
390                 Multi_pinfo_buttons[gr_screen.res][idx].button.create(&Multi_pinfo_window, "", Multi_pinfo_buttons[gr_screen.res][idx].x, Multi_pinfo_buttons[gr_screen.res][idx].y, 1, 1, 0, 1);
391
392                 // set the sound to play when highlighted
393                 Multi_pinfo_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
394
395                 // set the ani for the button
396                 Multi_pinfo_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pinfo_buttons[gr_screen.res][idx].filename);
397
398                 // set the hotspot
399                 Multi_pinfo_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pinfo_buttons[gr_screen.res][idx].hotspot);
400         }                       
401
402         // add xstrs
403         for(idx=0; idx<MULTI_PINFO_NUM_TEXT; idx++){
404                 Multi_pinfo_window.add_XSTR(&Multi_pinfo_text[gr_screen.res][idx]);
405         }
406
407         // disable medals button for the demo
408 #ifdef FS2_DEMO
409         Multi_pinfo_buttons[gr_screen.res][MPI_MEDALS].button.hide();
410         Multi_pinfo_buttons[gr_screen.res][MPI_MEDALS].button.disable();
411 #endif
412
413         // initialize strings   
414         Multi_pinfo_stats_labels[0] = strdup(XSTR("Rank", 1007));
415         Multi_pinfo_stats_labels[1] = strdup(XSTR("Missions Flown", 1008));
416         Multi_pinfo_stats_labels[2] = strdup(XSTR("Flight Time", 1009));
417         Multi_pinfo_stats_labels[3] = strdup(XSTR("Last Flown",1010));
418         Multi_pinfo_stats_labels[4] = strdup(XSTR("Total Kills", 115));
419         Multi_pinfo_stats_labels[5] = strdup(XSTR("Primary Shots Fired", 1012));
420         Multi_pinfo_stats_labels[6] = strdup(XSTR("Primary Hit %", 1013));
421         Multi_pinfo_stats_labels[7] = strdup(XSTR("Secondary Shots Fired",      1014));
422         Multi_pinfo_stats_labels[8] = strdup(XSTR("Secondary Hit %", 1015));                            
423 }
424
425 // run the popup in a tight loop (no states)
426 void multi_pinfo_popup_do()
427 {
428         int k;
429         
430         // if there was an error in initialization, return immediately
431         if(Multi_pinfo_popup_error){
432                 return;
433         }
434
435         // tight loop
436         while(!Multi_pinfo_popup_done){         
437                 multi_pinfo_maybe_reload_pic(&Mp_pilot);                
438                 multi_pinfo_maybe_reload_pic(&Mp_squad);                
439
440                 // process the window
441                 k = Multi_pinfo_window.process();
442                 switch(k){
443                 case KEY_ESC :
444                         Multi_pinfo_popup_done = 1;
445                         break;
446                 }
447
448                 // check button presses
449                 multi_pinfo_popup_check_buttons();
450
451                 // set frametime and run background stuff
452                 game_set_frametime(-1);
453                 game_do_state_common(gameseq_get_state());
454                 
455                 // draw the background bitmap and the ui window over it
456                 Assert(Multi_pinfo_screen_save != -1);
457                 gr_reset_clip();
458                 gr_restore_screen(Multi_pinfo_screen_save);             
459
460                 // grey the screen
461                 gr_set_shader(&Grey_shader);
462                 gr_shade(0,0,gr_screen.clip_width, gr_screen.clip_height);
463                 
464                 // draw the background bitmap
465                 gr_set_bitmap(Multi_pinfo_bitmap);
466                 gr_bitmap(0,0);         
467
468                 // blit the selected pilot image
469                 multi_pinfo_blit_pilot_image();
470
471                 // blit the squadron logo
472                 multi_pinfo_blit_squadron_logo();
473
474                 // blit the player statistics
475                 multi_pinfo_blit_player_stats();                
476
477                 // draw the ui window and flip
478                 Multi_pinfo_window.draw();              
479                 gr_flip();
480         }
481 }
482
483 // close the popup
484 void multi_pinfo_popup_close()
485 {
486         int idx;
487         
488         // unload any bitmaps
489         if(Multi_pinfo_bitmap != -1){
490                 bm_release(Multi_pinfo_bitmap);         
491         }       
492
493         // free the background screen if possible
494         if(Multi_pinfo_screen_save >= 0){
495                 gr_free_screen(Multi_pinfo_screen_save);        
496         }
497
498         // release the pilot/squad images
499         if(Mp_pilot.bitmap != -1){
500                 bm_release(Mp_pilot.bitmap);
501         }
502         if(Mp_squad.bitmap != -1){
503                 bm_release(Mp_squad.bitmap);
504         }
505
506         // free up strings
507         for(idx=0; idx<MULTI_PINFO_NUM_STATS_LABELS; idx++){
508                 if(Multi_pinfo_stats_labels[idx] != NULL){
509                         free(Multi_pinfo_stats_labels[idx]);
510                         Multi_pinfo_stats_labels[idx] = NULL;
511                 }
512         }       
513
514         // unset the player handle
515         Multi_pinfo_popup_player = NULL;
516
517         // mark the popup as not running
518         Multi_pinfo_popup_running = 0;
519         
520         // destroy the UI_WINDOW
521         Multi_pinfo_window.destroy();
522
523         // restore hardware textures detail level
524         Detail.hardware_textures = Multi_pinfo_hardware_texture_backup;
525 }
526
527 // blit the pilot image
528 void multi_pinfo_blit_pilot_image()
529 {
530         char place_text[100];   
531         int w;
532
533         // if we don't have a bitmap handle, blit a placeholder
534         if(Mp_pilot.bitmap == -1){
535                 gr_set_color_fast(&Color_normal);               
536
537                 // if there is no image
538                 if(strlen(Mp_pilot.filename) <= 0){
539                         strcpy(place_text,XSTR("No/Invalid Image", 1053));
540                 } 
541                 // if the image is xferring
542                 else if(multi_xfer_lookup(Mp_pilot.filename)){
543                         strcpy(place_text,XSTR("Image Transferring", 691));
544                 }
545                 // if we're not accepting images
546                 else if(!(Net_player->p_info.options.flags & MLO_FLAG_ACCEPT_PIX) || !(Netgame.options.flags & MSO_FLAG_ACCEPT_PIX)){
547                         strcpy(place_text,XSTR("No Image", 692));
548                 }
549                 // otherwise we wait
550                 else {
551                         strcpy(place_text,XSTR("Waiting", 690));
552                 }               
553
554                 // center the text
555                 gr_get_string_size(&w,NULL,place_text);
556                 gr_string(Multi_pinfo_pilot_coords[gr_screen.res][0] + ((Multi_pinfo_pilot_coords[gr_screen.res][2] - w)/2), Multi_pinfo_pilot_coords[gr_screen.res][1], place_text);
557         } 
558         // otherwise blit the bitmap
559         else {
560                 gr_set_bitmap(Mp_pilot.bitmap);
561
562                 // get width and heigh
563                 int w, h;
564                 bm_get_info(Mp_pilot.bitmap, &w, &h, NULL, NULL, NULL, NULL);
565
566                 gr_bitmap(Multi_pinfo_pilot_coords[gr_screen.res][0] + ((Multi_pinfo_pilot_coords[gr_screen.res][2] - w)/2), 
567                                          Multi_pinfo_pilot_coords[gr_screen.res][1] + ((Multi_pinfo_pilot_coords[gr_screen.res][3] - h)/2));
568                 // g3_draw_2d_poly_bitmap(Multi_pinfo_pilot_coords[gr_screen.res][0], Multi_pinfo_pilot_coords[gr_screen.res][1], Multi_pinfo_pilot_coords[gr_screen.res][2], Multi_pinfo_pilot_coords[gr_screen.res][3]);
569         }
570 }
571
572 // blit the pilot squadron logo
573 void multi_pinfo_blit_squadron_logo()
574 {
575         char place_text[100];   
576         int w;
577         player *p = Multi_pinfo_popup_player->player;
578
579         // if we don't have a bitmap handle, blit a placeholder
580         if(Mp_squad.bitmap == -1){
581                 gr_set_color_fast(&Color_normal);               
582
583                 // if there is no image
584                 if(strlen(p->squad_filename) <= 0){
585                         strcpy(place_text,XSTR("No/Invalid Image", 1053));
586                 } 
587                 // if the image is xferring
588                 else if(multi_xfer_lookup(p->squad_filename)){
589                         strcpy(place_text,XSTR("Image Transferring", 691));
590                 }
591                 // if we're not accepting images
592                 else if(!(Net_player->p_info.options.flags & MLO_FLAG_ACCEPT_PIX) || !(Netgame.options.flags & MSO_FLAG_ACCEPT_PIX)){
593                         strcpy(place_text,XSTR("No Image", 692));
594                 }
595                 // otherwise we wait
596                 else {
597                         strcpy(place_text,XSTR("Waiting", 690));
598                 }                               
599
600                 // center the text
601                 gr_get_string_size(&w, NULL, place_text);
602                 gr_string(Multi_pinfo_squad_coords[gr_screen.res][0] + ((Multi_pinfo_squad_coords[gr_screen.res][2] - w)/2), Multi_pinfo_squad_coords[gr_screen.res][1], place_text);
603         } 
604         // otherwise blit the bitmap
605         else {
606                 gr_set_bitmap(Mp_squad.bitmap);
607                 // gr_bitmap(MPI_SQUAD_X, MPI_SQUAD_Y);
608
609                 // get width and heigh
610                 int w, h;
611                 bm_get_info(Mp_squad.bitmap, &w, &h, NULL, NULL, NULL, NULL);
612
613                 gr_bitmap(Multi_pinfo_squad_coords[gr_screen.res][0] + ((Multi_pinfo_squad_coords[gr_screen.res][2] - w)/2), 
614                                          Multi_pinfo_squad_coords[gr_screen.res][1] + ((Multi_pinfo_squad_coords[gr_screen.res][3] - h)/2));
615                 // g3_draw_2d_poly_bitmap(Multi_pinfo_squad_coords[gr_screen.res][0], Multi_pinfo_squad_coords[gr_screen.res][1], Multi_pinfo_squad_coords[gr_screen.res][2], Multi_pinfo_squad_coords[gr_screen.res][3]);
616         }
617 }
618
619 // blit the player statistics
620 void multi_pinfo_blit_player_stats()
621 {
622         int idx,y_start;        
623
624         // blit the player's callsign and "all time stats"
625         gr_set_color_fast(&Color_bright);
626         gr_string(Multi_pinfo_stats_area_coords[gr_screen.res][0], Multi_pinfo_stats_area_coords[gr_screen.res][1], Multi_pinfo_popup_player->player->callsign);
627         gr_string(Multi_pinfo_stats_x[gr_screen.res], Multi_pinfo_stats_area_coords[gr_screen.res][1], XSTR("All Time Stats", 128));
628         
629         gr_set_color_fast(&Color_normal);
630
631         // blit all the labels
632         y_start = Multi_pinfo_stats_area_coords[gr_screen.res][1] + 15;
633         for(idx=0;idx<MULTI_PINFO_NUM_STATS_LABELS;idx++){
634                 gr_string(Multi_pinfo_stats_area_coords[gr_screen.res][0], y_start, Multi_pinfo_stats_labels[idx]);
635                 y_start += Multi_pinfo_stats_label_offsets[idx];
636         }       
637
638         // blit all the stats values themselves
639         y_start = Multi_pinfo_stats_area_coords[gr_screen.res][1] + 15;
640         for(idx=0;idx<MULTI_PINFO_NUM_STATS_LABELS;idx++){
641                 gr_string(Multi_pinfo_stats_x[gr_screen.res], y_start, Multi_pinfo_stats_vals[idx]);
642                 y_start += Multi_pinfo_stats_label_offsets[idx];
643         }       
644 }
645
646 // check for button presses
647 void multi_pinfo_popup_check_buttons()
648 {
649         int idx;
650
651         // check for all buttons
652         for(idx=0;idx<MULTI_PINFO_NUM_BUTTONS;idx++){
653                 if(Multi_pinfo_buttons[gr_screen.res][idx].button.pressed()){
654                         multi_pinfo_popup_button_pressed(idx);
655                         break;
656                 }
657         }
658 }
659
660 // act on a button press
661 void multi_pinfo_popup_button_pressed(int n)
662 {
663         net_player *swap;
664
665         switch(n){
666         case MPI_EXIT:
667                 Multi_pinfo_popup_done = 1;             
668                 break;
669
670         case MPI_MEDALS:
671                 gamesnd_play_iface(SND_USER_SELECT);
672                 multi_pinfo_do_medals();
673                 break;
674
675         case MPI_SCROLL_STATS_UP:
676                 swap = multi_pinfo_get_prev_player(Multi_pinfo_popup_player);
677                 if(swap != NULL){
678                         gamesnd_play_iface(SND_USER_SELECT);
679                         multi_pinfo_reset_player(swap);
680                 } else {
681                         gamesnd_play_iface(SND_GENERAL_FAIL);
682                 }
683                 break;
684
685         case MPI_SCROLL_STATS_DOWN:
686                 swap = multi_pinfo_get_next_player(Multi_pinfo_popup_player);
687                 if(swap != NULL){
688                         gamesnd_play_iface(SND_USER_SELECT);
689                         multi_pinfo_reset_player(swap);
690                 } else {
691                         gamesnd_play_iface(SND_GENERAL_FAIL);
692                 }
693                 break;
694
695         default :
696                 gamesnd_play_iface(SND_GENERAL_FAIL);
697                 break;
698         }
699 }
700
701 // display the medals screen for this player
702 void multi_pinfo_do_medals()
703 {
704 #ifdef FS2DEMO
705         game_feature_not_in_demo_popup();
706 #else
707         int ret_code;
708
709         // initialize the medals screen
710         medal_main_init(Multi_pinfo_popup_player->player,MM_POPUP);
711
712         // run the medals screen until it says that it should be closed
713         do {
714                 // set frametime and run common functions
715                 game_set_frametime(-1);
716                 game_do_state_common(gameseq_get_state());
717
718                 // run the medals screen
719                 ret_code = medal_main_do();             
720         } while(ret_code && !Multi_pinfo_popup_done);
721
722         // close the medals screen down
723         medal_main_close();
724
725         // restore the proper palette
726         multi_pinfo_set_palette();
727 #endif
728 }
729
730 // load up and use the proper palette
731 void multi_pinfo_set_palette()
732 {
733 #ifndef HARDWARE_ONLY
734         palette_use_bm_palette(Multi_pinfo_bitmap);
735 #endif
736 }
737
738 // build the stats value strings for this player
739 void multi_pinfo_build_stats()
740 {
741         // int idx;
742         // int fighter_kills,other_kills;
743         scoring_struct *sc = &Multi_pinfo_popup_player->player->stats;
744
745         // build alltime fighter and non-fighter kills
746         /*
747         fighter_kills = 0;
748         other_kills = 0;
749         for(idx=0;idx<MAX_SHIP_TYPES;idx++){
750                 if(sc->kills[idx] > 0){
751                         if(Ship_info[idx].flags & SIF_FIGHTER){
752                                 fighter_kills += sc->kills[idx];
753                         } else {
754                                 other_kills += sc->kills[idx];
755                         }
756                 }
757         }       
758         */
759         sprintf(Multi_pinfo_stats_vals[MPI_FIGHTER_KILLS], "%d", sc->kill_count);
760         
761         // sprintf(Multi_pinfo_stats_vals[MPI_OTHER_KILLS],"%d",other_kills);
762
763         // missions flown
764         sprintf(Multi_pinfo_stats_vals[MPI_MISSIONS_FLOWN],"%d",(int)sc->missions_flown);
765
766         // flight time          
767         game_format_time(fl2f((float)sc->flight_time),Multi_pinfo_stats_vals[MPI_FLIGHT_TIME]);         
768
769         // last flown   
770         if(sc->last_flown == 0){
771                 strcpy(Multi_pinfo_stats_vals[MPI_LAST_FLOWN],XSTR("No missions flown",693));
772         } else {
773                 tm *tmr = gmtime(&sc->last_flown);
774                 if(tmr != NULL){
775                         strftime(Multi_pinfo_stats_vals[MPI_LAST_FLOWN],MAX_LABEL_TEXT,"%m/%d/%y %H:%M",tmr);
776                 } else {
777                         strcpy(Multi_pinfo_stats_vals[MPI_LAST_FLOWN], "");                     
778                 }
779         }       
780
781         // rank
782         strcpy(Multi_pinfo_stats_vals[MPI_RANK],Ranks[sc->rank].name);
783
784         // primary shots fired
785         sprintf(Multi_pinfo_stats_vals[MPI_PSHOTS_FIRED],"%d",sc->p_shots_fired);
786
787         // primary shots hit
788         // sprintf(Multi_pinfo_stats_vals[MPI_PSHOTS_HIT],"%d",sc->p_shots_hit);
789         
790         // primary hit pct
791         sprintf(Multi_pinfo_stats_vals[MPI_PSHOTS_PCT],"%d%%",(int)(100.0f * ((float)sc->p_shots_hit / (float)sc->p_shots_fired)));
792         // primary shots fired
793         sprintf(Multi_pinfo_stats_vals[MPI_SSHOTS_FIRED],"%d",sc->s_shots_fired);
794
795         // primary shots hit
796         // sprintf(Multi_pinfo_stats_vals[MPI_SSHOTS_HIT],"%d",sc->s_shots_hit);
797         
798         // primary hit pct
799         sprintf(Multi_pinfo_stats_vals[MPI_SSHOTS_PCT],"%d%%",(int)(100.0f * ((float)sc->s_shots_hit / (float)sc->s_shots_fired)));
800 }
801
802 // if the pilot's image was currently loading when we started the popup, load it up now if its finished
803 void multi_pinfo_maybe_reload_pic(np_bitmap *b)
804 {       
805         // if the bitmap is valid, do nothing
806         if(b->bitmap >= 0){
807                 return;
808         }       
809
810         // if the local player is not accepting pix or the netgame is not accepting pix, bail here
811         if(!(Net_player->p_info.options.flags & MLO_FLAG_ACCEPT_PIX) || !(Netgame.options.flags & MSO_FLAG_ACCEPT_PIX)){                
812                 return;
813         }                       
814
815         // if the bitmap filename is bogus
816         if(strlen(b->filename) <= 0){
817                 return;
818         }       
819
820         // try again
821         b->bitmap = bm_load_duplicate(b->filename);     
822 }
823
824 // attempt to validate a bitmap (ie, return whether its displayable or not)
825 /*
826 int multi_pinfo_validate_bitmap(int bitmap)
827 {
828         int w,h;
829         
830         // if the bitmap handle is invalid false
831         if(bitmap == -1){
832                 return 0;
833         }
834         
835         // get the bitmap info
836         w = -1;
837         h = -1;
838         bm_get_info(bitmap,&w,&h);      
839
840         // return fail
841         if((w != MPI_IMAGE_W) || (h != MPI_IMAGE_H)){
842                 return 0;
843         }
844
845         // return success
846         return 1;
847 }
848 */
849
850 // is the pilot info popup currently active?
851 int multi_pinfo_popup_active()
852 {
853         return Multi_pinfo_popup_running;
854 }
855
856 // kill the currently active popup (if any)
857 void multi_pinfo_popup_kill()
858 {
859         // we're done, byatch
860         Multi_pinfo_popup_done = 1;
861 }
862
863 // reset the player infomation for this popup
864 void multi_pinfo_reset_player(net_player *np)
865 {       
866         // assign the player
867         Multi_pinfo_popup_player = np;
868
869         // unload any old image data if necessary
870         strcpy(Mp_pilot.filename, "");
871         if(Mp_pilot.bitmap != -1){
872                 bm_release(Mp_pilot.bitmap);
873                 Mp_pilot.bitmap = -1;
874         }
875         strcpy(Mp_squad.filename, "");
876         if(Mp_squad.bitmap != -1){
877                 bm_release(Mp_squad.bitmap);
878                 Mp_squad.bitmap = -1;
879         }       
880         
881         // try and load pilot pic/squad logo
882         if(strlen(np->player->image_filename) >= 0){
883                 strcpy(Mp_pilot.filename, np->player->image_filename);
884                 Mp_pilot.bitmap = bm_load_duplicate(Mp_pilot.filename);
885         }
886         if(strlen(np->player->squad_filename) >= 0){
887                 strcpy(Mp_squad.filename, np->player->squad_filename);
888                 Mp_squad.bitmap = bm_load_duplicate(Mp_squad.filename);
889         }
890
891         // build the stats value strings for this player
892         multi_pinfo_build_stats();
893 }
894
895 // lookup the "previous" player in the netplayer list, return null if not found
896 net_player *multi_pinfo_get_prev_player(net_player *np)
897 {
898         int start_index;
899         int idx;
900
901         // get the starting index to look from
902         start_index = NET_PLAYER_INDEX(np);
903         if(start_index > 0){            
904                 // look backwards
905                 for(idx=start_index-1; idx>=0; idx--){
906                         if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
907                                 return &Net_players[idx];
908                         }
909                 }
910         }
911         
912         return NULL;
913 }
914
915 // lookup the "next" player in the netplayer list, return null if not found
916 net_player *multi_pinfo_get_next_player(net_player *np)
917 {
918         int start_index;
919         int idx;
920
921         // get the starting index to look from
922         start_index = NET_PLAYER_INDEX(np);     
923         if(start_index < (MAX_PLAYERS - 1)){            
924                 // look forwards
925                 for(idx=start_index+1; idx<MAX_PLAYERS; idx++){
926                         if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
927                                 return &Net_players[idx];
928                         }
929                 }
930         }
931         
932         return NULL;
933 }