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