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