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