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