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