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