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