]> icculus.org git repositories - taylor/freespace2.git/blob - src/network/multiui.cpp
implemented osregistry
[taylor/freespace2.git] / src / network / multiui.cpp
1 /*
2  * $Logfile: /Freespace2/code/Network/MultiUI.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  *
7  * C file for all the UI controls of the mulitiplayer screens
8  *
9  * $Log$
10  * Revision 1.5  2002/06/02 00:31:35  relnev
11  * implemented osregistry
12  *
13  * Revision 1.4  2002/06/01 07:12:33  relnev
14  * a few NDEBUG updates.
15  *
16  * removed a few warnings.
17  *
18  * Revision 1.3  2002/05/26 20:49:54  theoddone33
19  * More progress
20  *
21  * Revision 1.2  2002/05/07 03:16:47  theoddone33
22  * The Great Newline Fix
23  *
24  * Revision 1.1.1.1  2002/05/03 03:28:10  root
25  * Initial import.
26  *
27  * 
28  * 94    6/16/00 3:16p Jefff
29  * sim of the year dvd version changes, a few german soty localization
30  * fixes
31  * 
32  * 93    10/14/99 2:51p Jefff
33  * localization fixes
34  * 
35  * 92    10/13/99 3:50p Jefff
36  * fixed unnumbered XSTRs
37  * 
38  * 91    9/15/99 1:45a Dave
39  * Don't init joystick on standalone. Fixed campaign mode on standalone.
40  * Fixed no-score-report problem in TvT
41  * 
42  * 90    9/14/99 12:51a Jefff
43  * small text nudge
44  * 
45  * 89    9/13/99 4:52p Dave
46  * RESPAWN FIX
47  * 
48  * 88    9/13/99 11:30a Dave
49  * Added checkboxes and functionality for disabling PXO banners as well as
50  * disabling d3d zbuffer biasing.
51  * 
52  * 87    9/12/99 10:06p Jefff
53  * changed instances of "Squad War" to "SquadWar"
54  * 
55  * 86    9/03/99 1:32a Dave
56  * CD checking by act. Added support to play 2 cutscenes in a row
57  * seamlessly. Fixed super low level cfile bug related to files in the
58  * root directory of a CD. Added cheat code to set campaign mission # in
59  * main hall.
60  * 
61  * 85    9/01/99 10:49p Dave
62  * Added nice SquadWar checkbox to the client join wait screen.
63  * 
64  * 84    8/30/99 2:49p Jefff
65  * 
66  * 83    8/26/99 8:49p Jefff
67  * Updated medals screen and about everything that ever touches medals in
68  * one way or another.  Sheesh.
69  * 
70  * 82    8/25/99 4:38p Dave
71  * Updated PXO stuff. Make squad war report stuff much more nicely.
72  * 
73  * 81    8/20/99 2:09p Dave
74  * PXO banner cycling.
75  * 
76  * 80    8/20/99 10:06a Jefff
77  * removed closed/rstricted buttons from multi start screen
78  * 
79  * 79    8/18/99 11:30a Jefff
80  * 
81  * 78    8/18/99 10:38a Jefff
82  * 
83  * 77    8/16/99 4:06p Dave
84  * Big honking checkin.
85  * 
86  * 76    8/16/99 1:08p Jefff
87  * added sounds to a few controls, made input boxes lose focus on ENTER
88  * 
89  * 75    8/16/99 9:52a Jefff
90  * fixed bitmap loading on buttons in multi-sync screen
91  * 
92  * 74    8/11/99 5:54p Dave
93  * Fixed collision problem. Fixed standalone ghost problem.
94  * 
95  * 73    8/10/99 4:35p Jefff
96  * fixed hi-res coords
97  * 
98  * 72    8/06/99 12:29a Dave
99  * Multiple bug fixes.
100  * 
101  * 71    8/05/99 3:13p Jasenw
102  * tweaked some text placement coords.
103  * 
104  * 70    8/04/99 1:38p Jefff
105  * moved some text in multi join wait
106  * 
107  * 69    8/03/99 12:45p Dave
108  * Update checksums.
109  * 
110  * 68    7/25/99 5:17p Jefff
111  * campaign descriptions show up on multicreate screen
112  * 
113  * 67    7/20/99 1:49p Dave
114  * Peter Drake build. Fixed some release build warnings.
115  * 
116  * 66    7/19/99 2:13p Dave
117  * Added some new strings for Heiko.
118  * 
119  * 65    7/15/99 9:20a Andsager
120  * FS2_DEMO initial checkin
121  * 
122  * 64    7/08/99 10:53a Dave
123  * New multiplayer interpolation scheme. Not 100% done yet, but still
124  * better than the old way.
125  * 
126  * 63    6/30/99 10:49a Jasenw
127  * Fixed coords for new launch countdown ani
128  * 
129  * 62    6/29/99 7:39p Dave
130  * Lots of small bug fixes.
131  * 
132  * 61    6/25/99 11:59a Dave
133  * Multi options screen.
134  * 
135  * 60    6/09/99 2:17p Dave
136  * Fixed up pleasewait bitmap rendering.
137  * 
138  * 59    6/05/99 3:42p Dave
139  * New multi sync screen.
140  * 
141  * 58    6/01/99 6:07p Dave
142  * New loading/pause/please wait bar.
143  * 
144  * 57    5/21/99 6:45p Dave
145  * Sped up ui loading a bit. Sped up localization disk access stuff. Multi
146  * start game screen, multi password, and multi pxo-help screen.
147  * 
148  * 56    5/06/99 11:10a Dave
149  * Fixed coord on multi create screen.
150  * 
151  * 55    5/04/99 6:38p Dave
152  * Finished multi join-wait screen.
153  * 
154  * 54    5/04/99 5:20p Dave
155  * Fixed up multiplayer join screen and host options screen. Should both
156  * be at 100% now.
157  * 
158  * 53    5/03/99 11:04p Dave
159  * Most of the way done with the multi join screen.
160  * 
161  * 52    5/03/99 8:32p Dave
162  * New version of multi host options screen.
163  * 
164  * 51    4/29/99 2:15p Neilk
165  * slider2 code got modified; changed parameters for create
166  * 
167  * 50    4/25/99 3:02p Dave
168  * Build defines for the E3 build.
169  * 
170  * 49    4/21/99 6:15p Dave
171  * Did some serious housecleaning in the beam code. Made it ready to go
172  * for anti-fighter "pulse" weapons. Fixed collision pair creation. Added
173  * a handy macro for recalculating collision pairs for a given object.
174  * 
175  * 48    4/16/99 5:27p Neilk
176  * added slider support and hir res for multi_create
177  * 0
178  * 47    4/14/99 6:37p Dave
179  * Fixed scroll button bug on host create screen.
180  * 
181  * 46    4/14/99 5:28p Dave
182  * Minor bug fixes.
183  * 
184  * 45    4/12/99 10:07p Dave
185  * Made network startup more forgiving. Added checkmarks to dogfight
186  * screen for players who hit commit.
187  * 
188  * 44    4/09/99 2:21p Dave
189  * Multiplayer beta stuff. CD checking.
190  * 
191  * 43    4/08/99 1:28p Dave
192  * Small bug fixes for refresh button on the multi create screen.
193  * 
194  * 42    4/08/99 11:55a Neilk
195  * Converted Multi_Create to new artwork (just lowres)
196  * 
197  * 41    4/08/99 2:10a Dave
198  * Numerous bug fixes for the beta. Added builtin mission info for the
199  * beta.
200  * 
201  * 40    3/20/99 3:48p Andsager
202  * Do mission_loop stuff for PXO
203  * 
204  * 39    3/10/99 6:50p Dave
205  * Changed the way we buffer packets for all clients. Optimized turret
206  * fired packets. Did some weapon firing optimizations.
207  * 
208  * 38    3/09/99 6:24p Dave
209  * More work on object update revamping. Identified several sources of
210  * unnecessary bandwidth.
211  * 
212  * 37    3/08/99 7:03p Dave
213  * First run of new object update system. Looks very promising.
214  * 
215  * 36    2/25/99 4:19p Dave
216  * Added multiplayer_beta defines. Added cd_check define. Fixed a few
217  * release build warnings. Added more data to the squad war request and
218  * response packets.
219  * 
220  * 35    2/24/99 3:26p Anoop
221  * Make sure the host is the only guy who bashes skill level for TvT.
222  * 
223  * 34    2/24/99 2:25p Dave
224  * Fixed up chatbox bugs. Made squad war reporting better. Fixed a respawn
225  * bug for dogfight more.
226  * 
227  * 33    2/23/99 2:29p Dave
228  * First run of oldschool dogfight mode. 
229  * 
230  * 32    2/17/99 2:11p Dave
231  * First full run of squad war. All freespace and tracker side stuff
232  * works.
233  * 
234  * 31    2/12/99 6:16p Dave
235  * Pre-mission Squad War code is 95% done.
236  * 
237  * 30    2/11/99 3:08p Dave
238  * PXO refresh button. Very preliminary squad war support.
239  * 
240  * 29    2/08/99 5:07p Dave
241  * FS2 chat server support. FS2 specific validated missions.
242  * 
243  * 28    2/04/99 6:29p Dave
244  * First full working rev of FS2 PXO support.  Fixed Glide lighting
245  * problems.
246  * 
247  * 27    1/30/99 5:08p Dave
248  * More new hi-res stuff.Support for nice D3D textures.
249  * 
250  * 26    1/29/99 2:08a Dave
251  * Fixed beam weapon collisions with players. Reduced size of scoring
252  * struct for multiplayer. Disabled PXO.
253  * 
254  * 25    1/15/99 2:36p Neilk
255  * fixed multi_jw coordinates
256  * 
257  * 24    1/13/99 7:19p Neilk
258  * Converted Mission Brief, Barracks, Synch to high res support
259  * 
260  * 23    1/12/99 7:17p Neilk
261  * 
262  * 22    1/12/99 5:45p Dave
263  * Moved weapon pipeline in multiplayer to almost exclusively client side.
264  * Very good results. Bandwidth goes down, playability goes up for crappy
265  * connections. Fixed object update problem for ship subsystems.
266  * 
267  * 21    1/12/99 4:07a Dave
268  * Put in barracks code support for selecting squad logos. Properly
269  * distribute squad logos in a multiplayer game.
270  * 
271  * 20    1/11/99 7:19p Neilk
272  * Converted multi_join interface to support multiple resolutions
273  * 
274  * 19    12/18/98 1:13a Dave
275  * Rough 1024x768 support for Direct3D. Proper detection and usage through
276  * the launcher.
277  * 
278  * 18    12/17/98 4:50p Andsager
279  * Added debrief_assemble_optional_mission_popup_text() for single and
280  * multiplayer
281  * 
282  * 17    12/14/98 12:13p Dave
283  * Spiffed up xfer system a bit. Put in support for squad logo file xfer.
284  * Need to test now.
285  * 
286  * 16    12/10/98 10:19a Andsager
287  * Fix mission loop assert
288  * 
289  * 15    12/10/98 9:59a Andsager
290  * Fix some bugs with mission loops
291  * 
292  * 14    12/09/98 1:56p Andsager
293  * Initial checkin of mission loop
294  * 
295  * 13    12/03/98 5:22p Dave
296  * Ported over Freespace 1 multiplayer ships.tbl and weapons.tbl
297  * checksumming.
298  * 
299  * 12    11/30/98 1:07p Dave
300  * 16 bit conversion, first run.
301  * 
302  * 11    11/20/98 11:16a Dave
303  * Fixed up IPX support a bit. Making sure that switching modes and
304  * loading/saving pilot files maintains proper state.
305  * 
306  * 10    11/19/98 4:57p Dave
307  * Ignore PXO option if IPX is selected.
308  * 
309  * 9     11/19/98 4:19p Dave
310  * Put IPX sockets back in psnet. Consolidated all multiplayer config
311  * files into one.
312  * 
313  * 8     11/19/98 8:04a Dave
314  * Full support for D3-style reliable sockets. Revamped packet lag/loss
315  * system, made it receiver side and at the lowest possible level.
316  * 
317  * 7     11/17/98 11:12a Dave
318  * Removed player identification by address. Now assign explicit id #'s.
319  * 
320  * 6     10/19/98 11:15a Dave
321  * Changed requirements for stats storing in PXO mode.
322  * 
323  * 5     10/16/98 9:40a Andsager
324  * Remove ".h" files from model.h
325  * 
326  * 4     10/13/98 9:29a Dave
327  * Started neatening up freespace.h. Many variables renamed and
328  * reorganized. Added AlphaColors.[h,cpp]
329  * 
330  * 3     10/07/98 6:27p Dave
331  * Globalized mission and campaign file extensions. Removed Silent Threat
332  * special code. Moved \cache \players and \multidata into the \data
333  * directory.
334  * 
335  * 2     10/07/98 10:53a Dave
336  * Initial checkin.
337  * 
338  * 1     10/07/98 10:50a Dave
339  * 
340  * 333   10/02/98 3:22p Allender
341  * fix up the -connect option and fix the -port option
342  * 
343  * 332   9/17/98 9:26p Dave
344  * Externalized new string.
345  * 
346  * 331   9/17/98 3:08p Dave
347  * PXO to non-pxo game warning popup. Player icon stuff in create and join
348  * game screens. Upped server count refresh time in PXO to 35 secs (from
349  * 20).
350  * 
351  * 330   9/17/98 9:43a Allender
352  * removed an Assert that Dave called bogus.
353  * 
354  * 329   9/16/98 6:54p Dave
355  * Upped  max sexpression nodes to 1800 (from 1600). Changed FRED to sort
356  * the ship list box. Added code so that tracker stats are not stored with
357  * only 1 player.
358  * 
359  * 328   9/15/98 7:24p Dave
360  * Minor UI changes. Localized bunch of new text.
361  * 
362  * 327   9/15/98 4:03p Dave
363  * Changed readyroom and multi screens to display "st" icon for all
364  * missions with mission disk content (not necessarily just those that
365  * come with Silent Threat).
366  * 
367  * 326   9/15/98 11:44a Dave
368  * Renamed builtin ships and wepaons appropriately in FRED. Put in scoring
369  * scale factors. Fixed standalone filtering of MD missions to non-MD
370  * hosts.
371  * 
372  * 325   9/13/98 9:36p Dave
373  * Support for new info icons for multiplayer missions (from-volition,
374  * valid, mission disk, etc).
375  * 
376  * 324   9/11/98 4:14p Dave
377  * Fixed file checksumming of < file_size. Put in more verbose kicking and
378  * PXO stats store reporting.
379  * 
380  * 323   9/10/98 1:17p Dave
381  * Put in code to flag missions and campaigns as being MD or not in Fred
382  * and Freespace. Put in multiplayer support for filtering out MD
383  * missions. Put in multiplayer popups for warning of non-valid missions.
384  * 
385  * 322   9/04/98 3:51p Dave
386  * Put in validated mission updating and application during stats
387  * updating.
388  * 
389  * 321   8/31/98 2:06p Dave
390  * Make cfile sort the ordering or vp files. Added support/checks for
391  * recognizing "mission disk" players.
392  * 
393  * 320   8/21/98 1:15p Dave
394  * Put in log system hooks in useful places.
395  * 
396  * 319   8/20/98 5:31p Dave
397  * Put in handy multiplayer logfile system. Now need to put in useful
398  * applications of it all over the code.
399  * 
400  * 318   8/12/98 4:53p Dave
401  * Put in 32 bit checksumming for PXO missions. No validation on the
402  * actual tracker yet, though.
403  * 
404  * 317   8/07/98 10:40a Allender
405  * new command line flags for starting netgames.  Only starting currently
406  * works, and PXO isn't implemented yet
407  * 
408  * 316   7/24/98 9:27a Dave
409  * Tidied up endgame sequencing by removing several old flags and
410  * standardizing _all_ endgame stuff with a single function call.
411  * 
412  * 315   7/14/98 10:04a Allender
413  * fixed the countdown code to not be reliant on timer_get_fixed_seconds
414  * 
415  * 314   7/10/98 5:04p Dave
416  * Fix connection speed bug on standalone server.
417  * 
418  * 313   7/09/98 6:01p Dave
419  * Firsts full version of PXO updater. Put in stub for displaying
420  * connection status.
421  * 
422  * 312   7/07/98 2:49p Dave
423  * UI bug fixes.
424  * 
425  * 311   6/30/98 2:17p Dave
426  * Revised object update system. Removed updates for all weapons. Put
427  * button info back into control info packet.
428  * 
429  * 310   6/13/98 9:32p Mike
430  * Kill last character in file which caused "Find in Files" to report the
431  * file as "not a text file." 
432  *
433  * $NoKeywords: $
434  */
435
436 #ifndef PLAT_UNIX
437 #include <io.h>
438 #include <winsock.h>    // for inet_addr()
439 #else
440 #include <sys/types.h>
441 #include <sys/socket.h>
442 #include <netinet/in.h>
443 #include <arpa/inet.h>
444 #include <netdb.h>
445 #endif
446 #include "multi.h"
447 #include "multiui.h"
448 #include "multiutil.h"
449 #include "multimsgs.h"
450 #include "multi.h"
451 #include "ui.h"
452 #include "2d.h"
453 #include "key.h"
454 #include "timer.h"
455 #include "gamesequence.h"
456 #include "freespace.h"
457 #include "contexthelp.h"
458 #include "psnet.h"
459 #include "player.h"
460 #include "cfile.h"
461 #include "ship.h"
462 #include "missionshipchoice.h"
463 #include "multi_xfer.h"
464 #include "cmdline.h"
465 #include "stand_gui.h"
466 #include "linklist.h"
467 #include "multiteamselect.h"
468 #include "missioncampaign.h"
469 #include "bmpman.h"
470 #include "font.h"
471 #include "mouse.h"
472 #include "gamesnd.h"
473 #include "chatbox.h"
474 #include "popup.h"
475 #include "missiondebrief.h"
476 #include "multi_ingame.h"
477 #include "multi_kick.h"
478 #include "multi_data.h"
479 #include "multi_campaign.h"
480 #include "multi_team.h"
481 #include "multi_pinfo.h"
482 #include "multi_observer.h"
483 #include "multi_voice.h"
484 #include "multi_endgame.h"
485 #include "managepilot.h"
486 #include "stats.h"
487 #include "object.h"
488 #include "objcollide.h"
489 #include "palman.h"
490 #include "multi_pmsg.h"
491 #include "multi_obj.h"
492 #include "multi_log.h"
493 #include "alphacolors.h"
494 #include "animplay.h"
495 #include "multi_dogfight.h"
496 #include "missionpause.h"
497
498 // -------------------------------------------------------------------------------------------------------------
499 // 
500 // MULTIPLAYER COMMON interface controls
501 //
502
503 // the common text info box stuff. This is lifted almost directly from Alans briefing code (minus the spiffy colored, scrolling
504 // text crap :)
505 int Multi_common_text_coords[GR_NUM_RESOLUTIONS][4] = {
506         { // GR_640
507                 29, 396, 393, 76
508         },
509         { // GR_1024
510                 47, 634, 630, 122
511         }
512 };
513
514 int Multi_common_text_max_display[GR_NUM_RESOLUTIONS] = {
515         8,              // GR_640
516         12,     // GR_1024
517 };
518
519 #define MULTI_COMMON_TEXT_META_CHAR                             '$'
520 #define MULTI_COMMON_TEXT_MAX_LINE_LENGTH               100
521 #define MULTI_COMMON_TEXT_MAX_LINES                             20
522 #define MULTI_COMMON_MAX_TEXT                                           (MULTI_COMMON_TEXT_MAX_LINES * MULTI_COMMON_TEXT_MAX_LINE_LENGTH)
523
524 char Multi_common_all_text[MULTI_COMMON_MAX_TEXT];
525 char Multi_common_text[MULTI_COMMON_TEXT_MAX_LINES][MULTI_COMMON_TEXT_MAX_LINE_LENGTH];
526
527 int Multi_common_top_text_line = -1;            // where to start displaying from
528 int Multi_common_num_text_lines = 0;            // how many lines we have
529
530 void multi_common_scroll_text_up();
531 void multi_common_scroll_text_down();
532 void multi_common_move_to_bottom();
533 void multi_common_render_text();
534 void multi_common_split_text();
535
536 #define MAX_IP_STRING           255                             // maximum length for ip string
537
538 void multi_common_scroll_text_up()
539 {
540         Multi_common_top_text_line--;
541         if ( Multi_common_top_text_line < 0 ) {
542                 Multi_common_top_text_line = 0;
543                 if ( !mouse_down(MOUSE_LEFT_BUTTON) )
544                         gamesnd_play_iface(SND_GENERAL_FAIL);
545
546         } else {
547                 gamesnd_play_iface(SND_SCROLL);
548         }
549 }
550
551 void multi_common_scroll_text_down()
552 {
553         Multi_common_top_text_line++;
554         if ( (Multi_common_num_text_lines - Multi_common_top_text_line) < Multi_common_text_max_display[gr_screen.res] ) {
555                 Multi_common_top_text_line--;
556                 if ( !mouse_down(MOUSE_LEFT_BUTTON) ){
557                         gamesnd_play_iface(SND_GENERAL_FAIL);
558                 }
559         } else {
560                 gamesnd_play_iface(SND_SCROLL);
561         }
562 }
563
564 void multi_common_move_to_bottom()
565 {
566         // if there's nowhere to scroll down, do nothing
567         if(Multi_common_num_text_lines <= Multi_common_text_max_display[gr_screen.res]){
568                 return;
569         }
570                 
571         Multi_common_top_text_line = Multi_common_num_text_lines - Multi_common_text_max_display[gr_screen.res];
572 }
573
574 void multi_common_set_text(char *str,int auto_scroll)
575 {
576         // make sure it fits
577         // store the entire string as well
578         if(strlen(str) > MULTI_COMMON_MAX_TEXT){
579                 return ;
580         } else {
581                 strcpy(Multi_common_all_text,str);
582         }
583         
584         // split the whole thing up
585         multi_common_split_text();
586
587         // scroll to the bottom if we're supposed to
588         if(auto_scroll){
589                 multi_common_move_to_bottom();
590         }
591 }
592
593 void multi_common_add_text(char *str,int auto_scroll)
594 {
595         // make sure it fits
596         // store the entire string as well
597         if((strlen(str) + strlen(Multi_common_all_text)) > MULTI_COMMON_MAX_TEXT){
598                 return ;
599         } else {
600                 strcat(Multi_common_all_text,str);
601         }
602         
603         // split the whole thing up
604         multi_common_split_text();
605
606         // scroll to the bottom if we're supposed to
607         if(auto_scroll){
608                 multi_common_move_to_bottom();
609         }
610 }
611
612 void multi_common_split_text()
613 {
614         int     n_lines, i;
615         int     n_chars[MAX_BRIEF_LINES];
616         char    *p_str[MAX_BRIEF_LINES];
617
618         n_lines = split_str(Multi_common_all_text, Multi_common_text_coords[gr_screen.res][2], n_chars, p_str, MULTI_COMMON_TEXT_MAX_LINES, MULTI_COMMON_TEXT_META_CHAR);
619         Assert(n_lines != -1);
620
621         for ( i = 0; i < n_lines; i++ ) {
622                 Assert(n_chars[i] < MULTI_COMMON_TEXT_MAX_LINE_LENGTH);
623                 strncpy(Multi_common_text[i], p_str[i], n_chars[i]);
624                 Multi_common_text[i][n_chars[i]] = 0;
625                 drop_leading_white_space(Multi_common_text[i]);         
626         }
627
628         Multi_common_top_text_line = 0;
629         Multi_common_num_text_lines = n_lines;  
630 }
631
632 void multi_common_render_text()
633 {
634         int i, fh, line_count;
635
636         fh = gr_get_font_height();
637         
638         line_count = 0;
639         gr_set_color_fast(&Color_text_normal);
640         for ( i = Multi_common_top_text_line; i < Multi_common_num_text_lines; i++ ) {
641                 if ( line_count >= Multi_common_text_max_display[gr_screen.res] ){
642                         break;  
643                 }
644                 gr_string(Multi_common_text_coords[gr_screen.res][0], Multi_common_text_coords[gr_screen.res][1] + (line_count*fh), Multi_common_text[i]);              
645                 line_count++;
646         }
647
648         if ( (Multi_common_num_text_lines - Multi_common_top_text_line) > Multi_common_text_max_display[gr_screen.res] ) {
649                 gr_set_color_fast(&Color_bright_red);
650                 gr_string(Multi_common_text_coords[gr_screen.res][0], (Multi_common_text_coords[gr_screen.res][1] + Multi_common_text_coords[gr_screen.res][3])-5, XSTR("more",755));
651         }
652 }
653
654 // common notification messaging stuff
655 #define MULTI_COMMON_NOTIFY_TIME                3500
656 int Multi_common_join_y[GR_NUM_RESOLUTIONS] = {
657         375,            // GR_640
658         605             // GR_1024
659 };
660 int Multi_common_create_y[GR_NUM_RESOLUTIONS] = {
661         380,            // GR_640
662         610             // GR_1024
663 };
664
665 int Multi_common_jw_y[GR_NUM_RESOLUTIONS] = {
666         380,            // GR_640
667         610             // GR_1024
668 };
669
670 int Multi_common_msg_y[GR_NUM_RESOLUTIONS] = {
671         380,            // GR_640
672         610             // GR_1024
673 };
674
675 char Multi_common_notify_text[200];
676 int Multi_common_notify_stamp;
677
678 void multi_common_notify_init()
679 {
680         strcpy(Multi_common_notify_text,"");
681         Multi_common_notify_stamp = -1;
682 }
683
684 // add a notification string, drawing appropriately depending on the state/screen we're in
685 void multi_common_add_notify(char *str)
686 {
687         if(str){
688                 strcpy(Multi_common_notify_text,str);
689                 Multi_common_notify_stamp = timestamp(MULTI_COMMON_NOTIFY_TIME);
690         }
691 }
692
693 // process/display notification messages
694 void multi_common_notify_do()
695 {
696         if(Multi_common_notify_stamp != -1){
697                 if(timestamp_elapsed(Multi_common_notify_stamp)){
698                         Multi_common_notify_stamp = -1;
699                 } else {
700                         int w,h,y;
701                         gr_get_string_size(&w,&h,Multi_common_notify_text);
702                         gr_set_color_fast(&Color_white);
703                         
704                         // determine where it should be placed based upon which screen we're on
705                         y = -1;
706                         switch(gameseq_get_state()){
707                         case GS_STATE_MULTI_JOIN_GAME :
708                                 y = Multi_common_join_y[gr_screen.res];
709                                 break;
710                         case GS_STATE_MULTI_HOST_SETUP :
711                                 y = Multi_common_create_y[gr_screen.res];
712                                 break;
713                         case GS_STATE_MULTI_CLIENT_SETUP :
714                                 y = Multi_common_jw_y[gr_screen.res];
715                                 break;
716                         case GS_STATE_MULTI_START_GAME :
717                                 y = Multi_common_msg_y[gr_screen.res];
718                                 break;                  
719                         }
720                         if(y != -1){
721                                 gr_string((gr_screen.max_w - w)/2, y, Multi_common_notify_text);
722                         }
723                 }
724         }
725 }
726
727 // common icon stuff
728 int Multi_common_icons[MULTI_NUM_COMMON_ICONS];
729 //XSTR:OFF
730 char *Multi_common_icon_names[MULTI_NUM_COMMON_ICONS] = {
731         "DotRed",                               // voice denied
732         "DotGreen",                             // voice recording
733         "OvalGreen",                    // team 0
734         "OvalGreen01",                  // team 0 select
735         "OvalRed",                              // team 1
736         "OvalRed01",                    // team 1 select
737         "mp_coop",                              // coop mission
738         "mp_teams",                             // TvT mission
739         "mp_furball",                   // furball mission
740         "icon-volition",                // volition mission     
741         "icon-valid",                   // mission is valid
742         "cd"                                            // cd icon
743 };
744 //XSTR:ON
745 // width and height of the icons
746 int Multi_common_icon_dims[MULTI_NUM_COMMON_ICONS][2] = {
747         {11, 11},                               // voice denied
748         {11, 11},                               // voice recording
749         {11, 11},                               // team 0
750         {11, 11},                               // team 0 select
751         {11, 11},                               // team 1
752         {11, 11},                               // team 1 select
753         {18, 11},                               // mp coop
754         {18, 11},                               // mp TvT
755         {18, 11},                               // mp furball
756         {9, 9},                                 // volition mission     
757         {8, 8},                                 // mission is valid
758         {8, 8}                                  // cd icon
759 };
760
761 void multi_load_common_icons()
762 {
763         int idx;
764
765         // load all icons
766         for(idx=0; idx<MULTI_NUM_COMMON_ICONS; idx++){
767                 Multi_common_icons[idx] = -1;
768                 Multi_common_icons[idx] = bm_load(Multi_common_icon_names[idx]);
769         }
770 }
771
772 void multi_unload_common_icons()
773 {
774         int idx;
775
776         // unload all icons
777         for(idx=0; idx<MULTI_NUM_COMMON_ICONS; idx++){
778                 if(Multi_common_icons[idx] != -1){
779                         bm_unload(Multi_common_icons[idx]);
780                         Multi_common_icons[idx] = -1;
781                 }
782         }
783 }
784
785 // display any relevant voice status icons
786 void multi_common_voice_display_status()
787 {       
788         switch(multi_voice_status()){
789         // i have been denied the voice token
790         case MULTI_VOICE_STATUS_DENIED:
791                 if(Multi_common_icons[MICON_VOICE_DENIED] != -1){
792                         gr_set_bitmap(Multi_common_icons[MICON_VOICE_DENIED]);
793                         gr_bitmap(0,0);
794                 }
795                 break;
796
797         // i am currently recording
798         case MULTI_VOICE_STATUS_RECORDING:
799                 if(Multi_common_icons[MICON_VOICE_RECORDING] != -1){
800                         gr_set_bitmap(Multi_common_icons[MICON_VOICE_RECORDING]);
801                         gr_bitmap(0,0);
802                 }
803                 break;
804
805         // i am currently playing back sound
806         case MULTI_VOICE_STATUS_PLAYING:
807                 break;
808
809         // the system is currently idle
810         case MULTI_VOICE_STATUS_IDLE:
811                 break;
812         }
813 }
814
815 //XSTR:OFF
816 // palette initialization stuff
817 #define MULTI_COMMON_PALETTE_FNAME                      "InterfacePalette"
818 //XSTR:ON
819
820 int Multi_common_interface_palette = -1;
821
822 void multi_common_load_palette();
823 void multi_common_set_palette();
824 void multi_common_unload_palette();
825
826 // load in the palette if it doesn't already exist
827 void multi_common_load_palette()
828 {
829         if(Multi_common_interface_palette != -1){
830                 return;
831         }
832
833         Multi_common_interface_palette = bm_load(MULTI_COMMON_PALETTE_FNAME);
834         if(Multi_common_interface_palette == -1){
835                 nprintf(("Network","Error loading multiplayer common palette!\n"));
836         }
837 }
838
839 // set the common palette to be the active one
840 void multi_common_set_palette()
841 {
842         // if the palette is not loaded yet, do so now
843         if(Multi_common_interface_palette == -1){
844                 multi_common_load_palette();
845         }
846         
847         if(Multi_common_interface_palette != -1){
848 #ifndef HARDWARE_ONLY
849                 palette_use_bm_palette(Multi_common_interface_palette);
850 #endif
851         }
852 }
853
854 // unload the bitmap palette
855 void multi_common_unload_palette()
856 {
857         if(Multi_common_interface_palette != -1){
858                 bm_unload(Multi_common_interface_palette);
859                 Multi_common_interface_palette = -1;
860         }
861 }
862
863 void multi_common_verify_cd()
864 {
865 #ifdef GAME_CD_CHECK
866         // otherwise, call the freespace function to determine if we have a cd
867         Multi_has_cd = 0;
868         if((find_freespace_cd(FS_CDROM_VOLUME_1) >= 0) || (find_freespace_cd(FS_CDROM_VOLUME_2) >= 0) || (find_freespace_cd(FS_CDROM_VOLUME_3) >= 0) ){
869                 Multi_has_cd = 1;
870         } 
871 #else
872         Multi_has_cd = 1;
873 #endif
874 }
875
876
877 // -------------------------------------------------------------------------------------------------------------
878 // 
879 // MULTIPLAYER JOIN SCREEN 
880 //
881
882 #define MULTI_JOIN_NUM_BUTTONS  11
883
884 //XSTR:OFF
885 // bitmaps defs
886 #define MULTI_JOIN_PALETTE                              "InterfacePalette"
887
888 static char *Multi_join_bitmap_fname[GR_NUM_RESOLUTIONS] = {
889         "MultiJoin",            // GR_640
890         "2_MultiJoin"                   // GR_1024
891 };
892
893 static char *Multi_join_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
894         "MultiJoin-M",          // GR_640
895         "2_MultiJoin-M"         // GR_1024
896 };
897 //XSTR:ON
898
899 // slider
900 char *Mj_slider_name[GR_NUM_RESOLUTIONS] = {
901         "slider",
902         "2_slider"
903 };
904 int Mj_slider_coords[GR_NUM_RESOLUTIONS][4] = {
905         { // GR_640
906                 2, 91, 15, 202
907         },
908         { // GR_1024
909                 8, 147, 30, 322
910         }
911 };
912
913 // button defs
914 #define MJ_SCROLL_UP                            0
915 #define MJ_SCROLL_DOWN                  1
916 #define MJ_REFRESH                              2
917 #define MJ_SCROLL_INFO_UP               3
918 #define MJ_SCROLL_INFO_DOWN     4
919 #define MJ_JOIN_OBSERVER                5
920 #define MJ_START_GAME                   6
921 #define MJ_CANCEL                                       7
922 #define MJ_HELP                                 8
923 #define MJ_OPTIONS                              9
924 #define MJ_ACCEPT                                       10
925
926 // uses MULTI_JOIN_REFRESH_TIME as its timestamp
927 int Multi_join_glr_stamp;
928
929 #define MULTI_JOIN_PING_TIME     15000        // how often we ping all the known servers
930 int Multi_join_ping_stamp;
931 UI_WINDOW Multi_join_window;                                                                                    // the window object for the join screen
932 UI_BUTTON Multi_join_select_button;                                                                     // for selecting list items
933 UI_SLIDER2 Multi_join_slider;                                                                                   // handy dandy slider
934 int Multi_join_bitmap;                                                                                                  // the background bitmap
935
936 ui_button_info Multi_join_buttons[GR_NUM_RESOLUTIONS][MULTI_JOIN_NUM_BUTTONS] = {
937         { // GR_640
938                 ui_button_info( "MJ_00",        1,              57,     -1,     -1,     0 ),                                            // scroll up
939                 ui_button_info( "MJ_02",        1,              297,    -1,     -1,     2 ),                                            // scroll down
940                 ui_button_info( "MJ_03",        10,     338,    65,     364,    3 ),                                            // refresh
941                 ui_button_info( "MJ_04",        1,              405,    -1,     -1,     4 ),                                            // scroll info up
942                 ui_button_info( "MJ_05",        1,              446,    -1,     -1,     5 ),                                            // scroll info down
943                 ui_button_info( "MJ_06",        489,    339,    -1,     -1,     6 ),                                            // join as observer
944                 ui_button_info( "MJ_07",        538,    339,    -1,     -1,     7 ),                                            // create game
945                 ui_button_info( "MJ_08",        583,    339,    588,    376,    8 ),                                            // cancel
946                 ui_button_info( "MJ_09",        534,    426,    -1,     -1,     9 ),                                            // help
947                 ui_button_info( "MJ_10",        534,    454,    -1,     -1,     10 ),                                           // options
948                 ui_button_info( "MJ_11",        571,    426,    589,    416,    11 ),                                           // join
949         },
950         { // GR_1024
951                 ui_button_info( "2_MJ_00",      2,              92,     -1,     -1,     0 ),                                            // scroll up
952                 ui_button_info( "2_MJ_02",      2,              475,    -1,     -1,     2 ),                                            // scroll down
953                 ui_button_info( "2_MJ_03",      16,     541,    104,    582,    3 ),                                            // refresh
954                 ui_button_info( "2_MJ_04",      2,              648,    -1,     -1,     4 ),                                            // scroll info up
955                 ui_button_info( "2_MJ_05",      2,              713,    -1,     -1,     5 ),                                            // scroll info down
956                 ui_button_info( "2_MJ_06",      783,    542,    -1,     -1,     6 ),                                            // join as observer
957                 ui_button_info( "2_MJ_07",      861,    542,    -1,     -1,     7 ),                                            // create game
958                 ui_button_info( "2_MJ_08",      933,    542,    588,    376,    8 ),                                            // cancel
959                 ui_button_info( "2_MJ_09",      854,    681,    -1,     -1,     9 ),                                            // help
960                 ui_button_info( "2_MJ_10",      854,    727,    -1,     -1,     10 ),                                           // options
961                 ui_button_info( "2_MJ_11",      914,    681,    937,    668,    11 ),                                           // join
962         }
963 };
964
965 #define MULTI_JOIN_NUM_TEXT                     13
966
967 UI_XSTR Multi_join_text[GR_NUM_RESOLUTIONS][MULTI_JOIN_NUM_TEXT] = {
968         { // GR_640             
969                 {"Refresh",                                                     1299, 65,       364,    UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_REFRESH].button},
970                 {"Join as",                                                     1300,   476,    376,    UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_JOIN_OBSERVER].button},
971                 {"Observer",                                            1301,   467,    385,    UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_JOIN_OBSERVER].button},
972                 {"Create",                                                      1408,   535,    376,    UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_START_GAME].button}, 
973                 {"Game",                                                                1302,   541,    385,    UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_START_GAME].button},
974                 {"Cancel",                                                      387,    588,    376,    UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[0][MJ_CANCEL].button},      
975                 {"Help",                                                                928,    479,    436,    UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_HELP].button},
976                 {"Options",                                                     1036,   479,    460,    UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_OPTIONS].button},
977                 {"Join",                                                                1303,   589,    416,    UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[0][MJ_ACCEPT].button},
978                 {"Status",                                                      1304,   37,     37,     UI_XSTR_COLOR_GREEN,    -1, NULL},
979                 {"Server",                                                      1305,   116,    37,     UI_XSTR_COLOR_GREEN, -1, NULL},
980                 {"Players",                                                     1306,   471,    37,     UI_XSTR_COLOR_GREEN,    -1, NULL},
981                 {"Ping",                                                                1307,   555,    37,     UI_XSTR_COLOR_GREEN, -1, NULL}
982         },
983         { // GR_1024            
984                 {"Refresh",                                                     1299, 104,      582,    UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_REFRESH].button},
985                 {"Join as",                                                     1300,   783,    602,    UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_JOIN_OBSERVER].button},
986                 {"Observer",                                            1301,   774,    611,    UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_JOIN_OBSERVER].button},              
987                 {"Create",                                                      1408,   868,    602,    UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_START_GAME].button},
988                 {"Game",                                                                1302,   872,    611,    UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_START_GAME].button},
989                 {"Cancel",                                                      387,    941,    602,    UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[1][MJ_CANCEL].button},
990                 {"Help",                                                                928,    782,    699,    UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_HELP].button},
991                 {"Options",                                                     1036,   782,    736,    UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_OPTIONS].button},
992                 {"Join",                                                                1303,   937,    668,    UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[1][MJ_ACCEPT].button},
993                 {"Status",                                                      1304,   60,     60,     UI_XSTR_COLOR_GREEN,    -1, NULL},
994                 {"Server",                                                      1305,   186,    60,     UI_XSTR_COLOR_GREEN, -1, NULL},
995                 {"Players",                                                     1306,   753,    60,     UI_XSTR_COLOR_GREEN,    -1, NULL},
996                 {"Ping",                                                                1307,   888,    60,     UI_XSTR_COLOR_GREEN, -1, NULL}
997         }
998 };
999
1000 // constants for coordinate look ups
1001 #define MJ_X_COORD 0
1002 #define MJ_Y_COORD 1
1003 #define MJ_W_COORD 2
1004 #define MJ_H_COORD 3
1005
1006 #define MULTI_JOIN_SENT_WAIT            10000                                   // wait this long since a join was sent to allow another
1007 int Multi_join_sent_stamp;
1008
1009 // game information text areas
1010 int Mj_max_game_items[GR_NUM_RESOLUTIONS] = {
1011         28,                     // GR_640
1012         46                              // GR_1024
1013 };
1014
1015 int Mj_list_y[GR_NUM_RESOLUTIONS] = {
1016         53,                     // GR_640
1017         77                              // GR_1024
1018 };
1019
1020 int Mj_status_coords[GR_NUM_RESOLUTIONS][4] = {
1021         { // GR_640
1022                 34, 53, 55, 287
1023         },
1024         { // GR_1024
1025                 53, 77, 57, 459
1026         }
1027 };
1028
1029 int Mj_game_icon_coords[GR_NUM_RESOLUTIONS][3] = {
1030         { // GR_640
1031                 98, 53, 28
1032         },
1033         { // GR_1024
1034                 124, 77, 28
1035         }
1036 };      
1037
1038 int Mj_speed_coords[GR_NUM_RESOLUTIONS][4] = {
1039         { // GR_640
1040                 122, 53, 56, 261
1041         },
1042         { // GR_1024
1043                 152, 77, 56, 261
1044         }
1045 };      
1046
1047 int Mj_game_name_coords[GR_NUM_RESOLUTIONS][4] = {
1048         { // GR_640
1049                 186, 53, 280, 261
1050         },
1051         { // GR_1024
1052                 206, 77, 311, 261
1053         }
1054 };      
1055
1056 int Mj_players_coords[GR_NUM_RESOLUTIONS][4] = {
1057         { // GR_640
1058                 473, 53, 50, 261
1059         },
1060         { // GR_1024
1061                 748, 77, 67, 459
1062         }
1063 };      
1064
1065 int Mj_ping_coords[GR_NUM_RESOLUTIONS][4] = {
1066         { // GR_640
1067                 551, 53, 47, 261
1068         },
1069         { // GR_1024
1070                 880, 77, 48, 459
1071         }
1072 };      
1073
1074 // game speed labels
1075 #define MJ_NUM_SPEED_LABELS             5
1076 char *Multi_join_speed_labels[MJ_NUM_SPEED_LABELS] = {
1077         "< 56k",
1078         "56k",
1079         "isdn",
1080         "cable",
1081         "t1/adsl+"
1082 };
1083 color *Multi_join_speed_colors[MJ_NUM_SPEED_LABELS] = {
1084         &Color_bright_red,
1085         &Color_bright_red,
1086         &Color_bright_green,
1087         &Color_bright_green,
1088         &Color_bright_green
1089 };
1090
1091 int Mj_cd_coords[GR_NUM_RESOLUTIONS] = {
1092         20, 30
1093 };
1094
1095 //XSTR:OFF
1096 #define IP_CONFIG_FNAME                         "tcp.cfg"               // name of the file which contains known TCP addresses
1097
1098 // extents of the entire boundable game info region
1099 // NOTE : these numbers are completely empirical
1100 #define MJ_PING_GREEN                           160
1101 #define MJ_PING_YELLOW                          300
1102
1103 int Mj_list_area_coords[GR_NUM_RESOLUTIONS][4] = {
1104         { // GR_640
1105                 23, 53, 565, 279 
1106         },
1107         { // GR_1024
1108                 53, 76, 887, 461 
1109         }
1110 };      
1111
1112 // PXO channel filter
1113 #define MJ_PXO_FILTER_Y                         0
1114
1115 // special chars to indicate various status modes for servers
1116 #define MJ_CHAR_STANDALONE                      "*"
1117 #define MJ_CHAR_CAMPAIGN                        "c"
1118 //XSTR:ON
1119
1120 // various interface indices
1121 int Multi_join_list_start;                                                      // where to start displaying from
1122 active_game *Multi_join_list_start_item;                // a pointer to the corresponding active_game
1123 int Multi_join_list_selected;                                           // which item we have selected
1124 active_game *Multi_join_selected_item;                  // a pointer to the corresponding active_game
1125
1126 // use this macro to modify the list start
1127 #define MJ_LIST_START_INC()                     do { Multi_join_list_start++; } while(0);
1128 #define MJ_LIST_START_DEC()                     do { Multi_join_list_start--; } while(0);
1129 #define MJ_LIST_START_SET(vl)                   do { Multi_join_list_start = vl; } while(0);
1130
1131 // if we should be sending a join request at the end of the frame
1132 int Multi_join_should_send = -1;
1133
1134 // master tracker details
1135 int Multi_join_frame_count;                                             // keep a count of frames displayed
1136 int Multi_join_mt_tried_verify;                                 // already tried verifying the pilot with the tracker
1137
1138 // data stuff for auto joining a game
1139 #define MULTI_AUTOJOIN_JOIN_STAMP               2000
1140 #define MULTI_AUTOJOIN_QUERY_STAMP              2000
1141
1142 int Multi_did_autojoin;
1143 net_addr Multi_autojoin_addr;
1144 int Multi_autojoin_join_stamp;
1145 int Multi_autojoin_query_stamp;
1146
1147 // our join request
1148 join_request Multi_join_request;
1149
1150 // LOCAL function definitions
1151 void multi_join_check_buttons();
1152 void multi_join_button_pressed(int n);
1153 void multi_join_display_games();
1154 void multi_join_blit_game_status(active_game *game, int y);
1155 void multi_join_load_tcp_addrs();
1156 void multi_join_do_netstuff();
1157 void multi_join_ping_all();
1158 void multi_join_process_select();
1159 void multi_join_list_scroll_up();
1160 void multi_join_list_scroll_down();
1161 void multi_join_list_page_up();
1162 void multi_join_list_page_down();
1163 active_game *multi_join_get_game(int n);
1164 void multi_join_cull_timeouts();
1165 void multi_join_handle_item_cull(active_game *item, int item_index);
1166 void multi_join_send_join_request(int as_observer);
1167 void multi_join_create_game();
1168 void multi_join_blit_top_stuff();
1169 int multi_join_maybe_warn();
1170 int multi_join_warn_pxo();
1171 void multi_join_blit_protocol();
1172
1173 DCF(mj_make, "")
1174 {
1175         active_game ag, *newitem;;
1176         int idx;
1177
1178         dc_get_arg(ARG_INT);
1179         for(idx=0; idx<Dc_arg_int; idx++){
1180                 // stuff some fake info
1181                 memset(&ag, 0, sizeof(active_game));
1182                 sprintf(ag.name, "Game %d", idx);
1183                 ag.version = MULTI_FS_SERVER_VERSION;
1184                 ag.comp_version = MULTI_FS_SERVER_VERSION;
1185                 ag.server_addr.addr[0] = (char)idx;
1186                 ag.flags = (AG_FLAG_COOP | AG_FLAG_FORMING | AG_FLAG_STANDALONE);               
1187
1188                 // add the game
1189                 newitem = multi_update_active_games(&ag);
1190
1191                 // timestamp it so we get random timeouts
1192                 if(newitem != NULL){
1193                         // newitem->heard_from_timer = timestamp((int)frand_range(500.0f, 10000.0f));
1194                 }
1195         }       
1196 }
1197
1198 void multi_join_notify_new_game()
1199 {       
1200         // reset the # of items 
1201         Multi_join_slider.set_numberItems(Active_game_count > Mj_max_game_items[gr_screen.res] ? Active_game_count - Mj_max_game_items[gr_screen.res] : 0);
1202         Multi_join_slider.force_currentItem(Multi_join_list_start);
1203 }
1204
1205 int multi_join_autojoin_do()
1206 {
1207         // if we have an active game on the list, then return a positive value so that we
1208         // can join the game
1209         if ( Active_game_head && (Active_game_count > 0) ) {
1210                 Multi_join_selected_item = Active_game_head;
1211                 return 1;
1212         }
1213
1214         // send out a server_query again
1215         if ( timestamp_elapsed(Multi_autojoin_query_stamp) ) {
1216                 send_server_query(&Multi_autojoin_addr);
1217                 Multi_autojoin_query_stamp = timestamp(MULTI_AUTOJOIN_QUERY_STAMP);
1218         }
1219
1220         return -1;
1221 }
1222
1223 void multi_join_game_init()
1224 {
1225         int idx;
1226
1227         // do the multiplayer init stuff - multi_level_init() now does all net_player zeroing.
1228         // setup various multiplayer things
1229         Assert( Game_mode & GM_MULTIPLAYER );
1230         Assert( Net_player != NULL );
1231
1232         switch (Multi_options_g.protocol) {     
1233         case NET_IPX:
1234                 ADDRESS_LENGTH = IPX_ADDRESS_LENGTH;
1235                 PORT_LENGTH = IPX_PORT_LENGTH;
1236                 break;
1237
1238         case NET_TCP:
1239                 ADDRESS_LENGTH = IP_ADDRESS_LENGTH;             
1240                 PORT_LENGTH = IP_PORT_LENGTH;                   
1241                 break;
1242
1243         default :
1244                 Int3();
1245         } // end switch
1246         
1247         HEADER_LENGTH = 1;
1248
1249         memset( &Netgame, 0, sizeof(Netgame) );
1250
1251         multi_level_init();             
1252         Net_player->flags |= NETINFO_FLAG_DO_NETWORKING;        
1253         Net_player->player = Player;
1254         memcpy(&Net_player->p_info.addr,&Psnet_my_addr,sizeof(net_addr));
1255
1256         // check for the existence of a CD
1257         multi_common_verify_cd();
1258
1259         // load my local netplayer options
1260         multi_options_local_load(&Net_player->p_info.options, Net_player);      
1261
1262         game_flush();
1263
1264         // set the palette
1265         // common_set_interface_palette(MULTI_JOIN_PALETTE);
1266
1267         // destroy any chatbox contents which previously existed (from another game)
1268         chatbox_clear();
1269
1270         // create the interface window
1271         Multi_join_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
1272         Multi_join_window.set_mask_bmap(Multi_join_bitmap_mask_fname[gr_screen.res]);
1273
1274         // load the background bitmap
1275         Multi_join_bitmap = bm_load(Multi_join_bitmap_fname[gr_screen.res]);
1276         if(Multi_join_bitmap < 0){
1277                 // we failed to load the bitmap - this is very bad
1278                 Int3();
1279         }
1280
1281         // intialize the endgame system
1282         multi_endgame_init();   
1283
1284         // initialize the common notification messaging
1285         multi_common_notify_init();
1286
1287         // initialize the common text area
1288         multi_common_set_text("");      
1289
1290         // load and use the common interface palette
1291         multi_common_load_palette();
1292         multi_common_set_palette();
1293
1294         // load the help overlay
1295         help_overlay_load(MULTI_JOIN_OVERLAY);
1296         help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1297         
1298         // do TCP and VMT specific initialization
1299         if(Multi_options_g.protocol == NET_TCP){                
1300                 // if this is a TCP (non tracker) game, we'll load up our default address list right now                
1301                 multi_join_load_tcp_addrs();            
1302         }       
1303
1304         // initialize any and all timestamps    
1305         Multi_join_glr_stamp = -1;
1306         Multi_join_ping_stamp = -1;
1307         Multi_join_sent_stamp = -1;
1308
1309         // reset frame count
1310         Multi_join_frame_count = 0;
1311
1312         // haven't tried to verify on the tracker yet.
1313         Multi_join_mt_tried_verify = 0;
1314
1315         // clear our all game lists to save hassles
1316         multi_join_clear_game_list();
1317
1318         // create the interface buttons
1319         for(idx=0; idx<MULTI_JOIN_NUM_BUTTONS; idx++){
1320                 // create the object
1321                 Multi_join_buttons[gr_screen.res][idx].button.create(&Multi_join_window,"", Multi_join_buttons[gr_screen.res][idx].x, Multi_join_buttons[gr_screen.res][idx].y, 1, 1, 0, 1);
1322
1323                 // set the sound to play when highlighted
1324                 Multi_join_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
1325
1326                 // set the ani for the button
1327                 Multi_join_buttons[gr_screen.res][idx].button.set_bmaps(Multi_join_buttons[gr_screen.res][idx].filename);
1328
1329                 // set the hotspot
1330                 Multi_join_buttons[gr_screen.res][idx].button.link_hotspot(Multi_join_buttons[gr_screen.res][idx].hotspot);
1331         }               
1332
1333         // create all xstrs
1334         for(idx=0; idx<MULTI_JOIN_NUM_TEXT; idx++){
1335                 Multi_join_window.add_XSTR(&Multi_join_text[gr_screen.res][idx]);
1336         }
1337
1338         Multi_join_should_send = -1;
1339
1340         // close any previously open chatbox
1341         chatbox_close();
1342
1343         // create the list item select button
1344         Multi_join_select_button.create(&Multi_join_window, "", Mj_list_area_coords[gr_screen.res][MJ_X_COORD], Mj_list_area_coords[gr_screen.res][MJ_Y_COORD], Mj_list_area_coords[gr_screen.res][MJ_W_COORD], Mj_list_area_coords[gr_screen.res][MJ_H_COORD], 0, 1);
1345         Multi_join_select_button.hide();
1346
1347         // slider
1348         Multi_join_slider.create(&Multi_join_window, Mj_slider_coords[gr_screen.res][MJ_X_COORD], Mj_slider_coords[gr_screen.res][MJ_Y_COORD], Mj_slider_coords[gr_screen.res][MJ_W_COORD], Mj_slider_coords[gr_screen.res][MJ_H_COORD], 0, Mj_slider_name[gr_screen.res], &multi_join_list_scroll_up, &multi_join_list_scroll_down, NULL);
1349
1350         // if starting a network game, then go to the create game screen
1351         if ( Cmdline_start_netgame ) {
1352                 multi_join_create_game();               
1353         } else if ( Cmdline_connect_addr != NULL ) {
1354                 char *p;
1355                 short port_num;
1356                 int ip_addr;
1357
1358                 // joining a game.  Send a join request to the given IP address, and wait for the return.
1359                 memset( &Multi_autojoin_addr, 0, sizeof(net_addr) );
1360                 Multi_autojoin_addr.type = NET_TCP;
1361
1362                 // create the address, looking out for port number at the end
1363                 port_num = DEFAULT_GAME_PORT;
1364                 p = strrchr(Cmdline_connect_addr, ':');
1365                 if ( p ) {
1366                         *p = 0;
1367                         p++;
1368                         port_num = (short)atoi(p);
1369                 }
1370                 ip_addr = inet_addr(Cmdline_connect_addr);
1371                 memcpy(Multi_autojoin_addr.addr, &ip_addr, 4);
1372                 Multi_autojoin_addr.port = port_num;
1373
1374                 send_server_query(&Multi_autojoin_addr);
1375                 Multi_autojoin_query_stamp = timestamp(MULTI_AUTOJOIN_QUERY_STAMP);
1376                 Multi_did_autojoin = 0;
1377         }
1378 }
1379
1380 void multi_join_clear_game_list()
1381 {
1382         // misc data    
1383         Multi_join_list_selected = -1;  
1384         Multi_join_selected_item = NULL;        
1385         MJ_LIST_START_SET(-1);
1386         Multi_join_list_start_item = NULL;      
1387
1388         // free up the active game list
1389         multi_free_active_games();
1390
1391         // initialize the active game list
1392         Active_game_head = NULL;
1393         Active_game_count = 0;
1394 }
1395
1396 void multi_join_game_do_frame()
1397 {
1398         // check the status of our reliable socket.  If not valid, popup error and return to main menu
1399         // I put this code here to avoid nasty gameseq issues with states.  Also, we will have nice
1400         // background for the popup
1401         if ( !psnet_rel_check() ) {
1402                 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("Network Error.  Try exiting and restarting FreeSpace to clear the error.  Otherwise, please reboot your machine.",756));
1403                 gameseq_post_event(GS_EVENT_MAIN_MENU);
1404                 return;
1405         }       
1406
1407         // return here since we will be moving to the next stage anyway -- I don't want to see the backgrounds of
1408         // all the screens for < 1 second for every screen we automatically move to.
1409         if ( Cmdline_start_netgame ) {
1410                 return;
1411         }
1412
1413         // when joining a network game, wait for the server query to come back, and then join the game
1414         if ( Cmdline_connect_addr != NULL ) {
1415                 int rval;
1416
1417                 if ( !Multi_did_autojoin ) {
1418                         rval = popup_till_condition(multi_join_autojoin_do, XSTR("&Cancel", 779), XSTR("Joining netgame", 1500) );
1419                         if ( rval == 0 ) {
1420                                 // cancel was hit.  Send the user back to the main hall
1421                                 gameseq_post_event(GS_EVENT_MAIN_MENU);
1422                                 Cmdline_connect_addr = NULL;            // reset this value.
1423                         }
1424
1425                         // when we get here, we have the data -- join the game.
1426                         multi_join_send_join_request(0);
1427                         Multi_autojoin_join_stamp = timestamp(MULTI_AUTOJOIN_JOIN_STAMP);
1428                         Multi_did_autojoin = 1;
1429                 }
1430
1431                 if ( timestamp_elapsed(Multi_autojoin_join_stamp) ) {
1432                         multi_join_send_join_request(0);
1433                         Multi_autojoin_join_stamp = timestamp(MULTI_AUTOJOIN_JOIN_STAMP);
1434                 }
1435                 return;
1436
1437         }       
1438
1439         // reset the should send var
1440         Multi_join_should_send = -1;
1441
1442         int k = Multi_join_window.process();
1443
1444         // process any keypresses
1445         switch(k){
1446         case KEY_ESC :
1447                 if(help_overlay_active(MULTI_JOIN_OVERLAY)){
1448                         help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1449                 } else {                
1450                         gameseq_post_event(GS_EVENT_MAIN_MENU);                 
1451                         gamesnd_play_iface(SND_USER_SELECT);
1452                 }
1453                 break;
1454
1455         // page up the game list
1456         case KEY_PAGEUP:
1457                 multi_join_list_page_up();      
1458                 Multi_join_slider.force_currentItem(Multi_join_list_start);
1459                 break;
1460
1461         case KEY_T:
1462                 multi_pinfo_popup(Net_player);
1463                 break;
1464
1465         // page down the game list
1466         case KEY_PAGEDOWN:
1467                 multi_join_list_page_down();
1468                 Multi_join_slider.force_currentItem(Multi_join_list_start);
1469                 break;
1470
1471         // send out a ping-all
1472         case KEY_P :            
1473                 multi_join_ping_all();          
1474                 Multi_join_ping_stamp = timestamp(MULTI_JOIN_PING_TIME);
1475                 break;  
1476
1477         // shortcut to start a game     
1478         case KEY_S :            
1479                 multi_join_create_game();               
1480                 break;
1481
1482         // scroll the game list up
1483         case KEY_UP:
1484                 multi_join_list_scroll_up();
1485                 Multi_join_slider.force_currentItem(Multi_join_list_start);
1486                 break;
1487
1488         // scroll the game list down
1489         case KEY_DOWN:
1490                 multi_join_list_scroll_down();
1491                 Multi_join_slider.force_currentItem(Multi_join_list_start);
1492                 break;
1493         }       
1494
1495         if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
1496                 help_overlay_set_state(MULTI_JOIN_OVERLAY, 0);
1497         }
1498
1499         // do any network related stuff
1500         multi_join_do_netstuff(); 
1501
1502         // process any button clicks
1503         multi_join_check_buttons();
1504
1505         // process any list selection stuff
1506         multi_join_process_select();
1507
1508         // draw the background, etc
1509         gr_reset_clip();
1510         GR_MAYBE_CLEAR_RES(Multi_join_bitmap);
1511         if(Multi_join_bitmap != -1){            
1512                 gr_set_bitmap(Multi_join_bitmap);
1513                 gr_bitmap(0,0);
1514         }
1515         Multi_join_window.draw();
1516
1517         // display the active games
1518         multi_join_display_games();
1519
1520         // display any text in the info area
1521         multi_common_render_text();
1522
1523         // display any pending notification messages
1524         multi_common_notify_do();
1525
1526         // blit the CD icon and any PXO filter stuff
1527         multi_join_blit_top_stuff();
1528
1529         // draw the help overlay
1530         help_overlay_maybe_blit(MULTI_JOIN_OVERLAY);
1531         
1532         // flip the buffer
1533         gr_flip();
1534
1535         // if we are supposed to be sending a join request
1536         if(Multi_join_should_send != -1){               
1537                 multi_join_send_join_request(Multi_join_should_send);
1538         }
1539         Multi_join_should_send = -1;
1540
1541         // increment the frame count
1542         Multi_join_frame_count++;
1543 }
1544
1545 void multi_join_game_close()
1546 {
1547         // unload any bitmaps
1548         if(!bm_unload(Multi_join_bitmap)){
1549                 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_join_bitmap_fname[gr_screen.res]));
1550         }
1551
1552         // unload the help overlay
1553         help_overlay_unload(MULTI_JOIN_OVERLAY);        
1554
1555         // free up the active game list
1556         multi_free_active_games();
1557         
1558         // destroy the UI_WINDOW
1559         Multi_join_window.destroy();
1560 }
1561
1562 void multi_join_check_buttons()
1563 {
1564         int idx;
1565         for(idx=0;idx<MULTI_JOIN_NUM_BUTTONS;idx++){
1566                 // we only really need to check for one button pressed at a time, so we can break after 
1567                 // finding one.
1568                 if(Multi_join_buttons[gr_screen.res][idx].button.pressed()){
1569                         multi_join_button_pressed(idx);
1570                         break;
1571                 }
1572         }
1573 }
1574
1575 void multi_join_button_pressed(int n)
1576 {
1577         switch(n){
1578         case MJ_CANCEL :
1579                 // if we're player PXO, go back there   
1580                 gameseq_post_event(GS_EVENT_MAIN_MENU);         
1581                 gamesnd_play_iface(SND_USER_SELECT);            
1582                 break;
1583         case MJ_ACCEPT :
1584                 if(Active_game_count <= 0){
1585                         multi_common_add_notify(XSTR("No games found!",757));
1586                         gamesnd_play_iface(SND_GENERAL_FAIL);
1587                 } else if(Multi_join_list_selected == -1){
1588                         multi_common_add_notify(XSTR("No game selected!",758));
1589                         gamesnd_play_iface(SND_GENERAL_FAIL);
1590                 } else if((Multi_join_sent_stamp != -1) && !timestamp_elapsed(Multi_join_sent_stamp)){
1591                         multi_common_add_notify(XSTR("Still waiting on previous join request!",759));
1592                         gamesnd_play_iface(SND_GENERAL_FAIL);
1593                 } else {                        
1594                         // otherwise, if he's already played PXO games, warn him        
1595                         /*
1596                         if(Player->flags & PLAYER_FLAGS_HAS_PLAYED_PXO){
1597                                 if(!multi_join_warn_pxo()){
1598                                         break;
1599                                 }                       
1600                         }
1601                         */
1602
1603                         // send the join request here
1604                         Assert(Multi_join_selected_item != NULL);
1605
1606                         // send a join request packet
1607                         Multi_join_should_send = 0;                     
1608                         
1609                         gamesnd_play_iface(SND_COMMIT_PRESSED);
1610                 }
1611                 break;
1612
1613         // help overlay
1614         case MJ_HELP:
1615                 if(!help_overlay_active(MULTI_JOIN_OVERLAY)){
1616                         help_overlay_set_state(MULTI_JOIN_OVERLAY,1);
1617                 } else {
1618                         help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1619                 }
1620                 break;
1621
1622         // scroll the game list up
1623         case MJ_SCROLL_UP:
1624                 multi_join_list_scroll_up();
1625                 Multi_join_slider.force_currentItem(Multi_join_list_start);
1626                 break;
1627
1628         // scroll the game list down
1629         case MJ_SCROLL_DOWN:
1630                 multi_join_list_scroll_down();
1631                 Multi_join_slider.force_currentItem(Multi_join_list_start);
1632                 break;
1633
1634         // scroll the info text box up
1635         case MJ_SCROLL_INFO_UP:
1636                 multi_common_scroll_text_up();
1637                 break;
1638
1639         // scroll the info text box down
1640         case MJ_SCROLL_INFO_DOWN:
1641                 multi_common_scroll_text_down();
1642                 break;
1643
1644         // go to the options screen
1645         case MJ_OPTIONS:
1646                 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
1647                 break;
1648
1649         // go to the start game screen  
1650         case MJ_START_GAME:
1651                 multi_join_create_game();               
1652                 break;
1653
1654         // refresh the game/server list
1655         case MJ_REFRESH:                
1656                 gamesnd_play_iface(SND_USER_SELECT);
1657                 broadcast_game_query();         
1658                 break;
1659
1660         // join a game as an observer
1661         case MJ_JOIN_OBSERVER:
1662                 if(Active_game_count <= 0){
1663                         multi_common_add_notify(XSTR("No games found!",757));
1664                         gamesnd_play_iface(SND_GENERAL_FAIL);
1665                 } else if(Multi_join_list_selected == -1){
1666                         multi_common_add_notify(XSTR("No game selected!",758));
1667                         gamesnd_play_iface(SND_GENERAL_FAIL);
1668                 } else if((Multi_join_sent_stamp != -1) && !timestamp_elapsed(Multi_join_sent_stamp)){
1669                         multi_common_add_notify(XSTR("Still waiting on previous join request!",759));
1670                         gamesnd_play_iface(SND_GENERAL_FAIL);
1671                 } else {                        
1672                         // send the join request here
1673                         Assert(Multi_join_selected_item != NULL);
1674
1675                         Multi_join_should_send = 1;             
1676
1677                         gamesnd_play_iface(SND_COMMIT_PRESSED);
1678                 }
1679                 break;
1680
1681         default :
1682                 multi_common_add_notify(XSTR("Not implemented yet!",760));
1683                 gamesnd_play_iface(SND_GENERAL_FAIL);
1684                 break;
1685         }
1686 }
1687
1688 // display all relevant info for active games
1689 void multi_join_display_games()
1690 {
1691         active_game *moveup = Multi_join_list_start_item;       
1692         char str[200];          
1693         int w,h;
1694         int con_type;
1695         int y_start = Mj_list_y[gr_screen.res];
1696         int count = 0;
1697         
1698         if(moveup != NULL){
1699                 do {                    
1700                         // blit the game status (including text and type icon)
1701                         multi_join_blit_game_status(moveup,y_start);                    
1702                         
1703                         // get the connection type
1704                         con_type = (moveup->flags & AG_FLAG_CONNECTION_SPEED_MASK) >> AG_FLAG_CONNECTION_BIT;
1705                         if((con_type > 4) || (con_type < 0)){
1706                                 con_type = 0;
1707                         }
1708
1709                         // display the connection speed
1710                         str[0] = '\0';
1711                         strcpy(str, Multi_join_speed_labels[con_type]);
1712                         gr_set_color_fast(Multi_join_speed_colors[con_type]);
1713                         gr_string(Mj_speed_coords[gr_screen.res][MJ_X_COORD], y_start, str);
1714
1715                         // we'll want to have different colors for highlighted items, etc.
1716                         if(moveup == Multi_join_selected_item){
1717                                 gr_set_color_fast(&Color_text_selected);
1718                         } else {
1719                                 gr_set_color_fast(&Color_text_normal);
1720                         }
1721
1722                         // display the game name, adding appropriate status chars
1723                         str[0] = '\0';
1724                         if(moveup->flags & AG_FLAG_STANDALONE){
1725                                 strcat(str,MJ_CHAR_STANDALONE);
1726                         }
1727                         if(moveup->flags & AG_FLAG_CAMPAIGN){
1728                                 strcat(str,MJ_CHAR_CAMPAIGN);
1729                         }
1730
1731                         // tack on the actual server name                       
1732                         strcat(str," ");
1733                         strcat(str,moveup->name);
1734                         if(strlen(moveup->mission_name) > 0){
1735                                 strcat(str, " / ");
1736                                 strcat(str,moveup->mission_name);
1737                         } 
1738
1739                         // make sure the string fits in the display area and draw it
1740                         gr_force_fit_string(str,200,Mj_game_name_coords[gr_screen.res][MJ_W_COORD]);                    
1741                         gr_string(Mj_game_name_coords[gr_screen.res][MJ_X_COORD],y_start,str);
1742
1743                         // display the ping time
1744                         if(moveup->ping.ping_avg > 0){
1745                                 if(moveup->ping.ping_avg > 1000){
1746                                         gr_set_color_fast(&Color_bright_red);
1747                                         strcpy(str,XSTR("> 1 sec",761));
1748                                 } else {
1749                                         // set the appropriate ping time color indicator
1750                                         if(moveup->ping.ping_avg > MJ_PING_YELLOW){
1751                                                 gr_set_color_fast(&Color_bright_red);
1752                                         } else if(moveup->ping.ping_avg > MJ_PING_YELLOW){
1753                                                 gr_set_color_fast(&Color_bright_yellow);
1754                                         } else {
1755                                                 gr_set_color_fast(&Color_bright_green);
1756                                         }
1757
1758                                         sprintf(str,"%d",moveup->ping.ping_avg);
1759                                         strcat(str,XSTR(" ms",762));  // [[ Milliseconds ]]
1760                                 }
1761
1762                                 gr_string(Mj_ping_coords[gr_screen.res][MJ_X_COORD],y_start,str);
1763                         }
1764
1765                         // display the number of players (be sure to center it)
1766                         if(moveup == Multi_join_selected_item){
1767                                 gr_set_color_fast(&Color_text_selected);
1768                         } else {
1769                                 gr_set_color_fast(&Color_text_normal);
1770                         }
1771                         sprintf(str,"%d",moveup->num_players);                  
1772                         gr_get_string_size(&w,&h,str);
1773                         gr_string(Mj_players_coords[gr_screen.res][MJ_X_COORD] + (Mj_players_coords[gr_screen.res][MJ_W_COORD] - w)/2,y_start,str);                     
1774
1775                         count++;
1776                         y_start += 10;
1777                         moveup = moveup->next;
1778                 } while((moveup != Active_game_head) && (count < Mj_max_game_items[gr_screen.res]));
1779         }
1780         // if there are no items on the list, display this info
1781         else {
1782                 gr_set_color_fast(&Color_bright);
1783                 gr_string(Mj_game_name_coords[gr_screen.res][MJ_X_COORD] - 30,y_start,XSTR("<No game servers found>",763));
1784         }
1785 }
1786
1787 void multi_join_blit_game_status(active_game *game, int y)
1788 {
1789         int draw,str_w;
1790         char status_text[25];
1791
1792         // blit the proper icon
1793         draw = 0;       
1794         switch( game->flags & AG_FLAG_TYPE_MASK ){
1795         // coop game
1796         case AG_FLAG_COOP:
1797                 if(Multi_common_icons[MICON_COOP] != -1){
1798                         gr_set_bitmap(Multi_common_icons[MICON_COOP]);          
1799                         draw = 1;
1800                 }
1801                 break;  
1802         
1803         // team vs. team game
1804         case AG_FLAG_TEAMS:
1805                 if(Multi_common_icons[MICON_TVT] != -1){
1806                         gr_set_bitmap(Multi_common_icons[MICON_TVT]);
1807                         draw = 1;
1808                 } 
1809                 break;  
1810
1811         // dogfight game
1812         case AG_FLAG_DOGFIGHT:
1813                 if(Multi_common_icons[MICON_DOGFIGHT] != -1){
1814                         gr_set_bitmap(Multi_common_icons[MICON_DOGFIGHT]);
1815                         draw = 1;
1816                 } 
1817                 break;  
1818         }
1819         // if we're supposed to draw a bitmap
1820         if(draw){
1821                 gr_bitmap(Mj_game_icon_coords[gr_screen.res][MJ_X_COORD],y-1);
1822         }
1823
1824         // blit the proper status text
1825         memset(status_text,0,25);
1826
1827         switch( game->flags & AG_FLAG_STATE_MASK ){
1828         case AG_FLAG_FORMING:
1829                 gr_set_color_fast(&Color_bright_green);
1830                 strcpy(status_text,XSTR("Forming",764));
1831                 break;
1832         case AG_FLAG_BRIEFING:
1833                 gr_set_color_fast(&Color_bright_red);
1834                 strcpy(status_text,XSTR("Briefing",765));
1835                 break;
1836         case AG_FLAG_DEBRIEF:
1837                 gr_set_color_fast(&Color_bright_red);
1838                 strcpy(status_text,XSTR("Debrief",766));
1839                 break;
1840         case AG_FLAG_PAUSE:
1841                 gr_set_color_fast(&Color_bright_red);
1842                 strcpy(status_text,XSTR("Paused",767));
1843                 break;
1844         case AG_FLAG_IN_MISSION:
1845                 gr_set_color_fast(&Color_bright_red);
1846                 strcpy(status_text,XSTR("Playing",768));
1847                 break;
1848         default:
1849                 gr_set_color_fast(&Color_bright);
1850                 strcpy(status_text,XSTR("Unknown",769));
1851                 break;
1852         }               
1853         gr_get_string_size(&str_w,NULL,status_text);
1854         gr_string(Mj_status_coords[gr_screen.res][MJ_X_COORD] + ((Mj_status_coords[gr_screen.res][MJ_W_COORD] - str_w)/2),y,status_text);
1855 }
1856
1857 // load in a list of active games from our tcp.cfg file
1858 void multi_join_load_tcp_addrs()
1859 {
1860         char line[MAX_IP_STRING];
1861         net_addr addr;  
1862         server_item *item;
1863         CFILE *file = NULL;
1864
1865         // attempt to open the ip list file
1866         file = cfopen(IP_CONFIG_FNAME,"rt",CFILE_NORMAL,CF_TYPE_DATA);  
1867         if(file == NULL){
1868                 nprintf(("Network","Error loading tcp.cfg file!\n"));
1869                 return;
1870         }
1871
1872         // free up any existing server list
1873         multi_free_server_list();
1874
1875         // read in all the strings in the file
1876         while(!cfeof(file)){
1877                 line[0] = '\0';
1878                 cfgets(line,MAX_IP_STRING,file);
1879
1880                 // strip off any newline character
1881                 if(line[strlen(line) - 1] == '\n'){
1882                         line[strlen(line) - 1] = '\0';
1883                 }
1884                 
1885                 // empty lines don't get processed
1886                 if( (line[0] == '\0') || (line[0] == '\n') ){
1887                         continue;
1888                 }
1889
1890                 if ( !psnet_is_valid_ip_string(line) ) {
1891                         nprintf(("Network","Invalid ip string (%s)\n",line));
1892                 } else {                         
1893                         // copy the server ip address
1894                         memset(&addr,0,sizeof(net_addr));
1895                         addr.type = NET_TCP;
1896                         psnet_string_to_addr(&addr,line);
1897                         if ( addr.port == 0 ){
1898                                 addr.port = DEFAULT_GAME_PORT;
1899                         }
1900
1901                         // create a new server item on the list
1902                         item = multi_new_server_item();
1903                         if(item != NULL){
1904                                 memcpy(&item->server_addr,&addr,sizeof(net_addr));
1905                         }                       
1906                 }
1907         }
1908
1909         cfclose(file);  
1910 }
1911
1912 // do stuff like pinging servers, sending out requests, etc
1913 void multi_join_do_netstuff()
1914 {
1915         // handle game query stuff
1916         if(Multi_join_glr_stamp == -1){
1917                 broadcast_game_query();
1918                 
1919                 if(Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST){
1920                         Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME_LOCAL);
1921                 } else {
1922                         Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME);
1923                 }
1924         } 
1925         // otherwise send out game query and restamp
1926         else if(timestamp_elapsed(Multi_join_glr_stamp)){                       
1927                 broadcast_game_query();
1928
1929                 if(Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST){
1930                         Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME_LOCAL);
1931                 } else {
1932                         Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME);
1933                 }               
1934         }
1935
1936         // check to see if we've been accepted.  If so, put up message saying so
1937         if ( Net_player->flags & (NETINFO_FLAG_ACCEPT_INGAME|NETINFO_FLAG_ACCEPT_CLIENT|NETINFO_FLAG_ACCEPT_HOST|NETINFO_FLAG_ACCEPT_OBSERVER) ) {
1938                 multi_common_add_notify(XSTR("Accepted.  Waiting for player data.",770));
1939         }
1940
1941         // check to see if any join packets we have sent have timed out
1942         if((Multi_join_sent_stamp != -1) && (timestamp_elapsed(Multi_join_sent_stamp))){
1943                 Multi_join_sent_stamp = -1;
1944                 multi_common_add_notify(XSTR("Join request timed out!",771));
1945         }
1946
1947         // check to see if we should be pinging everyone
1948         if((Multi_join_ping_stamp == -1) || (timestamp_elapsed(Multi_join_ping_stamp))){
1949                 multi_join_ping_all();
1950                 Multi_join_ping_stamp = timestamp(MULTI_JOIN_PING_TIME);
1951         } 
1952
1953         // cull timeouts
1954         multi_join_cull_timeouts();
1955 }
1956
1957 // evaluate a returned pong.
1958 void multi_join_eval_pong(net_addr *addr, fix pong_time)
1959 {       
1960         int found;
1961         active_game *moveup = Active_game_head;
1962
1963         found = 0;
1964         if(moveup != NULL){
1965                 do {                            
1966                         if(psnet_same(&moveup->server_addr,addr)){
1967                                 found = 1;
1968                                 multi_ping_eval_pong(&moveup->ping);
1969                                 
1970                                 break;
1971                         } else {
1972                                 moveup = moveup->next;
1973                         }
1974                 } while(moveup != Active_game_head);
1975         }       
1976
1977         // update the game's ping
1978         /*
1979         if(found && (moveup->ping_end > moveup->ping_start)){           
1980                 moveup->ping_time = f2fl(moveup->ping_end - moveup->ping_start);
1981                 moveup->ping_start = -1;
1982                 moveup->ping_end = -1;
1983         }
1984         */
1985 }
1986
1987 // ping all the server on the list
1988 void multi_join_ping_all()
1989 {
1990         active_game *moveup = Active_game_head; 
1991         
1992         if(moveup != NULL){
1993                 do {
1994                         /*
1995                         moveup->ping_start = timer_get_fixed_seconds();
1996                         moveup->ping_end = -1;
1997                         send_ping(&moveup->server_addr);
1998                         */
1999                         multi_ping_send(&moveup->server_addr,&moveup->ping);
2000         
2001                         moveup = moveup->next;
2002                 } while(moveup != Active_game_head);
2003         }
2004 }
2005
2006 void multi_join_process_select()
2007 {
2008         // if we don't have anything selected and there are items on the list - select the first one
2009         if((Multi_join_list_selected == -1) && (Active_game_count > 0)){
2010                 Multi_join_list_selected = 0;
2011                 Multi_join_selected_item = multi_join_get_game(0);                              
2012                 MJ_LIST_START_SET(0);
2013                 Multi_join_list_start_item = Multi_join_selected_item;
2014
2015                 // send a mission description request to this guy               
2016                 send_netgame_descript_packet(&Multi_join_selected_item->server_addr,0);
2017                 multi_common_set_text("");
2018
2019                 // I sure hope this doesn't happen
2020                 Assert(Multi_join_selected_item != NULL);               
2021                 return;
2022         } 
2023         // otherwise see if he's clicked on an item
2024         else if(Multi_join_select_button.pressed() && (Active_game_count > 0)){          
2025                 int y,item;             
2026                 Multi_join_select_button.get_mouse_pos(NULL,&y);
2027                 item = y / 10;
2028                 if(item + Multi_join_list_start < Active_game_count){           
2029                         gamesnd_play_iface(SND_IFACE_MOUSE_CLICK);
2030
2031                         Multi_join_list_selected = item + Multi_join_list_start;
2032                         Multi_join_selected_item = multi_join_get_game(Multi_join_list_selected);
2033                         
2034                         // I sure hope this doesn't happen
2035                         Assert(Multi_join_selected_item != NULL);
2036
2037                         // send a mission description request to this guy
2038                         send_netgame_descript_packet(&Multi_join_selected_item->server_addr,0);
2039                         multi_common_set_text("");                      
2040                 }               
2041         }
2042
2043         // if he's double clicked, then select it and accept            
2044         if(Multi_join_select_button.double_clicked()){                  
2045                 int y,item;             
2046                 Multi_join_select_button.get_mouse_pos(NULL,&y);
2047                 item = y / 10;
2048                 if(item == Multi_join_list_selected){           
2049                         multi_join_button_pressed(MJ_ACCEPT);
2050                 }
2051         }
2052 }
2053
2054 // return game n (0 based index)
2055 active_game *multi_join_get_game(int n)
2056 {
2057         active_game *moveup = Active_game_head;
2058
2059         if(moveup != NULL){
2060                 if(n == 0){
2061                         return moveup;
2062                 } else {
2063                         int count = 1;
2064                         moveup = moveup->next;
2065                         while((moveup != Active_game_head) && (count != n)){
2066                                 moveup = moveup->next;
2067                                 count++;
2068                         }
2069                         if(moveup == Active_game_head){
2070                                 nprintf(("Network","Warning, couldn't find game item %d!\n",n));
2071                                 return NULL;
2072                         } else {
2073                                 return moveup;
2074                         }
2075                 }
2076         } 
2077         return NULL;
2078 }
2079
2080 // scroll through the game list
2081 void multi_join_list_scroll_up()
2082 {
2083         // if we're not at the beginning of the list, scroll up 
2084         if(Multi_join_list_start_item != Active_game_head){
2085                 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2086                 
2087                 MJ_LIST_START_DEC();            
2088
2089                 gamesnd_play_iface(SND_SCROLL);
2090         } else {
2091                 gamesnd_play_iface(SND_GENERAL_FAIL);
2092         }
2093 }
2094
2095 // scroll through the game list
2096 void multi_join_list_scroll_down()
2097 {
2098         if((Active_game_count - Multi_join_list_start) > Mj_max_game_items[gr_screen.res]){
2099                 Multi_join_list_start_item = Multi_join_list_start_item->next;
2100
2101                 MJ_LIST_START_INC();            
2102
2103                 gamesnd_play_iface(SND_SCROLL);
2104         } else {
2105                 gamesnd_play_iface(SND_GENERAL_FAIL);
2106         }
2107 }
2108
2109 void multi_join_list_page_up()
2110 {
2111         // in this case, just set us to the beginning of the list
2112         if((Multi_join_list_start - Mj_max_game_items[gr_screen.res]) < 0){
2113                 Multi_join_list_start_item = Active_game_head;          
2114
2115                 MJ_LIST_START_SET(0);
2116
2117                 gamesnd_play_iface(SND_SCROLL);         
2118         } else {
2119                 // otherwise page the whole thing up
2120                 int idx;
2121                 for(idx=0; idx<Mj_max_game_items[gr_screen.res]; idx++){
2122                         Multi_join_list_start_item = Multi_join_list_start_item->prev;
2123
2124                         MJ_LIST_START_DEC();                    
2125                 }
2126                 gamesnd_play_iface(SND_SCROLL);
2127         }
2128 }
2129
2130 void multi_join_list_page_down()
2131 {       
2132         int count = 0;
2133
2134         // page the whole thing down            
2135         while((count < Mj_max_game_items[gr_screen.res]) && ((Active_game_count - Multi_join_list_start) > Mj_max_game_items[gr_screen.res])){
2136                 Multi_join_list_start_item = Multi_join_list_start_item->next;
2137                 MJ_LIST_START_INC();                    
2138
2139                 // next 
2140                 count++;
2141         }       
2142         gamesnd_play_iface(SND_SCROLL);  
2143 }
2144
2145 void multi_join_cull_timeouts()
2146 {
2147         active_game *backup;
2148         int count;
2149         active_game *moveup = Active_game_head;
2150
2151         // traverse through the entire list if any items exist  
2152         count = 0;
2153         if(moveup != NULL){
2154                 do {
2155                         if((moveup->heard_from_timer != -1) && (timestamp_elapsed(moveup->heard_from_timer))){
2156                                 Active_game_count--;
2157
2158                                 // if this is the head of the list
2159                                 if(moveup == Active_game_head){                                 
2160                                         // if this is the _only_ item on the list
2161                                         if(moveup->next == Active_game_head){
2162                                                 // handle any gui details related to deleting this item
2163                                                 multi_join_handle_item_cull(Active_game_head, count);
2164                                                 
2165                                                 free(Active_game_head);
2166                                                 Active_game_head = NULL;                                                
2167                                                 return;
2168                                         } 
2169                                         // if there are other items on the list
2170                                         else {
2171                                                 // handle any gui details related to deleting this item
2172                                                 multi_join_handle_item_cull(moveup, count);
2173                                                 
2174                                                 Active_game_head = moveup->next;
2175                                                 Active_game_head->prev = moveup->prev;
2176                                                 Active_game_head->prev->next = Active_game_head;
2177                                                 free(moveup);
2178                                                 moveup = Active_game_head;                                                                                      
2179                                         }
2180                                 }
2181                                 // if its somewhere else on the list
2182                                 else {
2183                                         // handle any gui details related to deleting this item
2184                                         multi_join_handle_item_cull(moveup, count);
2185                                         
2186                                         // if its the last item on the list                                     
2187                                         moveup->next->prev = moveup->prev;
2188                                         moveup->prev->next = moveup->next;                                      
2189                                         
2190                                         // if it was the last element on the list, return
2191                                         if(moveup->next == Active_game_head){
2192                                                 free(moveup);
2193                                                 return;
2194                                         } else {
2195                                                 backup = moveup->next;
2196                                                 free(moveup);
2197                                                 moveup = backup;                                                
2198                                         }
2199                                 }
2200                         } else {
2201                                 moveup = moveup->next;                          
2202                                 count++;
2203                         }
2204                 } while(moveup != Active_game_head);
2205         }
2206 }
2207
2208 // deep magic begins here. 
2209 void multi_join_handle_item_cull(active_game *item, int item_index)
2210 {       
2211         // if this is the only item on the list, unset everything
2212         if(item->next == item){
2213                 Multi_join_list_selected = -1;
2214                 Multi_join_selected_item = NULL;
2215                 
2216                 Multi_join_slider.set_numberItems(0);
2217                 MJ_LIST_START_SET(-1);
2218                 Multi_join_list_start_item = NULL;
2219
2220                 // return
2221                 return;
2222         }       
2223         
2224         // see if we should be adjusting our currently selected item
2225         if(item_index <= Multi_join_list_selected){
2226                 // the selected item is the head of the list
2227                 if(Multi_join_selected_item == Active_game_head){
2228                         // move the pointer up since this item is about to be destroyed
2229                         Multi_join_selected_item = Multi_join_selected_item->next;
2230                 } else {                        
2231                         // if this is the item being deleted, select the previous one
2232                         if(item == Multi_join_selected_item){
2233                                 // previous item
2234                                 Multi_join_selected_item = Multi_join_selected_item->prev;
2235
2236                                 // decrement the selected index by 1
2237                                 Multi_join_list_selected--;             
2238                         }
2239                         // now we know its a previous item, so our pointer stays the same but our index goes down by one, since there will be
2240                         // 1 less item on the list
2241                         else {
2242                                 // decrement the selected index by 1
2243                                 Multi_join_list_selected--;             
2244                         }
2245                 }
2246         }
2247         
2248         // see if we should be adjusting out current start position
2249         if(item_index <= Multi_join_list_start){
2250                 // the start position is the head of the list
2251                 if(Multi_join_list_start_item == Active_game_head){
2252                         // move the pointer up since this item is about to be destroyed
2253                         Multi_join_list_start_item = Multi_join_list_start_item->next;
2254                 } else {
2255                         // if this is the item being deleted, select the previous one
2256                         if(item == Multi_join_list_start_item){
2257                                 Multi_join_list_start_item = Multi_join_list_start_item->prev;                  
2258                                 
2259                                 // decrement the starting index by 1
2260                                 MJ_LIST_START_DEC();                                                            
2261                         } else {
2262                                 // but decrement the starting index by 1
2263                                 MJ_LIST_START_DEC();                                                            
2264                         }
2265                 }
2266         }
2267
2268         // maybe go back up a bit so that we always have a full page of items   
2269         if(Active_game_count > Mj_max_game_items[gr_screen.res]){
2270                 while((Active_game_count - Multi_join_list_start) < Mj_max_game_items[gr_screen.res]){
2271                         Multi_join_list_start_item = Multi_join_list_start_item->prev;
2272                         MJ_LIST_START_DEC();
2273                 }
2274         }       
2275
2276         // set slider location
2277         Multi_join_slider.set_numberItems(Active_game_count > Mj_max_game_items[gr_screen.res] ? Active_game_count - Mj_max_game_items[gr_screen.res] : 0);
2278         Multi_join_slider.force_currentItem(Multi_join_list_start);     
2279 }
2280
2281 void multi_join_send_join_request(int as_observer)
2282 {       
2283         // don't do anything if we have no items selected
2284         if(Multi_join_selected_item == NULL){
2285                 return;
2286         }
2287
2288         // 5/26/98 -- for team v team games, don't allow ingame joining :-(
2289         if ( (Multi_join_selected_item->flags & AG_FLAG_TEAMS) && (Multi_join_selected_item->flags & (AG_FLAG_PAUSE|AG_FLAG_IN_MISSION)) ) {
2290                 popup(0, 1, POPUP_OK, XSTR("Joining ingame is currently not allowed for team vs. team games",772));
2291                 return;
2292         }
2293
2294         memset(&Multi_join_request,0,sizeof(join_request));
2295
2296         // if the netgame is in password mode, put up a request for the password
2297         if(Multi_join_selected_item->flags & AG_FLAG_PASSWD){
2298                 if(!multi_passwd_popup(Multi_join_request.passwd)){
2299                         return;
2300                 }
2301
2302                 nprintf(("Password : %s\n",Multi_join_request.passwd));
2303         }       
2304                 
2305         // fill out the join request struct     
2306         strcpy(Multi_join_request.callsign,Player->callsign);
2307         if(strlen(Player->image_filename) > 0){
2308                 strcpy(Multi_join_request.image_filename, Player->image_filename);
2309         }       
2310         if(strlen(Player->squad_filename) > 0){
2311                 strcpy(Multi_join_request.squad_filename, Player->squad_filename);
2312         }
2313
2314         // tracker id (if any)
2315         Multi_join_request.tracker_id = Multi_tracker_id;
2316
2317         // player's rank (at least, what he wants you to _believe_)
2318         Multi_join_request.player_rank = (ubyte)Player->stats.rank;
2319         
2320         // misc join flags
2321         Multi_join_request.flags = 0;
2322         if(as_observer){
2323                 Multi_join_request.flags |= JOIN_FLAG_AS_OBSERVER;
2324         }       
2325
2326         // if the player has hacked data
2327         if(game_hacked_data()){
2328                 Multi_join_request.flags |= JOIN_FLAG_HAXOR;
2329         }
2330         
2331         // pxo squad info
2332         strncpy(Multi_join_request.pxo_squad_name, Multi_tracker_squad_name, LOGIN_LEN);
2333
2334         // version of this server
2335         Multi_join_request.version = MULTI_FS_SERVER_VERSION;
2336
2337         // server compatible version
2338         Multi_join_request.comp_version = MULTI_FS_SERVER_COMPATIBLE_VERSION;
2339
2340         // his local player options
2341         memcpy(&Multi_join_request.player_options,&Player->m_local_options,sizeof(multi_local_options));
2342                         
2343         // set the server address for the netgame
2344         memcpy(&Netgame.server_addr,&Multi_join_selected_item->server_addr,sizeof(net_addr));
2345
2346         // send a join request to the guy
2347         send_join_packet(&Multi_join_selected_item->server_addr,&Multi_join_request);
2348
2349    // now we wait
2350         Multi_join_sent_stamp = timestamp(MULTI_JOIN_SENT_WAIT);
2351
2352         psnet_flush();
2353         multi_common_add_notify(XSTR("Sending join request...",773));
2354 }
2355
2356 void multi_join_create_game()
2357 {
2358         // maybe warn the player about possible crappy server conditions
2359         if(!multi_join_maybe_warn()){
2360                 return;
2361         }
2362
2363         // make sure to flag ourself as being the master
2364         Net_player->flags |= (NETINFO_FLAG_AM_MASTER | NETINFO_FLAG_GAME_HOST);
2365         Net_player->state = NETPLAYER_STATE_HOST_SETUP; 
2366
2367         // if we're in PXO mode, mark it down in our player struct
2368         if(MULTI_IS_TRACKER_GAME){
2369                 Player->flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
2370                 Player->save_flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
2371         } 
2372         // otherwise, if he's already played PXO games, warn him
2373         else {
2374                 /*
2375                 if(Player->flags & PLAYER_FLAGS_HAS_PLAYED_PXO){
2376                         if(!multi_join_warn_pxo()){
2377                                 return;
2378                         }                       
2379                 }
2380                 */
2381         }
2382
2383         gameseq_post_event(GS_EVENT_MULTI_START_GAME);
2384         gamesnd_play_iface(SND_USER_SELECT);                                                            
2385 }
2386
2387 void multi_join_reset_join_stamp()
2388 {
2389         // unset the timestamp here so the user can immediately send another join request
2390         Multi_join_sent_stamp = -1;
2391         multi_common_add_notify("");
2392 }
2393
2394 void multi_join_blit_top_stuff()
2395 {       
2396         // blit the cd icon if he has one
2397         if(Multi_has_cd && (Multi_common_icons[MICON_CD] != -1)){
2398                 // get bitmap width
2399                 int cd_w;
2400                 bm_get_info(Multi_common_icons[MICON_CD], &cd_w, NULL, NULL, NULL, NULL);
2401
2402                 gr_set_bitmap(Multi_common_icons[MICON_CD]);
2403                 gr_bitmap((gr_screen.max_w / 2) - (cd_w / 2), Mj_cd_coords[gr_screen.res]);
2404         }       
2405 }
2406
2407 #define CW_CODE_CANCEL                          0                               // cancel the action
2408 #define CW_CODE_OK                                      1                               // continue anyway
2409 #define CW_CODE_INFO                                    2                               // gimme some more information
2410
2411 #define LOW_WARN_TEXT                           XSTR("Warning - You have low object updates selected. A server with low object updates will not be able to handle more than 1 client without performance problems",775)
2412 #define LOW_INFO_TEXT                           XSTR("Low update level caps all bandwidth at ~2000 bytes/second. It is appropriate for clients with 28.8 modems, but is not reccomended for servers. In addition, any clients connecting to this server should use low object updates as well. To change your settings go to the options menu (f2), and select the Multi button",776)
2413
2414 #define MED_WARN_TEXT                           XSTR("Warning - You have medium object updates selected. A server with medium object updates will not be able to handle more than 1 or 2 clients without performance problems",777)
2415 #define MED_INFO_TEXT                           XSTR("Medium update level caps all bandwidth at ~4000 bytes/second. It is appropriate for clients with 56.6 modems, but is not reccomended for servers. In addition, any clients connecting to this server should use low object updates as well. To change your settings go to the options menu (f2), and select the Multi button",778)
2416
2417 int multi_join_warn_update_low(int code)
2418 {
2419         switch(code){
2420         case CW_CODE_OK:
2421                 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),LOW_WARN_TEXT);
2422
2423         case CW_CODE_INFO:
2424                 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),LOW_INFO_TEXT);
2425         }
2426
2427         return CW_CODE_CANCEL;
2428 }
2429
2430 int multi_join_warn_update_medium(int code)
2431 {
2432         switch(code){
2433         case CW_CODE_OK:
2434                 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),MED_WARN_TEXT);
2435
2436         case CW_CODE_INFO:
2437                 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),MED_INFO_TEXT);
2438         }
2439
2440         return CW_CODE_CANCEL;
2441 }
2442
2443 int multi_join_maybe_warn()
2444 {
2445         int code;
2446
2447         // if the player is set for low updates
2448         if(Player->m_local_options.obj_update_level == OBJ_UPDATE_LOW){
2449                 code = CW_CODE_OK;
2450                 do {
2451                         code = multi_join_warn_update_low(code);
2452                 } while((code != CW_CODE_CANCEL) && (code != CW_CODE_OK));
2453                 
2454                 return code;
2455         }
2456         
2457         // if the player is set for medium updates
2458         else if(Player->m_local_options.obj_update_level == OBJ_UPDATE_MEDIUM){
2459                 code = CW_CODE_OK;
2460                 do {
2461                         code = multi_join_warn_update_medium(code);
2462                 } while((code != CW_CODE_CANCEL) && (code != CW_CODE_OK));
2463                 
2464                 return code;
2465         } 
2466         
2467         return 1;
2468 }
2469
2470 int multi_join_warn_pxo()
2471 {
2472         // return popup(PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON | PF_TITLE_BIG | PF_TITLE_RED, 2, XSTR("&Back", 995), XSTR("&Continue",780), XSTR("Warning\n\nThis pilot has played PXO games. If you continue and play a non-PXO game, your stats will not be updated", 1006)) <= 0 ? 0 : 1;
2473         return 1;
2474 }
2475
2476 void multi_join_blit_protocol()
2477 {
2478         gr_set_color_fast(&Color_bright);
2479
2480         switch(Socket_type){
2481         case NET_TCP:           
2482                 // straight TCP         
2483                 gr_string(5, 2, "TCP");         
2484                 break;
2485
2486         case NET_IPX:
2487                 gr_string(5, 2, "IPX");
2488                 break;
2489         }
2490 }
2491
2492
2493 // -------------------------------------------------------------------------------------------------
2494 //
2495 // MULTIPLAYER START GAME screen
2496 //
2497
2498 //XSTR:OFF
2499 // bitmap defs
2500 #define MULTI_SG_PALETTE                        "InterfacePalette"
2501
2502 static char *Multi_sg_bitmap_fname[GR_NUM_RESOLUTIONS] = {
2503         "MultiStartGame",                       // GR_640
2504         "2_MultiStartGame"                      // GR_1024
2505 };
2506
2507 static char *Multi_sg_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
2508         "MultiStartGame-M",                     // GR_640
2509         "2_MultiStartGame-M"                    // GR_1024
2510 };
2511
2512 //XSTR:ON
2513
2514 int Multi_sg_rank_max_display[GR_NUM_RESOLUTIONS] = {
2515         2,              // GR_640
2516         4               // GR_1024
2517 };
2518
2519 // constants for coordinate look ups
2520 #define MSG_X_COORD 0
2521 #define MSG_Y_COORD 1
2522 #define MSG_W_COORD 2
2523 #define MSG_H_COORD 3
2524
2525 // area definitions
2526
2527 // input password field
2528 int Msg_passwd_coords[GR_NUM_RESOLUTIONS][4] = {
2529         { // GR_640
2530                 36, 236, 408, 20
2531         },
2532         { // GR_1024
2533                 58, 377, 652, 32
2534         }
2535 };
2536
2537 // input game title field
2538 int Msg_title_coords[GR_NUM_RESOLUTIONS][4] = {
2539         { // GR_640
2540                 29, 49, 415, 23
2541         },
2542         { // GR_1024
2543                 46, 78, 664, 36
2544         }
2545 };
2546
2547 // rank selected field
2548 int Msg_rank_sel_coords[GR_NUM_RESOLUTIONS][4] = {
2549         { // GR_640
2550                 242, 254, 126, 12
2551         },
2552         { // GR_1024
2553                 242, 254, 126, 12
2554         }
2555 };
2556
2557 // rank list field
2558 int Msg_rank_list_coords[GR_NUM_RESOLUTIONS][4] = {
2559         { // GR_640
2560                 37, 297, 131, 16
2561         },
2562         { // GR_1024
2563                 60, 469, 652, 32
2564         }
2565 };
2566
2567
2568 // button defs
2569 #define MULTI_SG_NUM_BUTTONS    10
2570 #define MSG_OPEN_GAME                   0
2571 //#define MSG_CLOSED_GAME                       1
2572 //#define MSG_RESTRICTED_GAME           2
2573 #define MSG_PASSWD_GAME                 1
2574 #define MSG_RANK_SET_GAME               2
2575 #define MSG_RANK_SCROLL_UP              3
2576 #define MSG_RANK_SCROLL_DOWN    4
2577 #define MSG_RANK_ABOVE                  5
2578 #define MSG_RANK_BELOW                  6
2579 #define MSG_HELP                                        7
2580 #define MSG_OPTIONS                             8
2581 #define MSG_ACCEPT                              9
2582
2583 UI_WINDOW Multi_sg_window;                                                                                              // the window object for the join screen
2584 UI_BUTTON Multi_sg_rank_button;                                                                         // for selecting the rank marker
2585 UI_INPUTBOX     Multi_sg_game_name;                                                                             // for Netgame.name
2586 UI_INPUTBOX Multi_sg_game_passwd;                                                                       // for Netgame.passwd
2587 int Multi_sg_bitmap;                                                                                                            // the background bitmap
2588
2589 ui_button_info Multi_sg_buttons[GR_NUM_RESOLUTIONS][MULTI_SG_NUM_BUTTONS] = {
2590         { // GR_640
2591                 ui_button_info("MSG_00",        1,              184,    34,     191,    2),             // open
2592 //              ui_button_info("MSG_01",        1,              159,    34,     166,    1),             // closed
2593 //              ui_button_info("MSG_02",        1,              184,    34,     191,    2),             // restricted
2594                 ui_button_info("MSG_03",        1,              209,    34,     218,    3),             // password
2595                 ui_button_info("MSG_04",        1,              257,    34,     266,    4),             // rank set
2596                 ui_button_info("MSG_05",        1,              282,    -1,     -1,     5),             // rank scroll up
2597                 ui_button_info("MSG_06",        1,              307,    -1,     -1,     6),             // rank scroll down
2598                 ui_button_info("MSG_07",        177,    282,    210,    290,    7),             // rank above
2599                 ui_button_info("MSG_08",        177,    307,    210,    315,    8),             // rank below
2600                 ui_button_info("MSG_09",        536,    429,    500,    440,    9),             // help
2601                 ui_button_info("MSG_10",        536,    454,    479,    464,    10),            // options
2602                 ui_button_info("MSG_11",        576,    432,    571,    415,    11),            // accept
2603         },
2604         { // GR_1024
2605                 ui_button_info("2_MSG_00",      2,              295,    51,     307,    2),             // open
2606 //              ui_button_info("2_MSG_01",      2,              254,    51,     267,    1),             // closed
2607 //              ui_button_info("2_MSG_02",      2,              295,    51,     307,    2),             // restricted
2608                 ui_button_info("2_MSG_03",      2,              335,    51,     350,    3),             // password
2609                 ui_button_info("2_MSG_04",      2,              412,    51,     426,    4),             // rank set
2610                 ui_button_info("2_MSG_05",      2,              452,    -1,     -1,     5),             // rank scroll up
2611                 ui_button_info("2_MSG_06",      2,              492,    -1,     -1,     6),             // rank scroll down
2612                 ui_button_info("2_MSG_07",      284,    452,    335,    465,    7),             // rank above
2613                 ui_button_info("2_MSG_08",      284,    492,    335,    505,    8),             // rank below
2614                 ui_button_info("2_MSG_09",      858,    687,    817,    706,    9),             // help
2615                 ui_button_info("2_MSG_10",      858,    728,    797,    743,    10),            // options
2616                 ui_button_info("2_MSG_11",      921,    692,    921,    664,    11),            // accept
2617         },
2618 };
2619
2620 #define MULTI_SG_NUM_TEXT                       11
2621 UI_XSTR Multi_sg_text[GR_NUM_RESOLUTIONS][MULTI_SG_NUM_TEXT] = {
2622         { // GR_640
2623                 {"Open",                                        1322,           34,     191,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_sg_buttons[0][MSG_OPEN_GAME].button},
2624 //              {"Closed",                              1323,           34,     166,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_sg_buttons[0][MSG_CLOSED_GAME].button},
2625 //              {"Restricted",                  1324,           34,     191,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_sg_buttons[0][MSG_RESTRICTED_GAME].button},
2626                 {"Password Protected",  1325,   34,     218,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_sg_buttons[0][MSG_PASSWD_GAME].button},
2627                 {"Allow Rank",                  1326,           34,     266,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_sg_buttons[0][MSG_RANK_SET_GAME].button},
2628                 {"Above",                               1327,           210,    290,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_sg_buttons[0][MSG_RANK_ABOVE].button},
2629                 {"Below",                               1328,           210,    315,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_sg_buttons[0][MSG_RANK_BELOW].button},
2630                 {"Help",                                        928,            500,    440,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_sg_buttons[0][MSG_HELP].button},
2631                 {"Options",                             1036,           479,    464,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_sg_buttons[0][MSG_OPTIONS].button},
2632                 {"Accept",                              1035,           571,    415,    UI_XSTR_COLOR_PINK,     -1,     &Multi_sg_buttons[0][MSG_ACCEPT].button},
2633                 {"Start Game",                  1329,           26,     10,     UI_XSTR_COLOR_GREEN,    -1,     NULL},
2634                 {"Title",                               1330,           26,     31,     UI_XSTR_COLOR_GREEN,    -1,     NULL},
2635                 {"Game Type",                   1331,           12,     165,    UI_XSTR_COLOR_GREEN,    -1,     NULL},
2636         },
2637         { // GR_1024
2638                 {"Open",                                        1322,           51,     307,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_sg_buttons[1][MSG_OPEN_GAME].button},
2639 //              {"Closed",                              1323,           51,     267,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_sg_buttons[1][MSG_CLOSED_GAME].button},
2640 //              {"Restricted",                  1324,           51,     307,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_sg_buttons[1][MSG_RESTRICTED_GAME].button},
2641                 {"Password Protected",  1325,   51,     350,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_sg_buttons[1][MSG_PASSWD_GAME].button},
2642                 {"Allow Rank",                  1326,           51,     426,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_sg_buttons[1][MSG_RANK_SET_GAME].button},
2643                 {"Above",                               1327,           335,    465,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_sg_buttons[1][MSG_RANK_ABOVE].button},
2644                 {"Below",                               1328,           335,    505,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_sg_buttons[1][MSG_RANK_BELOW].button},
2645                 {"Help",                                        928,            817,    706,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_sg_buttons[1][MSG_HELP].button},
2646                 {"Options",                             1036,           797,    743,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_sg_buttons[1][MSG_OPTIONS].button},
2647                 {"Accept",                              1035,           921,    664,    UI_XSTR_COLOR_PINK,     -1,     &Multi_sg_buttons[1][MSG_ACCEPT].button},
2648                 {"Start Game",                  1329,           42,     22,     UI_XSTR_COLOR_GREEN,    -1,     NULL},
2649                 {"Title",                               1330,           42,     50,     UI_XSTR_COLOR_GREEN,    -1,     NULL},
2650                 {"Game Type",                   1331,           20,     264,    UI_XSTR_COLOR_GREEN,    -1,     NULL},
2651         }
2652 };
2653
2654 // starting index for displaying ranks
2655 int Multi_sg_rank_start;
2656 int Multi_sg_rank_select;
2657
2658 // netgame pointer to indirect through
2659 netgame_info *Multi_sg_netgame;
2660
2661 // hold temporary values in this structure when on a standalone server
2662 netgame_info Multi_sg_netgame_temp;
2663
2664 // forward declarations
2665 void multi_sg_check_buttons();
2666 void multi_sg_button_pressed(int n);
2667 void multi_sg_init_gamenet();
2668 void multi_sg_draw_radio_buttons();
2669 void multi_sg_rank_scroll_up();
2670 void multi_sg_rank_scroll_down();
2671 void multi_sg_rank_display_stuff();
2672 void multi_sg_rank_process_select();
2673 void multi_sg_rank_build_name(char *in,char *out);
2674 void multi_sg_check_passwd();
2675 void multi_sg_check_name();
2676 void multi_sg_release_passwd();
2677 int multi_sg_rank_select_valid(int rank);
2678 void multi_sg_select_rank_default();
2679
2680 // function which takes a rank name and returns the index.  Useful for commandline options
2681 // for above and below rank.  We return the index of the rank in the Ranks[] array.  If
2682 // the rank isn't found, we return -1
2683 int multi_start_game_rank_from_name( char *rank ) {
2684         int i;
2685
2686         for ( i = 0; i <= MAX_FREESPACE2_RANK; i++ ) {
2687                 if ( !stricmp(Ranks[i].name, rank) ) {
2688                         return i;
2689                 }
2690         }
2691
2692         return -1;
2693 }
2694
2695 void multi_start_game_init()
2696 {
2697         int idx;
2698
2699         // initialize the gamenet
2700         multi_sg_init_gamenet();
2701         
2702         // create the interface window
2703         Multi_sg_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
2704         Multi_sg_window.set_mask_bmap(Multi_sg_bitmap_mask_fname[gr_screen.res]);
2705
2706         // load the background bitmap
2707         Multi_sg_bitmap = bm_load(Multi_sg_bitmap_fname[gr_screen.res]);
2708         if(Multi_sg_bitmap < 0){
2709                 // we failed to load the bitmap - this is very bad
2710                 Int3();
2711         }
2712         
2713         // initialize the common notification messaging
2714         multi_common_notify_init();
2715
2716         // initialize the common text area
2717         multi_common_set_text("");
2718
2719         // use the common interface palette
2720         multi_common_set_palette();
2721         
2722         // create the interface buttons
2723         for(idx=0; idx<MULTI_SG_NUM_BUTTONS; idx++){
2724                 // create the object
2725                 Multi_sg_buttons[gr_screen.res][idx].button.create(&Multi_sg_window, "", Multi_sg_buttons[gr_screen.res][idx].x, Multi_sg_buttons[gr_screen.res][idx].y, 1, 1, 0, 1);
2726
2727                 // set the sound to play when highlighted
2728                 Multi_sg_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
2729
2730                 // set the ani for the button
2731                 Multi_sg_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sg_buttons[gr_screen.res][idx].filename);
2732
2733                 // set the hotspot
2734                 Multi_sg_buttons[gr_screen.res][idx].button.link_hotspot(Multi_sg_buttons[gr_screen.res][idx].hotspot);
2735         }       
2736
2737         // add all xstrs
2738         for(idx=0; idx<MULTI_SG_NUM_TEXT; idx++){
2739                 Multi_sg_window.add_XSTR(&Multi_sg_text[gr_screen.res][idx]);
2740         }
2741
2742         // load the help overlay
2743         help_overlay_load(MULTI_START_OVERLAY);
2744         help_overlay_set_state(MULTI_START_OVERLAY,0);
2745
2746         // intiialize the rank selection items  
2747         multi_sg_select_rank_default(); 
2748         Multi_sg_rank_start = Multi_sg_rank_select;
2749
2750         // create the rank select button
2751         Multi_sg_rank_button.create(&Multi_sg_window,"",Msg_rank_list_coords[gr_screen.res][MSG_X_COORD],Msg_rank_list_coords[gr_screen.res][MSG_Y_COORD],Msg_rank_list_coords[gr_screen.res][MSG_W_COORD],Msg_rank_list_coords[gr_screen.res][MSG_H_COORD],0,1);       
2752         Multi_sg_rank_button.hide();            
2753
2754         // create the netgame name input box
2755         Multi_sg_game_name.create(&Multi_sg_window,Msg_title_coords[gr_screen.res][MSG_X_COORD],Msg_title_coords[gr_screen.res][MSG_Y_COORD],Msg_title_coords[gr_screen.res][MSG_W_COORD],MAX_GAMENAME_LEN,"",UI_INPUTBOX_FLAG_ESC_CLR | UI_INPUTBOX_FLAG_INVIS,-1,&Color_normal);
2756
2757         // create the netgame password input box, and disable it by default
2758         Multi_sg_game_passwd.create(&Multi_sg_window,Msg_passwd_coords[gr_screen.res][MSG_X_COORD],Msg_passwd_coords[gr_screen.res][MSG_Y_COORD],Msg_passwd_coords[gr_screen.res][MSG_W_COORD],16,"",UI_INPUTBOX_FLAG_ESC_CLR | UI_INPUTBOX_FLAG_PASSWD | UI_INPUTBOX_FLAG_INVIS,-1,&Color_normal);
2759         Multi_sg_game_passwd.hide();
2760         Multi_sg_game_passwd.disable();
2761
2762         // set the netgame text to this gadget and make it have focus
2763         Multi_sg_game_name.set_text(Multi_sg_netgame->name);
2764         Multi_sg_game_name.set_focus(); 
2765
2766         // if starting a netgame, set the name of the game and any other options that are appropriate
2767         if ( Cmdline_start_netgame ) {
2768                 if ( Cmdline_game_name != NULL ) {
2769                         strcpy( Multi_sg_netgame->name, Cmdline_game_name );
2770                         Multi_sg_game_name.set_text(Multi_sg_netgame->name);
2771                 }
2772
2773                 // deal with the different game types -- only one should even be active, so we will just go down
2774                 // the line.  Last one wins.
2775                 if ( Cmdline_closed_game ) {
2776                         Multi_sg_netgame->mode = NG_MODE_CLOSED;
2777                 } else if ( Cmdline_restricted_game ) {
2778                         Multi_sg_netgame->mode = NG_MODE_RESTRICTED;
2779                 } else if ( Cmdline_game_password != NULL ) {
2780                         Multi_sg_netgame->mode = NG_MODE_PASSWORD;
2781                         strcpy(Multi_sg_netgame->passwd, Cmdline_game_password);
2782                         Multi_sg_game_passwd.set_text(Multi_sg_netgame->passwd);
2783                 }
2784
2785                 // deal with rank above and rank below
2786                 if ( (Cmdline_rank_above != NULL) || (Cmdline_rank_below != NULL) ) {
2787                         int rank;
2788                         char *rank_str;
2789
2790                         if ( Cmdline_rank_above != NULL ) {
2791                                 rank_str = Cmdline_rank_above;
2792                         } else {
2793                                 rank_str = Cmdline_rank_below;
2794                         }
2795
2796                         // try and get the rank index from the name -- if found, then set the rank base
2797                         // and the game type.  apparently we only support either above or below, not both
2798                         // together, so I make a random choice
2799                         rank = multi_start_game_rank_from_name( rank_str );
2800                         if ( rank != -1 ) {
2801                                 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
2802
2803                                 // now an arbitrary decision
2804                                 if ( Cmdline_rank_above != NULL ) {
2805                                         Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
2806                                 } else {
2807                                         Multi_sg_netgame->mode = NG_MODE_RANK_BELOW;
2808                                 }
2809                         }
2810                 }
2811
2812                 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
2813         }
2814 }
2815
2816 void multi_start_game_do()
2817 {
2818         // return here since we will be moving to the next stage anyway -- I don't want to see the backgrounds of
2819         // all the screens for < 1 second for every screen we automatically move to.
2820         if ( Cmdline_start_netgame ) {
2821                 return;
2822         }
2823
2824         int k = Multi_sg_window.process();
2825
2826         // process any keypresses
2827         switch(k){
2828         case KEY_ESC :          
2829                 if(help_overlay_active(MULTI_START_OVERLAY)){
2830                         help_overlay_set_state(MULTI_START_OVERLAY,0);
2831                 } else {
2832                         gamesnd_play_iface(SND_USER_SELECT);
2833                         multi_quit_game(PROMPT_NONE);
2834                 }
2835                 break;
2836         
2837         // same as ACCEPT
2838         case KEY_LCTRL + KEY_ENTER :
2839         case KEY_RCTRL + KEY_ENTER :            
2840                 gamesnd_play_iface(SND_COMMIT_PRESSED);
2841                 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
2842                 break;
2843         }       
2844
2845         if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
2846                 help_overlay_set_state(MULTI_START_OVERLAY, 0);
2847         }
2848
2849         // check to see if the user has selected a different rank
2850         multi_sg_rank_process_select();
2851
2852         // check any button presses
2853         multi_sg_check_buttons();
2854
2855         // check to see if any of the input boxes have changed, and update the appropriate Netgame fields if necessary
2856         multi_sg_check_passwd();
2857         multi_sg_check_name();
2858
2859         // draw the background, etc
2860         gr_reset_clip();
2861         GR_MAYBE_CLEAR_RES(Multi_sg_bitmap);
2862         if(Multi_sg_bitmap != -1){
2863                 gr_set_bitmap(Multi_sg_bitmap);
2864                 gr_bitmap(0,0);
2865         }
2866         Multi_sg_window.draw();
2867         
2868         // display rank stuff
2869         multi_sg_rank_display_stuff();
2870
2871         // display any pending notification messages
2872         multi_common_notify_do();
2873
2874         // draw all radio button
2875         multi_sg_draw_radio_buttons();
2876
2877         // draw the help overlay
2878         help_overlay_maybe_blit(MULTI_START_OVERLAY);
2879         
2880         // flip the buffer
2881         gr_flip();
2882 }
2883
2884 void multi_start_game_close()
2885 {
2886         // if i'm the host on a standalone server, send him my start game options (passwd, mode, etc)
2887         if((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
2888                 multi_options_update_start_game(Multi_sg_netgame);
2889         }
2890         
2891         // unload any bitmaps
2892         if(!bm_unload(Multi_sg_bitmap)){
2893                 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_sg_bitmap_fname[gr_screen.res]));
2894         }
2895
2896         // unload the help overlay
2897         help_overlay_unload(MULTI_START_OVERLAY);
2898         
2899         // destroy the UI_WINDOW
2900         Multi_sg_window.destroy();      
2901 }
2902
2903 void multi_sg_check_buttons()
2904 {
2905         int idx;
2906         for(idx=0;idx<MULTI_SG_NUM_BUTTONS;idx++){
2907                 // we only really need to check for one button pressed at a time, so we can break after 
2908                 // finding one.
2909                 if(Multi_sg_buttons[gr_screen.res][idx].button.pressed()){
2910                         multi_sg_button_pressed(idx);
2911                         break;
2912                 }
2913         }
2914 }
2915
2916 void multi_sg_button_pressed(int n)
2917 {
2918         switch(n){              
2919         // go to the options screen
2920         case MSG_OPTIONS:
2921                 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
2922                 break;  
2923
2924         // help overlay 
2925         case MSG_HELP:
2926                 if(!help_overlay_active(MULTI_START_OVERLAY)){
2927                         help_overlay_set_state(MULTI_START_OVERLAY,1);
2928                 } else {
2929                         help_overlay_set_state(MULTI_START_OVERLAY,0);
2930                 }
2931                 break;
2932
2933         // the open button was pressed
2934         case MSG_OPEN_GAME:             
2935                 // if the closed option is selected
2936                 if(Multi_sg_netgame->mode != NG_MODE_OPEN){
2937                         Multi_sg_netgame->mode = NG_MODE_OPEN;
2938
2939                         gamesnd_play_iface(SND_USER_SELECT);
2940                         
2941                         // release the password control if necessary
2942                         multi_sg_release_passwd();
2943                 }
2944                 // if its already selected
2945                 else {
2946                         gamesnd_play_iface(SND_GENERAL_FAIL);
2947                 }
2948                 break;
2949
2950                 /*
2951         // the open button was pressed
2952         case MSG_CLOSED_GAME:           
2953                 // if the closed option is selected
2954                 if(Multi_sg_netgame->mode != NG_MODE_CLOSED){
2955                         Multi_sg_netgame->mode = NG_MODE_CLOSED;
2956
2957                         gamesnd_play_iface(SND_USER_SELECT);
2958                         
2959                         // release the password control if necessary
2960                         multi_sg_release_passwd();
2961                 }
2962                 // if its already selected
2963                 else {
2964                         gamesnd_play_iface(SND_GENERAL_FAIL);
2965                 }
2966                 break;
2967 */
2968         // toggle password protection
2969         case MSG_PASSWD_GAME:           
2970                 // if we selected it
2971                 if(Multi_sg_netgame->mode != NG_MODE_PASSWORD){
2972                         gamesnd_play_iface(SND_USER_SELECT);            
2973
2974                         Multi_sg_game_passwd.enable();                  
2975                         Multi_sg_game_passwd.unhide();
2976                         Multi_sg_game_passwd.set_focus();
2977
2978                         Multi_sg_netgame->mode = NG_MODE_PASSWORD;                      
2979
2980                         // copy in the current network password
2981                         Multi_sg_game_passwd.set_text(Multi_sg_netgame->passwd);
2982                 } else {
2983                         gamesnd_play_iface(SND_GENERAL_FAIL);
2984                 }                       
2985                 break;
2986
2987 /*
2988         // toggle "restricted" on or off
2989         case MSG_RESTRICTED_GAME:
2990                 if(Multi_sg_netgame->mode != NG_MODE_RESTRICTED){
2991                         gamesnd_play_iface(SND_USER_SELECT);
2992                         Multi_sg_netgame->mode = NG_MODE_RESTRICTED;
2993
2994                         // release the password control if necessary
2995                         multi_sg_release_passwd();
2996                 } else {
2997                         gamesnd_play_iface(SND_GENERAL_FAIL);
2998                 }
2999                 break;
3000 */
3001         // turn off all rank requirements
3002         case MSG_RANK_SET_GAME:         
3003                 // if either is set, then turn then both off
3004                 if((Multi_sg_netgame->mode != NG_MODE_RANK_BELOW) && (Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE)){
3005                         gamesnd_play_iface(SND_USER_SELECT);
3006
3007                         // set it to the default case if we're turning it off
3008                         multi_sg_select_rank_default();
3009                         Multi_sg_rank_start = Multi_sg_rank_select;                     
3010
3011                         Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
3012
3013                         // release the password control if necessary
3014                         multi_sg_release_passwd();
3015                 } else {
3016                         gamesnd_play_iface(SND_GENERAL_FAIL);
3017                 }               
3018                 break;
3019
3020         // rank above was pressed
3021         case MSG_RANK_ABOVE :
3022                 if((Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE) || (Multi_sg_netgame->mode == NG_MODE_RANK_BELOW)){
3023                         Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
3024
3025                         // select the first item
3026                         multi_sg_select_rank_default();
3027                         Multi_sg_rank_start = Multi_sg_rank_select;
3028
3029                         // play a sound
3030                         gamesnd_play_iface(SND_USER_SELECT);                    
3031                 } else {
3032                         gamesnd_play_iface(SND_GENERAL_FAIL);
3033                 }
3034                 break;
3035
3036         // rank below was pressed
3037         case MSG_RANK_BELOW :
3038                 if((Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE) || (Multi_sg_netgame->mode == NG_MODE_RANK_BELOW)){
3039                         Multi_sg_netgame->mode = NG_MODE_RANK_BELOW;
3040                         
3041                         // select the first item
3042                         multi_sg_select_rank_default();
3043                         Multi_sg_rank_start = Multi_sg_rank_select;                     
3044
3045                         // play a sound
3046                         gamesnd_play_iface(SND_USER_SELECT);                    
3047                 } else {
3048                         gamesnd_play_iface(SND_GENERAL_FAIL);
3049                 }       
3050                 break;
3051
3052         // scroll the rank list up
3053         case MSG_RANK_SCROLL_UP:
3054                 multi_sg_rank_scroll_up();
3055                 break;
3056
3057         // scroll the rank list down
3058         case MSG_RANK_SCROLL_DOWN:
3059                 multi_sg_rank_scroll_down();
3060                 break;
3061
3062         // move to the create game screen
3063         case MSG_ACCEPT:
3064                 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
3065                 gamesnd_play_iface(SND_COMMIT_PRESSED);
3066                 break;
3067
3068         default :
3069                 gamesnd_play_iface(SND_GENERAL_FAIL);
3070                 multi_common_add_notify(XSTR("Not implemented yet!",760));              
3071                 break;
3072         }
3073 }
3074
3075 // NOTE : this is where all Netgame initialization should take place on the host
3076 void multi_sg_init_gamenet()
3077 {
3078         char buf[128],out_name[128];            
3079         net_addr save;
3080         net_player *server_save;        
3081
3082         // back this data up in case we are already connected to a standalone
3083         memcpy(&save,&Netgame.server_addr,sizeof(net_addr));
3084         server_save = Netgame.server;
3085
3086         // remove campaign flags
3087         Game_mode &= ~(GM_CAMPAIGN_MODE);
3088
3089         // clear out the Netgame structure and start filling in the values
3090         memset( &Netgame, 0, sizeof(Netgame) ); 
3091         memset( &Multi_sg_netgame_temp, 0, sizeof(netgame_info) );
3092         
3093         // if we're on the standalone, we're not the server, so we don't care about setting the netgame state
3094         if(Net_player->state != NETPLAYER_STATE_STD_HOST_SETUP){                
3095                 Netgame.game_state = NETGAME_STATE_HOST_SETUP;
3096                 Multi_sg_netgame = &Netgame;    
3097
3098                 // NETLOG
3099                 ml_string(NOX("Starting netgame as Host/Server"));              
3100         } else {
3101                 Multi_sg_netgame = &Multi_sg_netgame_temp;
3102
3103                 // NETLOG
3104                 in_addr temp_addr;
3105                 memcpy(&temp_addr.s_addr, &Netgame.server_addr, 4);
3106                 char *server_addr = inet_ntoa(temp_addr);                               
3107                 ml_printf(NOX("Starting netgame as Host on Standalone server : %s"), (server_addr == NULL) ? NOX("Unknown") : server_addr);
3108         }
3109         
3110         Net_player->tracker_player_id = Multi_tracker_id;
3111
3112         Multi_sg_netgame->security = (rand() % 32766) + 1;                      // get some random security number      
3113         Multi_sg_netgame->mode = NG_MODE_OPEN;
3114         Multi_sg_netgame->rank_base = RANK_ENSIGN;
3115         if(Multi_sg_netgame->security < 16){
3116                 Multi_sg_netgame->security += 16;
3117         }
3118
3119         // set the version_info field
3120         Multi_sg_netgame->version_info = NG_VERSION_ID;
3121         
3122         if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
3123                 Netgame.host = Net_player;
3124         }
3125         
3126         // set the default netgame flags
3127         Multi_sg_netgame->flags = 0;
3128
3129         // intialize endgame stuff
3130         multi_endgame_init();
3131
3132         // load in my netgame options
3133         multi_options_netgame_load(&Netgame.options);           
3134
3135         // load my local netplayer options
3136         multi_options_local_load(&Net_player->p_info.options, Net_player);      
3137         
3138         // setup the default game name, taking care of string length and player callsigns
3139         memset(out_name,0,128);
3140         memset(buf,0,128);
3141         pilot_format_callsign_personal(Player->callsign,out_name);
3142         sprintf(buf, XSTR("%s game",782), out_name);  // [[ %s will be a pilot's name ]]
3143         if ( strlen(buf) > MAX_GAMENAME_LEN ){
3144                 strcpy(buf, XSTR("Temporary name",783));
3145         }
3146         strcpy(Multi_sg_netgame->name, buf);
3147
3148         // set the default qos and duration
3149         multi_voice_maybe_update_vars(Netgame.options.voice_qos,Netgame.options.voice_record_time);
3150
3151         // make sure to set the server correctly (me or the standalone)
3152         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){         
3153                 memcpy(&Netgame.server_addr, &Psnet_my_addr,sizeof(net_addr));
3154                 Netgame.server = Net_player;
3155                 Net_player->player_id = multi_get_new_id();
3156
3157                 // setup debug flags
3158                 Netgame.debug_flags = 0;
3159                 /*
3160                 if(!Cmdline_server_firing){
3161                         Netgame.debug_flags |= NETD_FLAG_CLIENT_FIRING;
3162                 }
3163                 if(!Cmdline_client_dodamage){
3164                         Netgame.debug_flags |= NETD_FLAG_CLIENT_NODAMAGE;
3165                 }
3166                 */
3167         } else {
3168                 memcpy(&Netgame.server_addr,&save,sizeof(net_addr));
3169                 Netgame.server = server_save;
3170         }
3171
3172         // if I have a cd or not
3173         if(Multi_has_cd){
3174                 Net_player->flags |= NETINFO_FLAG_HAS_CD;
3175         }       
3176
3177         // if I have hacked data
3178         if(game_hacked_data()){
3179                 Net_player->flags |= NETINFO_FLAG_HAXOR;
3180         }
3181
3182         // assign my player struct and other data       
3183         Net_player->flags |= (NETINFO_FLAG_CONNECTED | NETINFO_FLAG_DO_NETWORKING);
3184         Net_player->s_info.voice_token_timestamp = -1;  
3185
3186         // if we're supposed to flush our cache directory, do so now
3187         if(Net_player->p_info.options.flags & MLO_FLAG_FLUSH_CACHE){
3188                 multi_flush_multidata_cache();
3189
3190                 // NETLOG
3191                 ml_string(NOX("Flushing multi-data cache"));
3192         }
3193                         
3194         game_flush();
3195 }
3196
3197 void multi_sg_draw_radio_buttons()
3198 {
3199         // draw the appropriate radio button
3200         switch(Multi_sg_netgame->mode){
3201         case NG_MODE_OPEN:
3202                 Multi_sg_buttons[gr_screen.res][MSG_OPEN_GAME].button.draw_forced(2);
3203                 break;
3204 /*
3205         case NG_MODE_CLOSED:
3206                 Multi_sg_buttons[gr_screen.res][MSG_CLOSED_GAME].button.draw_forced(2);
3207                 break;
3208                 */
3209         case NG_MODE_PASSWORD:
3210                 Multi_sg_buttons[gr_screen.res][MSG_PASSWD_GAME].button.draw_forced(2);
3211                 break;
3212 /*      case NG_MODE_RESTRICTED:
3213                 Multi_sg_buttons[gr_screen.res][MSG_RESTRICTED_GAME].button.draw_forced(2);
3214                 break;
3215                 */
3216         case NG_MODE_RANK_ABOVE:
3217                 Multi_sg_buttons[gr_screen.res][MSG_RANK_SET_GAME].button.draw_forced(2);
3218                 Multi_sg_buttons[gr_screen.res][MSG_RANK_ABOVE].button.draw_forced(2);
3219                 break;
3220         case NG_MODE_RANK_BELOW:
3221                 Multi_sg_buttons[gr_screen.res][MSG_RANK_SET_GAME].button.draw_forced(2);
3222                 Multi_sg_buttons[gr_screen.res][MSG_RANK_BELOW].button.draw_forced(2);
3223                 break;
3224         }
3225 }
3226
3227 void multi_sg_rank_scroll_up()
3228 {       
3229         // if he doesn't have either of the rank flags set, then ignore this
3230         if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3231                 return;
3232         }
3233
3234         if(Multi_sg_rank_start > 0){
3235                 Multi_sg_rank_start--;
3236                 gamesnd_play_iface(SND_SCROLL);
3237         } else {
3238                 gamesnd_play_iface(SND_GENERAL_FAIL);
3239         }
3240 }
3241
3242 void multi_sg_rank_scroll_down()
3243 {
3244         // if he doesn't have either of the rank flags set, then ignore this
3245         if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3246                 return;
3247         }
3248         
3249         if((NUM_RANKS - Multi_sg_rank_start) > Multi_sg_rank_max_display[gr_screen.res]){
3250                 Multi_sg_rank_start++;
3251                 gamesnd_play_iface(SND_SCROLL);
3252         } else {
3253                 gamesnd_play_iface(SND_GENERAL_FAIL);
3254         }       
3255 }
3256
3257 void multi_sg_rank_display_stuff()
3258 {
3259         int y,idx,count;
3260         char rank_name[40];
3261
3262         // if he doesn't have either of the rank flags set, then ignore this
3263         if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3264                 return;
3265         }
3266                 
3267         // display the list of ranks
3268         y = Msg_rank_list_coords[gr_screen.res][MSG_Y_COORD];
3269         idx = Multi_sg_rank_start;
3270         count = 0;
3271         while((count < NUM_RANKS) && (count < Multi_sg_rank_max_display[gr_screen.res]) && (idx < NUM_RANKS)){  
3272                 // if its the selected item, then color it differently
3273                 if(idx == Multi_sg_rank_select){
3274                         gr_set_color_fast(&Color_text_selected);
3275                 } else {
3276                         gr_set_color_fast(&Color_text_normal);
3277                 }
3278
3279                 // print the text
3280                 multi_sg_rank_build_name(Ranks[idx].name,rank_name);
3281                 gr_string(Msg_rank_list_coords[gr_screen.res][MSG_X_COORD],y,rank_name);
3282
3283                 // increment stuff
3284                 y+=10;
3285                 idx++;
3286                 count++;
3287         }
3288
3289         // display the selected rank
3290         /*
3291         gr_set_color_fast(&Color_bright);
3292         multi_sg_rank_build_name(Ranks[Multi_sg_netgame->rank_base].name,rank_name);
3293         gr_string(Msg_rank_sel_coords[gr_screen.res][MSG_X_COORD],Msg_rank_sel_coords[gr_screen.res][MSG_Y_COORD],rank_name);
3294         */
3295 }
3296
3297 void multi_sg_rank_process_select()
3298 {
3299         char string[255];
3300         
3301         // if he doesn't have either of the rank flags set, then ignore this
3302         if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3303                 return;
3304         }
3305         
3306         // see if he's clicked on an item on the rank list
3307         if(Multi_sg_rank_button.pressed()){              
3308                 int y,item;             
3309                 Multi_sg_rank_button.get_mouse_pos(NULL,&y);
3310                 item = y / 10;
3311                 
3312                 if(item + Multi_sg_rank_start < NUM_RANKS){             
3313                         // evaluate whether this rank is valid for the guy to pick              
3314                         if(multi_sg_rank_select_valid(item + Multi_sg_rank_start)){
3315                                 gamesnd_play_iface(SND_USER_SELECT);
3316
3317                                 Multi_sg_rank_select = item + Multi_sg_rank_start;                                              
3318
3319                                 // set the Netgame rank
3320                                 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
3321                         } else {
3322                                 gamesnd_play_iface(SND_GENERAL_FAIL);
3323
3324                                 memset(string,0,255);
3325                                 sprintf(string,XSTR("Illegal value for a host of your rank (%s)\n",784),Ranks[Net_player->player->stats.rank].name);
3326                                 multi_common_add_notify(string);
3327                         }
3328                 }               
3329         }
3330 }
3331
3332 void multi_sg_rank_build_name(char *in,char *out)
3333 {
3334         char use[100];
3335         char *first;
3336
3337         strcpy(use,in);
3338         first = strtok(use," ");
3339
3340         // just copy the string
3341         if(first == NULL){
3342                 strcpy(out,in);
3343         }
3344         
3345         // if the first part of the string is lieutenant, then abbreivate it and tack on the rest of the string 
3346         if (stricmp(first,XSTR("lieutenant",785)) == 0) {
3347                 first = strtok(NULL, NOX("\n"));
3348
3349                 // if he's not just a plain lieutenant
3350                 if(first != NULL){
3351                         strcpy(out,XSTR("Lt. ",786));  // [[ lieutenant ]]
3352                         strcat(out,first);
3353                 }
3354                 // if he _is_ just a plain lieutenant
3355                 else {
3356                         strcpy(out,in);
3357                 }
3358         } else {
3359                 strcpy(out,in);
3360         }
3361 }
3362
3363 void multi_sg_check_passwd()
3364 {
3365         // check to see if the password input box has been pressed
3366         if(Multi_sg_game_passwd.changed()){
3367                 Multi_sg_game_passwd.get_text(Multi_sg_netgame->passwd);
3368         }
3369 }
3370
3371 void multi_sg_check_name()
3372 {
3373         // check to see if the game name input box has been pressed
3374         if(Multi_sg_game_name.changed()){
3375                 Multi_sg_game_name.get_text(Multi_sg_netgame->name);
3376         }
3377 }
3378
3379 void multi_sg_release_passwd()
3380 {
3381         // hide and disable the password input box
3382         Multi_sg_game_passwd.hide();
3383         Multi_sg_game_passwd.disable();
3384
3385         // set the focus back to the name input box
3386         Multi_sg_game_name.set_focus();
3387 }
3388
3389 int multi_sg_rank_select_valid(int rank)
3390 {
3391         // rank above mode
3392         if(Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE){
3393                 if(Net_player->player->stats.rank >= rank){
3394                         return 1;
3395                 }
3396         }
3397         // rank below mode
3398         else {
3399                 if(Net_player->player->stats.rank <= rank){
3400                         return 1;
3401                 }
3402         }
3403         
3404         return 0;
3405 }
3406
3407 void multi_sg_select_rank_default()
3408 {
3409         // pick our rank for now
3410         Multi_sg_rank_select = Net_player->player->stats.rank;
3411
3412         // set the Netgame rank
3413         Multi_sg_netgame->rank_base = Multi_sg_rank_select;
3414 }
3415                 
3416 // -------------------------------------------------------------------------------------------------
3417 //
3418 // MULTIPLAYER CREATE GAME screen
3419 //
3420
3421 //XSTR:OFF
3422 // bitmaps defs
3423 char *Multi_create_bitmap_fname[GR_NUM_RESOLUTIONS] = {
3424         "MultiCreate",                  // GR_640
3425         "2_MultiCreate"         // GR_1024
3426 };
3427
3428 char *Multi_create_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
3429         "MultiCreate-M",                // GR_640
3430         "2_MultiCreate-M"               // GR_1024
3431 };
3432
3433 char *Multi_create_loading_fname[GR_NUM_RESOLUTIONS] = {
3434         "PleaseWait",                   // GR_640
3435         "2_PleaseWait"                  // GR_1024
3436 };
3437 //XSTR:ON
3438
3439 #define MULTI_CREATE_NUM_BUTTONS        23
3440
3441 // button defs
3442 #define MC_SHOW_ALL                                     0
3443 #define MC_SHOW_COOP                                    1
3444 #define MC_SHOW_TEAM                                    2
3445 #define MC_SHOW_DOGFIGHT                        3
3446 #define MC_PXO_REFRESH                          4
3447 #define MC_PILOT_INFO                           5
3448 #define MC_SCROLL_LIST_UP                       6 
3449 #define MC_SCROLL_LIST_DOWN             7
3450 #define MC_SCROLL_PLAYERS_UP            8
3451 #define MC_SCROLL_PLAYERS_DOWN  9
3452 #define MC_MISSION_FILTER                       10
3453 #define MC_CAMPAIGN_FILTER                      11
3454 #define MC_CANCEL                                               12
3455 #define MC_TEAM0                                                13
3456 #define MC_TEAM1                                                14
3457 #define MC_KICK                                         15
3458 #define MC_CLOSE                                                16
3459 #define MC_SCROLL_INFO_UP                       17
3460 #define MC_SCROLL_INFO_DOWN             18
3461 #define MC_HOST_OPTIONS                         19
3462 #define MC_HELP                                         20
3463 #define MC_OPTIONS                                      21
3464 #define MC_ACCEPT                                               22
3465
3466
3467 UI_WINDOW Multi_create_window;                                                                          // the window object for the create screen
3468 UI_BUTTON Multi_create_player_select_button;                                            // for selecting players
3469 UI_BUTTON Multi_create_list_select_button;                                              // for selecting missions/campaigns
3470 int Multi_create_bitmap;                                                                                                // the background bitmap
3471 UI_SLIDER2 Multi_create_slider;                                                                         // for create list
3472
3473 // constants for coordinate look ups
3474 #define MC_X_COORD 0
3475 #define MC_Y_COORD 1
3476 #define MC_W_COORD 2
3477 #define MC_H_COORD 3
3478
3479 ui_button_info Multi_create_buttons[GR_NUM_RESOLUTIONS][MULTI_CREATE_NUM_BUTTONS] = {   
3480         { // GR_640
3481                 ui_button_info("MC_00", 32,     129,    36,     158,    0),             // show all missions
3482                 ui_button_info("MC_01", 76,     129,    71,     158,    1),             // show coop missions
3483                 ui_button_info("MC_02", 121,    129,    119,    158,    2),             // show team missions
3484                 ui_button_info("MC_03", 164,    129,    166,    158,    3),             // show dogfight missions
3485                 ui_button_info("MC_04", 399,    129,    229,    130,    4),             // pxo mission refresh
3486                 ui_button_info("MC_05", 567,    123,    467,    132,    5),             // pilot info
3487                 ui_button_info("MC_06", 1,              161,    -1,     -1,     6),             // scroll mission info up
3488                 ui_button_info("MC_08", 1,              304,    -1,     -1,     8),             // scroll mission info down
3489                 ui_button_info("MC_09", 613,    160,    -1,     -1,     9),             // scroll players up
3490                 ui_button_info("MC_10", 613,    202,    -1,     -1,     10),            // scroll players down
3491                 ui_button_info("MC_11", 22,     346,    27,     376,    11),            // mission filter
3492                 ui_button_info("MC_12", 104,    346,    110,    376,    12),            // campaign filter
3493                 ui_button_info("MC_13", 392,    341,    328,    364,    13),            // cancel
3494                 ui_button_info("MC_14", 472,    352,    482,    381,    14),            // team 0       
3495                 ui_button_info("MC_15", 506,    352,    514,    381,    15),            // team 1
3496                 ui_button_info("MC_16", 539,    346,    539,    381,    16),            // kick
3497                 ui_button_info("MC_17", 589,    346,    582,    381,    17),            // close
3498                 ui_button_info("MC_18", 1,              406,    -1,     -1,     18),            // scroll list up
3499                 ui_button_info("MC_19", 1,              447,    -1,     -1,     19),            // scroll list down
3500                 ui_button_info("MC_20", 499,    434,    436,    423,    20),            // host options
3501                 ui_button_info("MC_21", 534,    426,    -1,     -1,     21),            // help
3502                 ui_button_info("MC_22", 534,    452,    -1,     -1,     22),            // options
3503                 ui_button_info("MC_23", 571,    426,    572,    413,    23),            // commit
3504         },      
3505         { // GR_1024
3506                 ui_button_info("2_MC_00", 51,           207,    61,     253,    0),             // show all missions
3507                 ui_button_info("2_MC_01", 122,  207,    124,    253,    1),             // show coop missions
3508                 ui_button_info("2_MC_02", 193,  207,    194,    253,    2),             // show team missions
3509                 ui_button_info("2_MC_03", 263,  207,    261,    253,    3),             // show dogfight missions
3510                 ui_button_info("2_MC_04", 639,  207,    479,    218,    4),             // pxo mission refresh
3511                 ui_button_info("2_MC_05", 907,  197,    748,    216,    5),             // pilot info
3512                 ui_button_info("2_MC_06", 1,            258,    -1,     -1,     6),             // scroll mission info up
3513                 ui_button_info("2_MC_08", 1,            487,    -1,     -1,     8),             // scroll mission info down
3514                 ui_button_info("2_MC_09", 981,  256,    -1,     -1,     9),             // scroll players up
3515                 ui_button_info("2_MC_10", 981,  323,    -1,     -1,     10),            // scroll players down
3516                 ui_button_info("2_MC_11",  35,  554,    46,     601,    11),            // mission filter
3517                 ui_button_info("2_MC_12", 166,  554,    174,    601,    12),            // campaign filter
3518                 ui_button_info("2_MC_13", 628,  545,    559,    582,    13),            // cancel
3519                 ui_button_info("2_MC_14", 756,  564,    772,    610,    14),            // team 0       
3520                 ui_button_info("2_MC_15", 810,  564,    826,    610,    15),            // team 1
3521                 ui_button_info("2_MC_16", 862,  554,    872,    610,    16),            // kick
3522                 ui_button_info("2_MC_17", 943,  554,    949,    610,    17),            // close
3523                 ui_button_info("2_MC_18", 1,            649,    -1,     -1,     18),            // scroll list up
3524                 ui_button_info("2_MC_19", 1,            716,    -1,     -1,     19),            // scroll list down
3525                 ui_button_info("2_MC_20", 798,  695,    726,    667,    20),            // host options
3526                 ui_button_info("2_MC_21", 854,  681,    -1,     -1,     21),            // help
3527                 ui_button_info("2_MC_22", 854,  724,    -1,     -1,     22),            // options
3528                 ui_button_info("2_MC_23", 914,  681,    932,    667,    23),            // commit
3529         },      
3530 };
3531
3532 #define MULTI_CREATE_NUM_TEXT                           15
3533 UI_XSTR Multi_create_text[GR_NUM_RESOLUTIONS][MULTI_CREATE_NUM_BUTTONS] = {
3534         { // GR_640
3535                 {"All",                                 1256,           36,     158,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[0][MC_SHOW_ALL].button},
3536                 {"Coop",                                        1257,           71,     158,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[0][MC_SHOW_COOP].button},
3537                 {"Team",                                        1258,           119,    158,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[0][MC_SHOW_TEAM].button},
3538                 {"Dogfight",                    1259,           166,    158,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[0][MC_SHOW_DOGFIGHT].button},
3539                 {"Refresh Missions",    1260,           229,    130,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[0][MC_PXO_REFRESH].button},
3540                 {"Pilot Info",                  1261,           467,    132,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[0][MC_PILOT_INFO].button},        
3541                 {"Missions",                    1262,           27,     376,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[0][MC_MISSION_FILTER].button},
3542                 {"Campaigns",                   1263,           110,    376,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[0][MC_CAMPAIGN_FILTER].button},
3543                 {"Cancel",                              387,            328,    364,    UI_XSTR_COLOR_PINK,     -1,     &Multi_create_buttons[0][MC_CANCEL].button},
3544                 {"1",                                           1264,           482,    381,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[0][MC_TEAM0].button},
3545                 {"2",                                           1265,           514,    381,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[0][MC_TEAM1].button},
3546                 {"Kick",                                        1266,           539,    381,    UI_XSTR_COLOR_PINK,     -1,     &Multi_create_buttons[0][MC_KICK].button},
3547                 {"Close",                               1508,           582,    381,    UI_XSTR_COLOR_PINK,     -1,     &Multi_create_buttons[0][MC_CLOSE].button},     
3548                 {"Host Options",                1267,           436,    423,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[0][MC_HOST_OPTIONS].button},              
3549                 {"Commit",                              1062,           572,    413,    UI_XSTR_COLOR_PINK,     -1,     &Multi_create_buttons[0][MC_ACCEPT].button}
3550         },              
3551         { // GR_1024            
3552                 {"All",                                 1256,           61,     253,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[1][MC_SHOW_ALL].button},
3553                 {"Coop",                                        1257,           124,    253,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[1][MC_SHOW_COOP].button},
3554                 {"Team",                                        1258,           194,    253,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[1][MC_SHOW_TEAM].button},
3555                 {"Dogfight",                    1259,           261,    253,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[1][MC_SHOW_DOGFIGHT].button},
3556                 {"Refresh Missions",    1260,           501,    218,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[1][MC_PXO_REFRESH].button},
3557                 {"Pilot Info",                  1261,           814,    216,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[1][MC_PILOT_INFO].button},        
3558                 {"Missions",                    1262,           46,     601,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[1][MC_MISSION_FILTER].button},
3559                 {"Campaigns",                   1263,           174,    601,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[1][MC_CAMPAIGN_FILTER].button},
3560                 {"Cancel",                              387,            559,    582,    UI_XSTR_COLOR_PINK,     -1,     &Multi_create_buttons[1][MC_CANCEL].button},
3561                 {"1",                                           1264,           772,    610,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[1][MC_TEAM0].button},
3562                 {"2",                                           1265,           826,    610,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[1][MC_TEAM1].button},
3563                 {"Kick",                                        1266,           872,    610,    UI_XSTR_COLOR_PINK,     -1,     &Multi_create_buttons[1][MC_KICK].button},
3564                 {"Close",                               1508,           949,    610,    UI_XSTR_COLOR_PINK,     -1,     &Multi_create_buttons[1][MC_CLOSE].button},     
3565                 {"Host Options",                1267,           755,    683,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[1][MC_HOST_OPTIONS].button},              
3566                 {"Commit",                              1062,           932,    667,    UI_XSTR_COLOR_PINK,     -1,     &Multi_create_buttons[1][MC_ACCEPT].button}
3567         },
3568 };
3569
3570 // squad war checkbox
3571 UI_CHECKBOX     Multi_create_sw_checkbox;
3572 char *Multi_create_sw_checkbox_fname[GR_NUM_RESOLUTIONS] = {
3573         "MC_SW_00",
3574         "MC_SW_00",
3575 };
3576 int Multi_create_sw_checkbox_coords[GR_NUM_RESOLUTIONS][2] = {
3577         { // GR_640
3578                 6, 75
3579         },
3580         { // GR_1024
3581                 18, 135
3582         }
3583 };
3584 int Multi_create_sw_checkbox_text[GR_NUM_RESOLUTIONS][2] = {
3585         { // GR_640
3586                 6, 95
3587         },
3588         { // GR_640
3589                 18, 155
3590         },
3591 };
3592
3593 // game information text areas
3594 int Mc_list_coords[GR_NUM_RESOLUTIONS][4] = {
3595         { // GR_640
3596                 105, 173, 311, 152
3597         },
3598         { // GR_1024
3599                 62, 275, 600, 262
3600         }
3601 };
3602
3603 int Mc_players_coords[GR_NUM_RESOLUTIONS][4] = {
3604         { // GR_640
3605                 463, 164, 144, 180
3606         },
3607         { // GR_1024
3608                 741, 262, 144, 180
3609         }
3610 };
3611
3612 int Mc_info_coords[GR_NUM_RESOLUTIONS][4] = {
3613         { // GR_640
3614                 47, 405, 363, 59
3615         },
3616         { // GR_1024
3617                 75, 648, 363, 59
3618         }
3619 };
3620
3621 // mission icon stuff
3622 int Mc_icon_type_coords[GR_NUM_RESOLUTIONS][2] = {
3623         { // GR_640
3624                 38, -2          // y is an offset
3625         },
3626         { // GR_1024
3627                 61, -2          // y is an offset
3628         }
3629 };
3630
3631 int Mc_icon_volition_coords[GR_NUM_RESOLUTIONS][2] = {
3632         { // GR_640
3633                 61, -1          // y is an offset
3634         },
3635         { // GR_1024
3636                 98, 1           // y is an offset
3637         }
3638 };
3639
3640 int Mc_icon_silent_coords[GR_NUM_RESOLUTIONS][2] = {
3641         { // GR_640
3642                 72, 0           // y is an offset
3643         },
3644         { // GR_1024
3645                 115, 0          // y is an offset
3646         }
3647 };
3648
3649 int Mc_icon_valid_coords[GR_NUM_RESOLUTIONS][2] = {
3650         { // GR_640
3651                 91, 0           // y is an offset
3652         },
3653         { // GR_1024
3654                 146, 0          // y is an offset
3655         }
3656 };
3657
3658 // mission/campaign list column areas
3659 int Mc_column1_w[GR_NUM_RESOLUTIONS] = {
3660         194,            // GR_640
3661         310             // GR_1024
3662 };
3663
3664 int Mc_column2_w[GR_NUM_RESOLUTIONS] = {
3665         38,             // GR_640
3666         61                      // GR_1024
3667 };
3668
3669 int Mc_column3_w[GR_NUM_RESOLUTIONS] = {
3670         77,             // GR_640
3671         123             // GR_1024
3672 };
3673
3674 int Mc_mission_name_x[GR_NUM_RESOLUTIONS] = {
3675         105,            // GR_640
3676         168             // GR_1024
3677 };
3678
3679 int Mc_mission_count_x[GR_NUM_RESOLUTIONS] = {
3680         314,            // GR_640
3681         502             // GR_1024
3682 };
3683
3684 int Mc_mission_fname_x[GR_NUM_RESOLUTIONS] = {
3685         337,            // GR_640
3686         539             // GR_1024
3687 };
3688
3689 int Mc_create_game_text[GR_NUM_RESOLUTIONS][2] = {
3690         {13, 116},      // GR_640
3691         {21, 186}       // GR_1024
3692 };
3693
3694 int Mc_players_text[GR_NUM_RESOLUTIONS][2] = {
3695         {467, 150},     // GR_640
3696         {747, 240}      // GR_1024
3697 };
3698
3699 int Mc_team_text[GR_NUM_RESOLUTIONS][2] = {
3700         {484, 342},     // GR_640
3701         {774, 547}      // GR_1024
3702 };
3703
3704 int Mc_slider_coords[GR_NUM_RESOLUTIONS][4] = {
3705         {
3706                 3, 197, 13, 105 // GR_640
3707         },
3708         {
3709                 5, 316, 20, 168 // GR_1024
3710         }
3711 };
3712
3713 char *Mc_slider_bitmap[GR_NUM_RESOLUTIONS] = {
3714         "slider",
3715         "2_slider"
3716 };
3717
3718 // player list control thingie defs
3719 #define MULTI_CREATE_PLIST_MAX_DISPLAY          20
3720 int Multi_create_plist_select_flag;                                                                     // flag indicating if we have a play selected
3721 short Multi_create_plist_select_id;                                                     // the net address of the currently selected player (for lookup)
3722
3723 // master tracker details
3724 int Multi_create_frame_count;                                                                                   // framecount
3725 int Multi_create_mt_tried_login;                                                                                // attempted to login this server on the MT
3726
3727 // mission filter settings                                                                              
3728 int Multi_create_filter;                                                                                                // what mode we're in
3729
3730 // game/campaign list control defs
3731 int Multi_create_list_max_display[GR_NUM_RESOLUTIONS] = {
3732         15,             // GR_640
3733         26                      // GR_1024
3734 };
3735
3736
3737 int Multi_create_list_count;                                                                                    // number of items in listbox
3738 int Multi_create_list_mode;                                                                                     // 0 == mission mode, 1 == campaign mode
3739 int Multi_create_list_start;                                                                                    // where to start displaying from
3740 int Multi_create_list_select;                                                                                   // which item is currently highlighted
3741 int Multi_create_files_loaded;
3742
3743 char Multi_create_files_array[MULTI_CREATE_MAX_LIST_ITEMS][MAX_FILENAME_LEN];
3744
3745 int Multi_create_mission_count;                                                                                 // how many we have
3746 int Multi_create_campaign_count;
3747 multi_create_info Multi_create_mission_list[MULTI_CREATE_MAX_LIST_ITEMS];
3748 multi_create_info Multi_create_campaign_list[MULTI_CREATE_MAX_LIST_ITEMS];
3749
3750 // use a pointer for the file list.  Will point to either the missions or the campaigns
3751 multi_create_info *Multi_create_file_list;
3752
3753 // LOCAL function definitions
3754 void multi_create_check_buttons();
3755 void multi_create_button_pressed(int n);
3756 void multi_create_init_as_server();
3757 void multi_create_init_as_client();
3758 void multi_create_do_netstuff();
3759 void multi_create_plist_scroll_up();
3760 void multi_create_plist_scroll_down();
3761 void multi_create_plist_process();
3762 void multi_create_plist_blit_normal();
3763 void multi_create_plist_blit_team();
3764 void multi_create_list_scroll_up();
3765 void multi_create_list_scroll_down();
3766 void multi_create_list_do();
3767 void multi_create_list_select_item(int n);
3768 void multi_create_list_blit_icons(int list_index, int y_start);
3769 void multi_create_accept_hit();
3770 void multi_create_draw_filter_buttons();
3771 void multi_create_set_selected_team(int team);
3772 short multi_create_get_mouse_id();
3773 int multi_create_ok_to_commit();
3774 int multi_create_verify_cds();
3775 void multi_create_refresh_pxo();
3776 void multi_create_sw_clicked();
3777
3778 // since we can selectively filter out mission/campaign types we always need to map a selected index (which is relative 
3779 // to the displayed list), to an absolute index (which is relative to the total file list - some of which may filtered out)
3780 void multi_create_select_to_filename(int select_index,char *filename);
3781 int multi_create_select_to_index(int select_index);
3782
3783 int Multi_create_should_show_popup = 0;
3784
3785
3786 // sorting function to sort mission lists.. Basic sorting on mission name
3787 int multi_create_sort_func(const void *a, const void *b)
3788 {
3789         multi_create_info *m1, *m2;
3790
3791         m1 = (multi_create_info *)a;
3792         m2 = (multi_create_info *)b;
3793
3794         return ( strcmp(m1->name, m2->name) );
3795 }
3796
3797 void multi_create_setup_list_data(int mode)
3798 {       
3799         int idx,should_sort,switched_modes;
3800         
3801         // set the current mode
3802         should_sort = 0;
3803         switched_modes = 0;
3804         if((Multi_create_list_mode != mode) && (mode != -1)){
3805                 Multi_create_list_mode = mode;  
3806                 switched_modes = 1;
3807
3808                 // set up the list pointers
3809                 if ( Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ) {
3810                         Multi_create_file_list = Multi_create_mission_list;                     
3811                 } else if ( Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ) {
3812                         Multi_create_file_list = Multi_create_campaign_list;                    
3813                 } else {
3814                         Int3();
3815                 }
3816         }
3817
3818         // get the mission count based upon the filter selected
3819         if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){               
3820                 switch(Multi_create_filter){
3821                 case MISSION_TYPE_MULTI:
3822                         Multi_create_list_count = Multi_create_mission_count;
3823                         break;
3824                 default : 
3825                         Multi_create_list_count = 0;
3826                         // find all missions which match 
3827                         for(idx=0;idx<Multi_create_mission_count;idx++){
3828                                 if(Multi_create_mission_list[idx].flags & Multi_create_filter){
3829                                         Multi_create_list_count++;
3830                                 }
3831                         }
3832
3833                         // if we switched modes and we have more than 0 items, sort them
3834                         if(switched_modes && (Multi_create_list_count > 0)){
3835                                 should_sort = 1;
3836                         }
3837                         break;
3838                 }
3839         } else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
3840                 switch(Multi_create_filter){
3841                 case MISSION_TYPE_MULTI:
3842                         Multi_create_list_count = Multi_create_campaign_count;
3843                         break;
3844                 default :
3845                         Multi_create_list_count = 0;
3846                         // find all missions which match 
3847                         for(idx=0;idx<Multi_create_campaign_count;idx++){
3848                                 if(Multi_create_campaign_list[idx].flags & Multi_create_filter){
3849                                         Multi_create_list_count++;
3850                                 }
3851                         }
3852
3853                         // if we switched modes and we have more than 0 items, sort them
3854                         if(switched_modes && (Multi_create_list_count > 0)){
3855                                 should_sort = 1;
3856                         }
3857                         break;
3858                 }
3859         }
3860         
3861         // reset the list start and selected indices
3862         Multi_create_list_start = 0;
3863         Multi_create_list_select = -1;
3864         multi_create_list_select_item(Multi_create_list_start); 
3865
3866         // sort the list of missions if necessary
3867         if( should_sort ) {             
3868                 qsort(Multi_create_file_list, Multi_create_list_count, sizeof(multi_create_info), multi_create_sort_func);              
3869         }
3870
3871         // reset the slider
3872         Multi_create_slider.set_numberItems(Multi_create_list_count > Multi_create_list_max_display[gr_screen.res] ? Multi_create_list_count-Multi_create_list_max_display[gr_screen.res] : 0);
3873 }
3874
3875 void multi_create_game_init()
3876 {
3877         int idx;
3878         ui_button_info *b;
3879         
3880         // now make sure to initialze various netgame stuff based upon whether we're on a standalone or not
3881         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3882                 multi_create_init_as_server();
3883         } else {
3884                 multi_create_init_as_client();
3885         }
3886
3887         // initialize the player list data              
3888         Multi_create_plist_select_flag = 0;
3889         Multi_create_plist_select_id = -1;      
3890
3891         // create the interface window
3892         Multi_create_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
3893         Multi_create_window.set_mask_bmap(Multi_create_bitmap_mask_fname[gr_screen.res]);
3894
3895         // load the background bitmap
3896         Multi_create_bitmap = bm_load(Multi_create_bitmap_fname[gr_screen.res]);
3897         if(Multi_create_bitmap < 0){
3898                 // we failed to load the bitmap - this is very bad
3899                 Int3();
3900         }
3901
3902         // close any previous existing instances of the chatbox and create a new one
3903         chatbox_close();
3904         chatbox_create();
3905
3906         // load the help overlay 
3907         help_overlay_load(MULTI_CREATE_OVERLAY);
3908         help_overlay_set_state(MULTI_CREATE_OVERLAY, 0);
3909
3910         // initialize the common notification messaging
3911         multi_common_notify_init();             
3912
3913         // use the common interface palette
3914         multi_common_set_palette();
3915
3916         // create the interface buttons
3917         for(idx=0; idx<MULTI_CREATE_NUM_BUTTONS; idx++){
3918                 b = &Multi_create_buttons[gr_screen.res][idx];
3919         
3920                 // create the object            
3921                 b->button.create(&Multi_create_window, "", b->x, b->y, 1, 1, ((idx == MC_SCROLL_LIST_UP) || (idx == MC_SCROLL_LIST_DOWN)), 1);
3922
3923                 // set the sound to play when highlighted
3924                 b->button.set_highlight_action(common_play_highlight_sound);
3925
3926                 // set the ani for the button
3927                 b->button.set_bmaps(b->filename);
3928
3929                 // set the hotspot
3930                 b->button.link_hotspot(b->hotspot);             
3931
3932                 // some special case stuff for the pxo refresh button
3933                 if(idx == MC_PXO_REFRESH){                      
3934                         // if not a PXO game, or if I'm not a server disable and hide the button
3935                         if(!MULTI_IS_TRACKER_GAME || !MULTIPLAYER_MASTER){
3936                                 b->button.hide();
3937                                 b->button.disable();
3938                         }                       
3939                 }
3940         }       
3941
3942         // create xstrs
3943         for(idx=0; idx<MULTI_CREATE_NUM_TEXT; idx++){
3944                 Multi_create_window.add_XSTR(&Multi_create_text[gr_screen.res][idx]);
3945         }
3946
3947         // if this is a PXO game, enable the squadwar checkbox  
3948         Multi_create_sw_checkbox.create(&Multi_create_window, "", Multi_create_sw_checkbox_coords[gr_screen.res][0], Multi_create_sw_checkbox_coords[gr_screen.res][1], 0);
3949         Multi_create_sw_checkbox.set_bmaps(Multi_create_sw_checkbox_fname[gr_screen.res], 6, 0);
3950         if(!MULTI_IS_TRACKER_GAME){
3951                 Multi_create_sw_checkbox.hide();
3952                 Multi_create_sw_checkbox.disable();
3953         }
3954         
3955 #ifdef FS2_DEMO
3956         // disable squad war button in demo
3957         Multi_create_sw_checkbox.hide();
3958         Multi_create_sw_checkbox.disable();
3959 #endif
3960
3961         // initialize the mission type filtering mode
3962         Multi_create_filter = MISSION_TYPE_MULTI;
3963
3964         // initialize the list mode, and load in a list
3965         memset(Multi_create_mission_list, 0, sizeof(multi_create_info) * MULTI_CREATE_MAX_LIST_ITEMS);
3966         memset(Multi_create_campaign_list, 0, sizeof(multi_create_info) * MULTI_CREATE_MAX_LIST_ITEMS);
3967         for(idx=0; idx<MULTI_CREATE_MAX_LIST_ITEMS; idx++){
3968                 Multi_create_mission_list[idx].valid_status = MVALID_STATUS_UNKNOWN;
3969                 Multi_create_campaign_list[idx].valid_status = MVALID_STATUS_UNKNOWN;
3970         }
3971         Multi_create_list_mode = MULTI_CREATE_SHOW_MISSIONS;
3972         Multi_create_list_start = -1;
3973         Multi_create_list_select = -1;
3974         Multi_create_list_count = 0;
3975
3976         Multi_create_slider.create(&Multi_create_window, Mc_slider_coords[gr_screen.res][MC_X_COORD], Mc_slider_coords[gr_screen.res][MC_Y_COORD], Mc_slider_coords[gr_screen.res][MC_W_COORD],Mc_slider_coords[gr_screen.res][MC_H_COORD], MULTI_CREATE_MAX_LIST_ITEMS, Mc_slider_bitmap[gr_screen.res], &multi_create_list_scroll_up, &multi_create_list_scroll_down, NULL);
3977
3978         // create the player list select button
3979         Multi_create_player_select_button.create(&Multi_create_window, "", Mc_players_coords[gr_screen.res][MC_X_COORD], Mc_players_coords[gr_screen.res][MC_Y_COORD], Mc_players_coords[gr_screen.res][MC_W_COORD], Mc_players_coords[gr_screen.res][MC_H_COORD], 0, 1);
3980         Multi_create_player_select_button.hide();               
3981         
3982         // create the mission/campaign list select button
3983         Multi_create_list_select_button.create(&Multi_create_window, "", Mc_list_coords[gr_screen.res][MC_X_COORD], Mc_list_coords[gr_screen.res][MC_Y_COORD], Mc_list_coords[gr_screen.res][MC_W_COORD], Mc_list_coords[gr_screen.res][MC_H_COORD], 0, 1);
3984         Multi_create_list_select_button.hide(); 
3985
3986         // set hotkeys for a couple of things.
3987         Multi_create_buttons[gr_screen.res][MC_ACCEPT].button.set_hotkey(KEY_CTRLED+KEY_ENTER); 
3988
3989         // init some master tracker stuff
3990         Multi_create_frame_count = 0;
3991         Multi_create_mt_tried_login = 0;
3992
3993         // remove campaign flags
3994         Game_mode &= ~(GM_CAMPAIGN_MODE);
3995         
3996         // send any pilots as appropriate
3997         multi_data_send_my_junk();
3998         Multi_create_file_list = Multi_create_mission_list;
3999
4000         Multi_create_campaign_count = 0;
4001         Multi_create_mission_count = 0;
4002         Multi_create_files_loaded = 0;  
4003 }
4004
4005 void multi_create_game_do()
4006 {
4007         int player_index;
4008         char *loading_str = XSTR("Loading", 1336);
4009         int str_w, str_h;
4010
4011         // set this if we want to show the pilot info popup
4012         Multi_create_should_show_popup = 0;
4013
4014         // first thing is to load the files
4015         if ( !Multi_create_files_loaded ) {
4016                 // if I am a client, send a list request to the server for the missions
4017                 if ( MULTIPLAYER_CLIENT ) {
4018                         send_mission_list_request( MISSION_LIST_REQUEST );
4019                 } else {
4020                         int loading_bitmap;
4021
4022                         loading_bitmap = bm_load(Multi_create_loading_fname[gr_screen.res]);
4023
4024                         // draw the background, etc
4025                         gr_reset_clip();
4026                         GR_MAYBE_CLEAR_RES(Multi_create_bitmap);
4027                         if(Multi_create_bitmap != -1){
4028                                 gr_set_bitmap(Multi_create_bitmap);
4029                                 gr_bitmap(0, 0);
4030                         }
4031                         chatbox_render();
4032                         if ( loading_bitmap > -1 ){
4033                                 gr_set_bitmap(loading_bitmap);
4034                         }
4035                         gr_bitmap( Please_wait_coords[gr_screen.res][MC_X_COORD], Please_wait_coords[gr_screen.res][MC_Y_COORD] );
4036
4037                         // draw "Loading" on it
4038                         gr_set_color_fast(&Color_normal);
4039                         gr_set_font(FONT2);
4040                         gr_get_string_size(&str_w, &str_h, loading_str);
4041                         gr_string((gr_screen.max_w - str_w) / 2, (gr_screen.max_h - str_h) / 2, loading_str);
4042                         gr_set_font(FONT1);
4043
4044                         gr_flip();
4045
4046                         multi_create_list_load_missions();
4047                         multi_create_list_load_campaigns();
4048
4049                         // if this is a tracker game, validate missions
4050                         if(MULTI_IS_TRACKER_GAME){
4051                                 multi_update_valid_missions();
4052                         }
4053
4054                         // update the file list
4055                         multi_create_setup_list_data(MULTI_CREATE_SHOW_MISSIONS);                                               
4056                 }
4057
4058                 // don't bother setting netgame state if ont the server
4059                 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4060                         Netgame.game_state = NETGAME_STATE_FORMING;
4061                         send_netgame_update_packet();
4062                 }       
4063
4064                 // if we're on the standalone we have to tell him that we're now in the host setup screen       
4065                 Net_player->state = NETPLAYER_STATE_HOST_SETUP; 
4066                 send_netplayer_update_packet();
4067
4068                 Multi_create_files_loaded = 1;
4069         }
4070
4071         int k = chatbox_process();
4072         k = Multi_create_window.process(k,0);
4073
4074         switch(k){      
4075         // same as the cancel button
4076         case KEY_ESC:
4077                 if(help_overlay_active(MULTI_CREATE_OVERLAY)){
4078                         help_overlay_set_state(MULTI_CREATE_OVERLAY,0);
4079                 } else {                
4080                         gamesnd_play_iface(SND_USER_SELECT);            
4081                         multi_quit_game(PROMPT_HOST);           
4082                 }
4083                 break;  
4084         }       
4085
4086         if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
4087                 help_overlay_set_state(MULTI_CREATE_OVERLAY, 0);
4088         }
4089
4090         // process any button clicks
4091         multi_create_check_buttons();
4092
4093         // do any network related stuff
4094         multi_create_do_netstuff(); 
4095
4096         // draw the background, etc
4097         gr_reset_clip();
4098         GR_MAYBE_CLEAR_RES(Multi_create_bitmap);
4099         if(Multi_create_bitmap != -1){
4100                 gr_set_bitmap(Multi_create_bitmap);
4101                 gr_bitmap(0,0);
4102         }
4103
4104         // if we're not in team vs. team mode, don't draw the team buttons
4105         if(!(Netgame.type_flags & NG_TYPE_TEAM)){
4106                 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.hide();
4107                 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.hide();
4108                 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.disable();
4109                 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.disable();
4110         } else {
4111                 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.enable();
4112                 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.enable();
4113                 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.unhide();
4114                 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.unhide();                          
4115         }               
4116
4117         // draw the window itself
4118         Multi_create_window.draw();
4119
4120         gr_set_color_fast(&Color_normal);
4121
4122         // draw Create Game text
4123         gr_string(Mc_create_game_text[gr_screen.res][MC_X_COORD], Mc_create_game_text[gr_screen.res][MC_Y_COORD], XSTR("Create Game", 1268));
4124
4125         // draw players text
4126         gr_string(Mc_players_text[gr_screen.res][MC_X_COORD], Mc_players_text[gr_screen.res][MC_Y_COORD], XSTR("Players", 1269));
4127
4128         // draw players text
4129         gr_string(Mc_team_text[gr_screen.res][MC_X_COORD], Mc_team_text[gr_screen.res][MC_Y_COORD], XSTR("Team", 1258));
4130
4131         // process and display the player list  
4132         // NOTE : this must be done before the buttons are checked to insure that a player hasn't dropped 
4133         multi_create_plist_process();
4134         if(Netgame.type_flags & NG_TYPE_TEAM){
4135                 multi_create_plist_blit_team();
4136         } else {
4137                 multi_create_plist_blit_normal();
4138         }
4139
4140         // process and display the game/campaign list
4141         multi_create_list_do();
4142         
4143         // draw the correct mission filter button
4144         multi_create_draw_filter_buttons();
4145
4146         // display any text in the info area
4147         multi_common_render_text();
4148
4149         // display any pending notification messages
4150         multi_common_notify_do();       
4151         
4152         // force the correct mission/campaign button to light up
4153         if( Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ){
4154                 Multi_create_buttons[gr_screen.res][MC_MISSION_FILTER].button.draw_forced(2);
4155         } else {
4156                 Multi_create_buttons[gr_screen.res][MC_CAMPAIGN_FILTER].button.draw_forced(2);
4157         }
4158
4159         // force draw the closed button if it is toggled on
4160         if(Netgame.flags & NG_FLAG_TEMP_CLOSED){
4161                 Multi_create_buttons[gr_screen.res][MC_CLOSE].button.draw_forced(2);
4162         }
4163
4164         // process and show the chatbox thingie 
4165         chatbox_render();
4166
4167         // draw tooltips
4168         Multi_create_window.draw_tooltip();
4169
4170         // display the voice status indicator
4171         multi_common_voice_display_status();
4172
4173         // blit the help overlay if necessary
4174         help_overlay_maybe_blit(MULTI_CREATE_OVERLAY);
4175
4176         // test code
4177         if(MULTI_IS_TRACKER_GAME){
4178                 if(Netgame.type_flags & NG_TYPE_SW){
4179                         gr_set_color_fast(&Color_bright);
4180                 } else {
4181                         gr_set_color_fast(&Color_normal);
4182                 }
4183                 gr_string(Multi_create_sw_checkbox_text[gr_screen.res][0], Multi_create_sw_checkbox_text[gr_screen.res][1], "SquadWar");
4184         }
4185
4186         // flip the buffer
4187         gr_flip();              
4188                 
4189         // if we're supposed to show the pilot info popup, do it now
4190         if(Multi_create_should_show_popup){             
4191                 // get the player index and address of the player item the mouse is currently over
4192                 if(Multi_create_plist_select_flag){             
4193                         player_index = find_player_id(Multi_create_plist_select_id);
4194                         if(player_index != -1){                 
4195                                 multi_pinfo_popup(&Net_players[player_index]);
4196                         }
4197                 }
4198         }
4199
4200         // increment the frame count
4201         Multi_create_frame_count++;     
4202 }
4203
4204 void multi_create_game_close()
4205 {
4206         // unload any bitmaps
4207         if(!bm_unload(Multi_create_bitmap)){
4208                 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_create_bitmap_fname[gr_screen.res]));
4209         }               
4210
4211         // unload the help overlay
4212         help_overlay_unload(MULTI_CREATE_OVERLAY);
4213         
4214         // destroy the chatbox
4215         // chatbox_close();
4216         
4217         // destroy the UI_WINDOW
4218         Multi_create_window.destroy();
4219 }
4220
4221 void multi_create_check_buttons()
4222 {
4223         int idx;
4224         for(idx=0;idx<MULTI_CREATE_NUM_BUTTONS;idx++){
4225                 // we only really need to check for one button pressed at a time, so we can break after 
4226                 // finding one.
4227                 if(Multi_create_buttons[gr_screen.res][idx].button.pressed()){
4228                         multi_create_button_pressed(idx);
4229                         break;
4230                 }
4231         }
4232
4233         // if the squad war checkbox was clicked
4234         if(Multi_create_sw_checkbox.changed()){
4235                 multi_create_sw_clicked();
4236         }
4237 }
4238
4239 void multi_create_button_pressed(int n)
4240 {
4241         int idx;
4242         
4243         switch(n){
4244         case MC_CANCEL :
4245                 gamesnd_play_iface(SND_USER_SELECT);            
4246                 multi_quit_game(PROMPT_HOST);           
4247                 break;
4248         case MC_ACCEPT :        
4249                 // if valid commit conditions have not been met
4250                 if(!multi_create_ok_to_commit()){
4251                         break;
4252                 }
4253
4254                 // commit
4255                 multi_create_accept_hit();              
4256                 break;
4257
4258         // help button
4259         case MC_HELP :
4260                 if(!help_overlay_active(MULTI_CREATE_OVERLAY)){
4261                         help_overlay_set_state(MULTI_CREATE_OVERLAY,1);
4262                 } else {
4263                         help_overlay_set_state(MULTI_CREATE_OVERLAY,0);
4264                 }
4265                 break;
4266
4267         // scroll the info text box up
4268         case MC_SCROLL_INFO_UP:
4269                 multi_common_scroll_text_up();
4270                 break;
4271
4272         // scroll the info text box down
4273         case MC_SCROLL_INFO_DOWN:
4274                 multi_common_scroll_text_down();
4275                 break;
4276
4277         // scroll the player list up
4278         case MC_SCROLL_PLAYERS_UP:
4279                 multi_create_plist_scroll_up();
4280                 break;
4281
4282         // scroll the player list down
4283         case MC_SCROLL_PLAYERS_DOWN:
4284                 multi_create_plist_scroll_down();
4285                 break;
4286
4287         // scroll the game/campaign list up
4288         case MC_SCROLL_LIST_UP:
4289                 multi_create_list_scroll_up();
4290                 Multi_create_slider.forceUp();  // move slider up
4291                 break;
4292
4293         // scroll the game/campaign list down
4294         case MC_SCROLL_LIST_DOWN:
4295                 multi_create_list_scroll_down();
4296                 Multi_create_slider.forceDown();        // move slider down
4297                 break;
4298
4299         // go to the options screen
4300         case MC_OPTIONS:                
4301                 gamesnd_play_iface(SND_USER_SELECT);
4302                 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
4303                 break;  
4304
4305         // show all missions
4306         case MC_SHOW_ALL:
4307                 if(Multi_create_filter != MISSION_TYPE_MULTI){
4308                         gamesnd_play_iface(SND_USER_SELECT);
4309                         Multi_create_filter = MISSION_TYPE_MULTI;
4310                         multi_create_setup_list_data(Multi_create_list_mode);                                           // update the file list
4311                 } else {
4312                         gamesnd_play_iface(SND_GENERAL_FAIL);
4313                 }
4314                 break;
4315
4316         // show cooperative missions
4317         case MC_SHOW_COOP:
4318                 if(Multi_create_filter != MISSION_TYPE_MULTI_COOP){
4319                         gamesnd_play_iface(SND_USER_SELECT);
4320                         Multi_create_filter = MISSION_TYPE_MULTI_COOP;                  
4321                         multi_create_setup_list_data(Multi_create_list_mode);                                           // update the file list
4322                 } else {
4323                         gamesnd_play_iface(SND_GENERAL_FAIL);
4324                 }
4325                 break;
4326
4327         // show team vs. team missions
4328         case MC_SHOW_TEAM:
4329                 if(Multi_create_filter != MISSION_TYPE_MULTI_TEAMS){
4330                         gamesnd_play_iface(SND_USER_SELECT);
4331                         Multi_create_filter = MISSION_TYPE_MULTI_TEAMS; 
4332                         multi_create_setup_list_data(Multi_create_list_mode);                                           // update the file list
4333                 } else {
4334                         gamesnd_play_iface(SND_GENERAL_FAIL);
4335                 }
4336                 break;  
4337
4338         // show dogfight missions
4339         case MC_SHOW_DOGFIGHT:
4340                 if (Multi_create_filter != MISSION_TYPE_MULTI_DOGFIGHT){
4341                         gamesnd_play_iface(SND_USER_SELECT);
4342                         Multi_create_filter = MISSION_TYPE_MULTI_DOGFIGHT;
4343                         multi_create_setup_list_data(Multi_create_list_mode);                                           // update the file list
4344                 } else {
4345                         gamesnd_play_iface(SND_GENERAL_FAIL);
4346                 }
4347                 break;
4348
4349         // toggle temporary netgame closed on/off
4350         case MC_CLOSE:
4351                 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4352                         Netgame.flags ^= NG_FLAG_TEMP_CLOSED;
4353                 } else {
4354                         Netgame.options.flags |= MLO_FLAG_TEMP_CLOSED;
4355                         multi_options_update_netgame();
4356                 }
4357                 gamesnd_play_iface(SND_USER_SELECT);
4358                 break;
4359
4360         // kick the currently selected player (if possible)
4361         case MC_KICK:
4362                 // lookup the player at the specified index             
4363                 if(Multi_create_plist_select_flag){              
4364                         idx = find_player_id(Multi_create_plist_select_id);
4365                         // kick him - but don't ban him
4366                         if(idx != -1){                  
4367                                 multi_kick_player(idx,0);                               
4368                         }
4369                 }
4370                 break;
4371                         
4372         // switch to individual mission mode and load in a list
4373         case MC_MISSION_FILTER:
4374                 if(Multi_create_list_mode != MULTI_CREATE_SHOW_MISSIONS){
4375                         Netgame.campaign_mode = MP_SINGLE;
4376
4377                         gamesnd_play_iface(SND_USER_SELECT);                                                                                            
4378                         
4379                         // update the file list
4380                         multi_create_setup_list_data(MULTI_CREATE_SHOW_MISSIONS);                                               
4381                 } else {
4382                         gamesnd_play_iface(SND_GENERAL_FAIL);
4383                 }
4384                 break;
4385
4386         // switch to campaign mode and load in a list
4387         case MC_CAMPAIGN_FILTER:                
4388                 // switch off squad war
4389                 Multi_create_sw_checkbox.set_state(0);
4390                 Netgame.type_flags = NG_TYPE_COOP;
4391
4392                 if(Multi_create_list_mode != MULTI_CREATE_SHOW_CAMPAIGNS){
4393                         Netgame.campaign_mode = MP_CAMPAIGN;
4394
4395                         gamesnd_play_iface(SND_USER_SELECT);                    
4396                         
4397                         // update the file list
4398                         multi_create_setup_list_data(MULTI_CREATE_SHOW_CAMPAIGNS);                                              
4399                 } else {
4400                         gamesnd_play_iface(SND_GENERAL_FAIL);
4401                 }
4402                 break;
4403
4404         // attempt to set the selected player's team
4405         case MC_TEAM0:
4406                 multi_create_set_selected_team(0);
4407                 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4408                         multi_team_send_update();
4409                 }
4410                 break;
4411
4412         // attempt to set the selected player's team
4413         case MC_TEAM1:
4414                 multi_create_set_selected_team(1);
4415                 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4416                         multi_team_send_update();
4417                 }
4418                 break;  
4419
4420         // popup the pilot info dialog for the currently selected pilot (will occur at the end of the frame)
4421         case MC_PILOT_INFO:
4422                 Multi_create_should_show_popup = 1;
4423                 break;
4424
4425         // go to the host options screen
4426         case MC_HOST_OPTIONS:
4427                 gamesnd_play_iface(SND_USER_SELECT);
4428                 gameseq_post_event(GS_EVENT_MULTI_HOST_OPTIONS);
4429                 break;
4430
4431         // refresh PXO file list
4432         case MC_PXO_REFRESH:            
4433                 if(!MULTI_IS_TRACKER_GAME){
4434                         break;
4435                 }
4436                 multi_create_refresh_pxo();             
4437                 break;
4438
4439         default :
4440                 gamesnd_play_iface(SND_GENERAL_FAIL);
4441                 multi_common_add_notify(XSTR("Not implemented yet!",760));              
4442                 break;
4443         }
4444 }
4445
4446 // do stuff like pinging servers, sending out requests, etc
4447 void multi_create_do_netstuff()
4448 {
4449 }
4450
4451 // if not on a standalone
4452 void multi_create_init_as_server()
4453 {
4454         // set me up as the host and master
4455         Net_player->flags |= (NETINFO_FLAG_AM_MASTER | NETINFO_FLAG_GAME_HOST);         
4456 }
4457
4458 // if on a standalone
4459 void multi_create_init_as_client()
4460 {
4461         Net_player->flags |= NETINFO_FLAG_GAME_HOST;    
4462 }
4463
4464 // scroll up through the player list
4465 void multi_create_plist_scroll_up()
4466 {       
4467         gamesnd_play_iface(SND_GENERAL_FAIL);
4468 }
4469
4470 // scroll down through the player list
4471 void multi_create_plist_scroll_down()
4472 {       
4473         gamesnd_play_iface(SND_GENERAL_FAIL);
4474 }
4475
4476 void multi_create_plist_process()
4477 {
4478         int test_count,idx,player_index;
4479         
4480         // first determine if there are 0 players in the game. This should never happen since the host is _always_ in the game
4481         test_count = 0;
4482         for(idx=0;idx<MAX_PLAYERS;idx++){
4483                 // count anyone except the standalone server (if applicable)
4484                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
4485                         test_count++;
4486                 }
4487         }
4488         if(test_count <= 0){
4489                 return;
4490         }
4491         
4492         // if we had a selected item but that player has left, select myself instead
4493         if(Multi_create_plist_select_flag){
4494                 player_index = find_player_id(Multi_create_plist_select_id);
4495                 if(player_index == -1){
4496                         Multi_create_plist_select_id = Net_player->player_id;
4497                 }
4498         } else {
4499                 Multi_create_plist_select_flag = 1;
4500                 Multi_create_plist_select_id = Net_player->player_id;           
4501         }       
4502                 
4503         // if the player has clicked somewhere in the player list area
4504         if(Multi_create_player_select_button.pressed()){                                
4505                 short player_id;
4506
4507                 // get the player index and address of the player item the mouse is currently over
4508                 player_id = multi_create_get_mouse_id();
4509                 player_index = find_player_id(player_id);
4510                 if(player_index != -1){
4511                         Multi_create_plist_select_flag = 1;
4512                         Multi_create_plist_select_id = player_id;                       
4513                 } 
4514         }               
4515 }
4516
4517 void multi_create_plist_blit_normal()
4518 {
4519         int idx;                
4520         char str[CALLSIGN_LEN+5];
4521         int y_start = Mc_players_coords[gr_screen.res][MC_Y_COORD];     
4522         int total_offset;
4523
4524         // display all the players      
4525         for(idx=0;idx<MAX_PLAYERS;idx++){               
4526                 // count anyone except the standalone server (if applicable)
4527                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
4528                         // total x offset
4529                         total_offset = 0;
4530
4531                         // highlight him if he's the host                       
4532                         if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4533                                 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4534                                         gr_set_color_fast(&Color_text_active_hi);
4535                                 } else {
4536                                         gr_set_color_fast(&Color_bright);
4537                                 }
4538                         } else {
4539                                 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4540                                         gr_set_color_fast(&Color_text_active);
4541                                 } else {
4542                                         gr_set_color_fast(&Color_text_normal);
4543                                 }
4544                         }
4545                         
4546                         // optionally draw his CD status
4547                         if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4548                                 gr_set_bitmap(Multi_common_icons[MICON_CD]);
4549                                 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4550
4551                                 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4552                         }                       
4553                         
4554                         // make sure the string will fit, then display it
4555                         strcpy(str,Net_players[idx].player->callsign);
4556                         if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4557                                 strcat(str,XSTR("(O)",787));  // [[ Observer ]]
4558                         }
4559                         gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4560                         gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4561
4562                         y_start += 10;                  
4563                 }
4564         }               
4565 }
4566
4567 void multi_create_plist_blit_team()
4568 {
4569         int idx;                
4570         char str[CALLSIGN_LEN+1];
4571         int y_start = Mc_players_coords[gr_screen.res][MC_Y_COORD];     
4572         int total_offset;
4573
4574         // display all the red players first
4575         for(idx=0;idx<MAX_PLAYERS;idx++){
4576                 // count anyone except the standalone server (if applicable)
4577                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
4578                         // reset total offset
4579                         total_offset = 0;
4580
4581                         // highlight him if he's the host                       
4582                         if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4583                                 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4584                                         gr_set_color_fast(&Color_text_active_hi);
4585
4586                                         // be sure to blit the correct team button 
4587                                         Multi_create_buttons[gr_screen.res][MC_TEAM0].button.draw_forced(2);
4588                                 } else {
4589                                         gr_set_color_fast(&Color_bright);
4590                                 }
4591                         } else {
4592                                 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4593                                         gr_set_color_fast(&Color_text_active);
4594
4595                                         // be sure to blit the correct team button 
4596                                         Multi_create_buttons[gr_screen.res][MC_TEAM0].button.draw_forced(2);
4597                                 } else {
4598                                         gr_set_color_fast(&Color_text_normal);
4599                                 }
4600                         }
4601
4602                         // optionally draw his CD status
4603                         if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4604                                 gr_set_bitmap(Multi_common_icons[MICON_CD]);                            
4605                                 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4606
4607                                 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4608                         }                       
4609
4610                         // blit the red team indicator                  
4611                         if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
4612                                 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
4613                                         gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT]);
4614                                         gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4615
4616                                         total_offset += Multi_common_icon_dims[MICON_TEAM0_SELECT][0] + 1;                      
4617                                 }
4618                         } else {
4619                                 if(Multi_common_icons[MICON_TEAM0] != -1){
4620                                         gr_set_bitmap(Multi_common_icons[MICON_TEAM0]);
4621                                         gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4622
4623                                         total_offset += Multi_common_icon_dims[MICON_TEAM0][0] + 1;                     
4624                                 }                               
4625                         }                                               
4626
4627                         // make sure the string will fit
4628                         strcpy(str,Net_players[idx].player->callsign);
4629                         if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4630                                 strcat(str,XSTR("(O)",787));
4631                         }
4632                         gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4633
4634                         // display him in the correct half of the list depending on his team
4635                         gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4636                         y_start += 10;
4637                 }
4638         }       
4639         
4640         // display all the green players next
4641         for(idx=0;idx<MAX_PLAYERS;idx++){
4642                 // count anyone except the standalone server (if applicable)
4643                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
4644                         // reset total offset
4645                         total_offset = 0;
4646
4647                         // highlight him if he's the host                       
4648                         if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4649                                 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4650                                         gr_set_color_fast(&Color_text_active_hi);
4651
4652                                         // be sure to blit the correct team button 
4653                                         Multi_create_buttons[gr_screen.res][MC_TEAM1].button.draw_forced(2);
4654                                 } else {
4655                                         gr_set_color_fast(&Color_bright);
4656                                 }
4657                         } else {
4658                                 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4659                                         gr_set_color_fast(&Color_text_active);
4660
4661                                         // be sure to blit the correct team button 
4662                                         Multi_create_buttons[gr_screen.res][MC_TEAM1].button.draw_forced(2);
4663                                 } else {
4664                                         gr_set_color_fast(&Color_text_normal);
4665                                 }
4666                         }
4667
4668                         // optionally draw his CD status
4669                         if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4670                                 gr_set_bitmap(Multi_common_icons[MICON_CD]);
4671                                 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4672
4673                                 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4674                         }                       
4675
4676                         // blit the red team indicator                  
4677                         if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
4678                                 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
4679                                         gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT]);
4680                                         gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4681
4682                                         total_offset += Multi_common_icon_dims[MICON_TEAM1_SELECT][0] + 1;
4683                                 }                               
4684                         } else {
4685                                 if(Multi_common_icons[MICON_TEAM1] != -1){
4686                                         gr_set_bitmap(Multi_common_icons[MICON_TEAM1]);
4687                                         gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4688
4689                                         total_offset += Multi_common_icon_dims[MICON_TEAM1][0] + 1;
4690                                 }
4691                         }
4692
4693                         // make sure the string will fit
4694                         strcpy(str,Net_players[idx].player->callsign);
4695                         if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4696                                 strcat(str,XSTR("(O)",787));
4697                         }
4698                         gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4699
4700                         // display him in the correct half of the list depending on his team
4701                         gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4702                         y_start += 10;
4703                 }
4704         }                       
4705 }
4706
4707 void multi_create_list_scroll_up()
4708 {
4709         if(Multi_create_list_start > 0){
4710                 Multi_create_list_start--;              
4711
4712                 gamesnd_play_iface(SND_SCROLL);
4713         } else {
4714                 gamesnd_play_iface(SND_GENERAL_FAIL);
4715         }
4716 }
4717
4718 void multi_create_list_scroll_down()
4719 {
4720         if((Multi_create_list_count - Multi_create_list_start) > Multi_create_list_max_display[gr_screen.res]){
4721                 Multi_create_list_start++;              
4722
4723                 gamesnd_play_iface(SND_SCROLL);
4724         } else {
4725                 gamesnd_play_iface(SND_GENERAL_FAIL);
4726         }
4727 }
4728
4729 // gets a list of multiplayer misisons
4730 void multi_create_list_load_missions()
4731 {
4732         char *fname, mission_name[NAME_LENGTH+1];
4733         char wild_card[256];
4734         int file_count,idx;
4735
4736         memset(wild_card, 0, 256);
4737         strcpy(wild_card, NOX("*"));
4738         strcat(wild_card, FS_MISSION_FILE_EXT);
4739         file_count = cf_get_file_list_preallocated(MULTI_CREATE_MAX_LIST_ITEMS, Multi_create_files_array, NULL, CF_TYPE_MISSIONS, wild_card);
4740         Multi_create_mission_count = 0;
4741
4742         // maybe create a standalone dialog
4743         if(Game_mode & GM_STANDALONE_SERVER){
4744                 std_create_gen_dialog("Loading missions");
4745                 std_gen_set_text("Mission:", 1);
4746         }
4747
4748         for(idx = 0; idx < file_count; idx++){
4749                 int flags,max_players;
4750                 char *filename;
4751                 uint m_respawn;         
4752
4753                 fname = Multi_create_files_array[idx];
4754                 
4755                 // tack on any necessary file extension
4756                 filename = cf_add_ext( fname, FS_MISSION_FILE_EXT );
4757
4758                 // for multiplayer beta builds, only accept builtin missions
4759 #if defined(MULTIPLAYER_BETA_BUILD) || defined(FS2_DEMO)
4760                 if(game_find_builtin_mission(filename) == NULL){
4761                         continue;
4762                 }
4763 #elif defined(PD_BUILD)
4764                 if((game_find_builtin_mission(filename) == NULL) && !strstr(filename, "peterdrake")){
4765                         continue;
4766                 }
4767 #endif
4768
4769                 if(Game_mode & GM_STANDALONE_SERVER){                   
4770                         std_gen_set_text(filename, 2);
4771                 }
4772
4773                 flags = mission_parse_is_multi(filename, mission_name);         
4774
4775                 // if the mission is a multiplayer mission, then add it to the mission list
4776                 if ( flags ) {
4777                         max_players = mission_parse_get_multi_mission_info( filename );                         
4778                         m_respawn = The_mission.num_respawns;
4779
4780                         if ( Multi_create_mission_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
4781                                 multi_create_info *mcip;
4782
4783                                 mcip = &Multi_create_mission_list[Multi_create_mission_count];                          
4784                                 strcpy(mcip->filename, filename );
4785                                 strcpy(mcip->name, mission_name );
4786                                 mcip->flags = flags;
4787                                 mcip->respawn = m_respawn;
4788                                 mcip->max_players = (ubyte)max_players;
4789
4790                                 // get any additional information for possibly builtin missions
4791                                 fs_builtin_mission *fb = game_find_builtin_mission(filename);
4792                                 if(fb != NULL){                                 
4793                                 }
4794
4795                                 Multi_create_mission_count++;
4796                         }
4797                 }
4798         }
4799
4800         Multi_create_slider.set_numberItems(Multi_create_mission_count > Multi_create_list_max_display[gr_screen.res] ? Multi_create_mission_count-Multi_create_list_max_display[gr_screen.res] : 0);
4801
4802         // maybe create a standalone dialog
4803         if(Game_mode & GM_STANDALONE_SERVER){
4804                 std_destroy_gen_dialog();               
4805         }
4806 }
4807
4808 void multi_create_list_load_campaigns()
4809 {       
4810         char *fname;
4811         int idx, file_count;
4812         int campaign_type,max_players;
4813         char title[255];
4814         char wild_card[256];
4815
4816         // maybe create a standalone dialog
4817         if(Game_mode & GM_STANDALONE_SERVER){
4818                 std_create_gen_dialog("Loading campaigns");
4819                 std_gen_set_text("Campaign:", 1);
4820         }
4821
4822         Multi_create_campaign_count = 0;
4823         memset(wild_card, 0, 256);
4824         strcpy(wild_card, NOX("*"));
4825         strcat(wild_card, FS_CAMPAIGN_FILE_EXT);
4826         file_count = cf_get_file_list_preallocated(MULTI_CREATE_MAX_LIST_ITEMS, Multi_create_files_array, NULL, CF_TYPE_MISSIONS, wild_card);
4827         for(idx = 0; idx < file_count; idx++){
4828                 int flags;
4829                 char *filename, name[NAME_LENGTH];
4830
4831                 fname = Multi_create_files_array[idx];
4832                 
4833                 // tack on any necessary file extension
4834                 filename = cf_add_ext( fname, FS_CAMPAIGN_FILE_EXT );
4835
4836                 // for multiplayer beta builds, only accept builtin missions
4837 #if defined(MULTIPLAYER_BETA_BUILD) || defined(FS2_DEMO)
4838                 if(game_find_builtin_mission(filename) == NULL){
4839                         continue;
4840                 }
4841 #elif defined(PD_BUILD)
4842                 if((game_find_builtin_mission(filename) == NULL) && !strstr(filename, "peterdrake")){
4843                         continue;
4844                 }
4845 #endif
4846
4847                 if(Game_mode & GM_STANDALONE_SERVER){                   
4848                         std_gen_set_text(filename, 2);
4849                 }
4850
4851                 // if the campaign is a multiplayer campaign, then add the data to the campaign list items
4852                 flags = mission_campaign_parse_is_multi( filename, name );
4853                 if( flags != CAMPAIGN_TYPE_SINGLE && mission_campaign_get_info(filename,title,&campaign_type,&max_players)) {
4854                         if ( Multi_create_campaign_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
4855                                 multi_create_info *mcip;
4856
4857                                 mcip = &Multi_create_campaign_list[Multi_create_campaign_count];
4858                                 strcpy(mcip->filename, filename );
4859                                 strcpy(mcip->name, name );
4860                                 
4861                                 // setup various flags
4862                                 if ( flags == CAMPAIGN_TYPE_MULTI_COOP ){
4863                                         mcip->flags = MISSION_TYPE_MULTI_COOP | MISSION_TYPE_MULTI;
4864                                 } else if ( flags == CAMPAIGN_TYPE_MULTI_TEAMS ) {
4865                                         mcip->flags = MISSION_TYPE_MULTI_TEAMS | MISSION_TYPE_MULTI;
4866                                 } else {
4867                                         Int3();                 // bogus campaign multi type -- find allender
4868                                 }                                                       
4869
4870                                 // 0 respawns for campaign files (should be contained within the mission files themselves)
4871                                 mcip->respawn = 0;
4872
4873                                 // 0 max players for campaign files
4874                                 mcip->max_players = (unsigned char)max_players;
4875
4876                                 // get any additional information for possibly builtin missions
4877                                 fs_builtin_mission *fb = game_find_builtin_mission(filename);
4878                                 if(fb != NULL){                                 
4879                                 }
4880
4881                                 Multi_create_campaign_count++;
4882                         }
4883                 }
4884         }       
4885         
4886         // maybe create a standalone dialog
4887         if(Game_mode & GM_STANDALONE_SERVER){
4888                 std_destroy_gen_dialog();               
4889         }
4890 }
4891
4892 void multi_create_list_do()
4893 {
4894         int idx;
4895         int start_index,stop_index;
4896         char selected_name[255];
4897
4898         // bail early if there aren't any selectable items
4899         if(Multi_create_list_count == 0){
4900                 return;
4901         }
4902         
4903         // first check to see if the user has clicked on an item
4904         if(Multi_create_list_select_button.pressed()){           
4905                 int y,item;                             
4906                 Multi_create_list_select_button.get_mouse_pos(NULL,&y);
4907                 item = (y / 10);
4908
4909                 // make sure we are selectedin valid indices
4910                 if((item < Multi_create_list_max_display[gr_screen.res]) && (item >= 0)){                                       
4911                         item += Multi_create_list_start;                
4912
4913                         if(item < Multi_create_list_count){             
4914                                 multi_create_list_select_item(item);
4915                                 gamesnd_play_iface(SND_IFACE_MOUSE_CLICK);
4916                         }               
4917                 }
4918         }       
4919
4920         // bail early if we don't have a start position
4921         if(Multi_create_list_start == -1){
4922                 return;
4923         }
4924
4925         // display the list of individual campaigns/missions
4926         int count = 0;
4927         int y_start = Mc_list_coords[gr_screen.res][MC_Y_COORD];                
4928
4929         start_index = multi_create_select_to_index(Multi_create_list_start);
4930         stop_index = Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ? Multi_create_mission_count : Multi_create_campaign_count;
4931         for(idx=start_index; idx<stop_index; idx++){
4932                 // see if we should drop out
4933                 if(count == Multi_create_list_max_display[gr_screen.res]){
4934                         break;
4935                 }
4936
4937                 // see if we should filter out this mission
4938                 if ( !(Multi_create_file_list[idx].flags & Multi_create_filter) ){
4939                         continue;
4940                 }
4941                 
4942                 // highlight the selected item
4943                 multi_create_select_to_filename(Multi_create_list_select,selected_name);
4944                 if(!strcmp(selected_name,Multi_create_file_list[idx].filename)){                
4945                         gr_set_color_fast(&Color_text_selected);
4946                 } else {
4947                         gr_set_color_fast(&Color_text_normal);
4948                 }               
4949
4950                 // draw the type icon           
4951                 multi_create_list_blit_icons(idx, y_start);             
4952                 
4953                 // force fit the mission name string
4954                 strcpy(selected_name,Multi_create_file_list[idx].name);
4955                 gr_force_fit_string(selected_name,255,Mc_column1_w[gr_screen.res]);
4956                 gr_string(Mc_mission_name_x[gr_screen.res],y_start,selected_name);
4957
4958                 // draw the max players if in mission mode              
4959                 sprintf(selected_name,"%d",(int)Multi_create_file_list[idx].max_players);
4960                 gr_string(Mc_mission_count_x[gr_screen.res],y_start,selected_name);             
4961
4962                 // force fit the mission filename string
4963                 strcpy(selected_name,Multi_create_file_list[idx].filename);
4964                 gr_force_fit_string(selected_name,255,Mc_column3_w[gr_screen.res]);
4965                 gr_string(Mc_mission_fname_x[gr_screen.res],y_start,selected_name);
4966
4967                 y_start+=10;
4968                 count++;
4969         }
4970 }
4971
4972 // takes care of stuff like changing indices around and setting up the netgame structure
4973 void multi_create_list_select_item(int n)
4974 {
4975         int abs_index,campaign_type,max_players;
4976         char title[NAME_LENGTH+1];
4977         netgame_info ng_temp;
4978         netgame_info *ng;
4979
4980         char *campaign_desc;
4981
4982         // if not on the standalone server
4983         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4984                 ng = &Netgame;  
4985         }
4986         // on the standalone
4987         else {
4988                 memset(&ng_temp,0,sizeof(netgame_info));
4989                 ng = &ng_temp;
4990         }
4991         
4992         if ( n != Multi_create_list_select ) {
4993                 // check to see if this is a valid index, and bail if it is not
4994                 abs_index = multi_create_select_to_index(n);
4995                 if(abs_index == -1){
4996                         return;
4997                 }
4998
4999                 Multi_create_list_select = n;
5000                                 
5001                 // set the mission name
5002                 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5003                         multi_create_select_to_filename(n,ng->mission_name);            
5004                 } else {
5005                         multi_create_select_to_filename(n,ng->campaign_name);
5006                 }
5007
5008                 // make sure the netgame type is properly set
5009                 int old_type = Netgame.type_flags;
5010                 abs_index = multi_create_select_to_index(n);
5011                 if(abs_index != -1){
5012                         if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_TEAMS){
5013                                 // if we're in squad war mode, leave it as squad war
5014                                 if(old_type & NG_TYPE_SW){
5015                                         ng->type_flags = NG_TYPE_SW;
5016                                 } else {
5017                                         ng->type_flags = NG_TYPE_TVT;
5018                                 }
5019                         } else if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_COOP){
5020                                 ng->type_flags = NG_TYPE_COOP;
5021                         } else if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_DOGFIGHT){
5022                                 ng->type_flags = NG_TYPE_DOGFIGHT;
5023                         }
5024                 }
5025
5026                 // if we're no longer in a TvT game, just uncheck the squadwar checkbox
5027                 if(!(ng->type_flags & NG_TYPE_TEAM)){
5028                         Multi_create_sw_checkbox.set_state(0);
5029                 }
5030
5031                 // if we switched from something else to team vs. team mode, do some special processing
5032                 if((ng->type_flags & NG_TYPE_TEAM) && (ng->type_flags != old_type) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
5033                         multi_team_reset();
5034                 }
5035
5036                 switch(Multi_create_list_mode){
5037                 case MULTI_CREATE_SHOW_MISSIONS:                
5038                         // don't forget to update the info box window thingie
5039                         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){                 
5040                                 ship_init();            // mwa -- 10/15/97.  Call this function to reset number of ships in mission
5041                                 ng->max_players = mission_parse_get_multi_mission_info( ng->mission_name );                             
5042                                 
5043                                 Assert(ng->max_players > 0);
5044                                 strcpy(ng->title,The_mission.name);                                                             
5045
5046                                 // set the information area text
5047                                 multi_common_set_text(The_mission.mission_desc);
5048                         }
5049                         // if we're on the standalone, send a request for the description
5050                         else {
5051                                 send_netgame_descript_packet(&Netgame.server_addr,0);
5052                                 multi_common_set_text("");
5053                         }
5054
5055                         // set the respawns as appropriate
5056                         if(Netgame.options.respawn <= Multi_create_file_list[abs_index].respawn){
5057                                 ng->respawn = Netgame.options.respawn;
5058                                 nprintf(("Network","Using netgame options for respawn count (%d %d)\n",Netgame.options.respawn,Multi_create_file_list[abs_index].respawn));
5059                         } else {
5060                                 ng->respawn = Multi_create_file_list[abs_index].respawn;
5061                                 nprintf(("Network","Using mission settings for respawn count (%d %d)\n",Netgame.options.respawn,Multi_create_file_list[abs_index].respawn));
5062                         }
5063                         break;
5064                 case MULTI_CREATE_SHOW_CAMPAIGNS:
5065                         // if not on the standalone server
5066                         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5067                                 // get the campaign info                                
5068                                 memset(title,0,NAME_LENGTH+1);
5069                                 if(!mission_campaign_get_info(ng->campaign_name,title,&campaign_type,&max_players, &campaign_desc)) {
5070                                         memset(ng->campaign_name,0,NAME_LENGTH+1);
5071                                         ng->max_players = 0;
5072                                 }
5073                                 // if we successfully got the # of players
5074                                 else {
5075                                         memset(ng->title,0,NAME_LENGTH+1);
5076                                         strcpy(ng->title,title);
5077                                         ng->max_players = max_players;                                  
5078                                 }
5079
5080                                 nprintf(("Network","MC MAX PLAYERS : %d\n",ng->max_players));
5081
5082                                 // set the information area text
5083                                 // multi_common_set_text(ng->title);
5084                                 multi_common_set_text(campaign_desc);
5085                         }
5086                         // if on the standalone server, send a request for the description
5087                         else {
5088                                 // no descriptions currently kept for campaigns
5089                         }
5090
5091                         // netgame respawns are always 0 for campaigns (until the first mission is loaded)
5092                         ng->respawn = 0;
5093                         break;
5094                 }
5095
5096                 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5097                         // update players 
5098                         send_netgame_update_packet();                   
5099
5100                         // update all machines about stuff like respawns, etc.
5101                         multi_options_update_netgame();
5102                 } else {
5103                         multi_options_update_mission(ng, Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ? 1 : 0);
5104                 }
5105         }
5106 }
5107
5108 void multi_create_list_blit_icons(int list_index, int y_start)
5109 {
5110         multi_create_info *mcip;
5111         fs_builtin_mission *fb; 
5112         int max_index;
5113
5114         // get a pointer to the list item
5115         max_index = Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ? Multi_create_mission_count - 1 : Multi_create_campaign_count - 1;
5116         if((list_index < 0) || (list_index > max_index)){
5117                 return;
5118         }       
5119         mcip = &Multi_create_file_list[list_index];
5120
5121         // blit the multiplayer type icons
5122         if(mcip->flags & MISSION_TYPE_MULTI_COOP){
5123                 if(Multi_common_icons[MICON_COOP] >= 0){
5124                         gr_set_bitmap(Multi_common_icons[MICON_COOP]);
5125                         gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5126                 }
5127         } else if(mcip->flags & MISSION_TYPE_MULTI_TEAMS){
5128                 if(Multi_common_icons[MICON_TVT] >= 0){
5129                         gr_set_bitmap(Multi_common_icons[MICON_TVT]);
5130                         gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5131                 }
5132         } else if(mcip->flags & MISSION_TYPE_MULTI_DOGFIGHT){
5133                 if(Multi_common_icons[MICON_DOGFIGHT] >= 0){
5134                         gr_set_bitmap(Multi_common_icons[MICON_DOGFIGHT]);
5135                         gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5136                 }
5137         } 
5138
5139         // if its a valid mission, blit the valid mission icon
5140         if(MULTI_IS_TRACKER_GAME && (mcip->valid_status == MVALID_STATUS_VALID)){
5141                 if(Multi_common_icons[MICON_VALID] >= 0){
5142                         gr_set_bitmap(Multi_common_icons[MICON_VALID]);
5143                         gr_bitmap(Mc_icon_valid_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_valid_coords[gr_screen.res][MC_Y_COORD]);
5144                 }
5145         }
5146
5147         // now see if its a builtin mission
5148         fb = game_find_builtin_mission(mcip->filename); 
5149         // if the mission is from volition, blit the volition icon
5150         if((fb != NULL) && (fb->flags & FSB_FROM_VOLITION)){
5151                 if(Multi_common_icons[MICON_VOLITION] >= 0){
5152                         gr_set_bitmap(Multi_common_icons[MICON_VOLITION]);
5153                         gr_bitmap(Mc_icon_volition_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_volition_coords[gr_screen.res][MC_Y_COORD]);
5154                 }
5155         }       
5156 }
5157
5158 void multi_create_accept_hit()
5159 {
5160         char selected_name[255];
5161         int start_campaign = 0; 
5162
5163         // make sure all players have finished joining
5164         if(!multi_netplayer_state_check(NETPLAYER_STATE_JOINED,1)){
5165                 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("Please wait until all clients have finished joining",788));
5166                 gamesnd_play_iface(SND_GENERAL_FAIL);
5167                 return;
5168         } else {
5169                 gamesnd_play_iface(SND_COMMIT_PRESSED);
5170         }       
5171         
5172         // do single mission stuff
5173         switch(Multi_create_list_mode){
5174         case MULTI_CREATE_SHOW_MISSIONS:        
5175                 if(Multi_create_list_select != -1){
5176                         // set the netgame mode
5177                         Netgame.campaign_mode = MP_SINGLE;
5178
5179                         // setup various filenames and mission names
5180                         multi_create_select_to_filename(Multi_create_list_select,selected_name);
5181                         strncpy( Game_current_mission_filename, selected_name, MAX_FILENAME_LEN );
5182                         strncpy(Netgame.mission_name,selected_name,MAX_FILENAME_LEN);                   
5183
5184                         // NETLOG
5185                         ml_printf(NOX("Starting single mission %s, with %d players"), Game_current_mission_filename, multi_num_players());
5186                 } else {
5187                         multi_common_add_notify(XSTR("No mission selected!",789));
5188                         return ;
5189                 }
5190                 break;
5191
5192         case MULTI_CREATE_SHOW_CAMPAIGNS:
5193                 // do campaign related stuff    
5194                 if(Multi_create_list_select != -1){
5195                         // set the netgame mode
5196                         Netgame.campaign_mode = MP_CAMPAIGN;
5197
5198                         // start a campaign instead of a single mission
5199                         multi_create_select_to_filename(Multi_create_list_select,selected_name);
5200                         multi_campaign_start(selected_name);                    
5201                         start_campaign = 1;
5202
5203                         // NETLOG
5204                         ml_printf(NOX("Starting campaign %s, with %d players"), selected_name, multi_num_players());
5205                 } else {
5206                         multi_common_add_notify(XSTR("No campaign selected!",790));
5207                         return ;
5208                 }
5209                 break;
5210         }
5211
5212         // if this is a team vs team situation, lock the players send a final team update
5213         if((Netgame.type_flags & NG_TYPE_TEAM) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
5214                 multi_team_host_lock_all();
5215                 multi_team_send_update();
5216         }
5217
5218         // if not on the standalone, move to the mission sync state which will take care of everything
5219         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5220                 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
5221                 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);                                
5222         } 
5223         // otherwise tell the standalone to do so
5224         else {
5225                 // when the standalone receives this, he'll do the mission syncing himself
5226                 send_mission_sync_packet(MULTI_SYNC_PRE_BRIEFING,start_campaign);
5227         }                               
5228 }
5229
5230 void multi_create_draw_filter_buttons()
5231 {
5232         // highlight the correct filter button
5233         if ( Multi_create_filter == MISSION_TYPE_MULTI ){
5234                 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL].button.draw_forced(2);
5235         } else if ( Multi_create_filter == MISSION_TYPE_MULTI_COOP ) {
5236                 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 1].button.draw_forced(2);
5237         } else if ( Multi_create_filter == MISSION_TYPE_MULTI_TEAMS ) {
5238                 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 2].button.draw_forced(2);
5239         } else if ( Multi_create_filter == MISSION_TYPE_MULTI_DOGFIGHT ){
5240                 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 3].button.draw_forced(2);
5241         } else {
5242                 Int3();
5243         }
5244 }
5245
5246 void multi_create_set_selected_team(int team)
5247 {       
5248         int player_index;
5249         
5250         // if we don't currently have a player selected, don't do anything
5251         if(!Multi_create_plist_select_flag){
5252                 gamesnd_play_iface(SND_GENERAL_FAIL);
5253                 return;
5254         }
5255         gamesnd_play_iface(SND_USER_SELECT);
5256
5257         // otherwise attempt to set the team for this guy       
5258         player_index = find_player_id(Multi_create_plist_select_id);
5259         if(player_index != -1){ 
5260                 multi_team_set_team(&Net_players[player_index],team);           
5261         }
5262 }
5263
5264 void multi_create_handle_join(net_player *pl)
5265 {
5266         // for now just play a bloop sound
5267         gamesnd_play_iface(SND_ICON_DROP_ON_WING);
5268 }
5269
5270 // fill in net address of player the mouse is over, return player index (or -1 if none)
5271 short multi_create_get_mouse_id()
5272 {
5273         // determine where he clicked (y pixel value)
5274         int y,nth,idx;          
5275         Multi_create_player_select_button.get_mouse_pos(NULL,&y);
5276
5277         // select things a little differently if we're in team vs. team or non-team vs. team mode                       
5278         nth = (y / 10);                 
5279         if(Netgame.type_flags & NG_TYPE_TEAM){
5280                 int player_index = -1;
5281
5282                 // look through all of team red first
5283                 for(idx=0;idx<MAX_PLAYERS;idx++){
5284                         if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
5285                                 nth--;
5286
5287                                 // if this is the _nth_ guy 
5288                                 if(nth < 0){
5289                                         player_index = idx;                                             
5290                                         break;
5291                                 }
5292                         }
5293                 }
5294                         
5295                 // if we still haven't found him yet, look through the green team
5296                 if(player_index == -1){
5297                         for(idx=0;idx<MAX_PLAYERS;idx++){                                       
5298                                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){                            
5299                                         nth--;
5300                                         // if this is the _nth_ guy 
5301                                         if(nth < 0){
5302                                                 player_index = idx;                                             
5303                                                 break;
5304                                         }
5305                                 }
5306                         }
5307                 }
5308
5309                 if(player_index != -1){
5310                         return Net_players[player_index].player_id;
5311                 }
5312         } else {
5313                 // select the nth active player if possible, disregarding the standalone server
5314                 for(idx=0;idx<MAX_PLAYERS;idx++){
5315                         if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
5316                                 nth--;
5317
5318                                 // if this is the _nth_ guy 
5319                                 if(nth < 0){
5320                                         return Net_players[idx].player_id;                                      
5321                                 }                               
5322                         }
5323                 }
5324         }                               
5325         
5326         return -1;
5327 }
5328
5329 void multi_create_select_to_filename(int select_index,char *filename)
5330 {
5331         int idx;
5332
5333         // look through the mission list
5334         if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5335                 for(idx=0;idx<Multi_create_mission_count;idx++){
5336                         if(Multi_create_file_list[idx].flags & Multi_create_filter){
5337                                 select_index--;
5338                         }
5339
5340                         // if we found the item
5341                         if(select_index < 0){
5342                                 strcpy(filename,Multi_create_file_list[idx].filename);
5343                                 return;
5344                         }
5345                 }
5346         }
5347         // look through the campaign list
5348         else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
5349                 for(idx=0;idx<Multi_create_campaign_count;idx++){
5350                         select_index--;
5351
5352                         // if we found the item
5353                         if(select_index < 0){
5354                                 strcpy(filename,Multi_create_file_list[idx].filename);
5355                                 return;
5356                         }               
5357                 }
5358         }
5359
5360         strcpy(filename,"");
5361 }
5362
5363 int multi_create_select_to_index(int select_index)
5364 {
5365         int idx;
5366         int lookup_index = 0;
5367
5368         // look through the mission list
5369         if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5370                 for(idx=0;idx<Multi_create_mission_count;idx++){
5371                         if(Multi_create_file_list[idx].flags & Multi_create_filter){
5372                                 lookup_index++;
5373                         } 
5374
5375                         // if we found the item
5376                         if(select_index < lookup_index){                                
5377                                 return idx;
5378                         }
5379                 }
5380         }
5381         // look through the campaign list
5382         else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
5383                 for(idx=0;idx<Multi_create_campaign_count;idx++){
5384                         select_index--;
5385
5386                         // if we found the item
5387                         if(select_index < 0){                           
5388                                 return idx;
5389                         }               
5390                 }
5391         }
5392
5393         return -1;
5394 }
5395
5396 int multi_create_ok_to_commit()
5397 {
5398         int player_count, observer_count, idx;
5399         int notify_of_hacked_ships_tbl = 0;
5400         int notify_of_hacked_weapons_tbl = 0;
5401         char err_string[255];
5402         int abs_index;
5403         int found_hack;
5404
5405         // make sure we have a valid mission selected
5406         if(Multi_create_list_select < 0){
5407                 return 0;
5408         }       
5409
5410         // if this is not a valid mission, let the player know
5411         abs_index = multi_create_select_to_index(Multi_create_list_select);             
5412         if(abs_index < 0){
5413                 return 0;
5414         }
5415
5416         // if we're playing with a hacked ships.tbl (on PXO)
5417         notify_of_hacked_ships_tbl = 0;
5418         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5419                 if(!Game_ships_tbl_valid){
5420                         notify_of_hacked_ships_tbl = 1;
5421                 }
5422         } else {
5423                 if(Netgame.flags & NG_FLAG_HACKED_SHIPS_TBL){
5424                         notify_of_hacked_ships_tbl = 1;
5425                 }
5426         }
5427         if(!MULTI_IS_TRACKER_GAME){
5428                 notify_of_hacked_ships_tbl = 0;
5429         }
5430         if(notify_of_hacked_ships_tbl){
5431                 if(popup(PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON, 2, XSTR("&Back", 995), XSTR("&Continue", 780), XSTR("You or the server you are playing on has a hacked ships.tbl. Your stats will not be updated on PXO", 1051)) <= 0){
5432                         return 0;
5433                 }
5434         }
5435
5436         // if we're playing with a hacked weapons.tbl (on PXO)
5437         notify_of_hacked_weapons_tbl = 0;
5438         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5439                 if(!Game_weapons_tbl_valid){
5440                         notify_of_hacked_weapons_tbl = 1;
5441                 }
5442         } else {
5443                 if(Netgame.flags & NG_FLAG_HACKED_WEAPONS_TBL){
5444                         notify_of_hacked_weapons_tbl = 1;
5445                 }
5446         }
5447         if(!MULTI_IS_TRACKER_GAME){
5448                 notify_of_hacked_weapons_tbl = 0;
5449         }
5450         if(notify_of_hacked_weapons_tbl){
5451                 if(popup(PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON, 2, XSTR("&Back", 995), XSTR("&Continue", 780), XSTR("You or the server you are playing on has a hacked weapons.tbl. Your stats will not be updated on PXO", 1052)) <= 0){
5452                         return 0;
5453                 }
5454         }
5455
5456         // if any of the players have hacked data
5457         found_hack = 0;
5458         for(idx=0; idx<MAX_PLAYERS; idx++){
5459                 // look for hacked players
5460                 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_HAXOR)){
5461                         // we found a hack
5462                         found_hack = 1;
5463
5464                         // message everyone - haha
5465                         if(Net_players[idx].player != NULL){
5466                                 sprintf(err_string, "%s %s", Net_players[idx].player->callsign, XSTR("has hacked tables/data", 1271)); 
5467                         } else {
5468                                 sprintf(err_string, "somebody %s", XSTR("has hacked tables/data", 1271)); 
5469                         }
5470                         send_game_chat_packet(Net_player, err_string, MULTI_MSG_ALL, NULL, NULL, 1);
5471                 }
5472         }
5473         // if we found a hacked set of data
5474         if(found_hack){
5475                 // if we're on PXO
5476                 if(MULTI_IS_TRACKER_GAME){
5477                         // don't allow squad war matches to continue
5478                         if(Netgame.type_flags & NG_TYPE_SW){
5479 #ifdef RELEASE_REAL
5480                                 // if this is squad war, don't allow it to continue                     
5481                                 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("One or more players has hacked data files. You cannot play a SquadWar match unless all clients have legal data", 1272));
5482
5483                                 return 0;
5484 #endif
5485                         }
5486                         // otherwise, warn the players that stats will not saved
5487                         else {
5488                                 // if this is squad war, don't allow it to continue                     
5489                                 if(popup(PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON, 2, XSTR("&Back", 995), XSTR("&Continue", 780), XSTR("One or more players has hacked data files. If you continue, stats will not be stored at the end of the mission", 1273)) <= 0){
5490                                         return 0;
5491                                 }
5492                         }
5493                 }
5494                 // non-pxo, just give a notice
5495                 else {
5496                         if(popup(PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON, 2, XSTR("&Back", 995), XSTR("&Continue", 780), XSTR("One or more players has hacked data files", 1274)) <= 0){
5497                                 return 0;
5498                         }
5499                 }
5500         }
5501
5502         // check to see that we don't have too many observers
5503         observer_count = multi_num_observers();
5504         if(observer_count > Netgame.options.max_observers){
5505                 // print up the error string
5506                 sprintf(err_string,XSTR("There are too many observers in the game\n\nMax : %d\nCurrently %d\n\nPlease dump a few",791),Netgame.options.max_observers,observer_count);
5507
5508                 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, err_string);
5509                 return 0;
5510         }
5511
5512         // check to see that we have a valid # of players for the the # of ships in the game            
5513         player_count = multi_num_players();
5514         if(player_count > Netgame.max_players){
5515                 // print up the error string
5516                 sprintf(err_string,XSTR("There are too many players in the game\n\nMax : %d\nCurrently %d\n\nPlease dump a few", 792), Netgame.max_players,player_count);
5517
5518                 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, err_string);
5519                 return 0;
5520         }
5521         
5522         // check to see if teams are assigned properly in a team vs. team situation
5523         if(Netgame.type_flags & NG_TYPE_TEAM){
5524                 if(!multi_team_ok_to_commit()){
5525                         gamesnd_play_iface(SND_GENERAL_FAIL);
5526                         popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("Teams and/or team captains are not assigned properly", 793));                   
5527                         return 0;
5528                 }
5529         }
5530
5531         // verify cd's  
5532         if(!multi_create_verify_cds()){
5533                 gamesnd_play_iface(SND_GENERAL_FAIL);
5534
5535 #ifdef MULTIPLAYER_BETA_BUILD
5536                 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, "You need 1 CD for every player!");                   
5537 #else 
5538         #ifdef DVD_MESSAGE_HACK
5539                         popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("You need 1 DVD for every 4 players!", 794));                    
5540         #else
5541                         popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("You need 1 CD for every 4 players!", 794));                     
5542         #endif
5543 #endif
5544                 return 0;
5545         }       
5546         
5547         // if we're playing on the tracker
5548         if(MULTI_IS_TRACKER_GAME){
5549 #ifdef PXO_CHECK_VALID_MISSIONS         
5550                 if((Multi_create_file_list == Multi_create_mission_list) && (Multi_create_file_list[abs_index].valid_status != MVALID_STATUS_VALID)){
5551                         if(popup(PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON, 2, XSTR("&Back", 995), XSTR("&Continue", 780), XSTR("You have selected a mission which is either invalid or unknown to PXO. Your stats will not be saved if you continue",996)) <= 0){
5552                                 return 0;
5553                         }
5554                 }               
5555 #endif
5556
5557                 // non-squad war
5558                 if(!(Netgame.type_flags & NG_TYPE_SW)){
5559                         // if he is playing by himself, tell him stats will not be accepted
5560                         if(multi_num_players() == 1){
5561                                 if(popup(PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG, 2, XSTR("&Back", 995), XSTR("&Continue", 780), XSTR("Warning\n\nIf you start a PXO mission by yourself, your stats will not be updated", 997)) <= 0){
5562                                         return 0;
5563                                 }
5564                         }
5565                 }
5566                 // squad war
5567                 else {                  
5568                 }
5569         }       
5570                 
5571         return 1;
5572 }
5573
5574 int multi_create_verify_cds()
5575 {
5576         int player_count = multi_num_players();
5577         int multi_cd_count;
5578         int idx;
5579
5580         // count how many cds we have
5581         multi_cd_count = 0;
5582         for(idx=0;idx<MAX_PLAYERS;idx++){
5583                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_HAS_CD)){
5584                         multi_cd_count++;
5585                 }
5586         }
5587
5588         // for the beta, everyone must have a CD
5589 #ifdef MULTIPLAYER_BETA_BUILD
5590         if(multi_cd_count < player_count){
5591                 return 0;
5592         }
5593 #else
5594         // determine if we have enough
5595         float ratio = (float)player_count / (float)multi_cd_count;
5596         // greater than a 4 to 1 ratio
5597         if(ratio > 4.0f){
5598                 return 0;
5599         } 
5600 #endif
5601
5602         // we meet the conditions
5603         return 1;
5604 }
5605
5606 // returns an index into Multi_create_mission_list
5607 int multi_create_lookup_mission(char *fname)
5608 {
5609         int idx;
5610
5611         for(idx=0; idx<Multi_create_mission_count; idx++){
5612                 if(!stricmp(fname, Multi_create_mission_list[idx].filename)){
5613                         return idx;
5614                 }
5615         }
5616
5617         // couldn't find the mission
5618         return -1;
5619 }
5620
5621 // returns an index into Multi_create_campaign_list
5622 int multi_create_lookup_campaign(char *fname)
5623 {
5624         int idx;
5625
5626         for(idx=0; idx<Multi_create_campaign_count; idx++){
5627                 if(!stricmp(fname, Multi_create_campaign_list[idx].filename)){
5628                         return idx;
5629                 }
5630         }
5631
5632         // couldn't find the campaign
5633         return -1;
5634 }
5635
5636 void multi_create_refresh_pxo()
5637 {
5638         // delete mvalid.cfg if it exists
5639         cf_delete(MULTI_VALID_MISSION_FILE, CF_TYPE_DATA);
5640
5641         // refresh missions from the tracker
5642         multi_update_valid_missions();
5643 }
5644
5645 void multi_create_sw_clicked()
5646 {
5647         netgame_info ng_temp;
5648         netgame_info *ng;
5649
5650         int file_index = multi_create_select_to_index(Multi_create_list_select);
5651
5652         // either a temporary netgame or the real one
5653         if(MULTIPLAYER_MASTER){
5654                 ng = &Netgame;
5655         } else {
5656                 ng_temp = Netgame;
5657                 ng = &ng_temp;
5658         }
5659
5660         // maybe switch squad war off
5661         if(!Multi_create_sw_checkbox.checked()){
5662                 // if the mission selected is a coop mission, go back to coop mode
5663                 Assert(file_index != -1);
5664                 if(file_index == -1){
5665                         ng->type_flags = NG_TYPE_COOP;                  
5666                 }               
5667                 if(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_TEAMS){
5668                         ng->type_flags = NG_TYPE_TVT;
5669                 } else if(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_DOGFIGHT){
5670                         ng->type_flags = NG_TYPE_DOGFIGHT;
5671                 } else {
5672                         ng->type_flags = NG_TYPE_COOP;
5673                 }
5674         }
5675         // switch squad war on
5676         else {
5677                 Assert(file_index != -1);
5678                 if((file_index == -1) || !(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_TEAMS)){                       
5679                         Multi_create_sw_checkbox.set_state(0);                  
5680                 } else {
5681                         // at this point we know its safe to switch squad war on
5682                         ng->type_flags = NG_TYPE_SW;
5683                 }
5684         }
5685
5686         // update 
5687         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){         
5688                 // update players 
5689                 send_netgame_update_packet();                   
5690
5691                 // update all machines about stuff like respawns, etc.
5692                 multi_options_update_netgame();
5693         }
5694         // on the standalone
5695         else {
5696                 // standalone will take care of polling the usertracker
5697                 multi_options_update_mission(ng, Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ? 1 : 0);
5698         }
5699 }
5700
5701
5702 // -------------------------------------------------------------------------------------------------------------
5703 // 
5704 // MULTIPLAYER HOST OPTIONS SCREEN
5705 //
5706
5707 #define MULTI_HO_NUM_BUTTONS                            12
5708 #define MULTI_HO_NUM_RADIO_BUTTONS              10
5709
5710 //XSTR:OFF
5711 // bitmaps defs
5712 #define MULTI_HO_PALETTE                                "InterfacePalette"
5713
5714 static char *Multi_ho_bitmap_fname[GR_NUM_RESOLUTIONS] = {
5715         "MultiHost",                    // GR_640
5716         "2_MultiHost"                   // GR_1024
5717 };
5718
5719 static char *Multi_ho_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
5720         "MultiHost-M",                  // GR_640
5721         "2_MultiHost-M"         // GR_1024
5722 };
5723
5724 //XSTR:ON
5725 UI_WINDOW Multi_ho_window;                                                                                              // the window object for the join screen
5726 UI_INPUTBOX Multi_ho_respawns;                                                                          // the # of respawns allowed in the game
5727 UI_INPUTBOX Multi_ho_time_limit;                                                                                // mission time limit
5728 UI_INPUTBOX Multi_ho_voice_wait;                                                                                // wait time between tokens
5729 UI_INPUTBOX Multi_ho_kill_limit;                                                                                // kill limit in a furball mission
5730 UI_INPUTBOX Multi_ho_obs;                                                                                               // # of observers we'll allow
5731 int Multi_ho_bitmap;                                                                                                            // the background bitmap
5732
5733 // constants for coordinate lookup
5734 #define MULTI_HO_X_COORD                        0
5735 #define MULTI_HO_Y_COORD                        1
5736 #define MULTI_HO_W_COORD                        2
5737 #define MULTI_HO_H_COORD                        3
5738 #define MULTI_HO_TEXT_X_COORD           4
5739 #define MULTI_HO_TEXT_Y_COORD           5
5740
5741 // button defs
5742 #define MULTI_HO_MSG_RANK                               0               // highest ranking players can do messaging
5743 #define MULTI_HO_MSG_LEADER                     1               // wing/team leaders can do messaging
5744 #define MULTI_HO_MSG_ANY                                2               // any player can do messaging
5745 #define MULTI_HO_MSG_HOST                               3               // only the host can do messaging
5746 #define MULTI_HO_END_RANK                               4               // highest rank can and host can end mission
5747 #define MULTI_HO_END_LEADER                     5               // wing/team leaders and host can end the mission
5748 #define MULTI_HO_END_ANY                                6               // any player can end the mission
5749 #define MULTI_HO_END_HOST                               7               // only host can end the mission
5750 #define MULTI_HO_VOICE_ON                               8               // voice toggled on
5751 #define MULTI_HO_VOICE_OFF                              9               // voice toggled off
5752 #define MULTI_HO_HOST_MODIFIES          10              // only the host or team captains can modify ships/weapons in briefing
5753 #define MULTI_HO_ACCEPT                                 11              // accept button
5754
5755 ui_button_info Multi_ho_buttons[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_BUTTONS] = {   
5756         { // GR_640
5757                 // who is allowed to message
5758                 ui_button_info("MH_00", 3,      160,    46,     166,    0),             // highest rank
5759                 ui_button_info("MH_01", 3,      179,    46,     185,    1),             // team/wing leader
5760                 ui_button_info("MH_02", 3,      196,    46,     203,    2),             // any
5761                 ui_button_info("MH_03", 3,      214,    46,     220,    3),             // host
5762                 
5763                 // who is allowed to end the mission
5764                 ui_button_info("MH_04", 3,      257,    46,     265,    4),             // highest rank
5765                 ui_button_info("MH_05", 3,      276,    46,     283,    5),             // team/wing leader
5766                 ui_button_info("MH_06", 3,      294,    46,     300,    6),             // any
5767                 ui_button_info("MH_07", 3,      311,    46,     317,    7),             // host         
5768
5769                 // voice on/off button
5770                 ui_button_info("MH_09", 542,    158,    545,    185,    9),     
5771                 ui_button_info("MH_10", 598,    158,    604,    185,    10),    
5772
5773                 // host modifies ships
5774                 ui_button_info("MH_13", 542,    377,    437,    363,    13),    
5775
5776                 // exit
5777                 ui_button_info("MH_14", 572,    428,    580,    414,    14),    
5778         },
5779         { // GR_1024
5780                 // who is allowed to message
5781                 ui_button_info("2_MH_00",       5,      256,    73,     269,    0),             // highest rank
5782                 ui_button_info("2_MH_01",       5,      286,    73,     297,    1),             // team/wing leader
5783                 ui_button_info("2_MH_02",       5,      314,    73,     325,    2),             // any
5784                 ui_button_info("2_MH_03",       5,      341,    73,     352,    3),             // host
5785                 
5786                 // who is allowed to end the mission
5787                 ui_button_info("2_MH_04",       5,      412,    73,     425,    4),             // highest rank
5788                 ui_button_info("2_MH_05",       5,      442,    73,     452,    5),             // team/wing leader
5789                 ui_button_info("2_MH_06",       5,      470,    73,     480,    6),             // any
5790                 ui_button_info("2_MH_07",       5,      497,    73,     508,    7),             // host         
5791
5792                 // voice on/off button
5793                 ui_button_info("2_MH_09",       867,    253,    872,    296,    9),     
5794                 ui_button_info("2_MH_10",       957,    253,    966,    296,    10),    
5795
5796                 // host modifies ships
5797                 ui_button_info("2_MH_13",       867,    603,    784,    581,    13),    
5798
5799                 // exit
5800                 ui_button_info("2_MH_14",       916,    685,    925,    665,    14),    
5801         },
5802 };
5803 UI_XSTR Multi_ho_text[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_BUTTONS] = {
5804         { // GR_640
5805                 {"Highest rank",                                        1280,   46,     166,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_RANK].button},
5806                 {"Team / wing-leader",                  1281, 46,       185,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_LEADER].button},
5807                 {"Any",                                                         1282, 46,       203,    UI_XSTR_COLOR_GREEN,    -1, &Multi_ho_buttons[0][MULTI_HO_MSG_ANY].button},
5808                 {"Host",                                                                1283,   46,     220,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_HOST].button},
5809                 {"Highest rank",                                        1280,   46,     265,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_RANK].button},
5810                 {"Team / wing-leader",                  1281,   46,     283,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_LEADER].button},
5811                 {"Any",                                                         1282, 46,       300,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_ANY].button},
5812                 {"Host",                                                                1283,   46,     317,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_HOST].button},               
5813                 {"On",                                                          1285,   545,    185,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_VOICE_ON].button},
5814                 {"Off",                                                         1286,   604,    185,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_VOICE_OFF].button},
5815                 {"Host modifies ships",                 1287,   437,    363,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_HOST_MODIFIES].button},
5816                 {"Exit",                                                                1417,   572,    418,    UI_XSTR_COLOR_PINK,     -1, &Multi_ho_buttons[0][MULTI_HO_ACCEPT].button},
5817         },
5818         { // GR_1024
5819                 {"Highest rank",                                        1280,   62,     269,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_RANK].button},
5820                 {"Team / wing-leader",                  1281, 62,       297,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_LEADER].button},
5821                 {"Any",                                                         1282, 62,       325,    UI_XSTR_COLOR_GREEN,    -1, &Multi_ho_buttons[1][MULTI_HO_MSG_ANY].button},
5822                 {"Host",                                                                1283,   62,     352,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_HOST].button},
5823                 {"Highest rank",                                        1280,   62,     425,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_RANK].button},
5824                 {"Team / wing-leader",                  1281,   62,     452,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_LEADER].button},
5825                 {"Any",                                                         1282, 62,       480,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_ANY].button},
5826                 {"Host",                                                                1283,   62,     508,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_HOST].button},               
5827                 {"On",                                                          1285,   877,    294,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_VOICE_ON].button},
5828                 {"Off",                                                         1286,   967,    293,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_VOICE_OFF].button},
5829                 {"Host modifies ships",                 1287,   869,    589,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_HOST_MODIFIES].button},
5830                 {"Exit",                                                                1417,   953,    672,    UI_XSTR_COLOR_PINK,     -1, &Multi_ho_buttons[1][MULTI_HO_ACCEPT].button},
5831         }
5832 };
5833
5834 // radio button controls
5835 #define MULTI_HO_NUM_RADIO_GROUPS               3
5836 #define MULTI_HO_MSG_GROUP                                      0                                                       // group dealing with squadmate messaging
5837 #define MULTI_HO_END_GROUP                                      1                                                       // group dealing with ending the mission
5838 #define MULTI_HO_VOICE_GROUP                            2                                                       // group dealing with voice stuff
5839 int Multi_ho_radio_groups[MULTI_HO_NUM_RADIO_GROUPS] = {                // currently selected button in the radio button group
5840         0,0,0
5841 };
5842 int Multi_ho_radio_info[MULTI_HO_NUM_RADIO_BUTTONS][3] = {      // info related to each of the radio buttons themselves
5843         // { group #, value, button id# }
5844         {0, 0, 0},                              // highest ranking players can do messaging
5845         {0, 1, 1},                              // wing/team leaders can do messaging
5846         {0, 2, 2},                              // any player can do messaging
5847         {0, 3, 3},                              // only host can do messaging   
5848         {1, 0, 4},                              // highest rank and host can end the mission
5849         {1, 1, 5},                              // team/wing leader can end the mission
5850         {1, 2, 6},                              // any player can end the mission
5851         {1, 3, 7},                              // only the host can end the mission
5852         {2, 0, 8},                              // voice toggled on
5853         {2, 1, 9}                               // voice toggled off
5854 };
5855
5856 // slider controls
5857 #define MULTI_HO_NUM_SLIDERS                                    3
5858 #define MULTI_HO_SLIDER_VOICE_QOS                       0                                               // voice quality of sound 
5859 #define MULTI_HO_SLIDER_VOICE_DUR                       1                                               // max duration of voice recording
5860 #define MULTI_HO_SLIDER_SKILL                                   2                                               // skill level
5861 struct ho_sliders {
5862         char *filename;
5863         int x, y, xt, yt;
5864         int hotspot;
5865         int dot_w;
5866         int dots;
5867         UI_DOT_SLIDER_NEW slider;  // because we have a class inside this struct, we need the constructor below..
5868
5869         ho_sliders(char *name, int x1, int y1, int xt1, int yt1, int h, int _dot_w, int _dots) : filename(name), x(x1), y(y1), xt(xt1), yt(yt1), hotspot(h), dot_w(_dot_w), dots(_dots){}
5870 };
5871 ho_sliders Multi_ho_sliders[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_SLIDERS] = {
5872         { // GR_640
5873                 ho_sliders("MH_11",     428,    214,    437,    199,    11,     19,     10),                    // voice qos
5874                 ho_sliders("MH_12",     428,    261,    437,    246,    12,     19,     10),                    // voice duration
5875                 ho_sliders("MH_08",     237,    454,    230,    411,    8,              36,     5),                     // skill level
5876         },
5877         { // GR_1024            
5878                 ho_sliders("2_MH_11",   684,    343,    690,    323,    11,     32,     10),                    // voice qos
5879                 ho_sliders("2_MH_12",   685,    418,    837,    468,    12,     32,     10),                    // voice duration
5880                 ho_sliders("2_MH_08",   379,    727,    369,    663,    8,              60,     5),                     // skill level
5881         }
5882 };
5883
5884 int Multi_ho_mission_respawn;
5885
5886 int Multi_ho_host_modifies;
5887
5888 // whether or not any of the inputboxes on this screen had focus last frame
5889 int Multi_ho_lastframe_input = 0;
5890
5891 // game information text areas
5892
5893 // ho titles
5894 #define MULTI_HO_NUM_TITLES                                     14
5895 UI_XSTR Multi_ho_titles[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_TITLES] = {
5896         { // GR_640
5897                 { "AI Orders",                          1289,           32,     144, UI_XSTR_COLOR_GREEN, -1, NULL },           
5898                 { "End Mission",                        1290,           32,     242, UI_XSTR_COLOR_GREEN, -1, NULL },
5899                 { "Time Limit",                 1291,           32,     347, UI_XSTR_COLOR_GREEN, -1, NULL },
5900                 { "Min",                                                1292,           74,     362, UI_XSTR_COLOR_GREEN, -1, NULL },
5901                 { "Respawn Limit",              1288,           32,     378, UI_XSTR_COLOR_GREEN, -1, NULL },
5902                 { "Kill Limit",                 1293,           32,     409, UI_XSTR_COLOR_GREEN, -1, NULL },
5903                 { "Observers",                          1294,           32,     441, UI_XSTR_COLOR_GREEN, -1, NULL },
5904                 { "Skill Level",                        1284,           230,    411, UI_XSTR_COLOR_GREEN, -1, NULL },
5905                 { "Voice Transmission", 1295,           437,    144, UI_XSTR_COLOR_GREEN, -1, NULL },
5906                 { "Voice Quality",              1296,           437,    199, UI_XSTR_COLOR_GREEN, -1, NULL },
5907                 { "Message Duration",   1297,           437,    246, UI_XSTR_COLOR_GREEN, -1, NULL },
5908                 { "sec",                                                1522,           523,    292, UI_XSTR_COLOR_GREEN, -1, NULL },           
5909                 { "sec",                                                1523,           523,    332, UI_XSTR_COLOR_GREEN, -1, NULL },           
5910                 { "Voice Wait",                 1298,           437,    313, UI_XSTR_COLOR_GREEN, -1, NULL },
5911         },
5912         { // GR_1024            
5913                 { "AI Orders",                          1289,           48,     238, UI_XSTR_COLOR_GREEN, -1, NULL },           
5914                 { "End Mission",                        1290,           48,     394, UI_XSTR_COLOR_GREEN, -1, NULL },
5915                 { "Time Limit",                 1291,           50,     568, UI_XSTR_COLOR_GREEN, -1, NULL },
5916                 { "Min",                                                1292,           119,    581, UI_XSTR_COLOR_GREEN, -1, NULL },
5917                 { "Respawn Limit",              1288,           50,     618, UI_XSTR_COLOR_GREEN, -1, NULL },
5918                 { "Kill Limit",                 1293,           50,     668, UI_XSTR_COLOR_GREEN, -1, NULL },
5919                 { "Observers",                          1294,           50,     718, UI_XSTR_COLOR_GREEN, -1, NULL },
5920                 { "Skill Level",                        1284,           398,    670, UI_XSTR_COLOR_GREEN, -1, NULL },
5921                 { "Voice Transmission", 1295,           869,    239, UI_XSTR_COLOR_GREEN, -1, NULL },
5922                 { "Voice Quality",              1296,           690,    331, UI_XSTR_COLOR_GREEN, -1, NULL },
5923                 { "Message Duration",   1297,           690,    405, UI_XSTR_COLOR_GREEN, -1, NULL },
5924                 { "sec",                                                1522,           837,    467, UI_XSTR_COLOR_GREEN, -1, NULL },
5925                 { "sec",                                                1523,           837,    534, UI_XSTR_COLOR_GREEN, -1, NULL },
5926                 { "Voice Wait",                 1298,           742,    510, UI_XSTR_COLOR_GREEN, -1, NULL },
5927         }
5928 };
5929
5930 // mission time limit input box
5931 int Ho_time_coords[GR_NUM_RESOLUTIONS][4] = {
5932         { // GR_640
5933                 36, 362, 36, 17
5934         },
5935         { // GR_1024
5936                 58, 581, 57, 27
5937         }
5938 };
5939
5940 // furball kill limit input box
5941 int Ho_kill_coords[GR_NUM_RESOLUTIONS][4] = {
5942         { // GR_640
5943                 36, 425, 45, 17
5944         },
5945         { // GR_1024
5946                 58, 684, 72, 27
5947         }
5948 };
5949
5950 // voice recording duration text display area
5951 int Ho_vd_coords[GR_NUM_RESOLUTIONS][4] = {
5952         { // GR_640
5953                 467, 292, 55, 15
5954         },
5955         { // GR_1024
5956                 750, 467, 85, 28
5957         }
5958 };
5959
5960 // voice token wait input box
5961 int Ho_vw_coords[GR_NUM_RESOLUTIONS][6] = {
5962         { // GR_640
5963                 467, 332, 55, 15
5964         },
5965         { // GR_1024
5966                 750, 534, 85, 28
5967         }
5968 };
5969
5970 // observer count input box
5971 int Ho_obs_coords[GR_NUM_RESOLUTIONS][4] = {
5972         { // GR_640
5973                 36, 457, 45, 17
5974         },
5975         { // GR_1024
5976                 58, 733, 72, 27
5977         }
5978 };
5979
5980 // skill text description area
5981 int Ho_st_coords[GR_NUM_RESOLUTIONS][4] = {
5982         { // GR_640
5983                 249, 435, 172, 10
5984         },
5985         { // GR_1024
5986                 403, 699, 172, 10
5987         }
5988 };
5989
5990 // respawn input box
5991 int Ho_rsp_coords[GR_NUM_RESOLUTIONS][6] = {
5992         { // GR_640
5993                 36, 394, 45, 17, 
5994         },
5995         { // GR_1024
5996                 58, 632, 72, 27
5997         }
5998 };
5999
6000 // respawn max text area
6001 int Ho_max_rsp_coords[GR_NUM_RESOLUTIONS][2] = {
6002         { // GR_640
6003                 150, 378
6004         },
6005         { // GR_1024
6006                 190, 618
6007         }
6008 };
6009
6010 // maximum values for various input boxes (to notify user of overruns)
6011 #define MULTI_HO_MAX_TIME_LIMIT                         500
6012 #define MULTI_HO_MAX_TOKEN_WAIT                         5
6013 #define MULTI_HO_MAX_KILL_LIMIT                         9999
6014 #define MULTI_HO_MAX_OBS                                                4
6015
6016 // LOCAL function definitions
6017 void multi_ho_check_buttons();
6018 void multi_ho_button_pressed(int n);
6019 void multi_ho_draw_radio_groups();
6020 void multi_ho_accept_hit();
6021 void multi_ho_get_options();
6022 void multi_ho_apply_options();
6023 void multi_ho_display_record_time();
6024 int multi_ho_check_values();
6025 void multi_ho_check_focus();
6026 void multi_ho_blit_max_respawns();
6027 void multi_ho_display_skill_level();
6028
6029 void multi_host_options_init()
6030 {
6031         int idx;
6032
6033         // create the interface window
6034         Multi_ho_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
6035         Multi_ho_window.set_mask_bmap(Multi_ho_bitmap_mask_fname[gr_screen.res]);
6036
6037         // load the background bitmap
6038         Multi_ho_bitmap = bm_load(Multi_ho_bitmap_fname[gr_screen.res]);
6039         if(Multi_ho_bitmap < 0){
6040                 // we failed to load the bitmap - this is very bad
6041                 Int3();
6042         }               
6043
6044         // initialize the common notification messaging
6045         multi_common_notify_init();     
6046
6047         // use the common interface palette
6048         multi_common_set_palette();     
6049
6050         // create the interface buttons
6051         for(idx=0;idx<MULTI_HO_NUM_BUTTONS;idx++){
6052                 // create the object
6053                 Multi_ho_buttons[gr_screen.res][idx].button.create(&Multi_ho_window, "", Multi_ho_buttons[gr_screen.res][idx].x, Multi_ho_buttons[gr_screen.res][idx].y, 1, 1, 0, 1);
6054
6055                 // set the sound to play when highlighted
6056                 Multi_ho_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
6057
6058                 // set the ani for the button
6059                 Multi_ho_buttons[gr_screen.res][idx].button.set_bmaps(Multi_ho_buttons[gr_screen.res][idx].filename);
6060
6061                 // set the hotspot, ignoring the skill level button             
6062                 Multi_ho_buttons[gr_screen.res][idx].button.link_hotspot(Multi_ho_buttons[gr_screen.res][idx].hotspot);
6063
6064                 // add xstr text
6065                 Multi_ho_window.add_XSTR(&Multi_ho_text[gr_screen.res][idx]);
6066         }               
6067
6068         // create misc text
6069         for(idx=0; idx<MULTI_HO_NUM_TITLES; idx++){
6070                 Multi_ho_window.add_XSTR(&Multi_ho_titles[gr_screen.res][idx]);
6071         }
6072
6073         // create the interface sliders
6074         for(idx=0; idx<MULTI_HO_NUM_SLIDERS; idx++){
6075                 // create the object
6076                 Multi_ho_sliders[gr_screen.res][idx].slider.create(&Multi_ho_window, Multi_ho_sliders[gr_screen.res][idx].x, Multi_ho_sliders[gr_screen.res][idx].y, Multi_ho_sliders[gr_screen.res][idx].dots, Multi_ho_sliders[gr_screen.res][idx].filename, Multi_ho_sliders[gr_screen.res][idx].hotspot, NULL, -1, -1, -1, NULL, -1, -1, -1, Multi_ho_sliders[gr_screen.res][idx].dot_w);
6077         }
6078
6079         // create the respawn count input box
6080         Multi_ho_respawns.create(&Multi_ho_window,Ho_rsp_coords[gr_screen.res][MULTI_HO_X_COORD],Ho_rsp_coords[gr_screen.res][MULTI_HO_Y_COORD],Ho_rsp_coords[gr_screen.res][MULTI_HO_W_COORD],6,"",UI_INPUTBOX_FLAG_ESC_FOC | UI_INPUTBOX_FLAG_INVIS | UI_INPUTBOX_FLAG_NO_LETTERS,-1,&Color_bright);          
6081         // if we're in campaign mode, disable it
6082         if(Netgame.campaign_mode == MP_CAMPAIGN){
6083                 Multi_ho_respawns.set_text(XSTR("NA",795));  // [[ Not applicable ]]
6084                 Multi_ho_respawns.disable();
6085         } else {
6086                 Multi_ho_respawns.set_valid_chars("0123456789");
6087         }
6088
6089         // create the time limit input box
6090         Multi_ho_time_limit.create(&Multi_ho_window, Ho_time_coords[gr_screen.res][MULTI_HO_X_COORD], Ho_time_coords[gr_screen.res][MULTI_HO_Y_COORD], Ho_time_coords[gr_screen.res][MULTI_HO_W_COORD], 3, "", UI_INPUTBOX_FLAG_ESC_FOC | UI_INPUTBOX_FLAG_INVIS | UI_INPUTBOX_FLAG_NO_LETTERS, -1, &Color_bright);
6091         Multi_ho_time_limit.set_valid_chars("-0123456789");
6092
6093         // create the voice token wait input box
6094         Multi_ho_voice_wait.create(&Multi_ho_window, Ho_vw_coords[gr_screen.res][MULTI_HO_X_COORD], Ho_vw_coords[gr_screen.res][MULTI_HO_Y_COORD], Ho_vw_coords[gr_screen.res][MULTI_HO_W_COORD], 1, "", UI_INPUTBOX_FLAG_ESC_FOC | UI_INPUTBOX_FLAG_INVIS | UI_INPUTBOX_FLAG_NO_LETTERS, -1, &Color_bright);
6095         Multi_ho_voice_wait.set_valid_chars("01243456789");
6096
6097         // create the furball kill limit input box
6098         Multi_ho_kill_limit.create(&Multi_ho_window, Ho_kill_coords[gr_screen.res][MULTI_HO_X_COORD], Ho_kill_coords[gr_screen.res][MULTI_HO_Y_COORD], Ho_kill_coords[gr_screen.res][MULTI_HO_W_COORD], 4, "", UI_INPUTBOX_FLAG_ESC_FOC | UI_INPUTBOX_FLAG_INVIS | UI_INPUTBOX_FLAG_NO_LETTERS, -1, &Color_bright);
6099         Multi_ho_kill_limit.set_valid_chars("0123456789");
6100
6101         // create the observer limit input box
6102         Multi_ho_obs.create(&Multi_ho_window, Ho_obs_coords[gr_screen.res][MULTI_HO_X_COORD], Ho_obs_coords[gr_screen.res][MULTI_HO_Y_COORD], Ho_obs_coords[gr_screen.res][MULTI_HO_W_COORD], 1, "", UI_INPUTBOX_FLAG_ESC_FOC | UI_INPUTBOX_FLAG_INVIS | UI_INPUTBOX_FLAG_NO_LETTERS, -1, &Color_bright);
6103         Multi_ho_obs.set_valid_chars("01234");  
6104         
6105         // load in the current netgame defaults
6106         multi_ho_get_options();
6107
6108         // whether or not any of the inputboxes on this screen had focus last frame
6109         Multi_ho_lastframe_input = 0;   
6110
6111         // get the # of respawns for the currently selected mission (if any)
6112         if(Multi_create_list_select != -1){
6113                 int abs_index = multi_create_select_to_index(Multi_create_list_select);
6114
6115                 // if he has a valid mission selected
6116                 if(abs_index >= 0){
6117                         Multi_ho_mission_respawn = (int)Multi_create_file_list[abs_index].respawn;
6118                 } else {
6119                         Multi_ho_mission_respawn = -1;
6120                 }
6121         } else {
6122                 Multi_ho_mission_respawn = -1;
6123         }
6124 }
6125
6126 void multi_ho_update_sliders()
6127 {
6128         // game skill slider
6129         if (Game_skill_level != Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos) {
6130                 if ( !(Netgame.type_flags & NG_TYPE_TEAM) ){            
6131                         Game_skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
6132                         gamesnd_play_iface(SND_USER_SELECT);
6133                 } else {        
6134                         Game_skill_level = NUM_SKILL_LEVELS / 2;
6135                 }
6136         }
6137
6138         // get the voice qos options
6139         if (Netgame.options.voice_qos != (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1)) {
6140                 Netgame.options.voice_qos = (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1); 
6141                 gamesnd_play_iface(SND_USER_SELECT);
6142         }
6143
6144         // get the voice duration options
6145         if (Netgame.options.voice_record_time != (int)(0.5f * (float)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 1000.0f)) {
6146                 Netgame.options.voice_record_time = (int)(0.5f * (float)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 1000.0f);
6147                 gamesnd_play_iface(SND_USER_SELECT);
6148         }
6149
6150 }
6151
6152 void multi_host_options_do()
6153 {
6154         int k;  
6155         
6156         // process stuff
6157         k = Multi_ho_window.process();          
6158         chatbox_process(k);     
6159
6160         // process any keypresses
6161         switch(k){
6162         case KEY_ESC :
6163                 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
6164                 break;
6165         // same as ACCEPT
6166         case KEY_CTRLED + KEY_ENTER :   
6167                 gamesnd_play_iface(SND_COMMIT_PRESSED);
6168                 multi_ho_accept_hit();
6169                 break;
6170         }
6171
6172         // process any button clicks
6173         multi_ho_check_buttons();       
6174
6175         // update the sliders
6176         multi_ho_update_sliders();
6177
6178         // make sure that the chatbox inputbox and any inputbox on this screen are mutually exclusive in terms of focus
6179         multi_ho_check_focus();
6180
6181         // draw the background, etc
6182         gr_reset_clip();
6183         GR_MAYBE_CLEAR_RES(Multi_ho_bitmap);
6184         if(Multi_ho_bitmap != -1){
6185                 gr_set_bitmap(Multi_ho_bitmap);
6186                 gr_bitmap(0,0);
6187         }
6188         Multi_ho_window.draw();
6189         
6190         // draw all the radio buttons properly
6191         multi_ho_draw_radio_groups();
6192                         
6193         // display any pending notification messages
6194         multi_common_notify_do();               
6195
6196         // display the voice record time settings
6197         multi_ho_display_record_time(); 
6198
6199         // maybe display the max # of respawns next to the respawn input box
6200         multi_ho_blit_max_respawns();
6201
6202         // blit the proper skill level  
6203         multi_ho_display_skill_level();
6204
6205         // blit the "host modifies button"
6206         if(Multi_ho_host_modifies){
6207                 Multi_ho_buttons[gr_screen.res][MULTI_HO_HOST_MODIFIES].button.draw_forced(2);
6208         }
6209
6210         // process and show the chatbox thingie 
6211         chatbox_render();
6212
6213         // draw tooltips
6214         Multi_ho_window.draw_tooltip(); 
6215
6216         // display the voice status indicator
6217         multi_common_voice_display_status();
6218
6219         // flip the buffer
6220         gr_flip();
6221 }
6222
6223 void multi_host_options_close()
6224 {
6225         // unload any bitmaps
6226         if(!bm_unload(Multi_ho_bitmap)){
6227                 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_ho_bitmap_fname[gr_screen.res]));
6228         }       
6229         
6230         // destroy the UI_WINDOW
6231         Multi_ho_window.destroy();
6232 }
6233
6234 void multi_ho_check_buttons()
6235 {
6236         int idx;
6237         for(idx=0;idx<MULTI_HO_NUM_BUTTONS;idx++){
6238                 // we only really need to check for one button pressed at a time, so we can break after 
6239                 // finding one.
6240                 if(Multi_ho_buttons[gr_screen.res][idx].button.pressed()){
6241                         multi_ho_button_pressed(idx);
6242                         break;
6243                 }
6244         }
6245 }
6246
6247 void multi_ho_button_pressed(int n)
6248 {
6249         int radio_index,idx;
6250         int x_pixel,y_pixel;
6251
6252         // get the pixel position of the click
6253         Multi_ho_buttons[gr_screen.res][n].button.get_mouse_pos(&x_pixel,&y_pixel);
6254                 
6255         switch(n){              
6256         // clicked on the accept button
6257         case MULTI_HO_ACCEPT:
6258                 gamesnd_play_iface(SND_COMMIT_PRESSED);
6259                 multi_ho_accept_hit();
6260                 return; 
6261         
6262         // clicked on the host/captains only modify button
6263         case MULTI_HO_HOST_MODIFIES:
6264                 // toggle it on or off
6265                 Multi_ho_host_modifies = !Multi_ho_host_modifies;
6266                 gamesnd_play_iface(SND_USER_SELECT);
6267                 return;
6268         }
6269
6270         // look through the radio buttons and see which one this corresponds to
6271         radio_index = -1;
6272         for(idx=0;idx<MULTI_HO_NUM_RADIO_BUTTONS;idx++){
6273                 if(Multi_ho_radio_info[idx][2] == n){
6274                         radio_index = idx;
6275                         break;
6276                 }
6277         }
6278         Assert(radio_index != -1);
6279
6280         // check to see if a radio button was pressed
6281         if(radio_index < MULTI_HO_NUM_RADIO_BUTTONS){
6282                 // see if this value is already picked for this radio group
6283                 if(Multi_ho_radio_groups[Multi_ho_radio_info[radio_index][0]] != Multi_ho_radio_info[radio_index][1]){
6284                         gamesnd_play_iface(SND_USER_SELECT);
6285                         Multi_ho_radio_groups[Multi_ho_radio_info[radio_index][0]] = Multi_ho_radio_info[radio_index][1];
6286                 } else {
6287                         gamesnd_play_iface(SND_GENERAL_FAIL);
6288                 }
6289         }
6290 }
6291
6292 void multi_ho_draw_radio_groups()
6293 {
6294         int idx;
6295         
6296         // go through each item and draw it if it is the selected button in its respective group
6297         for(idx=0;idx<MULTI_HO_NUM_RADIO_BUTTONS;idx++){
6298                 /// if this button is the currently selected one in its group
6299                 if(Multi_ho_radio_info[idx][1] == Multi_ho_radio_groups[Multi_ho_radio_info[idx][0]]){
6300                         Multi_ho_buttons[gr_screen.res][Multi_ho_radio_info[idx][2]].button.draw_forced(2);
6301                 }
6302         }
6303 }
6304
6305 void multi_ho_accept_hit()
6306 {
6307         char resp_str[10];
6308
6309         // check the values in the input boxes
6310         if(!multi_ho_check_values()){
6311                 return;
6312         }
6313         
6314         // zero out the netgame flags
6315         Netgame.flags = 0;
6316         
6317         // set default options
6318         Netgame.options.flags = (MSO_FLAG_INGAME_XFER | MSO_FLAG_ACCEPT_PIX);
6319
6320         // set the squadmate messaging flags
6321         switch(Multi_ho_radio_groups[MULTI_HO_MSG_GROUP]){
6322         case 0 : 
6323                 Netgame.options.squad_set = MSO_SQUAD_RANK;
6324                 break;
6325         case 1 :
6326                 Netgame.options.squad_set = MSO_SQUAD_LEADER;           
6327                 break;
6328         case 2 :
6329                 Netgame.options.squad_set = MSO_SQUAD_ANY;              
6330                 break;
6331         case 3 :
6332                 Netgame.options.squad_set = MSO_SQUAD_HOST;
6333                 break;
6334         default : 
6335                 Int3(); 
6336         }
6337
6338         // set the end mission flags
6339         switch(Multi_ho_radio_groups[MULTI_HO_END_GROUP]){
6340         case 0 : 
6341                 Netgame.options.endgame_set = MSO_END_RANK;             
6342                 break;
6343         case 1 :
6344                 Netgame.options.endgame_set = MSO_END_LEADER;           
6345                 break;                  
6346         case 2 : 
6347                 Netgame.options.endgame_set = MSO_END_ANY;              
6348                 break;
6349         case 3 :
6350                 Netgame.options.endgame_set = MSO_END_HOST;             
6351                 break;  
6352         default : 
6353                 Int3(); 
6354         }
6355         
6356         // set the voice toggle
6357         switch(Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP]){
6358         case 0 :
6359                 Netgame.options.flags &= ~(MSO_FLAG_NO_VOICE);
6360                 break;
6361         case 1 :
6362                 Netgame.options.flags |= MSO_FLAG_NO_VOICE;
6363                 break;
6364         default : 
6365                 Int3();
6366         }       
6367
6368         // get the voice qos options
6369         Netgame.options.voice_qos = (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1); 
6370
6371         // get the voice duration options
6372         Netgame.options.voice_record_time = (int)(0.5f * (float)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 1000.0f);
6373         
6374         // set the skill level.  If in team vs. team mode, preserve the old setting before saving
6375         // the pilot file.  I'll bet that this doesn't work though because the pilot file gets
6376         // written in a bunch of locations....sigh.
6377         if ( !(Netgame.type_flags & NG_TYPE_TEAM) ){            
6378                 Game_skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
6379         } else {        
6380                 Game_skill_level = NUM_SKILL_LEVELS / 2;
6381         }
6382
6383         // set the netgame respawn count
6384         // maybe warn the user that respawns will not be used for a campaign mission
6385         if(Netgame.campaign_mode == MP_SINGLE){
6386                 Multi_ho_respawns.get_text(resp_str);
6387                 uint temp_respawn = (uint)atoi(resp_str);
6388                 // if he currently has no mission selected, let the user set any # of respawns
6389                 if((int)temp_respawn > Multi_ho_mission_respawn){
6390                         if(Multi_ho_mission_respawn == -1){     
6391                                 Netgame.respawn = temp_respawn;         
6392                                 Netgame.options.respawn = temp_respawn;
6393                         }
6394                         // this should have been taken care of by the interface code
6395                         else {
6396                                 Int3();
6397                         }
6398                 } else {        
6399                         Netgame.options.respawn = temp_respawn;
6400                         Netgame.respawn = temp_respawn;
6401                 }
6402         }
6403
6404         // get the mission time limit
6405         Multi_ho_time_limit.get_text(resp_str);
6406         int temp_time = atoi(resp_str);
6407         if(temp_time <= 0){
6408                 Netgame.options.mission_time_limit = fl2f(-1.0f);
6409         } else if(temp_time > MULTI_HO_MAX_TIME_LIMIT){
6410                 Int3();
6411         } else {
6412                 Netgame.options.mission_time_limit = fl2f(60.0f * (float)temp_time);    
6413         }
6414
6415         // get observer count options
6416         Multi_ho_obs.get_text(resp_str);
6417         int temp_obs = atoi(resp_str);
6418         if(temp_obs > MULTI_HO_MAX_OBS){
6419                 Int3();
6420         } 
6421         Netgame.options.max_observers = (ubyte)temp_obs;        
6422
6423         // get the furball kill limit
6424         Multi_ho_kill_limit.get_text(resp_str);
6425         int temp_kills = atoi(resp_str);
6426         if(temp_kills > MULTI_HO_MAX_KILL_LIMIT){
6427                 Int3();
6428         }
6429         Netgame.options.kill_limit = temp_kills;
6430
6431         // get the token wait limit
6432         Multi_ho_voice_wait.get_text(resp_str);
6433         int temp_wait = atoi(resp_str);
6434         if(temp_wait > MULTI_HO_MAX_TOKEN_WAIT){
6435                 Int3();
6436         } 
6437         Netgame.options.voice_token_wait = (temp_wait * 1000);          
6438
6439         // set the netgame option
6440         Netgame.options.skill_level = (ubyte)Game_skill_level;
6441
6442         // get whether we're in host/captains only modify mode
6443         Netgame.options.flags &= ~(MSO_FLAG_SS_LEADERS);
6444         if(Multi_ho_host_modifies){
6445                 Netgame.options.flags |= MSO_FLAG_SS_LEADERS;
6446         }       
6447
6448         // store these values locally
6449         memcpy(&Player->m_local_options,&Net_player->p_info.options,sizeof(multi_local_options));
6450         memcpy(&Player->m_server_options,&Netgame.options,sizeof(multi_server_options));
6451         write_pilot_file(Player);       
6452
6453         // apply any changes in settings (notify everyone of voice qos changes, etc)
6454         multi_ho_apply_options();
6455
6456         // move back to the create game screen
6457         gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
6458 }
6459
6460 void multi_ho_get_options()
6461 {       
6462         char resp_str[10];
6463         
6464         // set the squadmate messaging buttons  
6465         switch(Netgame.options.squad_set){
6466         case MSO_SQUAD_RANK :           
6467                 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 0;
6468                 break;
6469         case MSO_SQUAD_LEADER:                          
6470                 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 1;
6471                 break;
6472         case MSO_SQUAD_ANY:                     
6473                 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 2;
6474                 break;
6475         case MSO_SQUAD_HOST:            
6476                 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 3;
6477                 break;
6478         default : 
6479                 Int3();         
6480         }
6481         
6482         // set the mission end buttons  
6483         switch(Netgame.options.endgame_set){
6484         case MSO_END_RANK:                      
6485                 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 0;
6486                 break;
6487         case MSO_END_LEADER:                    
6488                 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 1;
6489                 break;
6490         case MSO_END_ANY:
6491                 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 2;
6492                 break;
6493         case MSO_END_HOST:
6494                 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 3;
6495                 break;
6496         default : 
6497                 Int3();
6498         }                       
6499
6500         // set the voice toggle buttons
6501         if(Netgame.options.flags & MSO_FLAG_NO_VOICE){
6502                 Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP] = 1;
6503         } else {
6504                 Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP] = 0;
6505         }       
6506
6507         // get the voice qos options
6508         Assert((Netgame.options.voice_qos >= 1) && (Netgame.options.voice_qos <= 10));
6509         Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos = (Netgame.options.voice_qos - 1);
6510
6511         // get the voice duration options
6512         Assert((Netgame.options.voice_record_time > 0) && (Netgame.options.voice_record_time <= MULTI_VOICE_MAX_TIME));
6513         Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos = ((int)((float)Netgame.options.voice_record_time / 500.0f)) - 1; 
6514
6515         // get the current skill level
6516         Assert((Game_skill_level >= 0) && (Game_skill_level < NUM_SKILL_LEVELS));
6517         Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos = Game_skill_level;   
6518
6519         // get the # of observers
6520         memset(resp_str,0,10);
6521         sprintf(resp_str,"%d",Netgame.options.max_observers);
6522         Multi_ho_obs.set_text(resp_str);
6523
6524         // set the respawn count
6525         if(Netgame.campaign_mode == MP_SINGLE){
6526                 memset(resp_str,0,10);
6527                 sprintf(resp_str,"%d",Netgame.respawn);
6528                 Multi_ho_respawns.set_text(resp_str);   
6529         }
6530
6531         // set the mission time limit
6532         memset(resp_str,0,10);
6533         float tl = f2fl(Netgame.options.mission_time_limit);
6534         sprintf(resp_str,"%d",(int)(tl / 60.0f));
6535         Multi_ho_time_limit.set_text(resp_str);
6536
6537         // set the furball kill limit
6538         memset(resp_str,0,10);
6539         sprintf(resp_str,"%d",Netgame.options.kill_limit);
6540         Multi_ho_kill_limit.set_text(resp_str);
6541
6542         // set the token wait time
6543         memset(resp_str,0,10);
6544         sprintf(resp_str,"%d",Netgame.options.voice_token_wait / 1000);
6545         Multi_ho_voice_wait.set_text(resp_str); 
6546
6547         // get whether we're in host/captains only modify mode
6548         if(Netgame.options.flags & MSO_FLAG_SS_LEADERS){
6549                 Multi_ho_host_modifies = 1;
6550         } else {
6551                 Multi_ho_host_modifies = 0;
6552         }
6553 }
6554
6555 void multi_ho_apply_options()
6556 {
6557         // if the voice qos or duration has changed, apply the change
6558         multi_voice_maybe_update_vars(Netgame.options.voice_qos,Netgame.options.voice_record_time);             
6559
6560         // send an options update
6561         multi_options_update_netgame(); 
6562 }
6563
6564 // display the voice record time settings
6565 void multi_ho_display_record_time()
6566 {
6567         char time_str[30];
6568         int full_seconds, half_seconds;
6569
6570         // clear the string
6571         memset(time_str,0,30);
6572
6573         // get the seconds
6574         full_seconds = (((Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 500) / 1000);
6575         
6576         // get the half-seconds
6577         half_seconds = ((((Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 500) % 1000) / 500) * 5;
6578
6579         // format the string
6580         sprintf(time_str,"%d.%d",full_seconds,half_seconds);
6581         gr_set_color_fast(&Color_bright);
6582         gr_string(Ho_vd_coords[gr_screen.res][MULTI_HO_X_COORD],Ho_vd_coords[gr_screen.res][MULTI_HO_Y_COORD],time_str);
6583 }
6584
6585 int multi_ho_check_values()
6586 {
6587         char val_txt[255];
6588
6589         memset(val_txt,0,255);
6590
6591         // check against respawn settings       
6592         if(Multi_ho_mission_respawn != -1){
6593                 Multi_ho_respawns.get_text(val_txt);
6594                 // if the value is invalid, let the user know
6595                 if(atoi(val_txt) > Multi_ho_mission_respawn){
6596                         memset(val_txt,0,255);
6597                         sprintf(val_txt,XSTR("Warning\nRespawn count in greater than mission specified max (%d)",796),Multi_ho_mission_respawn);                        
6598                         popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6599                         return 0;
6600                 }
6601         }
6602
6603         // check against mission time limit max
6604         Multi_ho_time_limit.get_text(val_txt);
6605         // if the value is invalid, force it to be valid
6606         if(atoi(val_txt) > MULTI_HO_MAX_TIME_LIMIT){
6607                 memset(val_txt,0,255);
6608                 sprintf(val_txt,XSTR("Warning\nMission time limit is greater than max allowed (%d)",797),MULTI_HO_MAX_TIME_LIMIT);              
6609                 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6610                 return 0;
6611         }
6612
6613         // check against max observer limit     
6614         Multi_ho_obs.get_text(val_txt);
6615         // if the value is invalid, force it to be valid
6616         if(atoi(val_txt) > MULTI_HO_MAX_OBS){
6617                 memset(val_txt,0,255);
6618                 sprintf(val_txt,XSTR("Warning\nObserver count is greater than max allowed (%d)",798),MULTI_HO_MAX_OBS);         
6619                 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6620                 return 0;
6621         }
6622
6623         // check against furball kill limit     
6624         Multi_ho_kill_limit.get_text(val_txt);
6625         // if the value is invalid, force it to be valid
6626         if(atoi(val_txt) > MULTI_HO_MAX_KILL_LIMIT){
6627                 memset(val_txt,0,255);
6628                 sprintf(val_txt,XSTR("Warning\nMission kill limit is greater than max allowed (%d)",799),MULTI_HO_MAX_KILL_LIMIT);              
6629                 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6630                 return 0;
6631         }
6632
6633         // check against the token wait limit   
6634         Multi_ho_voice_wait.get_text(val_txt);
6635         if(atoi(val_txt) > MULTI_HO_MAX_TOKEN_WAIT){
6636                 memset(val_txt,0,255);
6637                 sprintf(val_txt,XSTR("Warning\nvoice wait time is greater than max allowed (%d)",800),MULTI_HO_MAX_TOKEN_WAIT);         
6638                 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6639                 return 0;
6640         }
6641
6642         // all values are valid
6643         return 1;
6644 }
6645
6646 void multi_ho_check_focus()
6647 {
6648         // if an inputbox has been pressed (hit enter), lose its focus
6649         if (Multi_ho_respawns.pressed() || Multi_ho_time_limit.pressed() || Multi_ho_voice_wait.pressed() || Multi_ho_kill_limit.pressed() || Multi_ho_obs.pressed()) {
6650                 Multi_ho_respawns.clear_focus();
6651                 Multi_ho_time_limit.clear_focus();      
6652                 Multi_ho_voice_wait.clear_focus();                      
6653                 Multi_ho_kill_limit.clear_focus();
6654                 Multi_ho_obs.clear_focus();
6655                 gamesnd_play_iface(SND_COMMIT_PRESSED);
6656                 chatbox_set_focus();
6657                 Multi_ho_lastframe_input = 0;
6658
6659         } else if(!Multi_ho_lastframe_input) {
6660                 // if we didn't have focus last frame
6661                 if(Multi_ho_respawns.has_focus() || Multi_ho_time_limit.has_focus() || Multi_ho_kill_limit.has_focus() || Multi_ho_voice_wait.has_focus() ){
6662                         chatbox_lose_focus();
6663
6664                         Multi_ho_lastframe_input = 1;
6665                 }       
6666         } 
6667         // if we _did_ have focus last frame
6668         else {
6669                 // if we no longer have focus on any of the input boxes, set the focus on the chatbox
6670                 if(!Multi_ho_respawns.has_focus() && !Multi_ho_time_limit.has_focus() && !Multi_ho_kill_limit.has_focus() && !Multi_ho_voice_wait.has_focus() && !chatbox_has_focus()){
6671                         chatbox_set_focus();
6672                 }                       
6673                 // if the chatbox now has focus, clear all focus from our inputboxes
6674                 else if (chatbox_has_focus()) {
6675                         Multi_ho_respawns.clear_focus();
6676                         Multi_ho_time_limit.clear_focus();
6677                         Multi_ho_kill_limit.clear_focus();
6678                         Multi_ho_voice_wait.clear_focus();
6679
6680                         Multi_ho_lastframe_input = 0;
6681                 }
6682         }
6683 }
6684
6685 void multi_ho_blit_max_respawns()
6686 {
6687         char string[50];
6688         
6689         // if we're in campaign mode, do nothing
6690         if(Netgame.campaign_mode == MP_CAMPAIGN){
6691                 return;
6692         }
6693         
6694         // otherwise blit the max as specified by the current mission file      
6695         sprintf(string,"(%d)",Multi_ho_mission_respawn);        
6696         gr_set_color_fast(&Color_normal);
6697         gr_string(Ho_max_rsp_coords[gr_screen.res][MULTI_HO_X_COORD], Ho_max_rsp_coords[gr_screen.res][MULTI_HO_Y_COORD], string);
6698 }
6699
6700 void multi_ho_display_skill_level()
6701 {
6702         int skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
6703
6704         // sanity
6705         Assert((skill_level >= 0) && (skill_level < NUM_SKILL_LEVELS));
6706         if((skill_level < 0) || (skill_level >= NUM_SKILL_LEVELS)){
6707                 skill_level = 0;
6708         }
6709
6710         gr_set_color_fast(&Color_bright);
6711         gr_string(Ho_st_coords[gr_screen.res][0], Ho_st_coords[gr_screen.res][1], Skill_level_names(skill_level, 1));
6712 }
6713
6714 // -------------------------------------------------------------------------------------------------------------
6715 // 
6716 // MULTIPLAYER JOIN SCREEN 
6717 //
6718
6719 #define MULTI_JW_NUM_BUTTONS    8
6720
6721 //XSTR:OFF
6722 // bitmaps defs
6723 #define MULTI_JW_PALETTE                                "InterfacePalette"
6724
6725 static char *Multi_jw_bitmap_fname[GR_NUM_RESOLUTIONS] = {
6726         "MultiJoinWait",                // GR_640
6727         "2_MultiJoinWait"               // GR_1024
6728 };
6729
6730 static char *Multi_jw_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
6731         "MultiJoinWait-M",              // GR_640
6732         "2_MultiJoinWait-M"             // GR_1024
6733 };
6734
6735 //XSTR:ON
6736
6737 // button defs
6738 #define MJW_SCROLL_PLAYERS_UP           0
6739 #define MJW_SCROLL_PLAYERS_DOWN 1
6740 #define MJW_TEAM0                                               2
6741 #define MJW_TEAM1                                               3
6742 #define MJW_PILOT_INFO                          4
6743 #define MJW_SCROLL_INFO_UP                      5
6744 #define MJW_SCROLL_INFO_DOWN            6
6745 #define MJW_CANCEL                                      7
6746
6747 UI_WINDOW Multi_jw_window;                                                                                              // the window object for the join screen
6748 int Multi_jw_bitmap;                                                                                                            // the background bitmap
6749
6750 // constants for coordinate lookup
6751 #define MJW_X_COORD 0
6752 #define MJW_Y_COORD 1
6753 #define MJW_W_COORD 2
6754 #define MJW_H_COORD 3
6755
6756 ui_button_info Multi_jw_buttons[GR_NUM_RESOLUTIONS][MULTI_JW_NUM_BUTTONS] = {
6757         { // GR_640
6758                 ui_button_info("MJW_00",        1,              24,     -1,     -1,     0),
6759                 ui_button_info("MJW_01",        1,              66,     -1,     -1,     1),
6760                 ui_button_info("MJW_02",        30,     244,    20,     272,    2),
6761                 ui_button_info("MJW_03",        84,     244,    73,     272,    3),
6762                 ui_button_info("MJW_04",        139,    242,    134,    272,    4),
6763                 ui_button_info("MJW_05",        1,              406,    -1,     -1,     5),
6764                 ui_button_info("MJW_06",        1,              447,    -1,     -1,     6),
6765                 ui_button_info("MJW_07",        577,    428,    570,    414,    7),
6766         },
6767         { // GR_1024
6768                 ui_button_info("2_MJW_00",      2,              38,     -1,     -1,     0),
6769                 ui_button_info("2_MJW_01",      2,              106,    -1,     -1,     1),
6770                 ui_button_info("2_MJW_02",      48,     390,    47,     435,    2),
6771                 ui_button_info("2_MJW_03",      134,    390,    133,    435,    3),
6772                 ui_button_info("2_MJW_04",      223,    388,    225,    435,    4),
6773                 ui_button_info("2_MJW_05",      2,              649,    -1,     -1,     5),
6774                 ui_button_info("2_MJW_06",      2,              715,    -1,     -1,     6),
6775                 ui_button_info("2_MJW_07",      923,    685,    931,    667,    7),
6776         }
6777 };
6778
6779 #define MULTI_JW_NUM_TEXT                       7
6780
6781 UI_XSTR Multi_jw_text[GR_NUM_RESOLUTIONS][MULTI_JW_NUM_TEXT] = {
6782         { // GR_640
6783                 { "Team 1",                             1308,           20,     272,    UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_TEAM0].button },
6784                 { "Team 2",                             1309,           73,     272,    UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_TEAM1].button },
6785                 { "Pilot",                              1310,           134,    272,    UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_PILOT_INFO].button },
6786                 { "Info",                               1311,           134,    283,    UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_PILOT_INFO].button },
6787                 { "Cancel",                             387,            570,    414,    UI_XSTR_COLOR_PINK, -1, &Multi_jw_buttons[0][MJW_CANCEL].button },
6788                 { "Players",                    1269,           38,     8,              UI_XSTR_COLOR_GREEN, -1, NULL },
6789                 { "Choose Team",                1312,           27,     231,    UI_XSTR_COLOR_GREEN, -1, NULL },
6790         },
6791         { // GR_1024
6792                 { "Team 1",                             1308,           47,     435,    UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_TEAM0].button },
6793                 { "Team 2",                             1309,           133,    435,    UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_TEAM1].button },
6794                 { "Pilot",                              1310,           225,    435,    UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_PILOT_INFO].button },
6795                 { "Info",                               1311,           225,    446,    UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_PILOT_INFO].button },
6796                 { "Cancel",                             387,            931,    667,    UI_XSTR_COLOR_PINK, -1, &Multi_jw_buttons[1][MJW_CANCEL].button },
6797                 { "Players",                    1269,           165,    12,     UI_XSTR_COLOR_GREEN, -1, NULL },
6798                 { "Choose Team",                1312,           45,     373,    UI_XSTR_COLOR_GREEN, -1, NULL },
6799         }
6800 };
6801
6802 int Mjw_players_coords[GR_NUM_RESOLUTIONS][4] = {
6803         { // GR_640
6804                 29, 18, 153, 210
6805         },
6806         { // GR_1024
6807                 46, 29, 254, 336
6808         }
6809 };
6810
6811 int Mjw_mission_name_coords[GR_NUM_RESOLUTIONS][2] = {
6812         { // GR_640
6813                 5, 380
6814         },
6815         { // GR_1024
6816                 47, 618
6817         }
6818 };
6819
6820 // squad war checkbox
6821 UI_CHECKBOX     Multi_jw_sw_checkbox;
6822 char *Multi_jw_sw_checkbox_fname[GR_NUM_RESOLUTIONS] = {
6823         "MC_SW_00",
6824         "MC_SW_00",
6825 };
6826 int Multi_jw_sw_checkbox_coords[GR_NUM_RESOLUTIONS][2] = {
6827         { // GR_640
6828                 6, 285
6829         },
6830         { // GR_1024
6831                 18, 450
6832         }
6833 };
6834 int Multi_jw_sw_checkbox_text[GR_NUM_RESOLUTIONS][2] = {
6835         { // GR_640
6836                 6, 305
6837         },
6838         { // GR_640
6839                 18, 470
6840         },
6841 };
6842
6843
6844 // player list control thingie defs
6845 #define MULTI_JW_PLIST_MAX_DISPLAY              19
6846 int Multi_jw_plist_select_flag;                                 // indicates whether we currently have a selected player
6847 short Multi_jw_plist_select_id;                         // id of the current selected player
6848 UI_BUTTON Multi_jw_plist_select_button;         // for selecting a player
6849
6850 int Multi_jw_should_show_popup = 0;
6851
6852 // LOCAL function definitions
6853 void multi_jw_check_buttons();
6854 void multi_jw_button_pressed(int n);
6855 void multi_jw_do_netstuff();
6856 void multi_jw_scroll_players_up();
6857 void multi_jw_scroll_players_down();
6858 void multi_jw_plist_process();
6859 void multi_jw_plist_blit_normal();
6860 void multi_jw_plist_blit_team();
6861 short multi_jw_get_mouse_id();
6862
6863 void multi_game_client_setup_init()
6864 {
6865         int idx;
6866
6867         // create the interface window
6868         Multi_jw_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
6869         Multi_jw_window.set_mask_bmap(Multi_jw_bitmap_mask_fname[gr_screen.res]);
6870
6871         // load the background bitmap
6872         Multi_jw_bitmap = bm_load(Multi_jw_bitmap_fname[gr_screen.res]);
6873         if(Multi_jw_bitmap < 0){
6874                 // we failed to load the bitmap - this is very bad
6875                 Int3();
6876         }
6877
6878         // initialize the player list data      
6879         Multi_jw_plist_select_flag = 0;
6880         Multi_jw_plist_select_id = -1;  
6881         
6882         // kill any old instances of the chatbox and create a new one
6883         chatbox_close();
6884         chatbox_create(CHATBOX_FLAG_BIG | CHATBOX_FLAG_DRAW_BOX | CHATBOX_FLAG_BUTTONS);
6885
6886         // initialize the common notification messaging
6887         multi_common_notify_init();
6888
6889         // initialize the common mission info display area.
6890         multi_common_set_text("");      
6891
6892         // use the common interface palette
6893         multi_common_set_palette();     
6894
6895         // create the interface buttons
6896         for(idx=0; idx<MULTI_JW_NUM_BUTTONS; idx++){
6897                 // create the object
6898                 Multi_jw_buttons[gr_screen.res][idx].button.create(&Multi_jw_window, "", Multi_jw_buttons[gr_screen.res][idx].x, Multi_jw_buttons[gr_screen.res][idx].y, 1, 1, 0, 1);
6899
6900                 // set the sound to play when highlighted
6901                 Multi_jw_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
6902
6903                 // set the ani for the button
6904                 Multi_jw_buttons[gr_screen.res][idx].button.set_bmaps(Multi_jw_buttons[gr_screen.res][idx].filename);
6905
6906                 // set the hotspot
6907                 Multi_jw_buttons[gr_screen.res][idx].button.link_hotspot(Multi_jw_buttons[gr_screen.res][idx].hotspot);
6908         }               
6909
6910         // if this is a PXO game, enable the squadwar checkbox  
6911         Multi_jw_sw_checkbox.create(&Multi_jw_window, "", Multi_jw_sw_checkbox_coords[gr_screen.res][0], Multi_jw_sw_checkbox_coords[gr_screen.res][1], 0);
6912         Multi_jw_sw_checkbox.set_bmaps(Multi_jw_sw_checkbox_fname[gr_screen.res], 6, 0);
6913         Multi_jw_sw_checkbox.disable();
6914         if(!MULTI_IS_TRACKER_GAME){
6915                 Multi_jw_sw_checkbox.hide();            
6916         }
6917
6918         // create all xstrs
6919         for(idx=0; idx<MULTI_JW_NUM_TEXT; idx++){
6920                 Multi_jw_window.add_XSTR(&Multi_jw_text[gr_screen.res][idx]);
6921         }
6922         
6923         // create the player select list button and hide it
6924         Multi_jw_plist_select_button.create(&Multi_jw_window, "", Mjw_players_coords[gr_screen.res][MJW_X_COORD], Mjw_players_coords[gr_screen.res][MJW_Y_COORD], Mjw_players_coords[gr_screen.res][MJW_W_COORD], Mjw_players_coords[gr_screen.res][MJW_H_COORD], 0, 1);
6925         Multi_jw_plist_select_button.hide();
6926
6927         // set hotkeys
6928         Multi_jw_buttons[gr_screen.res][MJW_CANCEL].button.set_hotkey(KEY_ESC); 
6929
6930         // remove campaign flags
6931         Game_mode &= ~(GM_CAMPAIGN_MODE);
6932
6933         // tell the server we have finished joining
6934         Net_player->state = NETPLAYER_STATE_JOINED;
6935         send_netplayer_update_packet(); 
6936
6937         // NETLOG
6938         ml_printf(NOX("Joined netgame %s"), Netgame.name);
6939
6940         // send any appropriate files
6941         multi_data_send_my_junk();      
6942 }
6943
6944 void multi_game_client_setup_do_frame()
6945 {
6946         int player_index;
6947         int k = chatbox_process();
6948         char mission_text[255];
6949         k = Multi_jw_window.process(k,0);       
6950
6951         Multi_jw_should_show_popup = 0;
6952
6953         // process any button clicks
6954         multi_jw_check_buttons();
6955
6956         // do any network related stuff
6957         multi_jw_do_netstuff();                 
6958
6959         // draw the background, etc
6960         gr_reset_clip();
6961         GR_MAYBE_CLEAR_RES(Multi_jw_bitmap);
6962         if(Multi_jw_bitmap != -1){              
6963                 gr_set_bitmap(Multi_jw_bitmap);
6964                 gr_bitmap(0,0);
6965         }
6966
6967         // if we're not in team vs. team mode, don't draw the team buttons
6968         if(!(Netgame.type_flags & NG_TYPE_TEAM)){
6969                 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.hide();
6970                 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.hide();
6971                 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.disable();
6972                 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.disable();
6973         } else {
6974                 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.enable();
6975                 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.enable();
6976                 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.unhide();
6977                 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.unhide();             
6978         }
6979
6980         if(MULTI_IS_TRACKER_GAME){
6981                 // maybe check the squadwar button
6982                 if(Netgame.type_flags & NG_TYPE_SW){
6983                         Multi_jw_sw_checkbox.set_state(1);
6984                         gr_set_color_fast(&Color_bright);
6985                 } else {
6986                         Multi_jw_sw_checkbox.set_state(0);
6987                         gr_set_color_fast(&Color_normal);
6988                 }
6989                                 
6990                 gr_string(Multi_jw_sw_checkbox_text[gr_screen.res][0], Multi_jw_sw_checkbox_text[gr_screen.res][1], "SquadWar");
6991         }       
6992
6993         // draw the UI window
6994         Multi_jw_window.draw(); 
6995
6996         // process and display the player list  
6997         // NOTE : this must be done before the buttons are checked to insure that a player hasn't dropped 
6998         multi_jw_plist_process();
6999         if(Netgame.type_flags & NG_TYPE_TEAM){
7000                 multi_jw_plist_blit_team();
7001         } else {
7002                 multi_jw_plist_blit_normal();
7003         }
7004                 
7005         // display any text in the info area
7006         multi_common_render_text();
7007
7008         // display any pending notification messages
7009         multi_common_notify_do();
7010
7011         // blit the mission filename if possible
7012         if(Netgame.campaign_mode){
7013                 if(strlen(Netgame.campaign_name) > 0){                  
7014                         strcpy(mission_text,Netgame.campaign_name);
7015                         
7016                         if(strlen(Netgame.title) > 0){
7017                                 strcat(mission_text,", ");
7018                                 strcat(mission_text,Netgame.title);
7019                         }
7020
7021                         gr_set_color_fast(&Color_bright_white);
7022                         gr_string(Mjw_mission_name_coords[gr_screen.res][MJW_X_COORD],Mjw_mission_name_coords[gr_screen.res][MJW_Y_COORD],mission_text);
7023                 }                                                               
7024         } else {
7025                 if(strlen(Netgame.mission_name) > 0){                   
7026                         strcpy(mission_text,Netgame.mission_name);
7027
7028                         if(strlen(Netgame.title) > 0){
7029                                 strcat(mission_text,", ");
7030                                 strcat(mission_text,Netgame.title);
7031                         }                       
7032
7033                         gr_set_color_fast(&Color_bright_white);
7034                         gr_string(Mjw_mission_name_coords[gr_screen.res][MJW_X_COORD],Mjw_mission_name_coords[gr_screen.res][MJW_Y_COORD],mission_text);
7035                 }
7036         }       
7037
7038         // process and show the chatbox thingie 
7039         chatbox_render();
7040
7041         // draw tooltips
7042         Multi_jw_window.draw_tooltip();
7043
7044         // display the voice status indicator
7045         multi_common_voice_display_status();
7046         
7047         // flip the buffer
7048         gr_flip();      
7049
7050         // if we're supposed to be displaying a pilot info popup
7051         if(Multi_jw_should_show_popup){
7052                 player_index = find_player_id(Multi_jw_plist_select_id);
7053                 if(player_index != -1){                 
7054                         multi_pinfo_popup(&Net_players[player_index]);
7055                 }               
7056         }
7057 }
7058
7059 void multi_game_client_setup_close()
7060 {
7061         // unload any bitmaps
7062         if(!bm_unload(Multi_jw_bitmap)){
7063                 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_jw_bitmap_fname[gr_screen.res]));
7064         }               
7065
7066         // destroy the chatbox
7067         // chatbox_close();
7068         
7069         // destroy the UI_WINDOW
7070         Multi_jw_window.destroy();
7071
7072         // play a sound.
7073         if(Netgame.game_state == NETGAME_STATE_MISSION_SYNC){
7074                 gamesnd_play_iface(SND_COMMIT_PRESSED);
7075         }
7076 }
7077
7078
7079 void multi_jw_check_buttons()
7080 {
7081         int idx;
7082         for(idx=0;idx<MULTI_JW_NUM_BUTTONS;idx++){
7083                 // we only really need to check for one button pressed at a time, so we can break after 
7084                 // finding one.
7085                 if(Multi_jw_buttons[gr_screen.res][idx].button.pressed()){
7086                         multi_jw_button_pressed(idx);
7087                         break;
7088                 }
7089         }
7090 }
7091
7092 void multi_jw_button_pressed(int n)
7093 {
7094         switch(n){      
7095         case MJW_CANCEL:
7096                 gamesnd_play_iface(SND_USER_SELECT);            
7097                 multi_quit_game(PROMPT_CLIENT);         
7098                 break;  
7099         case MJW_SCROLL_PLAYERS_UP:
7100                 multi_jw_scroll_players_up();
7101                 break;
7102         case MJW_SCROLL_PLAYERS_DOWN:
7103                 multi_jw_scroll_players_down();
7104                 break;  
7105         case MJW_SCROLL_INFO_UP:
7106                 multi_common_scroll_text_up();
7107                 break;
7108         case MJW_SCROLL_INFO_DOWN:
7109                 multi_common_scroll_text_down();
7110                 break;
7111         
7112         // request to set myself to team 0
7113         case MJW_TEAM0:
7114                 gamesnd_play_iface(SND_USER_SELECT);
7115                 multi_team_set_team(Net_player,0);
7116                 break;
7117
7118         // request to set myself to team 1
7119         case MJW_TEAM1:
7120                 gamesnd_play_iface(SND_USER_SELECT);
7121                 multi_team_set_team(Net_player,1);
7122                 break;
7123
7124         // pilot info popup
7125         case MJW_PILOT_INFO:
7126                 Multi_jw_should_show_popup = 1;
7127                 break;
7128
7129         default :
7130                 multi_common_add_notify(XSTR("Not implemented yet!",760));
7131                 break;
7132         }
7133 }
7134
7135 // do stuff like pinging servers, sending out requests, etc
7136 void multi_jw_do_netstuff()
7137 {
7138 }
7139
7140 void multi_jw_scroll_players_up()
7141 {
7142         gamesnd_play_iface(SND_GENERAL_FAIL);
7143 }
7144
7145 // scroll down through the player list
7146 void multi_jw_scroll_players_down()
7147 {       
7148         gamesnd_play_iface(SND_GENERAL_FAIL);
7149 }
7150
7151 void multi_jw_plist_process()
7152 {
7153         int test_count,player_index,idx;
7154         
7155         // first determine if there are 0 players in the game. This should never happen since the host is _always_ in the game
7156         test_count = 0;
7157         for(idx=0;idx<MAX_PLAYERS;idx++){
7158                 // count anyone except the standalone server (if applicable)
7159                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7160                         test_count++;
7161                 }
7162         }
7163         if(test_count <= 0){
7164                 return;
7165         }
7166         
7167         // if we had a selected item but that player has left, select myself instead
7168         if(Multi_jw_plist_select_flag){
7169                 player_index = find_player_id(Multi_jw_plist_select_id);
7170                 if(player_index == -1){
7171                         Multi_jw_plist_select_id = Net_player->player_id;                                               
7172                 }
7173         } else {
7174                 Multi_jw_plist_select_flag = 1;
7175                 Multi_jw_plist_select_id = Net_player->player_id;               
7176         }
7177                 
7178         // if the player has clicked somewhere in the player list area
7179         if(Multi_jw_plist_select_button.pressed()){
7180                 short player_id;
7181                 int player_index;
7182         
7183                 player_id = multi_jw_get_mouse_id();
7184                 player_index = find_player_id(player_id);
7185                 if(player_index != -1){
7186                         Multi_jw_plist_select_id = player_id;
7187                         Multi_jw_plist_select_flag = 1;
7188                 }
7189         }
7190 }
7191
7192 void multi_jw_plist_blit_normal()
7193 {
7194         int idx;                
7195         char str[CALLSIGN_LEN+1];
7196         int y_start = Mjw_players_coords[gr_screen.res][MJW_Y_COORD];   
7197         int total_offset;
7198
7199         // display all the players      
7200         for(idx=0;idx<MAX_PLAYERS;idx++){               
7201                 // reset total offset
7202                 total_offset = 0;
7203
7204                 // count anyone except the standalone server (if applicable)
7205                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7206                         // highlight him if he's the host                       
7207                         if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7208                                 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7209                                         gr_set_color_fast(&Color_text_active_hi);
7210                                 } else {
7211                                         gr_set_color_fast(&Color_bright);
7212                                 }
7213                         } else {
7214                                 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7215                                         gr_set_color_fast(&Color_text_active);
7216                                 } else {
7217                                         gr_set_color_fast(&Color_text_normal);
7218                                 }
7219                         }
7220
7221                         // optionally draw his CD status
7222                         if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7223                                 gr_set_bitmap(Multi_common_icons[MICON_CD]);
7224                                 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7225
7226                                 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7227                         }                       
7228                         
7229                         // make sure the string will fit, then display it
7230                         strcpy(str,Net_players[idx].player->callsign);
7231                         if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
7232                                 strcat(str,"(0)");
7233                         }
7234                         gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7235                         gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7236
7237                         y_start += 10;                  
7238                 }
7239         }               
7240 }
7241
7242 void multi_jw_plist_blit_team()
7243 {
7244         int idx;                
7245         char str[CALLSIGN_LEN+1];
7246         int y_start = Mjw_players_coords[gr_screen.res][MJW_Y_COORD];   
7247         int total_offset;
7248
7249         // always blit the proper team button based on _my_ team status
7250         if(Net_player->p_info.team == 0){
7251                 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.draw_forced(2);
7252         } else {
7253                 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.draw_forced(2);
7254         }
7255
7256         // display all the red players first
7257         for(idx=0;idx<MAX_PLAYERS;idx++){
7258                 // reset total offset
7259                 total_offset = 0;
7260
7261                 // count anyone except the standalone server (if applicable)
7262                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
7263                         // highlight him if he's the host                       
7264                         if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7265                                 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7266                                         gr_set_color_fast(&Color_text_active_hi);                                       
7267                                 } else {
7268                                         gr_set_color_fast(&Color_bright);
7269                                 }
7270                         } else {
7271                                 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7272                                         gr_set_color_fast(&Color_text_active);                                  
7273                                 } else {
7274                                         gr_set_color_fast(&Color_text_normal);
7275                                 }
7276                         }
7277
7278                         // optionally draw his CD status
7279                         if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7280                                 gr_set_bitmap(Multi_common_icons[MICON_CD]);
7281                                 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7282
7283                                 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7284                         }                       
7285
7286                         // blit the red team indicator
7287                         if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
7288                                 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
7289                                         gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT]);
7290                                         gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7291
7292                                         total_offset += Multi_common_icon_dims[MICON_TEAM0_SELECT][0] + 1;
7293                                 }                               
7294                         } else {
7295                                 if(Multi_common_icons[MICON_TEAM0] != -1){
7296                                         gr_set_bitmap(Multi_common_icons[MICON_TEAM0]);
7297                                         gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7298
7299                                         total_offset += Multi_common_icon_dims[MICON_TEAM0][0] + 1;
7300                                 }
7301                         }
7302
7303                         // make sure the string will fit
7304                         strcpy(str,Net_players[idx].player->callsign);
7305                         gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7306
7307                         // display him in the correct half of the list depending on his team
7308                         gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7309                         y_start += 10;
7310                 }
7311         }       
7312         
7313         // display all the green players next
7314         for(idx=0;idx<MAX_PLAYERS;idx++){
7315                 // reset total offset
7316                 total_offset = 0;
7317
7318                 // count anyone except the standalone server (if applicable)
7319                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
7320                         // highlight him if he's the host                       
7321                         if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7322                                 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7323                                         gr_set_color_fast(&Color_text_active_hi);                               
7324                                 } else {
7325                                         gr_set_color_fast(&Color_bright);
7326                                 }
7327                         } else {
7328                                 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7329                                         gr_set_color_fast(&Color_text_active);                                  
7330                                 } else {
7331                                         gr_set_color_fast(&Color_text_normal);
7332                                 }
7333                         }
7334
7335                         // optionally draw his CD status
7336                         if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7337                                 gr_set_bitmap(Multi_common_icons[MICON_CD]);
7338                                 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7339
7340                                 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7341                         }                       
7342
7343                         // blit the red team indicator
7344                         if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
7345                                 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
7346                                         gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT]);
7347                                         gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7348
7349                                         total_offset += Multi_common_icon_dims[MICON_TEAM1_SELECT][0] + 1;
7350                                 }
7351                         } else {
7352                                 if(Multi_common_icons[MICON_TEAM1] != -1){
7353                                         gr_set_bitmap(Multi_common_icons[MICON_TEAM1]);
7354                                         gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7355
7356                                         total_offset += Multi_common_icon_dims[MICON_TEAM1][0] + 1;
7357                                 }
7358                         }
7359
7360                         // make sure the string will fit
7361                         strcpy(str,Net_players[idx].player->callsign);
7362                         if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
7363                                 strcat(str,"(0)");
7364                         }
7365                         gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7366
7367                         // display him in the correct half of the list depending on his team
7368                         gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7369                         y_start += 10;
7370                 }
7371         }                       
7372 }
7373
7374 void multi_jw_handle_join(net_player *pl)
7375 {
7376         // for now just play a bloop sound
7377         gamesnd_play_iface(SND_ICON_DROP_ON_WING);
7378 }
7379
7380 short multi_jw_get_mouse_id()
7381 {
7382         // determine where he clicked (y pixel value)
7383         int y,nth,idx;          
7384         Multi_jw_plist_select_button.get_mouse_pos(NULL,&y);
7385
7386         // select things a little differently if we're in team vs. team or non-team vs. team mode                       
7387         nth = (y / 10);                 
7388         if(Netgame.type_flags & NG_TYPE_TEAM){
7389                 int player_index = -1;
7390
7391                 // look through all of team red first
7392                 for(idx=0;idx<MAX_PLAYERS;idx++){
7393                         if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){                            
7394                                 nth--;
7395
7396                                 // if this is the _nth_ guy 
7397                                 if(nth < 0){
7398                                         player_index = idx;                                             
7399                                         break;
7400                                 }
7401                         }
7402                 }
7403                         
7404                 // if we still haven't found him yet, look through the green team
7405                 if(player_index == -1){
7406                         for(idx=0;idx<MAX_PLAYERS;idx++){
7407                                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){                                    
7408                                         nth--;
7409                                         // if this is the _nth_ guy 
7410                                         if(nth < 0){
7411                                                 player_index = idx;                                             
7412                                                 break;
7413                                         }
7414                                 }
7415                         }
7416                 }
7417                 if(player_index != -1){
7418                         return Net_players[idx].player_id;                      
7419                 }               
7420         } else {
7421                 // select the nth active player if possible, disregarding the standalone server
7422                 for(idx=0;idx<MAX_PLAYERS;idx++){
7423                         if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7424                                 nth--;
7425
7426                                 // if this is the _nth_ guy 
7427                                 if(nth < 0){
7428                                         return Net_players[idx].player_id;                                      
7429                                 }
7430                         }
7431                 }
7432                 return -1;
7433         }                               
7434
7435         return -1;
7436 }
7437
7438
7439 // -------------------------------------------------------------------------------------------------------------
7440 // 
7441 // MULTIPLAYER WAIT/SYNCH SCREEN 
7442 //
7443
7444 #define MULTI_SYNC_HOST_COUNT                           4               // host uses 4 buttons (and sometimes 5)
7445 #define MULTI_SYNC_CLIENT_COUNT                 3               // client only uses 3 buttons
7446
7447 char *Multi_sync_bitmap_fname[GR_NUM_RESOLUTIONS] = {
7448         "MultiSynch",           // GR_640
7449         "2_MultiSynch"          // GR_1024
7450 };
7451
7452 char *Multi_sync_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
7453         "MultiSynch-M",         // GR_640
7454         "2_MultiSynch-M"                        // GR_1024
7455 };
7456
7457 //XSTR:ON
7458
7459 // constants for coordinate lookup
7460 #define MS_X_COORD 0
7461 #define MS_Y_COORD 1
7462 #define MS_W_COORD 2
7463 #define MS_H_COORD 3
7464
7465 UI_WINDOW Multi_sync_window;                                                                                            // the window object for the join screen
7466 int Multi_sync_button_count;                                                                                            // the # of buttons to use for this instance of mission sync
7467 int Multi_sync_bitmap;                                                                                                          // the background bitmap
7468
7469 // button defs
7470 #define MULTI_SYNC_NUM_BUTTONS                  5
7471 #define MS_SCROLL_INFO_UP                                       0
7472 #define MS_SCROLL_INFO_DOWN                             1
7473 #define MS_CANCEL                                                               2
7474 #define MS_KICK                                                         3
7475 #define MS_LAUNCH                                                               4
7476 ui_button_info Multi_sync_buttons[GR_NUM_RESOLUTIONS][MULTI_SYNC_NUM_BUTTONS] = {
7477         { // GR_640
7478                 ui_button_info("MS_00",         1,              404,    -1,     -1,     0),
7479                 ui_button_info("MS_01",         1,              446,    -1,     -1,     1),
7480                 ui_button_info("MS_03",         518,    426,    519,    416,    3),
7481                 ui_button_info("MS_02",         469,    426,    479,    416,    2),             
7482                 ui_button_info("MS_04",         571,    420,    577,    416,    4),
7483         },
7484         { // GR_1024
7485                 ui_button_info("2_MS_00",               2,              647,    -1,     -1,     0),
7486                 ui_button_info("2_MS_01",               2,              713,    -1,     -1,     1),
7487                 ui_button_info("2_MS_03",               829,    682,    831,    667,    3),
7488                 ui_button_info("2_MS_02",               751,    682,    766,    667,    2),             
7489                 ui_button_info("2_MS_04",               914,    672,    924,    667,    4),
7490         }
7491 };
7492
7493 // text
7494 #define MULTI_SYNC_NUM_TEXT                             5
7495 #define MST_KICK                                                                0
7496 #define MST_LAUNCH                                                      2
7497 UI_XSTR Multi_sync_text[GR_NUM_RESOLUTIONS][MULTI_SYNC_NUM_TEXT] = {
7498         { // GR_640
7499                 { "Kick",               1266,           479,    416,    UI_XSTR_COLOR_PINK,     -1, &Multi_sync_buttons[0][MS_KICK].button },
7500                 { "Cancel",             387,            519,    416,    UI_XSTR_COLOR_PINK,     -1, &Multi_sync_buttons[0][MS_CANCEL].button },
7501                 { "Launch",             801,            577,    416,    UI_XSTR_COLOR_PINK,     -1, &Multi_sync_buttons[0][MS_LAUNCH].button },
7502                 { "Players",    1269,           23,     133,    UI_XSTR_COLOR_GREEN,    -1, NULL },
7503                 { "Status",             1304,           228,    133,    UI_XSTR_COLOR_GREEN,    -1, NULL }
7504         },
7505         { // GR_1024
7506                 { "Kick",               1266,           766,    667,    UI_XSTR_COLOR_PINK,     -1, &Multi_sync_buttons[1][MS_KICK].button },
7507                 { "Cancel",             387,            831,    667,    UI_XSTR_COLOR_PINK,     -1, &Multi_sync_buttons[1][MS_CANCEL].button },
7508                 { "Launch",             801,            924,    667,    UI_XSTR_COLOR_PINK,     -1, &Multi_sync_buttons[1][MS_LAUNCH].button },
7509                 { "Players",    1269,           38,     214,    UI_XSTR_COLOR_GREEN,    -1, NULL },
7510                 { "Status",             1304,           366,    214,    UI_XSTR_COLOR_GREEN,    -1, NULL }
7511         }
7512 };
7513
7514 // player name
7515 int Ms_status_coords[GR_NUM_RESOLUTIONS][4] = {
7516         { // GR_640
7517                 38, 150, 581, 220
7518         },
7519         { // GR_1024
7520                 38, 228, 958, 367
7521         }
7522 };
7523
7524 // player status coords
7525 int Ms_status2_coords[GR_NUM_RESOLUTIONS][4] = {
7526         { // GR_640
7527                 228, 150, 391, 220
7528         },
7529         { // GR_1024
7530                 370, 228, 626, 367
7531         }
7532 };
7533
7534 int Ms_cd_icon_offset[GR_NUM_RESOLUTIONS] = {
7535         10,             // GR_640
7536         10                      // GR_1024
7537 };
7538
7539 int Ms_team_icon_offset[GR_NUM_RESOLUTIONS] = {
7540         38,             // GR_640
7541         38                      // GR_1024
7542 };
7543
7544 // player currently selected, index into Net_players[]
7545 int Multi_sync_player_select = -1;
7546
7547 // player list control thingie defs
7548 #define MULTI_SYNC_PLIST_MAX_DISPLAY    15
7549 int Multi_sync_plist_start;             // where to start displaying from
7550 int Multi_sync_plist_count;             // how many we have
7551
7552 // list select button
7553 UI_BUTTON Multi_sync_plist_button;
7554
7555 int Multi_sync_mode = -1;
7556
7557 #define MULTI_SYNC_COUNTDOWN_TIME                       5                               // in seconds
7558 float Multi_sync_countdown_timer;
7559 int Multi_sync_countdown = -1;
7560
7561 int Multi_launch_button_created;
7562
7563 //XSTR:OFF
7564 // countdown animation timer
7565 char* Multi_sync_countdown_fname[GR_NUM_RESOLUTIONS] = {
7566         "Count",                // GR_640
7567         "2_Count"               // GR_1024
7568 };
7569
7570 int Multi_sync_countdown_coords[GR_NUM_RESOLUTIONS][2] = {
7571         {
7572                 0, 0            // GR_640
7573         },              
7574         {
7575                 0, 0            // GR_1024
7576         }
7577 };
7578
7579 //XSTR:ON
7580
7581 anim *Multi_sync_countdown_anim = NULL;
7582 anim_instance *Multi_sync_countdown_instance = NULL;
7583
7584
7585 // PREBRIEFING STUFF
7586 // syncing flags used by the server
7587 int Mission_sync_flags = 0;
7588 #define MS_FLAG_SENT_FILESIG                    (1<<0)  // sent filesig requests
7589 #define MS_FLAG_SENT_LOAD                               (1<<1)  // sent load packets
7590 #define MS_FLAG_PUSHED_BRIEFING         (1<<2)  // pushed everyone else into the briefing
7591 #define MS_FLAG_POST_DATA                               (1<<3)  // sent the post data block
7592 #define MS_FLAG_WSS_SLOTS                               (1<<4)  // all players have received wss slot data
7593 #define MS_FLAG_PSETTINGS                               (1<<5)  // send the player settings packet
7594 #define MS_FLAG_MT_STATS_START          (1<<6)  // server has started getting player stats from the tracker
7595 #define MS_FLAG_MT_STATS_DONE                   (1<<7)  // server has finished getting player stats from the tracker (success or fail)
7596 #define MS_FLAG_TS_SLOTS                                (1<<8)  // team/ship slots have been sent
7597 #define MS_FLAG_DATA_DONE                               (1<<9)  // done transferring all necessary data
7598 #define MS_FLAG_CAMP_DONE                               (1<<10) // send campaign pool/goal/event stuff
7599
7600 // POSTBRIEFING STUFF
7601 int Multi_state_timestamp;
7602 int Multi_sync_launch_pressed;
7603
7604 // LOCAL function definitions
7605 void multi_sync_check_buttons();
7606 void multi_sync_button_pressed(int n);
7607 void multi_sync_scroll_info_up();
7608 void multi_sync_scroll_info_down();
7609 void multi_sync_display_name(char *name,int index,int np_index);                // display info on the left hand portion of the status window thingie
7610 void multi_sync_display_status(char *status,int index);                                 // display info on the right hand portion of the status window thingie
7611 void multi_sync_force_start_pre();
7612 void multi_sync_force_start_post();
7613 void multi_sync_launch();
7614 void multi_sync_create_launch_button();
7615 void multi_sync_blit_screen_all();
7616 void multi_sync_handle_plist();
7617
7618 void multi_sync_common_init();
7619 void multi_sync_common_do();
7620 void multi_sync_common_close();
7621
7622 void multi_sync_pre_init();
7623 void multi_sync_pre_do();
7624 void multi_sync_pre_close();
7625
7626 void multi_sync_post_init();
7627 void multi_sync_post_do();
7628 void multi_sync_post_close();
7629
7630 int Sync_test = 1;
7631
7632
7633 // perform the correct init functions
7634 void multi_sync_init()
7635 {       
7636         Multi_sync_countdown = -1;
7637
7638         Sync_test = 1;
7639
7640         // reset all timestamp
7641         multi_reset_timestamps();
7642
7643         extern int Player_multi_died_check;
7644         Player_multi_died_check = -1;
7645
7646         if(!(Game_mode & GM_STANDALONE_SERVER)){
7647                 multi_sync_common_init();
7648         }
7649         
7650         switch(Multi_sync_mode){
7651         case MULTI_SYNC_PRE_BRIEFING:
7652                 multi_sync_pre_init();
7653                 break;
7654         case MULTI_SYNC_POST_BRIEFING:
7655                 multi_sync_post_init();
7656                 break;
7657         case MULTI_SYNC_INGAME:
7658                 multi_ingame_sync_init();
7659                 break;
7660         }
7661 }
7662
7663 // perform the correct do frame functions
7664 void multi_sync_do()
7665 {
7666         if(!(Game_mode & GM_STANDALONE_SERVER)){
7667                 multi_sync_common_do();
7668         }
7669
7670         // if the netgame is ending, don't do any sync processing
7671         if(multi_endgame_ending()){
7672                 return;
7673         }
7674
7675         // process appropriateliy
7676         switch(Multi_sync_mode){
7677         case MULTI_SYNC_PRE_BRIEFING:           
7678                 multi_sync_pre_do();            
7679                 break;
7680         case MULTI_SYNC_POST_BRIEFING:
7681                 multi_sync_post_do();
7682                 break;
7683         case MULTI_SYNC_INGAME:
7684                 multi_ingame_sync_do();
7685
7686                 gr_reset_clip();                
7687                 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
7688                 if(Multi_sync_bitmap != -1){
7689                         gr_set_bitmap(Multi_sync_bitmap);
7690                         gr_bitmap(0,0);
7691                 }
7692                 Multi_sync_window.draw();
7693
7694                 multi_sync_blit_screen_all();
7695
7696                 gr_flip();
7697                 break;
7698         }       
7699 }
7700
7701 // perform the correct close functions
7702 void multi_sync_close()
7703 {
7704         switch(Multi_sync_mode){
7705         case MULTI_SYNC_PRE_BRIEFING:
7706                 multi_sync_pre_close();
7707                 break;
7708         case MULTI_SYNC_POST_BRIEFING:
7709                 multi_sync_post_close();
7710                 break;
7711         case MULTI_SYNC_INGAME:
7712                 multi_ingame_sync_close();
7713                 break;
7714         }
7715         
7716         if(!(Game_mode & GM_STANDALONE_SERVER)){
7717                 multi_sync_common_close();
7718         }
7719 }
7720
7721 char *multi_sync_tooltip_handler(char *str)
7722 {
7723         if (!stricmp(str, NOX("@launch"))) {
7724                 if (Multi_launch_button_created){
7725                         return XSTR("Launch",801);
7726                 }
7727         }
7728
7729         return NULL;
7730 }
7731
7732 void multi_sync_common_init()
7733 {
7734         int idx;
7735
7736         // create the interface window
7737         Multi_sync_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
7738         Multi_sync_window.set_mask_bmap(Multi_sync_bitmap_mask_fname[gr_screen.res]);
7739         Multi_sync_window.tooltip_handler = multi_sync_tooltip_handler;
7740
7741         // load the background bitmap
7742         Multi_sync_bitmap = bm_load(Multi_sync_bitmap_fname[gr_screen.res]);
7743         if (Multi_sync_bitmap < 0) {
7744                 // we failed to load the bitmap - this is very bad
7745                 Int3();
7746         }
7747
7748         // initialize the player list data
7749         Multi_sync_plist_start = 0;
7750         Multi_sync_plist_count = 1;                     // we can pretty safely assume that there's one player in the game - me.        
7751
7752         Multi_launch_button_created = 0;        
7753
7754         // create the chatbox thingie   (shouldn't be necesary to do this, but we'll put it in for good measure)
7755         chatbox_create();
7756
7757         // force the chatbox to be small
7758         chatbox_force_small();
7759
7760         // initialize the common notification messaging
7761         multi_common_notify_init();
7762
7763         // initialize the common mission info display area.
7764         multi_common_set_text("");      
7765
7766         // use the common interface palette
7767         multi_common_set_palette();
7768
7769         // don't select any player yet.
7770         Multi_sync_player_select = -1;
7771         
7772         // determine how many of the 5 buttons to create
7773         if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
7774                 Multi_sync_button_count = MULTI_SYNC_HOST_COUNT;                
7775         } else {
7776                 Multi_sync_button_count = MULTI_SYNC_CLIENT_COUNT;
7777         }
7778         // create the interface buttons 
7779         for(idx=0; idx<Multi_sync_button_count; idx++){
7780                 // create the object
7781                 Multi_sync_buttons[gr_screen.res][idx].button.create(&Multi_sync_window, "", Multi_sync_buttons[gr_screen.res][idx].x, Multi_sync_buttons[gr_screen.res][idx].y, 1, 1, 0, 1);
7782
7783                 // set the sound to play when highlighted
7784                 Multi_sync_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
7785
7786                 // set the ani for the button
7787                 // this wierdness is necessary because cancel and kick buttons aren't drawn on the background bitmap,
7788                 //   so we have to load in frame 0, too (the file should exist)
7789                 if ((idx == MS_CANCEL) || (idx == MS_KICK) || (idx == MS_LAUNCH)) {
7790                         Multi_sync_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sync_buttons[gr_screen.res][idx].filename, 3, 0);
7791                 } else {
7792                         Multi_sync_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sync_buttons[gr_screen.res][idx].filename);
7793                 }
7794
7795                 // set the hotspot
7796                 Multi_sync_buttons[gr_screen.res][idx].button.link_hotspot(Multi_sync_buttons[gr_screen.res][idx].hotspot);
7797         }               
7798
7799         // add xstrs
7800         for(idx=0; idx<MULTI_SYNC_NUM_TEXT; idx++) {
7801                 // don't create the "launch" button text just yet
7802                 if(idx == MST_LAUNCH) {
7803                         continue;
7804                 }
7805                 // multiplayer clients should ignore the kick button
7806                 if(!MULTIPLAYER_MASTER && !MULTIPLAYER_HOST && (idx == MST_KICK)) {
7807                         continue;
7808                 }
7809
7810                 Multi_sync_window.add_XSTR(&Multi_sync_text[gr_screen.res][idx]);
7811         }
7812
7813         // create the player list select button and hide it
7814         Multi_sync_plist_button.create(&Multi_sync_window, "", Ms_status_coords[gr_screen.res][MS_X_COORD], Ms_status_coords[gr_screen.res][MS_Y_COORD], Ms_status_coords[gr_screen.res][MS_W_COORD], Ms_status_coords[gr_screen.res][MS_H_COORD], 0, 1);
7815         Multi_sync_plist_button.hide();
7816
7817         // set up hotkeys for certain common functions
7818         Multi_sync_buttons[gr_screen.res][MS_CANCEL].button.set_hotkey(KEY_ESC);
7819 }
7820
7821 void multi_sync_common_do()
7822 {       
7823         int k = chatbox_process();
7824         k = Multi_sync_window.process(k);       
7825
7826         // process the player list
7827         multi_sync_handle_plist();
7828
7829         // process any button clicks
7830         multi_sync_check_buttons();     
7831
7832         // process any keypresses
7833         switch(k){
7834         case KEY_ESC :
7835                 // Sync_test = 1;
7836                 gamesnd_play_iface(SND_USER_SELECT);
7837                 multi_quit_game(PROMPT_ALL);            
7838                 break;  
7839         }                               
7840 }
7841
7842 void multi_sync_common_close()
7843 {
7844         // unload any bitmaps
7845         if(!bm_unload(Multi_sync_bitmap)){
7846                 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_sync_bitmap_fname[gr_screen.res]));
7847         }       
7848
7849         extern int Player_multi_died_check;
7850         Player_multi_died_check = -1;
7851         
7852         // destroy the UI_WINDOW
7853         Multi_sync_window.destroy();
7854 }
7855
7856 void multi_sync_blit_screen_all()
7857 {
7858         int count,idx;  
7859         int state;
7860         float pct_complete;
7861         char txt[255];
7862         
7863         // display any text in the info area
7864         multi_common_render_text();
7865
7866         // display any pending notification messages
7867         multi_common_notify_do();
7868         
7869         // display any info about visible players
7870         count = 0;      
7871         for(idx=0;idx<MAX_PLAYERS;idx++){
7872                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7873                         // display his name and status
7874                         multi_sync_display_name(Net_players[idx].player->callsign,count,idx);
7875         
7876                         // get the player state
7877                         state = Net_players[idx].state;
7878
7879                         // if we're ingame joining, show all other players except myself as "playing"
7880                         if((Net_player != NULL) && (&Net_players[idx] != Net_player) && ((Multi_sync_mode == MULTI_SYNC_INGAME) || (Net_player->flags & NETINFO_FLAG_INGAME_JOIN)) ){
7881                                 state = NETPLAYER_STATE_IN_MISSION;
7882                         }
7883
7884                         switch(state){                          
7885                         case NETPLAYER_STATE_MISSION_LOADING:
7886                                 multi_sync_display_status(XSTR("Mission Loading",802),count);
7887                                 break;
7888                         case NETPLAYER_STATE_INGAME_SHIP_SELECT:                                                                        // I don't think its possible to see this state, but...
7889                                 multi_sync_display_status(XSTR("Ingame Ship Select",803),count);
7890                                 break;
7891                         case NETPLAYER_STATE_DEBRIEF:
7892                                 multi_sync_display_status(XSTR("Debriefing",804),count);
7893                                 break;
7894                         case NETPLAYER_STATE_MISSION_SYNC:
7895                                 multi_sync_display_status(XSTR("Mission Sync",805),count);
7896                                 break;
7897                         case NETPLAYER_STATE_JOINING:                                                           
7898                                 multi_sync_display_status(XSTR("Joining",806),count);
7899                                 break;
7900                         case NETPLAYER_STATE_JOINED:                            
7901                                 multi_sync_display_status(XSTR("Joined",807),count);
7902                                 break;
7903                         case NETPLAYER_STATE_SLOT_ACK :                         
7904                                 multi_sync_display_status(XSTR("Slot Ack",808),count);
7905                                 break;                  
7906                         case NETPLAYER_STATE_BRIEFING:                          
7907                                 multi_sync_display_status(XSTR("Briefing",765),count);
7908                                 break;
7909                         case NETPLAYER_STATE_SHIP_SELECT:                               
7910                                 multi_sync_display_status(XSTR("Ship Select",809),count);
7911                                 break;
7912                         case NETPLAYER_STATE_WEAPON_SELECT:                             
7913                                 multi_sync_display_status(XSTR("Weapon Select",810),count);
7914                                 break;
7915                         case NETPLAYER_STATE_WAITING:                           
7916                                 multi_sync_display_status(XSTR("Waiting",811),count);
7917                                 break;
7918                         case NETPLAYER_STATE_IN_MISSION:                                
7919                                 multi_sync_display_status(XSTR("In Mission",812),count);
7920                                 break;                  
7921                         case NETPLAYER_STATE_MISSION_LOADED:                    
7922                                 multi_sync_display_status(XSTR("Mission Loaded",813),count);
7923                                 break;                  
7924                         case NETPLAYER_STATE_DATA_LOAD:
7925                                 multi_sync_display_status(XSTR("Data loading",814),count);
7926                                 break;
7927                         case NETPLAYER_STATE_SETTINGS_ACK:
7928                                 multi_sync_display_status(XSTR("Ready To Enter Mission",815),count);
7929                                 break;                                  
7930                         case NETPLAYER_STATE_INGAME_SHIPS:
7931                                 multi_sync_display_status(XSTR("Ingame Ships Packet Ack",816),count);
7932                                 break;
7933                         case NETPLAYER_STATE_INGAME_WINGS:
7934                                 multi_sync_display_status(XSTR("Ingame Wings Packet Ack",817),count);
7935                                 break;          
7936                         case NETPLAYER_STATE_INGAME_RPTS:
7937                                 multi_sync_display_status(XSTR("Ingame Respawn Points Ack",818),count);
7938                                 break;
7939                         case NETPLAYER_STATE_SLOTS_ACK:
7940                                 multi_sync_display_status(XSTR("Ingame Weapon Slots Ack",819),count);
7941                                 break;                  
7942                         case NETPLAYER_STATE_POST_DATA_ACK:
7943                                 multi_sync_display_status(XSTR("Post Briefing Data Block Ack",820),count);
7944                                 break;
7945                         case NETPLAYER_STATE_FLAG_ACK :
7946                                 multi_sync_display_status(XSTR("Flags Ack",821),count);
7947                                 break;
7948                         case NETPLAYER_STATE_MT_STATS :
7949                                 multi_sync_display_status(XSTR("Parallax Online Stats Updating",822),count);
7950                                 break;
7951                         case NETPLAYER_STATE_WSS_ACK :
7952                                 multi_sync_display_status(XSTR("Weapon Slots Ack",823),count);
7953                                 break;
7954                         case NETPLAYER_STATE_HOST_SETUP :
7955                                 multi_sync_display_status(XSTR("Host setup",824),count);
7956                                 break;
7957                         case NETPLAYER_STATE_DEBRIEF_ACCEPT:
7958                                 multi_sync_display_status(XSTR("Debrief accept",825),count);
7959                                 break;
7960                         case NETPLAYER_STATE_DEBRIEF_REPLAY:
7961                                 multi_sync_display_status(XSTR("Debrief replay",826),count);
7962                                 break;
7963                         case NETPLAYER_STATE_CPOOL_ACK:
7964                                 multi_sync_display_status(XSTR("Campaign ship/weapon ack",827),count);
7965                                 break;                          
7966                         case NETPLAYER_STATE_MISSION_XFER :                             
7967                                 memset(txt,0,255);
7968                                 // server should display the pct completion of all clients                              
7969                                 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
7970                                         if(Net_players[idx].s_info.xfer_handle != -1){                                  
7971                                                 pct_complete = multi_xfer_pct_complete(Net_players[idx].s_info.xfer_handle);
7972
7973                                                 // if we've got a valid xfer handle
7974                                                 if((pct_complete >= 0.0) && (pct_complete <= 1.0)){                                             
7975                                                         sprintf(txt,XSTR("Mission file xfer %d%%",828),(int)(pct_complete * 100.0f));
7976                                                 }
7977                                                 // otherwise
7978                                                 else {
7979                                                         strcpy(txt,XSTR("Mission file xfer",829));
7980                                                 }                                       
7981                                         } else {
7982                                                 strcpy(txt,XSTR("Mission file xfer",829));
7983                                         }
7984                                 }
7985                                 // clients should display only for themselves (which is the only thing they know)
7986                                 else {
7987                                         // if we've got a valid file xfer handle
7988                                         if((&Net_players[idx] == Net_player) && (Net_player->s_info.xfer_handle != -1)){
7989                                                 pct_complete = multi_xfer_pct_complete(Net_player->s_info.xfer_handle);
7990
7991                                                 // if we've got a valid xfer handle
7992                                                 if((pct_complete >= 0.0) && (pct_complete <= 1.0)){                                             
7993                                                         sprintf(txt,XSTR("Mission file xfer %d%%",828),(int)(pct_complete * 100.0f));
7994                                                 }
7995                                                 // otherwise
7996                                                 else {
7997                                                         strcpy(txt,XSTR("Mission file xfer",829));
7998                                                 }
7999                                         }
8000                                         // otherwise
8001                                         else {
8002                                                 strcpy(txt,XSTR("Mission file xfer",829));
8003                                         }
8004                                 }
8005
8006                                 // display the text
8007                                 multi_sync_display_status(txt,count);
8008                                 break;
8009                         default :
8010                                 nprintf(("Network","Unhandled player state : %d !\n",Net_players[idx].state));
8011                                 break;
8012                         }
8013                         count++;
8014                 }
8015         }       
8016
8017         // display the mission start countdown timer (if any)
8018         anim_render_all(GS_STATE_MULTI_MISSION_SYNC,flFrametime);       
8019
8020         // process and show the chatbox thingie 
8021         chatbox_render();
8022
8023         // draw tooltips
8024         Multi_sync_window.draw_tooltip();
8025
8026         // display the voice status indicator
8027         multi_common_voice_display_status();
8028 }
8029
8030 void multi_sync_check_buttons()
8031 {
8032         int idx;
8033         for(idx=0;idx<Multi_sync_button_count;idx++){
8034                 // we only really need to check for one button pressed at a time, so we can break after 
8035                 // finding one.
8036                 if(Multi_sync_buttons[gr_screen.res][idx].button.pressed()){
8037                         multi_sync_button_pressed(idx);
8038                         break;
8039                 }
8040         }
8041 }
8042
8043 void multi_sync_button_pressed(int n)
8044 {
8045         switch(n){      
8046         // exit the game
8047         case MS_CANCEL:
8048                 gamesnd_play_iface(SND_USER_SELECT);            
8049                 multi_quit_game(PROMPT_ALL);            
8050                 break;  
8051         
8052         // scroll the info box up
8053         case MS_SCROLL_INFO_UP:
8054                 multi_common_scroll_text_up();          
8055                 break;
8056         
8057         // scroll the info box down
8058         case MS_SCROLL_INFO_DOWN:
8059                 multi_common_scroll_text_down();
8060                 break;  
8061
8062         // KICK (host only)
8063         case MS_KICK:
8064                 // if we have a currently selected player, kick him
8065                 if(Multi_sync_player_select >= 0){
8066                         multi_kick_player(Multi_sync_player_select);
8067                 }
8068                 break;
8069         
8070         // start the final launch countdown (post-sync only)
8071         case MS_LAUNCH:
8072                 multi_sync_start_countdown();
8073                 break;
8074         
8075         // doesn't do anything
8076         default :
8077                 Int3();         
8078         }
8079 }
8080
8081 void multi_sync_pre_init()
8082 {
8083         int idx;
8084
8085         Netgame.game_state = NETGAME_STATE_MISSION_SYNC;
8086
8087         // if we're in teamplay mode, always force skill level to be medium
8088         if((Netgame.type_flags & NG_TYPE_TEAM) && (Net_player->flags & NETINFO_FLAG_GAME_HOST)){
8089                 Netgame.options.skill_level = NUM_SKILL_LEVELS / 2;
8090                 Game_skill_level = NUM_SKILL_LEVELS / 2;
8091                 multi_options_update_netgame();
8092         }
8093
8094         // notify everyone of when we get here
8095         if(!(Game_mode & GM_STANDALONE_SERVER)){
8096                 Net_player->state = NETPLAYER_STATE_MISSION_SYNC;
8097                 send_netplayer_update_packet();
8098         }
8099         
8100         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){ 
8101                 // NETLOG
8102                 ml_string(NOX("Server performing pre-briefing data sync"));
8103
8104                 if(!(Game_mode & GM_STANDALONE_SERVER)){
8105                         multi_common_set_text(XSTR("Server performing sync\n",830),1);
8106                 }
8107                 
8108                 // maybe initialize tvt and squad war stuff
8109                 if(Netgame.type_flags & NG_TYPE_TEAM){
8110                         multi_team_level_init();
8111                 }       
8112
8113                 // force everyone into this state               
8114                 send_netgame_update_packet();           
8115
8116                 if(!(Game_mode & GM_STANDALONE_SERVER)){
8117                         multi_common_add_text(XSTR("Send update packet\n",831),1);
8118                 }
8119
8120                 // setup some of my own data
8121                 Net_player->flags |= NETINFO_FLAG_MISSION_OK;           
8122
8123                 // do any output stuff
8124                 if(Game_mode & GM_STANDALONE_SERVER){
8125                         std_debug_set_standalone_state_string("Mission Sync");                  
8126                 }               
8127
8128                 // do this here to insure we have the most up to date file checksum info
8129                 multi_get_mission_checksum(Game_current_mission_filename);
8130                 // parse_get_file_signature(Game_current_mission_filename);
8131                 
8132                 if(!(Game_mode & GM_STANDALONE_SERVER)){
8133                         multi_common_add_text(XSTR("Got file signatures\n",832),1);
8134                 }
8135         } else {
8136                 if(!(Game_mode & GM_STANDALONE_SERVER)){
8137                         multi_common_add_text(XSTR("Sending update state packet\n",833),1);
8138                 }
8139         }
8140
8141         // if we're not in team vs. team mode - set all player teams to be 0, and unset all captaincy bits
8142         if(!(Netgame.type_flags & NG_TYPE_TEAM)){
8143                 for(idx=0;idx<MAX_PLAYERS;idx++){
8144                         Net_players[idx].p_info.team = 0;
8145                         Net_players[idx].flags &= ~(NETINFO_FLAG_TEAM_CAPTAIN);
8146                 }
8147         }
8148
8149         // we aren't necessarily xferring the mission file yet  
8150         Assert(Net_player->s_info.xfer_handle == -1);
8151
8152         // always call this for good measure
8153         multi_campaign_flush_data();
8154
8155         Mission_sync_flags = 0;
8156         Multi_mission_loaded = 0;
8157 }
8158
8159 void multi_sync_pre_do()
8160 {               
8161         int idx;
8162         
8163         // If I'm the server, wait for everyone to arrive in this state, then begin transferring data, etc.
8164         // all servers (standalone or no, go through this)
8165         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8166                 // wait for everyone to arrive, then request filesig from all of them
8167                 if(multi_netplayer_state_check(NETPLAYER_STATE_MISSION_SYNC) && !(Mission_sync_flags & MS_FLAG_SENT_FILESIG) && !multi_endgame_ending()){
8168                         send_file_sig_request(Netgame.mission_name);
8169                         Mission_sync_flags |= MS_FLAG_SENT_FILESIG;
8170
8171                         if(!(Game_mode & GM_STANDALONE_SERVER)){
8172                                 multi_common_add_text(XSTR("Sent filesig request\n",834),1);
8173                         }
8174                 }               
8175
8176                 // if we're waiting for players to receive files, then check on their status
8177                 if((Mission_sync_flags & MS_FLAG_SENT_FILESIG) && !multi_netplayer_flag_check(NETINFO_FLAG_MISSION_OK) && !multi_endgame_ending()){
8178                         for(idx=0;idx<MAX_PLAYERS;idx++){
8179                                 // if this player is in the process of xferring a file
8180                                 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].s_info.xfer_handle != -1)){
8181                                         switch(multi_xfer_get_status(Net_players[idx].s_info.xfer_handle)){
8182                                         // if it has successfully completed, set his ok flag
8183                                         case MULTI_XFER_SUCCESS :
8184                                                 // set his ok flag
8185                                                 Net_players[idx].flags |= NETINFO_FLAG_MISSION_OK;
8186
8187                                                 // release the xfer instance handle
8188                                                 multi_xfer_release_handle(Net_players[idx].s_info.xfer_handle);
8189                                                 Net_players[idx].s_info.xfer_handle = -1;
8190                                                 break;
8191                                         // if it has failed or timed-out, kick the player
8192                                         case MULTI_XFER_TIMEDOUT:
8193                                         case MULTI_XFER_FAIL:
8194                                                 // release the xfer handle
8195                                                 multi_xfer_release_handle(Net_players[idx].s_info.xfer_handle);
8196                                                 Net_players[idx].s_info.xfer_handle = -1;
8197                                                 
8198                                                 // kick the loser
8199                                                 multi_kick_player(idx, 0, KICK_REASON_BAD_XFER);
8200                                                 break;
8201                                         }
8202                                 }
8203                         }
8204                 }
8205
8206                 // NOTE : this is now obsolete
8207                 // once everyone is verified, do any data transfer necessary
8208                 if(multi_netplayer_flag_check(NETINFO_FLAG_MISSION_OK) && !(Mission_sync_flags & MS_FLAG_DATA_DONE) && !multi_endgame_ending()){
8209                         // do nothing for now
8210                         Mission_sync_flags |= MS_FLAG_DATA_DONE;                                                
8211                         
8212                         // send campaign pool data
8213                         multi_campaign_send_pool_status();
8214                 }
8215
8216                 // wait for everyone to ack on campaign pool data (even in non-campaign situations)
8217                 if((Mission_sync_flags & MS_FLAG_DATA_DONE) && !(Mission_sync_flags & MS_FLAG_CAMP_DONE) && !multi_endgame_ending()){
8218                         // check to see if everyone has acked the campaign pool data
8219                         if(multi_netplayer_state_check(NETPLAYER_STATE_CPOOL_ACK)){
8220                                 Mission_sync_flags |= MS_FLAG_CAMP_DONE;
8221                         }
8222                 }
8223                                 
8224                 // once everyone is verified, tell them to load the mission
8225                 // also make sure to load the mission myself _AFTER_ telling everyone to do so. This makes the whole process
8226                 // move along faster
8227                 if((Mission_sync_flags & MS_FLAG_CAMP_DONE) && !(Mission_sync_flags & MS_FLAG_SENT_LOAD) && !multi_endgame_ending()){           
8228                         send_netplayer_load_packet(NULL);                       
8229                         Mission_sync_flags |= MS_FLAG_SENT_LOAD;
8230
8231                         if(!(Game_mode & GM_STANDALONE_SERVER)){
8232                                 multi_common_add_text(XSTR("Sent load packet\n",835),1);
8233                         }
8234
8235                         // load the mission myself, as soon as possible
8236                         if(!Multi_mission_loaded){
8237                                 nprintf(("Network","Server loading mission..."));
8238         
8239                                 // update everyone about my status
8240                                 Net_player->state = NETPLAYER_STATE_MISSION_LOADING;
8241                                 send_netplayer_update_packet();
8242
8243                                 game_start_mission();
8244                                 psnet_flush();
8245                                 nprintf(("Network","Done\n"));
8246                                 Multi_mission_loaded = 1;                               
8247
8248                                 // update everyone about my status
8249                                 Net_player->state = NETPLAYER_STATE_MISSION_LOADED;
8250                                 send_netplayer_update_packet();
8251
8252                                 if(!(Game_mode & GM_STANDALONE_SERVER)){
8253                                         multi_common_add_text(XSTR("Loaded mission locally\n",836),1);
8254                                 }
8255                         }
8256                 }
8257                 
8258                 // if everyone has loaded the mission, randomly assign players to ships 
8259                 if(multi_netplayer_state_check(NETPLAYER_STATE_MISSION_LOADED) && !(Mission_sync_flags & MS_FLAG_TS_SLOTS) && !multi_endgame_ending()){
8260                         // call the team select function to assign players to their ships, wings, etc
8261                         multi_ts_assign_players_all();
8262                         send_netplayer_slot_packet();                                                                           
8263
8264                         // mark this flag
8265                         Mission_sync_flags |= MS_FLAG_TS_SLOTS;
8266                 }
8267
8268                 // if everyone has loaded the mission, move to the team select stage
8269                 if(Sync_test && multi_netplayer_state_check(NETPLAYER_STATE_SLOT_ACK) && !(Mission_sync_flags & MS_FLAG_PUSHED_BRIEFING) && !multi_endgame_ending()){
8270                         Netgame.game_state = NETGAME_STATE_BRIEFING;
8271                         send_netgame_update_packet();   // this will push everyone into the next state
8272
8273                         // the standalone moves to his own wait state, whereas in the normal game mode, the server/host moves in to the
8274                         // team select state
8275                         if(Game_mode & GM_STANDALONE_SERVER){
8276                                 gameseq_post_event(GS_EVENT_MULTI_STD_WAIT);
8277                         } else {
8278                                 gameseq_post_event(GS_EVENT_START_GAME);                                        
8279                         }
8280
8281                         Mission_sync_flags |= MS_FLAG_PUSHED_BRIEFING;
8282
8283                         if(!(Game_mode & GM_STANDALONE_SERVER)){
8284                                 multi_common_add_text(XSTR("Moving to team select\n",837),1);
8285                         }
8286                 }               
8287         } else {
8288                 // clients should detect here if they are doing a file xfer and do error processing
8289                 if((Net_player->state == NETPLAYER_STATE_MISSION_XFER) && (Net_player->s_info.xfer_handle != -1) && !multi_endgame_ending()){
8290                         switch(multi_xfer_get_status(Net_player->s_info.xfer_handle)){
8291                         // if it has successfully completed, set his ok flag
8292                         case MULTI_XFER_SUCCESS :       
8293                                 // release my xfer handle
8294                                 multi_xfer_release_handle(Net_player->s_info.xfer_handle);
8295                                 Net_player->s_info.xfer_handle = -1;                            
8296                                 break;
8297                                 
8298                         // if it has failed or timed-out, kick the player
8299                         case MULTI_XFER_TIMEDOUT:
8300                         case MULTI_XFER_FAIL:
8301                                 // release my xfer handle
8302                                 multi_xfer_release_handle(Net_player->s_info.xfer_handle);
8303                                 Net_player->s_info.xfer_handle = -1;
8304
8305                                 // leave the game qith an error code
8306                                 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_XFER_FAIL);
8307                                 break;
8308                         }
8309                 }
8310         }
8311
8312         // blit stuff
8313         if(!(Game_mode & GM_STANDALONE_SERVER)){
8314                 gr_reset_clip();
8315                 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8316                 if(Multi_sync_bitmap != -1){
8317                         gr_set_bitmap(Multi_sync_bitmap);
8318                         gr_bitmap(0,0);
8319                 }
8320                 Multi_sync_window.draw();
8321
8322                 multi_sync_blit_screen_all();
8323
8324                 gr_flip();
8325         }
8326 }
8327
8328 void multi_sync_pre_close()
8329 {
8330         // at this point, we should shut down any file xfers...
8331         if(Net_player->s_info.xfer_handle != -1){
8332                 nprintf(("Network","WARNING - killing file xfer while leaving mission sync state!!!\n"));
8333
8334                 multi_xfer_abort(Net_player->s_info.xfer_handle);
8335                 Net_player->s_info.xfer_handle;
8336         }
8337 }
8338
8339 void multi_sync_post_init()
8340 {       
8341         multi_reset_timestamps();
8342
8343         Multi_state_timestamp = timestamp(0);
8344
8345         // NETLOG
8346         ml_string(NOX("Performing post-briefing data sync"));
8347         
8348         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8349                 multi_common_add_text(XSTR("Server performing sync\n",830),1);
8350         } else {
8351                 multi_common_add_text(XSTR("Client performing sync\n",838),1);
8352         }
8353
8354         // everyone should re-initialize these 
8355         init_multiplayer_stats();
8356
8357         // reset all sequencing info
8358         multi_oo_reset_sequencing();
8359
8360         // if I am not the master of the game, then send the firing information for my ship
8361         // to the host
8362         if ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) ){
8363                 send_firing_info_packet();
8364         }       
8365
8366         // if I'm not a standalone server, load up the countdown stuff
8367         if(!(Game_mode & GM_STANDALONE_SERVER)){                                
8368                 Multi_sync_countdown_anim = NULL;
8369                 Multi_sync_countdown_instance = NULL;
8370                 Multi_sync_countdown_anim = anim_load(Multi_sync_countdown_fname[gr_screen.res]);                               
8371                 if(Multi_sync_countdown_anim == NULL){
8372                         nprintf(("General","WARNING!, Could not load countdown animation %s!\n",Multi_sync_countdown_fname[gr_screen.res]));
8373                 }
8374         }
8375
8376         // create objects for all permanent observers
8377         multi_obs_level_init();
8378
8379         // clear the game start countdown timer
8380         Multi_sync_countdown_timer = -1.0f;     
8381         Multi_sync_countdown = -1;
8382
8383         // if this is a team vs. team mission, mark all ship teams appropriately
8384         if(Netgame.type_flags & NG_TYPE_TEAM){
8385                 multi_team_mark_all_ships();
8386         }
8387
8388         Mission_sync_flags = 0;
8389         Multi_sync_launch_pressed = 0;
8390 }
8391
8392 #define MULTI_POST_TIMESTAMP                    7000
8393
8394 extern int create_wings();
8395
8396 void multi_sync_post_do()
8397 {       
8398         int idx;
8399         
8400         // only if the host is also the master should he be doing this (non-standalone situation)
8401         if ( Net_player->flags & NETINFO_FLAG_AM_MASTER ) {
8402
8403                 // once everyone gets to this screen, send them the ship classes of all ships.
8404                 if(multi_netplayer_state_check(NETPLAYER_STATE_WAITING) && !(Mission_sync_flags & MS_FLAG_POST_DATA)) {                 
8405                         // only the host should ever do this
8406                         if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8407                                 // at this point we want to delete all necessary ships, change all necessary ship classes, and set all weapons up
8408                                 multi_ts_create_wings();
8409
8410                                 // update player ets settings
8411                                 for(idx=0;idx<MAX_PLAYERS;idx++){
8412                                         if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].player->objnum != -1)){
8413                                                 multi_server_update_player_weapons(&Net_players[idx],&Ships[Objects[Net_players[idx].player->objnum].instance]);
8414                                         }
8415                                 }                       
8416                         }                       
8417
8418                         // note that this is done a little differently for standalones and nonstandalones
8419                         send_post_sync_data_packet();
8420                         
8421                         multi_common_add_text(XSTR("Sending post briefing block information\n",839),1);                 
8422
8423                         Mission_sync_flags |= MS_FLAG_POST_DATA;
8424                 }
8425
8426                 // send weapon slots data
8427                 if(multi_netplayer_state_check(NETPLAYER_STATE_POST_DATA_ACK) && !(Mission_sync_flags & MS_FLAG_WSS_SLOTS)) {                   
8428                         // note that this is done a little differently for standalones and nonstandalones
8429                         if(Netgame.type_flags & NG_TYPE_TEAM){
8430                                 send_wss_slots_data_packet(0,0);
8431                                 send_wss_slots_data_packet(1,1);
8432                         } else {
8433                                 send_wss_slots_data_packet(0,1);
8434                         }
8435                         
8436                         multi_common_add_text(XSTR("Sending weapon slots information\n",840),1);                        
8437
8438                         Mission_sync_flags |= MS_FLAG_WSS_SLOTS;
8439                 }
8440                         
8441                 // once weapon information is received, send player settings info
8442                 if ( multi_netplayer_state_check(NETPLAYER_STATE_WSS_ACK) && !(Mission_sync_flags & MS_FLAG_PSETTINGS)) {                                                                       
8443                         send_player_settings_packet();
8444                         
8445                         // server (specifically, the standalone), should set this here
8446                         Net_player->state = NETPLAYER_STATE_SETTINGS_ACK;
8447                         send_netplayer_update_packet();
8448                         
8449                         multi_common_add_text(XSTR("Sending player settings packets\n",841),1);                         
8450
8451                         Mission_sync_flags |= MS_FLAG_PSETTINGS;                        
8452                 }                                       
8453
8454                 // check to see if the countdown timer has started and act appropriately
8455                 if( Multi_sync_countdown_timer > -1.0f ) {
8456
8457                         // increment by frametime.
8458                         Multi_sync_countdown_timer += flFrametime;
8459
8460                         // if the animation is not playing, start it
8461                         if(!(Game_mode & GM_STANDALONE_SERVER) && (Multi_sync_countdown_instance == NULL) && (Multi_sync_countdown_anim != NULL)){
8462                                 anim_play_struct aps;                           
8463
8464                                 anim_play_init(&aps, Multi_sync_countdown_anim, Multi_sync_countdown_coords[gr_screen.res][MS_X_COORD], Multi_sync_countdown_coords[gr_screen.res][MS_Y_COORD]);
8465                                 aps.screen_id = GS_STATE_MULTI_MISSION_SYNC;
8466                                 aps.framerate_independent = 1;
8467
8468                                 Multi_sync_countdown_instance = anim_play(&aps);
8469                         }
8470
8471                         // if the next second has expired
8472                         if( Multi_sync_countdown_timer >= 1.0f ) {
8473
8474                                 Multi_sync_countdown--;
8475                                 Multi_sync_countdown_timer = 0.0f;
8476
8477                                 // if the countdown has reached 0, launch the mission
8478                                 if(Multi_sync_countdown == 0){
8479                                         Multi_sync_countdown_timer = -1.0f;
8480
8481                                         Multi_sync_launch_pressed = 0;
8482                                         multi_sync_launch();
8483                                 }
8484                                 // otherwise send a countdown packet
8485                                 else {
8486                                         send_countdown_packet(Multi_sync_countdown);
8487                                 }
8488                         }
8489                 }
8490                         
8491                 // jump into the mission myself
8492                 if((Multi_sync_countdown == 0) && multi_netplayer_state_check(NETPLAYER_STATE_IN_MISSION)){
8493                         if(!((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !Multi_sync_launch_pressed)){                                                      
8494                                 Net_player->state = NETPLAYER_STATE_IN_MISSION;
8495                                 Netgame.game_state = NETGAME_STATE_IN_MISSION;                          
8496                                 gameseq_post_event(GS_EVENT_ENTER_GAME);
8497                         
8498                                 multi_common_add_text(XSTR("Moving into game\n",842),1);                        
8499                         }
8500                 }
8501         } else {
8502                 // maybe start the animation countdown 
8503                 if((Multi_sync_countdown >= 0) && (Multi_sync_countdown_instance == NULL) && (Multi_sync_countdown_anim != NULL)){
8504                         anim_play_struct aps;                           
8505
8506                         anim_play_init(&aps, Multi_sync_countdown_anim, Multi_sync_countdown_coords[gr_screen.res][MS_X_COORD], Multi_sync_countdown_coords[gr_screen.res][MS_Y_COORD]);
8507                         aps.screen_id = GS_STATE_MULTI_MISSION_SYNC;
8508                         aps.framerate_independent = 1;
8509
8510                         Multi_sync_countdown_instance = anim_play(&aps);
8511                 }
8512         }
8513
8514         // host - specific stuff
8515         if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8516                 // create the launch button so the host can click
8517                 if( Sync_test && multi_netplayer_state_check(NETPLAYER_STATE_SETTINGS_ACK) ){
8518                         multi_sync_create_launch_button();
8519                 }
8520         }
8521
8522         // blit stuff
8523         if(!(Game_mode & GM_STANDALONE_SERVER)){
8524                 gr_reset_clip();        
8525                 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8526                 if(Multi_sync_bitmap != -1){
8527                         gr_set_bitmap(Multi_sync_bitmap);
8528                         gr_bitmap(0,0);
8529                 }
8530                 Multi_sync_window.draw();
8531
8532                 multi_sync_blit_screen_all();
8533
8534                 gr_flip();
8535         }
8536 }
8537
8538 void multi_sync_post_close()
8539 {
8540         int idx;
8541
8542         // if I'm not a standalone server, unload up the countdown stuff
8543         if(!(Game_mode & GM_STANDALONE_SERVER)){                                
8544                 // release all rendering animation instances (should only be 1)
8545                 anim_release_all_instances(GS_STATE_MULTI_MISSION_SYNC);
8546                 Multi_sync_countdown_instance = NULL;
8547
8548                 // free up the countdown animation
8549                 if(Multi_sync_countdown_anim != NULL){
8550                         anim_free(Multi_sync_countdown_anim);
8551                         Multi_sync_countdown_anim = NULL;
8552                 }
8553         }
8554         
8555         // all players should reset sequencing
8556         for(idx=0;idx<MAX_PLAYERS;idx++){
8557                 if(Net_player->flags & NETINFO_FLAG_CONNECTED){
8558                         Net_players[idx].client_cinfo_seq = 0;
8559                         Net_players[idx].client_server_seq = 0; 
8560                 }
8561         }
8562
8563         // multiplayer dogfight
8564         multi_df_level_pre_enter();                             
8565
8566         // clients should clear obj_pair array and add pair for themselves
8567         /*
8568         if ( MULTIPLAYER_CLIENT ) {
8569                 obj_reset_pairs();
8570                 obj_add_pairs( OBJ_INDEX(Player_obj) ); 
8571         }
8572         */
8573 }
8574
8575 void multi_sync_display_name(char *name,int index,int np_index)
8576 {
8577         char fit[CALLSIGN_LEN]; 
8578         
8579         // make sure the string actually fits
8580         strcpy(fit,name);
8581
8582         // if we're in team vs. team mode
8583         if(Netgame.type_flags & NG_TYPE_TEAM){
8584                 gr_force_fit_string(fit,CALLSIGN_LEN, Ms_status2_coords[gr_screen.res][MS_X_COORD] - Ms_status_coords[gr_screen.res][MS_X_COORD] - 20 - Ms_cd_icon_offset[gr_screen.res] - Ms_team_icon_offset[gr_screen.res]);                 
8585
8586                 // if this is the currently selected player, draw him highlighted
8587                 if(np_index == Multi_sync_player_select){
8588                         gr_set_color_fast(&Color_text_selected);
8589                 } else {
8590                         gr_set_color_fast(&Color_text_normal);
8591                 }
8592
8593                 // blit the string
8594                 gr_string(Ms_status_coords[gr_screen.res][0] + Ms_cd_icon_offset[gr_screen.res] + Ms_team_icon_offset[gr_screen.res], Ms_status_coords[gr_screen.res][MS_Y_COORD] + (index * 10),fit);
8595
8596                 // blit his team icon 
8597                 // team 0               
8598                 if(Net_players[np_index].p_info.team == 0){
8599                         // blit the team captain icon
8600                         if(Net_players[np_index].flags & NETINFO_FLAG_TEAM_CAPTAIN){                            
8601                                 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
8602                                         gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT]);
8603                                         gr_bitmap(Ms_status_coords[gr_screen.res][MS_X_COORD] + Ms_cd_icon_offset[gr_screen.res], Ms_status_coords[gr_screen.res][MS_Y_COORD] + (index * 10) - 2);
8604                                 } 
8605                         }
8606                         // normal team member icon
8607                         else {
8608                                 if(Multi_common_icons[MICON_TEAM0] != -1){
8609                                         gr_set_bitmap(Multi_common_icons[MICON_TEAM0]);
8610                                         gr_bitmap(Ms_status_coords[gr_screen.res][MS_X_COORD] + Ms_cd_icon_offset[gr_screen.res], Ms_status_coords[gr_screen.res][MS_Y_COORD] + (index * 10) - 2);
8611                                 }
8612                         }
8613                 }
8614                 // team 1
8615                 else if(Net_players[np_index].p_info.team == 1){                        
8616                         // blit the team captain icon
8617                         if(Net_players[np_index].flags & NETINFO_FLAG_TEAM_CAPTAIN){
8618                                 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
8619                                         gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT]);
8620                                         gr_bitmap(Ms_status_coords[gr_screen.res][MS_X_COORD] + Ms_cd_icon_offset[gr_screen.res], Ms_status_coords[gr_screen.res][MS_Y_COORD] + (index * 10) - 2);
8621                                 }
8622                         }
8623                         // normal team member icon
8624                         else {
8625                                 if(Multi_common_icons[MICON_TEAM1] != -1){
8626                                         gr_set_bitmap(Multi_common_icons[MICON_TEAM1]);
8627                                         gr_bitmap(Ms_status_coords[gr_screen.res][MS_X_COORD] + Ms_cd_icon_offset[gr_screen.res], Ms_status_coords[gr_screen.res][MS_Y_COORD] + (index * 10) - 2);
8628                                 }
8629                         }
8630                 }               
8631         } else {
8632                 gr_force_fit_string(fit, CALLSIGN_LEN, Ms_status2_coords[gr_screen.res][MS_X_COORD] - Ms_status_coords[gr_screen.res][MS_X_COORD] - 20 - Ms_cd_icon_offset[gr_screen.res]);
8633
8634                 // if this is the currently selected player, draw him highlighted
8635                 if(np_index == Multi_sync_player_select){
8636                         gr_set_color_fast(&Color_text_selected);
8637                 } else {
8638                         gr_set_color_fast(&Color_text_normal);
8639                 }
8640
8641                 // blit the string
8642                 gr_string(Ms_status_coords[gr_screen.res][MS_X_COORD] + Ms_cd_icon_offset[gr_screen.res], Ms_status_coords[gr_screen.res][MS_Y_COORD] + (index * 10),fit);
8643         }
8644
8645         // maybe blit his CD status icon
8646         if((Net_players[np_index].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
8647                 gr_set_bitmap(Multi_common_icons[MICON_CD]);
8648                 gr_bitmap(Ms_status_coords[gr_screen.res][MS_X_COORD], Ms_status_coords[gr_screen.res][MS_Y_COORD] + (index * 10));
8649         }
8650 }
8651
8652 void multi_sync_display_status(char *status,int index)
8653 {
8654         char fit[250];
8655
8656         // make sure the string actually fits
8657         strcpy(fit, status);
8658         gr_force_fit_string(fit, 250, Ms_status2_coords[gr_screen.res][MS_W_COORD] - 20);
8659         gr_set_color_fast(&Color_bright);       
8660         gr_string(Ms_status2_coords[gr_screen.res][MS_X_COORD], Ms_status2_coords[gr_screen.res][MS_Y_COORD] + (index * 10), fit);              
8661 }
8662
8663 void multi_sync_force_start_pre()
8664 {
8665         int idx;
8666         int want_state = NETPLAYER_STATE_SLOT_ACK;      // kick any players who are still in this state
8667
8668         // go through the player list and boot anyone who isn't in the right state
8669         for(idx=0;idx<MAX_PLAYERS;idx++){
8670                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_player != &Net_players[idx]) && (Net_players[idx].state == want_state)){            
8671                         multi_kick_player(idx,0);                               
8672                 }
8673         }
8674 }
8675
8676 void multi_sync_force_start_post()
8677 {
8678         int idx,idx2;
8679         int kill_state[3];      
8680         int num_kill_states;
8681         
8682         // determine the state we want all players in so that we can find those who are not in the state        
8683         kill_state[0] = NETPLAYER_STATE_BRIEFING;
8684         kill_state[1] = NETPLAYER_STATE_SHIP_SELECT;
8685         kill_state[2] = NETPLAYER_STATE_WEAPON_SELECT;
8686         num_kill_states = 3;    
8687
8688         // go through the player list and boot anyone who isn't in the right state
8689         for(idx=0;idx<MAX_PLAYERS;idx++){
8690                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_player != &Net_players[idx])){
8691                         // check against all kill state
8692                         for(idx2 = 0;idx2<num_kill_states;idx2++){
8693                                 if(Net_players[idx].state == kill_state[idx2]){
8694                                         multi_kick_player(idx,0);
8695                                         break;
8696                                 }
8697                         }
8698                 }
8699         }
8700 }
8701
8702 void multi_sync_start_countdown()
8703 {
8704         // don't allow repeat button presses
8705         if(Multi_sync_launch_pressed){
8706                 return;
8707         }
8708         
8709         Multi_sync_launch_pressed = 1;
8710
8711         // if I'm the server, begin the countdown
8712         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8713                 gamesnd_play_iface(SND_COMMIT_PRESSED);
8714                 Multi_sync_countdown_timer = 0.0f;
8715                 Multi_sync_countdown = MULTI_SYNC_COUNTDOWN_TIME;
8716
8717                 // send an initial countdown value
8718                 send_countdown_packet(Multi_sync_countdown);
8719         }
8720         // otherwise send the "start countdown" packet to the standalone
8721         else {
8722                 Assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
8723                 send_countdown_packet(-1);
8724         }
8725 }
8726
8727 void multi_sync_launch()
8728 {       
8729         // don't allow repeat button presses
8730         if(Multi_sync_launch_pressed){
8731                 return;
8732         }
8733
8734         Multi_sync_launch_pressed = 1;
8735
8736         // NETLOG
8737         ml_printf(NOX("Entering mission %s"), Game_current_mission_filename);
8738
8739         // tell everyone to jump into the mission
8740         send_jump_into_mission_packet();
8741         Multi_state_timestamp = timestamp(MULTI_POST_TIMESTAMP);
8742
8743         // set the # of players at the start of the mission
8744         Multi_num_players_at_start = multi_num_players();
8745         nprintf(("Network","# of players at start of mission : %d\n", Multi_num_players_at_start));
8746         
8747         // initialize datarate limiting for all clients
8748         multi_oo_rate_init_all();       
8749                                 
8750         multi_common_add_text(XSTR("Sending mission start packet\n",843),1);                            
8751 }
8752
8753 void multi_sync_create_launch_button()
8754 {
8755         if (!Multi_launch_button_created) {             
8756                 // create the object
8757                 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.create(&Multi_sync_window, "", Multi_sync_buttons[gr_screen.res][MS_LAUNCH].x, Multi_sync_buttons[gr_screen.res][MS_LAUNCH].y, 1, 1, 0, 1);
8758
8759                 // set the sound to play when highlighted
8760                 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_highlight_action(common_play_highlight_sound);
8761
8762                 // set the ani for the button
8763                 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_bmaps(Multi_sync_buttons[gr_screen.res][MS_LAUNCH].filename, 3, 0);
8764
8765                 // set the hotspot
8766                 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.link_hotspot(Multi_sync_buttons[gr_screen.res][MS_LAUNCH].hotspot);
8767
8768                 // hotkey
8769                 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_hotkey(KEY_CTRLED+KEY_ENTER);
8770
8771                 // create the text for the button
8772                 Multi_sync_window.add_XSTR(&Multi_sync_text[gr_screen.res][MST_LAUNCH]);
8773
8774                 // increment the button count so we start checking this one
8775                 Multi_sync_button_count++;
8776
8777                 Multi_launch_button_created = 1;
8778         }
8779 }
8780
8781 void multi_sync_handle_plist()
8782 {
8783         int idx;
8784         int my;
8785         int select_index;
8786         
8787         // if we don't have a currently selected player, select one
8788         if((Multi_sync_player_select < 0) || !MULTI_CONNECTED(Net_players[Multi_sync_player_select])){
8789                 for(idx=0;idx<MAX_PLAYERS;idx++){
8790                         if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
8791                                 Multi_sync_player_select = idx;
8792                                 break;
8793                         }
8794                 }
8795         }
8796         
8797         // check for button list presses
8798         if(Multi_sync_plist_button.pressed()){
8799                 // get the y mouse coords
8800                 Multi_sync_plist_button.get_mouse_pos(NULL,&my);
8801
8802                 // get the index of the item selected
8803                 select_index = my / 10;
8804
8805                 // if the index is greater than the current # connections, do nothing
8806                 if(select_index > (multi_num_connections() - 1)){
8807                         return;
8808                 }
8809
8810                 // translate into an absolute Net_players[] index (get the Nth net player)
8811                 Multi_sync_player_select = -1;
8812                 for(idx=0;idx<MAX_PLAYERS;idx++){
8813                         if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
8814                                 select_index--;
8815                         }
8816
8817                         // if we've found the item we're looking for
8818                         if(select_index < 0){
8819                                 Multi_sync_player_select = idx;
8820                                 break;
8821                         }
8822                 }
8823
8824                 // if for some bizarre reason, this is an invalid player, unselect him and wait for the next interation
8825                 // of the loop
8826                 if((Multi_sync_player_select >= 0) && (!MULTI_CONNECTED(Net_players[Multi_sync_player_select]) || MULTI_STANDALONE(Net_players[Multi_sync_player_select])) ){
8827                         Multi_sync_player_select = -1;
8828                 }
8829         }
8830 }
8831
8832
8833 // -------------------------------------------------------------------------------------------------------------
8834 // 
8835 // MULTIPLAYER DEBRIEF SCREEN 
8836 //
8837
8838 // other relevant data
8839 int Multi_debrief_accept_hit;
8840 int Multi_debrief_replay_hit;
8841
8842 // set if the server has left the game
8843 int Multi_debrief_server_left = 0;
8844
8845 // if we've reported on TvT status all players are in the debrief
8846 int Multi_debrief_reported_tvt = 0;
8847
8848 // whether stats are being accepted
8849 // -1 == no decision yet
8850 // 1 == accepted
8851 // 0 == tossed
8852 int Multi_debrief_stats_accept_code = -1;
8853
8854 int Multi_debrief_server_framecount = 0;
8855
8856 float Multi_debrief_time = 0.0f;
8857 float Multi_debrief_resend_time = 10.0f;
8858
8859 void multi_debrief_init()
8860 {                       
8861         int idx;
8862
8863         Multi_debrief_time = 0.0f;
8864         Multi_debrief_resend_time = 10.0f;
8865         
8866         // do this to notify the standalone or the normal server that we're in the debrief state and ready to receive packets
8867         if (!(Net_player->flags & NETINFO_FLAG_AM_MASTER)) {
8868                 Net_player->state = NETPLAYER_STATE_DEBRIEF;
8869                 send_netplayer_update_packet();
8870         }       
8871
8872         // unflag some stuff
8873         for(idx=0;idx<MAX_PLAYERS;idx++){
8874                 if(MULTI_CONNECTED(Net_players[idx])){
8875                         Net_players[idx].flags &= ~(NETINFO_FLAG_RESPAWNING | NETINFO_FLAG_LIMBO | NETINFO_FLAG_WARPING_OUT);
8876                 }
8877         }
8878         
8879         // if text input mode is active, clear it
8880         multi_msg_text_flush(); 
8881         
8882         // the server has not left yet
8883         Multi_debrief_server_left = 0;
8884
8885         // have not hit accept or replay yet
8886         Multi_debrief_accept_hit = 0;
8887         Multi_debrief_replay_hit = 0;
8888
8889         // stats have not been accepted yet
8890         Multi_debrief_stats_accept_code = -1;
8891
8892         // mark stats as not being store yet
8893         Netgame.flags &= ~(NG_FLAG_STORED_MT_STATS);
8894
8895         // no report on TvT yet
8896         Multi_debrief_reported_tvt = 0;
8897
8898         Multi_debrief_server_framecount = 0;
8899 }
8900
8901 void multi_debrief_do_frame()
8902 {
8903         Multi_debrief_time += flFrametime;
8904
8905         // set the netgame state to be debriefing when appropriate
8906         if((Net_player->flags & NETINFO_FLAG_AM_MASTER) && (Netgame.game_state != NETGAME_STATE_DEBRIEF) && multi_netplayer_state_check3(NETPLAYER_STATE_DEBRIEF, NETPLAYER_STATE_DEBRIEF_ACCEPT, NETPLAYER_STATE_DEBRIEF_REPLAY)){
8907                 Netgame.game_state = NETGAME_STATE_DEBRIEF;
8908                 send_netgame_update_packet();
8909         }
8910         
8911         // evaluate all server stuff
8912         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8913                 multi_debrief_server_process();
8914         }                       
8915 }
8916
8917 void multi_debrief_close()
8918 {       
8919         if ( MULTIPLAYER_CLIENT && (Netgame.game_state == NETGAME_STATE_MISSION_SYNC) ){
8920                 gamesnd_play_iface( SND_COMMIT_PRESSED );
8921         }
8922 }
8923
8924 // handle optional mission loop
8925 void multi_maybe_set_mission_loop()
8926 {
8927         int cur = Campaign.current_mission;
8928         if (Campaign.missions[cur].has_mission_loop) {
8929                 Assert(Campaign.loop_mission != CAMPAIGN_LOOP_MISSION_UNINITIALIZED);
8930         }
8931         bool require_repeat_mission = (Campaign.current_mission == Campaign.next_mission);
8932
8933         // check for (1) mission loop available, (2) dont have to repeat last mission
8934         if ( (Campaign.missions[cur].has_mission_loop && (Campaign.loop_mission != -1)) && !require_repeat_mission ) {
8935
8936                 char buffer[512];
8937                 debrief_assemble_optional_mission_popup_text(buffer, Campaign.missions[cur].mission_loop_desc);
8938
8939                 int choice = popup(0 , 2, POPUP_NO, POPUP_YES, buffer);
8940                 if (choice == 1) {
8941                         Campaign.loop_enabled = 1;
8942                         Campaign.next_mission = Campaign.loop_mission;
8943                 }
8944         }
8945 }
8946
8947 // handle all cases for when the accept key is hit in a multiplayer debriefing
8948 void multi_debrief_accept_hit()
8949 {
8950         // if we already accepted, do nothing
8951         // but he may need to hit accept again after the server has left the game, so allow this
8952         if(Multi_debrief_accept_hit){
8953                 return;
8954         }
8955         
8956         // mark this so that we don't hit it again
8957         Multi_debrief_accept_hit = 1;
8958
8959         gamesnd_play_iface(SND_COMMIT_PRESSED);
8960
8961         // if the server has left the game, always just end the game. 
8962         if(Multi_debrief_server_left){
8963                 if(!multi_quit_game(PROMPT_ALL)){
8964                         Multi_debrief_server_left = 1;
8965                         Multi_debrief_accept_hit = 0;
8966                 } else {
8967                         Multi_debrief_server_left = 0;
8968                 }                       
8969         } else {                
8970                 // query the host and see if he wants to accept stats
8971                 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8972                         // if we're on a tracker game, he gets no choice for storing stats
8973                         if(MULTI_IS_TRACKER_GAME){
8974                                 multi_maybe_set_mission_loop();
8975                         } else {
8976                                 int res = popup(PF_TITLE | PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON | PF_IGNORE_ESC,3,XSTR("&Cancel",779),XSTR("&Accept",844),XSTR("&Toss",845),XSTR("(Continue Netgame)\nDo you wish to accept these stats?",846));
8977                 
8978                                 // evaluate the result
8979                                 switch(res){
8980                                 // undo the accept
8981                                 case -1:
8982                                 case 0:
8983                                         Multi_debrief_accept_hit = 0;
8984                                         return;
8985
8986                                 // set the accept code to be "not accepting"
8987                                 case 2 :
8988                                         multi_debrief_stats_toss();
8989                                         multi_maybe_set_mission_loop();
8990                                         break;
8991                                 
8992                                 // accept the stats and continue
8993                                 case 1 :
8994                                         multi_debrief_stats_accept();
8995                                         multi_maybe_set_mission_loop();
8996                                         break;
8997                                 }
8998                         }
8999                 }
9000
9001                 // set my netplayer state to be "debrief_accept", and be done with it
9002                 Net_player->state = NETPLAYER_STATE_DEBRIEF_ACCEPT;
9003                 send_netplayer_update_packet();
9004         }
9005 }
9006
9007 // handle all cases for when the escape key is hit in a multiplayer debriefing
9008 void multi_debrief_esc_hit()
9009 {
9010         int res;
9011
9012         // if the server has left
9013         if(Multi_debrief_server_left){
9014                 multi_quit_game(PROMPT_ALL);
9015                 return;
9016         }
9017         
9018         // display a popup
9019         if(Net_player->flags & NETINFO_FLAG_GAME_HOST){         
9020                 // if the stats have already been accepted
9021                 if((Multi_debrief_stats_accept_code != -1) || (MULTI_IS_TRACKER_GAME)){
9022                         multi_quit_game(PROMPT_HOST);
9023                 } else {
9024                         res = popup(PF_TITLE | PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON | PF_IGNORE_ESC,3,XSTR("&Cancel",779),XSTR("&Accept",844),XSTR("&Toss",845),XSTR("(Exit Netgame)\nDo you wish to accept these stats?",847));
9025                 
9026                         // evaluate the result
9027                         switch(res){                    
9028                         // undo the accept
9029                         case -1:
9030                         case 0:                         
9031                                 break;
9032
9033                         // set the accept code to be "not accepting"
9034                         case 2 :
9035                                 multi_debrief_stats_toss();
9036                                 multi_quit_game(PROMPT_NONE);
9037                                 break;
9038                                 
9039                         // accept the stats and continue
9040                         case 1 :
9041                                 multi_debrief_stats_accept();
9042                                 multi_quit_game(PROMPT_NONE);
9043                                 break;                                          
9044                         }               
9045                 }
9046         } else {                
9047                 // if the stats haven't been accepted yet, or this is a tracker game
9048                 if((Multi_debrief_stats_accept_code == -1) && !(MULTI_IS_TRACKER_GAME)){
9049                         res = popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON,2,XSTR("&Cancel",779),XSTR("&Leave",848),XSTR("Are you sure you want to leave the netgame before stats are stored?",849));
9050
9051                         // evaluate the result
9052                         if(res == 1){
9053                                 multi_quit_game(PROMPT_NONE);
9054                         }
9055                 }
9056                 // otherwise go through the normal endgame channels
9057                 else {
9058                         multi_quit_game(PROMPT_ALL);
9059                 }               
9060         }
9061 }
9062
9063 void multi_debrief_replay_hit()
9064 {
9065         // only the host should ever get here
9066         Assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
9067
9068         // if the button was already pressed, do nothing
9069         if(Multi_debrief_accept_hit){
9070                 return;
9071         }
9072         
9073         // same as hittin the except button except no stats are kept
9074         Multi_debrief_accept_hit = 1;
9075
9076         // mark myself as being in the replay state so we know what to do next
9077         Net_player->state = NETPLAYER_STATE_DEBRIEF_REPLAY;
9078         send_netplayer_update_packet();
9079 }
9080
9081 // call this when the server has left and we would otherwise be saying "contact lost with server
9082 void multi_debrief_server_left()
9083 {
9084         // the server left
9085         Multi_debrief_server_left = 1;
9086
9087         // undo any "accept" hit so that clients can hit accept again to leave
9088         Multi_debrief_accept_hit = 0;
9089 }
9090
9091 void multi_debrief_stats_accept()
9092 {
9093         // don't do anything if we've already accepted
9094         if(Multi_debrief_stats_accept_code != -1){
9095                 return;
9096         }
9097         
9098         Multi_debrief_stats_accept_code = 1;
9099
9100         // if we're the host, and we're on a standalone, tell the standalone to begin the stats storing process
9101         if((Net_player->flags & NETINFO_FLAG_GAME_HOST) || (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
9102                 // send a packet to the players telling them to store their stats
9103                 send_store_stats_packet(1);
9104         } 
9105
9106         // add a chat line saying "stats have been accepted"
9107         multi_display_chat_msg(XSTR("<stats have been accepted>",850),0,0);
9108
9109         // NETLOG
9110         ml_string(NOX("Stats stored"));
9111 }
9112
9113 void multi_debrief_stats_toss()
9114 {
9115         // don't do anything if we've already accepted
9116         if(Multi_debrief_stats_accept_code != -1){
9117                 return;
9118         }
9119         
9120         Multi_debrief_stats_accept_code = 0;
9121
9122         // if we're the host, and we're on a standalone, tell everyone to "toss" stats
9123         if((Net_player->flags & NETINFO_FLAG_GAME_HOST) || (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
9124                 // send a packet to the players telling them to store their stats
9125                 send_store_stats_packet(0);
9126         } 
9127
9128         // add a chat line saying "stats have been accepted"
9129         multi_display_chat_msg(XSTR("<stats have been tossed>",851),0,0);
9130
9131         // NETLOG
9132         ml_string(NOX("Stats tossed"));
9133 }
9134
9135 int multi_debrief_stats_accept_code()
9136 {
9137         return Multi_debrief_stats_accept_code;
9138 }
9139
9140 void multi_debrief_server_process()
9141 {       
9142         int idx;
9143         int player_status,other_status;
9144
9145         Multi_debrief_server_framecount++;
9146
9147         // if we're > 10 seconds into the debrief and not everyone is here, try warping everyone out again
9148         if((Multi_debrief_time >= Multi_debrief_resend_time) && !multi_netplayer_state_check3(NETPLAYER_STATE_DEBRIEF, NETPLAYER_STATE_DEBRIEF_ACCEPT, NETPLAYER_STATE_DEBRIEF_REPLAY, 1)){
9149                 // find all players who are not in the debrief state and hit them with the endgame packet
9150                 for(idx=0; idx<MAX_PLAYERS; idx++){
9151                         if( MULTI_CONNECTED(Net_players[idx]) && (Net_player != &Net_players[idx]) && ((Net_players[idx].state != NETPLAYER_STATE_DEBRIEF) || (Net_players[idx].state != NETPLAYER_STATE_DEBRIEF_ACCEPT) || (Net_players[idx].state != NETPLAYER_STATE_DEBRIEF_REPLAY)) ){
9152                                 send_endgame_packet(&Net_players[idx]);
9153                         }
9154                 }
9155
9156                 // next check time
9157                 Multi_debrief_resend_time += 7.0f;
9158         }
9159
9160         // evaluate the status of all players in the game (0 == not ready, 1 == ready to continue, 2 == ready to replay)
9161         other_status = 1;
9162
9163         // check all players
9164         for(idx=0;idx<MAX_PLAYERS;idx++){
9165                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_HOST(Net_players[idx])){                                  
9166                         if(Net_players[idx].state != NETPLAYER_STATE_DEBRIEF_ACCEPT){
9167                                 other_status = 0;
9168                                 break;
9169                         }
9170                 }
9171         }
9172
9173         // if we haven't already reported TvT results
9174         if(multi_netplayer_state_check3(NETPLAYER_STATE_DEBRIEF, NETPLAYER_STATE_DEBRIEF_ACCEPT, NETPLAYER_STATE_DEBRIEF_REPLAY) && (Netgame.type_flags & NG_TYPE_TEAM) && !Multi_debrief_reported_tvt && (Multi_debrief_server_framecount > 4)){
9175                 multi_team_report();
9176                 Multi_debrief_reported_tvt = 1;
9177         }       
9178
9179         // if all other players are good to go, check the host
9180         if(other_status){
9181                 // if he is ready to continue
9182                 if(Netgame.host->state == NETPLAYER_STATE_DEBRIEF_ACCEPT){                                      
9183                         player_status = 1;                      
9184                 } 
9185                 // if he wants to replay the mission
9186                 else if(Netgame.host->state == NETPLAYER_STATE_DEBRIEF_REPLAY){
9187                         player_status = 2;                      
9188                 } 
9189                 // if he is not ready
9190                 else {
9191                         player_status = 0;
9192                 }
9193         }
9194         // if all players are _not_ good to go
9195         else {
9196                 player_status = 0;
9197         }
9198                 
9199         // if we're in the debriefing state in a campaign mode, process accordingly
9200         if(Netgame.campaign_mode == MP_CAMPAIGN){
9201                 multi_campaign_do_debrief(player_status);
9202         }
9203         // otherwise process as normal (looking for all players to be ready to go to the next mission
9204         else {
9205                 if(player_status == 1){
9206                         multi_flush_mission_stuff();
9207
9208                         // set the netgame state to be forming and continue
9209                         Netgame.game_state = NETGAME_STATE_FORMING;
9210                         send_netgame_update_packet();
9211
9212                         // move to the proper state
9213                         if(Game_mode & GM_STANDALONE_SERVER){
9214                                 gameseq_post_event(GS_EVENT_STANDALONE_MAIN);
9215                         } else {
9216                                 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
9217                         }
9218
9219                         multi_reset_timestamps();
9220                 } else if(player_status == 2){
9221                         multi_flush_mission_stuff();
9222
9223                         // tell everyone to move into the pre-briefing sync state
9224                         Netgame.game_state = NETGAME_STATE_MISSION_SYNC;
9225                         send_netgame_update_packet();
9226
9227                         // move back to the mission sync screen for the same mission again
9228                         Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
9229                         gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
9230
9231                         multi_reset_timestamps();
9232                 }
9233         }       
9234 }
9235
9236
9237 // -------------------------------------------------------------------------------------------------------------
9238 // 
9239 // MULTIPLAYER PASSWORD POPUP
9240 //
9241
9242 //XSTR:OFF
9243 // bitmaps defs
9244 static char *Multi_pwd_bitmap_fname[GR_NUM_RESOLUTIONS] = {
9245         "Password",                     // GR_640
9246         "2_Password"            // GR_1024
9247 };
9248
9249 static char *Multi_pwd_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
9250         "Password-M",           // GR_640
9251         "2_Password-M"          // GR_1024
9252 };
9253
9254 //XSTR:ON
9255
9256 // constants for coordinate lookup
9257 #define MPWD_X_COORD 0
9258 #define MPWD_Y_COORD 1
9259 #define MPWD_W_COORD 2
9260 #define MPWD_H_COORD 3
9261
9262 // button defs
9263 #define MULTI_PWD_NUM_BUTTONS                   2
9264 #define MPWD_CANCEL                     0
9265 #define MPWD_COMMIT                     1
9266
9267 // password area defs
9268 int Mpwd_coords[GR_NUM_RESOLUTIONS][4] = {
9269         { // GR_640
9270                 134, 112, 367, 15
9271         },
9272         { // GR_1024
9273                 215, 190, 587, 24
9274         }
9275 };
9276
9277 UI_WINDOW Multi_pwd_window;                                                                                             // the window object for the join screen
9278 UI_INPUTBOX     Multi_pwd_passwd;                                                                                               // for Netgame.passwd
9279 int Multi_pwd_bitmap;                                                                                                           // the background bitmap
9280 int Multi_passwd_background = -1;
9281 int Multi_passwd_done = -1;
9282 int Multi_passwd_running = 0;
9283
9284 // password buttons
9285 ui_button_info Multi_pwd_buttons[GR_NUM_RESOLUTIONS][MULTI_PWD_NUM_BUTTONS] = {
9286         { // GR_640
9287                 ui_button_info("PWB_00",        411,    151,    405,    141,    0),
9288                 ui_button_info("PWB_01",        460,    151,    465,    141,    1),
9289         }, 
9290         { // GR_1024
9291                 ui_button_info("2_PWB_00",      659,    242,    649,    225,    0),
9292                 ui_button_info("2_PWB_01",      737,    242,    736,    225,    1),
9293         }, 
9294 };
9295
9296 // text
9297 #define MULTI_PWD_NUM_TEXT                              3
9298 UI_XSTR Multi_pwd_text[GR_NUM_RESOLUTIONS][MULTI_PWD_NUM_TEXT] = {
9299         { // GR_640
9300                 { "Cancel",                     387,    400,    141,    UI_XSTR_COLOR_GREEN, -1,        &Multi_pwd_buttons[0][MPWD_CANCEL].button},
9301                 { "Commit",                     1062,   455,    141,    UI_XSTR_COLOR_GREEN, -1,        &Multi_pwd_buttons[0][MPWD_COMMIT].button},
9302                 { "Enter Password",     1332,   149,    92,     UI_XSTR_COLOR_GREEN, -1,        NULL},
9303         },
9304         { // GR_1024
9305                 { "Cancel",                     387,    649,    225,    UI_XSTR_COLOR_GREEN, -1,        &Multi_pwd_buttons[1][MPWD_CANCEL].button},
9306                 { "Commit",                     1062,   736,    225,    UI_XSTR_COLOR_GREEN, -1,        &Multi_pwd_buttons[1][MPWD_COMMIT].button},
9307                 { "Enter Password",     1332,   239,    148,    UI_XSTR_COLOR_GREEN, -1,        NULL},
9308         }
9309 };
9310
9311 // initialize all graphics, etc
9312 void multi_passwd_init()
9313 {
9314         int idx;
9315
9316         // store the background as it currently is
9317         Multi_passwd_background = gr_save_screen();     
9318         
9319         // create the interface window
9320         Multi_pwd_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
9321         Multi_pwd_window.set_mask_bmap(Multi_pwd_bitmap_mask_fname[gr_screen.res]);
9322
9323         // load the background bitmap
9324         Multi_pwd_bitmap = bm_load(Multi_pwd_bitmap_fname[gr_screen.res]);
9325         if(Multi_pwd_bitmap < 0){
9326                 // we failed to load the bitmap - this is very bad
9327                 Int3();
9328         }
9329                         
9330         // create the interface buttons
9331         for(idx=0; idx<MULTI_PWD_NUM_BUTTONS; idx++){
9332                 // create the object
9333                 Multi_pwd_buttons[gr_screen.res][idx].button.create(&Multi_pwd_window, "", Multi_pwd_buttons[gr_screen.res][idx].x, Multi_pwd_buttons[gr_screen.res][idx].y, 1, 1, 0, 1);
9334
9335                 // set the sound to play when highlighted
9336                 Multi_pwd_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
9337
9338                 // set the ani for the button
9339                 Multi_pwd_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pwd_buttons[gr_screen.res][idx].filename);
9340
9341                 // set the hotspot
9342                 Multi_pwd_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pwd_buttons[gr_screen.res][idx].hotspot);
9343         }       
9344
9345         // add all xstrs
9346         for(idx=0; idx<MULTI_PWD_NUM_TEXT; idx++){
9347                 Multi_pwd_window.add_XSTR(&Multi_pwd_text[gr_screen.res][idx]);
9348         }
9349         
9350         // create the password input box
9351         Multi_pwd_passwd.create(&Multi_pwd_window, Mpwd_coords[gr_screen.res][MPWD_X_COORD], Mpwd_coords[gr_screen.res][MPWD_Y_COORD],Mpwd_coords[gr_screen.res][MPWD_W_COORD], MAX_PASSWD_LEN, "", UI_INPUTBOX_FLAG_ESC_CLR | UI_INPUTBOX_FLAG_INVIS, -1, &Color_normal);
9352         Multi_pwd_passwd.set_focus();
9353         
9354         // link the enter key to ACCEPT
9355         Multi_pwd_buttons[gr_screen.res][MPWD_COMMIT].button.set_hotkey(KEY_ENTER);
9356
9357         Multi_passwd_done = -1;
9358         Multi_passwd_running = 1;
9359 }
9360
9361 // close down all graphics, etc
9362 void multi_passwd_close()
9363 {
9364         // unload any bitmaps
9365         bm_release(Multi_pwd_bitmap);           
9366                 
9367         // destroy the UI_WINDOW
9368         Multi_pwd_window.destroy();
9369
9370         // free up the saved background screen
9371         if(Multi_passwd_background >= 0){
9372                 gr_free_screen(Multi_passwd_background);        
9373                 Multi_passwd_background = -1;
9374         }
9375
9376         Multi_passwd_running = 0;
9377 }
9378
9379 // process any button pressed
9380 void multi_passwd_process_buttons()
9381 {
9382         // if the accept button was pressed
9383         if(Multi_pwd_buttons[gr_screen.res][MPWD_COMMIT].button.pressed()){
9384                 gamesnd_play_iface(SND_USER_SELECT);
9385                 Multi_passwd_done = 1;
9386         }
9387
9388         // if the cancel button was pressed
9389         if(Multi_pwd_buttons[gr_screen.res][MPWD_CANCEL].button.pressed()){
9390                 gamesnd_play_iface(SND_USER_SELECT);
9391                 Multi_passwd_done = 0;
9392         }
9393 }
9394
9395 // run the passwd popup
9396 void multi_passwd_do(char *passwd)
9397 {
9398         int k;
9399
9400         while(Multi_passwd_done == -1){ 
9401                 // set frametime and run background stuff
9402                 game_set_frametime(-1);
9403                 game_do_state_common(gameseq_get_state());
9404
9405                 k = Multi_pwd_window.process();
9406
9407                 // process any keypresses
9408                 switch(k){
9409                 case KEY_ESC :                                                  
9410                         // set this to indicate the user has cancelled for one reason or another
9411                         Multi_passwd_done = 0;
9412                         break;          
9413                 }       
9414
9415                 // if the input box text has changed
9416                 if(Multi_pwd_passwd.changed()){
9417                         strcpy(passwd,"");
9418                         Multi_pwd_passwd.get_text(passwd);
9419                 }
9420
9421                 // process any button pressed
9422                 multi_passwd_process_buttons();
9423         
9424                 // draw the background, etc
9425                 gr_reset_clip();
9426                 gr_clear();
9427                 if(Multi_passwd_background >= 0){
9428                         gr_restore_screen(Multi_passwd_background);             
9429                 }
9430                 gr_set_bitmap(Multi_pwd_bitmap);
9431                 gr_bitmap(0,0);
9432                 Multi_pwd_window.draw();
9433                         
9434                 // flip the buffer
9435                 gr_flip();
9436         }
9437 }
9438
9439 // bring up the password string popup, fill in passwd (return 1 if accept was pressed, 0 if cancel was pressed)
9440 int multi_passwd_popup(char *passwd)
9441 {
9442         // if the popup is already running for some reason, don't do anything
9443         if(Multi_passwd_running){
9444                 return 0;
9445         }
9446
9447         // initialize all graphics
9448         multi_passwd_init();
9449
9450         // run the popup
9451         multi_passwd_do(passwd);
9452
9453         // shut everything down
9454         multi_passwd_close();
9455
9456         return Multi_passwd_done;
9457 }