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