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