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