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