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