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