]> icculus.org git repositories - taylor/freespace2.git/blob - src/network/multiui.cpp
clean up stats saving a bit more
[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(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),LOW_WARN_TEXT);
2504
2505         case CW_CODE_INFO:
2506                 return popup(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(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),MED_WARN_TEXT);
2517
2518         case CW_CODE_INFO:
2519                 return popup(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(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(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(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(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(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(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                         return multi_sw_ok_to_commit();
5819                 }
5820 #endif
5821         }       
5822                 
5823         return 1;
5824 }
5825
5826 int multi_create_verify_cds()
5827 {
5828         int player_count = multi_num_players();
5829         int multi_cd_count;
5830         int idx;
5831
5832         // count how many cds we have
5833         multi_cd_count = 0;
5834         for(idx=0;idx<MAX_PLAYERS;idx++){
5835                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_HAS_CD)){
5836                         multi_cd_count++;
5837                 }
5838         }
5839
5840         // for the beta, everyone must have a CD
5841 #ifdef MULTIPLAYER_BETA_BUILD
5842         if(multi_cd_count < player_count){
5843                 return 0;
5844         }
5845 #else
5846         // determine if we have enough
5847         float ratio = (float)player_count / (float)multi_cd_count;
5848         // greater than a 4 to 1 ratio
5849         if(ratio > 4.0f){
5850                 return 0;
5851         } 
5852 #endif
5853
5854         // we meet the conditions
5855         return 1;
5856 }
5857
5858 // returns an index into Multi_create_mission_list
5859 int multi_create_lookup_mission(char *fname)
5860 {
5861         int idx;
5862
5863         for(idx=0; idx<Multi_create_mission_count; idx++){
5864                 if(!SDL_strcasecmp(fname, Multi_create_mission_list[idx].filename)){
5865                         return idx;
5866                 }
5867         }
5868
5869         // couldn't find the mission
5870         return -1;
5871 }
5872
5873 // returns an index into Multi_create_campaign_list
5874 int multi_create_lookup_campaign(char *fname)
5875 {
5876         int idx;
5877
5878         for(idx=0; idx<Multi_create_campaign_count; idx++){
5879                 if(!SDL_strcasecmp(fname, Multi_create_campaign_list[idx].filename)){
5880                         return idx;
5881                 }
5882         }
5883
5884         // couldn't find the campaign
5885         return -1;
5886 }
5887
5888 void multi_create_refresh_pxo()
5889 {
5890         // delete mvalid.cfg if it exists
5891         cf_delete(MULTI_VALID_MISSION_FILE, CF_TYPE_DATA);
5892
5893         // refresh missions from the tracker
5894         multi_update_valid_missions();
5895 }
5896
5897 void multi_create_sw_clicked()
5898 {
5899 #ifndef MAKE_FS1
5900         netgame_info ng_temp;
5901         netgame_info *ng;
5902
5903         int file_index = multi_create_select_to_index(Multi_create_list_select);
5904
5905         // either a temporary netgame or the real one
5906         if(MULTIPLAYER_MASTER){
5907                 ng = &Netgame;
5908         } else {
5909                 ng_temp = Netgame;
5910                 ng = &ng_temp;
5911         }
5912
5913         // maybe switch squad war off
5914         if(!Multi_create_sw_checkbox.checked()){
5915                 // if the mission selected is a coop mission, go back to coop mode
5916                 SDL_assert(file_index != -1);
5917                 if(file_index == -1){
5918                         ng->type_flags = NG_TYPE_COOP;                  
5919                 }               
5920                 if(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_TEAMS){
5921                         ng->type_flags = NG_TYPE_TVT;
5922                 } else if(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_DOGFIGHT){
5923                         ng->type_flags = NG_TYPE_DOGFIGHT;
5924                 } else {
5925                         ng->type_flags = NG_TYPE_COOP;
5926                 }
5927         }
5928         // switch squad war on
5929         else {
5930                 SDL_assert(file_index != -1);
5931                 if((file_index == -1) || !(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_TEAMS)){                       
5932                         Multi_create_sw_checkbox.set_state(0);                  
5933                 } else {
5934                         // at this point we know its safe to switch squad war on
5935                         ng->type_flags = NG_TYPE_SW;
5936                 }
5937         }
5938
5939         // update 
5940         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){         
5941                 // update players 
5942                 send_netgame_update_packet();                   
5943
5944                 // update all machines about stuff like respawns, etc.
5945                 multi_options_update_netgame();
5946         }
5947         // on the standalone
5948         else {
5949                 // standalone will take care of polling the usertracker
5950                 multi_options_update_mission(ng, Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ? 1 : 0);
5951         }
5952 #endif
5953 }
5954
5955
5956 // -------------------------------------------------------------------------------------------------------------
5957 // 
5958 // MULTIPLAYER HOST OPTIONS SCREEN
5959 //
5960
5961 #define MULTI_HO_NUM_BUTTONS                            12
5962 #define MULTI_HO_NUM_RADIO_BUTTONS              10
5963
5964 //XSTR:OFF
5965 // bitmaps defs
5966 #define MULTI_HO_PALETTE                                "InterfacePalette"
5967
5968 static const char *Multi_ho_bitmap_fname[GR_NUM_RESOLUTIONS] = {
5969         "MultiHost",                    // GR_640
5970         "2_MultiHost"                   // GR_1024
5971 };
5972
5973 static const char *Multi_ho_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
5974         "MultiHost-M",                  // GR_640
5975         "2_MultiHost-M"         // GR_1024
5976 };
5977
5978 //XSTR:ON
5979 UI_WINDOW Multi_ho_window;                                                                                              // the window object for the join screen
5980 UI_INPUTBOX Multi_ho_respawns;                                                                          // the # of respawns allowed in the game
5981 UI_INPUTBOX Multi_ho_time_limit;                                                                                // mission time limit
5982 UI_INPUTBOX Multi_ho_voice_wait;                                                                                // wait time between tokens
5983 UI_INPUTBOX Multi_ho_kill_limit;                                                                                // kill limit in a furball mission
5984 UI_INPUTBOX Multi_ho_obs;                                                                                               // # of observers we'll allow
5985 int Multi_ho_bitmap;                                                                                                            // the background bitmap
5986
5987 // constants for coordinate lookup
5988 #define MULTI_HO_X_COORD                        0
5989 #define MULTI_HO_Y_COORD                        1
5990 #define MULTI_HO_W_COORD                        2
5991 #define MULTI_HO_H_COORD                        3
5992 #define MULTI_HO_TEXT_X_COORD           4
5993 #define MULTI_HO_TEXT_Y_COORD           5
5994
5995 // button defs
5996 #define MULTI_HO_MSG_RANK                               0               // highest ranking players can do messaging
5997 #define MULTI_HO_MSG_LEADER                     1               // wing/team leaders can do messaging
5998 #define MULTI_HO_MSG_ANY                                2               // any player can do messaging
5999 #define MULTI_HO_MSG_HOST                               3               // only the host can do messaging
6000 #define MULTI_HO_END_RANK                               4               // highest rank can and host can end mission
6001 #define MULTI_HO_END_LEADER                     5               // wing/team leaders and host can end the mission
6002 #define MULTI_HO_END_ANY                                6               // any player can end the mission
6003 #define MULTI_HO_END_HOST                               7               // only host can end the mission
6004 #define MULTI_HO_VOICE_ON                               8               // voice toggled on
6005 #define MULTI_HO_VOICE_OFF                              9               // voice toggled off
6006 #define MULTI_HO_HOST_MODIFIES          10              // only the host or team captains can modify ships/weapons in briefing
6007 #define MULTI_HO_ACCEPT                                 11              // accept button
6008
6009 ui_button_info Multi_ho_buttons[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_BUTTONS] = {   
6010         { // GR_640
6011 #ifdef MAKE_FS1
6012                 // who is allowed to message
6013                 ui_button_info("MH_00", 13,             157,    -1,     -1,     0),             // highest rank
6014                 ui_button_info("MH_01", 13,             179,    -1,     -1,     1),             // team/wing leader
6015                 ui_button_info("MH_02", 13,             200,    -1,     -1,     2),             // any
6016                 ui_button_info("MH_03", 13,             223,    -1,     -1,     3),             // host
6017                 
6018                 // who is allowed to end the mission
6019                 ui_button_info("MH_09", 13,             273,    -1,     -1,     9),             // highest rank
6020                 ui_button_info("MH_10", 13,             295,    -1,     -1,     10),            // team/wing leader
6021                 ui_button_info("MH_11", 13,             317,    -1,     -1,     11),            // any
6022                 ui_button_info("MH_12", 13,             339,    -1,     -1,     12),            // host         
6023
6024                 // voice on/off button
6025                 ui_button_info("MH_14", 396,    156,    -1,     -1,     14),    
6026                 ui_button_info("MH_15", 453,    156,    -1,     -1,     15),    
6027
6028                 // host modifies ships
6029                 ui_button_info("MH_19", 215,    410,    -1,     -1,     19),    
6030
6031                 // exit
6032                 ui_button_info("MH_18", 560,    411,    -1,     -1,     18),
6033 #else
6034                 // who is allowed to message
6035                 ui_button_info("MH_00", 3,      160,    46,     166,    0),             // highest rank
6036                 ui_button_info("MH_01", 3,      179,    46,     185,    1),             // team/wing leader
6037                 ui_button_info("MH_02", 3,      196,    46,     203,    2),             // any
6038                 ui_button_info("MH_03", 3,      214,    46,     220,    3),             // host
6039                 
6040                 // who is allowed to end the mission
6041                 ui_button_info("MH_04", 3,      257,    46,     265,    4),             // highest rank
6042                 ui_button_info("MH_05", 3,      276,    46,     283,    5),             // team/wing leader
6043                 ui_button_info("MH_06", 3,      294,    46,     300,    6),             // any
6044                 ui_button_info("MH_07", 3,      311,    46,     317,    7),             // host         
6045
6046                 // voice on/off button
6047                 ui_button_info("MH_09", 542,    158,    545,    185,    9),     
6048                 ui_button_info("MH_10", 598,    158,    604,    185,    10),    
6049
6050                 // host modifies ships
6051                 ui_button_info("MH_13", 542,    377,    437,    363,    13),    
6052
6053                 // exit
6054                 ui_button_info("MH_14", 572,    428,    580,    414,    14),    
6055 #endif
6056         },
6057         { // GR_1024
6058                 // who is allowed to message
6059                 ui_button_info("2_MH_00",       5,      256,    73,     269,    0),             // highest rank
6060                 ui_button_info("2_MH_01",       5,      286,    73,     297,    1),             // team/wing leader
6061                 ui_button_info("2_MH_02",       5,      314,    73,     325,    2),             // any
6062                 ui_button_info("2_MH_03",       5,      341,    73,     352,    3),             // host
6063                 
6064                 // who is allowed to end the mission
6065                 ui_button_info("2_MH_04",       5,      412,    73,     425,    4),             // highest rank
6066                 ui_button_info("2_MH_05",       5,      442,    73,     452,    5),             // team/wing leader
6067                 ui_button_info("2_MH_06",       5,      470,    73,     480,    6),             // any
6068                 ui_button_info("2_MH_07",       5,      497,    73,     508,    7),             // host         
6069
6070                 // voice on/off button
6071                 ui_button_info("2_MH_09",       867,    253,    872,    296,    9),     
6072                 ui_button_info("2_MH_10",       957,    253,    966,    296,    10),    
6073
6074                 // host modifies ships
6075                 ui_button_info("2_MH_13",       867,    603,    784,    581,    13),    
6076
6077                 // exit
6078                 ui_button_info("2_MH_14",       916,    685,    925,    665,    14),    
6079         },
6080 };
6081 UI_XSTR Multi_ho_text[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_BUTTONS] = {
6082         { // GR_640
6083                 // not needed for FS1
6084 #ifndef MAKE_FS1
6085                 {"Highest rank",                                        1280,   46,     166,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_RANK].button},
6086                 {"Team / wing-leader",                  1281, 46,       185,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_LEADER].button},
6087                 {"Any",                                                         1282, 46,       203,    UI_XSTR_COLOR_GREEN,    -1, &Multi_ho_buttons[0][MULTI_HO_MSG_ANY].button},
6088                 {"Host",                                                                1283,   46,     220,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_HOST].button},
6089                 {"Highest rank",                                        1280,   46,     265,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_RANK].button},
6090                 {"Team / wing-leader",                  1281,   46,     283,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_LEADER].button},
6091                 {"Any",                                                         1282, 46,       300,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_ANY].button},
6092                 {"Host",                                                                1283,   46,     317,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_HOST].button},               
6093                 {"On",                                                          1285,   545,    185,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_VOICE_ON].button},
6094                 {"Off",                                                         1286,   604,    185,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_VOICE_OFF].button},
6095                 {"Host modifies ships",                 1287,   437,    363,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_HOST_MODIFIES].button},
6096                 {"Exit",                                                                1417,   572,    418,    UI_XSTR_COLOR_PINK,     -1, &Multi_ho_buttons[0][MULTI_HO_ACCEPT].button},
6097 #endif
6098         },
6099         { // GR_1024
6100                 // not needed for FS1
6101 #ifndef MAKE_FS1
6102                 {"Highest rank",                                        1280,   62,     269,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_RANK].button},
6103                 {"Team / wing-leader",                  1281, 62,       297,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_LEADER].button},
6104                 {"Any",                                                         1282, 62,       325,    UI_XSTR_COLOR_GREEN,    -1, &Multi_ho_buttons[1][MULTI_HO_MSG_ANY].button},
6105                 {"Host",                                                                1283,   62,     352,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_HOST].button},
6106                 {"Highest rank",                                        1280,   62,     425,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_RANK].button},
6107                 {"Team / wing-leader",                  1281,   62,     452,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_LEADER].button},
6108                 {"Any",                                                         1282, 62,       480,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_ANY].button},
6109                 {"Host",                                                                1283,   62,     508,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_HOST].button},               
6110                 {"On",                                                          1285,   877,    294,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_VOICE_ON].button},
6111                 {"Off",                                                         1286,   967,    293,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_VOICE_OFF].button},
6112                 {"Host modifies ships",                 1287,   869,    589,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_HOST_MODIFIES].button},
6113                 {"Exit",                                                                1417,   953,    672,    UI_XSTR_COLOR_PINK,     -1, &Multi_ho_buttons[1][MULTI_HO_ACCEPT].button},
6114 #endif
6115         }
6116 };
6117
6118 // radio button controls
6119 #define MULTI_HO_NUM_RADIO_GROUPS               3
6120 #define MULTI_HO_MSG_GROUP                                      0                                                       // group dealing with squadmate messaging
6121 #define MULTI_HO_END_GROUP                                      1                                                       // group dealing with ending the mission
6122 #define MULTI_HO_VOICE_GROUP                            2                                                       // group dealing with voice stuff
6123 int Multi_ho_radio_groups[MULTI_HO_NUM_RADIO_GROUPS] = {                // currently selected button in the radio button group
6124         0,0,0
6125 };
6126 int Multi_ho_radio_info[MULTI_HO_NUM_RADIO_BUTTONS][3] = {      // info related to each of the radio buttons themselves
6127         // { group #, value, button id# }
6128         {0, 0, 0},                              // highest ranking players can do messaging
6129         {0, 1, 1},                              // wing/team leaders can do messaging
6130         {0, 2, 2},                              // any player can do messaging
6131         {0, 3, 3},                              // only host can do messaging   
6132         {1, 0, 4},                              // highest rank and host can end the mission
6133         {1, 1, 5},                              // team/wing leader can end the mission
6134         {1, 2, 6},                              // any player can end the mission
6135         {1, 3, 7},                              // only the host can end the mission
6136         {2, 0, 8},                              // voice toggled on
6137         {2, 1, 9}                               // voice toggled off
6138 };
6139
6140 // slider controls
6141 #define MULTI_HO_NUM_SLIDERS                                    3
6142 #define MULTI_HO_SLIDER_VOICE_QOS                       0                                               // voice quality of sound 
6143 #define MULTI_HO_SLIDER_VOICE_DUR                       1                                               // max duration of voice recording
6144 #define MULTI_HO_SLIDER_SKILL                                   2                                               // skill level
6145 struct ho_sliders {
6146         const char *filename;
6147         int x, y, xt, yt;
6148         int hotspot;
6149         int dot_w;
6150         int dots;
6151         UI_DOT_SLIDER_NEW slider;  // because we have a class inside this struct, we need the constructor below..
6152
6153         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){}
6154 };
6155 ho_sliders Multi_ho_sliders[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_SLIDERS] = {
6156         { // GR_640
6157 #ifdef MAKE_FS1
6158                 ho_sliders("MH_16",     396,    210,    -1,     -1,     16,     19,     10),                    // voice qos
6159                 ho_sliders("MH_17",     396,    263,    -1,     -1,     17,     19,     10),                    // voice duration
6160                 ho_sliders("MH_13",     10,             387,    -1,     -1,     13,     36,     5),                             // skill level
6161 #else
6162                 ho_sliders("MH_11",     428,    214,    437,    199,    11,     19,     10),                    // voice qos
6163                 ho_sliders("MH_12",     428,    261,    437,    246,    12,     19,     10),                    // voice duration
6164                 ho_sliders("MH_08",     237,    454,    230,    411,    8,              36,     5),                     // skill level
6165 #endif
6166         },
6167         { // GR_1024            
6168                 ho_sliders("2_MH_11",   684,    343,    690,    323,    11,     32,     10),                    // voice qos
6169                 ho_sliders("2_MH_12",   685,    418,    837,    468,    12,     32,     10),                    // voice duration
6170                 ho_sliders("2_MH_08",   379,    727,    369,    663,    8,              60,     5),                     // skill level
6171         }
6172 };
6173
6174 int Multi_ho_mission_respawn;
6175
6176 int Multi_ho_host_modifies;
6177
6178 // whether or not any of the inputboxes on this screen had focus last frame
6179 int Multi_ho_lastframe_input = 0;
6180
6181 // game information text areas
6182
6183 // ho titles
6184 #ifndef MAKE_FS1
6185 #define MULTI_HO_NUM_TITLES                                     14
6186
6187 UI_XSTR Multi_ho_titles[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_TITLES] = {
6188         { // GR_640
6189                 { "AI Orders",                          1289,           32,     144, UI_XSTR_COLOR_GREEN, -1, NULL },           
6190                 { "End Mission",                        1290,           32,     242, UI_XSTR_COLOR_GREEN, -1, NULL },
6191                 { "Time Limit",                 1291,           32,     347, UI_XSTR_COLOR_GREEN, -1, NULL },
6192                 { "Min",                                                1292,           74,     362, UI_XSTR_COLOR_GREEN, -1, NULL },
6193                 { "Respawn Limit",              1288,           32,     378, UI_XSTR_COLOR_GREEN, -1, NULL },
6194                 { "Kill Limit",                 1293,           32,     409, UI_XSTR_COLOR_GREEN, -1, NULL },
6195                 { "Observers",                          1294,           32,     441, UI_XSTR_COLOR_GREEN, -1, NULL },
6196                 { "Skill Level",                        1284,           230,    411, UI_XSTR_COLOR_GREEN, -1, NULL },
6197                 { "Voice Transmission", 1295,           437,    144, UI_XSTR_COLOR_GREEN, -1, NULL },
6198                 { "Voice Quality",              1296,           437,    199, UI_XSTR_COLOR_GREEN, -1, NULL },
6199                 { "Message Duration",   1297,           437,    246, UI_XSTR_COLOR_GREEN, -1, NULL },
6200                 { "sec",                                                1522,           523,    292, UI_XSTR_COLOR_GREEN, -1, NULL },           
6201                 { "sec",                                                1523,           523,    332, UI_XSTR_COLOR_GREEN, -1, NULL },           
6202                 { "Voice Wait",                 1298,           437,    313, UI_XSTR_COLOR_GREEN, -1, NULL },
6203         },
6204         { // GR_1024            
6205                 { "AI Orders",                          1289,           48,     238, UI_XSTR_COLOR_GREEN, -1, NULL },           
6206                 { "End Mission",                        1290,           48,     394, UI_XSTR_COLOR_GREEN, -1, NULL },
6207                 { "Time Limit",                 1291,           50,     568, UI_XSTR_COLOR_GREEN, -1, NULL },
6208                 { "Min",                                                1292,           119,    581, UI_XSTR_COLOR_GREEN, -1, NULL },
6209                 { "Respawn Limit",              1288,           50,     618, UI_XSTR_COLOR_GREEN, -1, NULL },
6210                 { "Kill Limit",                 1293,           50,     668, UI_XSTR_COLOR_GREEN, -1, NULL },
6211                 { "Observers",                          1294,           50,     718, UI_XSTR_COLOR_GREEN, -1, NULL },
6212                 { "Skill Level",                        1284,           398,    670, UI_XSTR_COLOR_GREEN, -1, NULL },
6213                 { "Voice Transmission", 1295,           869,    239, UI_XSTR_COLOR_GREEN, -1, NULL },
6214                 { "Voice Quality",              1296,           690,    331, UI_XSTR_COLOR_GREEN, -1, NULL },
6215                 { "Message Duration",   1297,           690,    405, UI_XSTR_COLOR_GREEN, -1, NULL },
6216                 { "sec",                                                1522,           837,    467, UI_XSTR_COLOR_GREEN, -1, NULL },
6217                 { "sec",                                                1523,           837,    534, UI_XSTR_COLOR_GREEN, -1, NULL },
6218                 { "Voice Wait",                 1298,           742,    510, UI_XSTR_COLOR_GREEN, -1, NULL },
6219         }
6220 };
6221 #endif
6222
6223 // mission time limit input box
6224 int Ho_time_coords[GR_NUM_RESOLUTIONS][4] = {
6225         { // GR_640
6226 #ifdef MAKE_FS1
6227                 217, 239, 41, 9
6228 #else
6229                 36, 362, 36, 17
6230 #endif
6231         },
6232         { // GR_1024
6233                 58, 581, 57, 27
6234         }
6235 };
6236
6237 // furball kill limit input box
6238 int Ho_kill_coords[GR_NUM_RESOLUTIONS][4] = {
6239         { // GR_640
6240 #ifdef MAKE_FS1
6241                 217, 364, 70, 9
6242 #else
6243                 36, 425, 45, 17
6244 #endif
6245         },
6246         { // GR_1024
6247                 58, 684, 72, 27
6248         }
6249 };
6250
6251 // voice recording duration text display area
6252 int Ho_vd_coords[GR_NUM_RESOLUTIONS][4] = {
6253         { // GR_640
6254 #ifdef MAKE_FS1
6255                 419, 300, 39, 9
6256 #else
6257                 467, 292, 55, 15
6258 #endif
6259         },
6260         { // GR_1024
6261                 750, 467, 85, 28
6262         }
6263 };
6264
6265 // voice token wait input box
6266 int Ho_vw_coords[GR_NUM_RESOLUTIONS][6] = {
6267         { // GR_640
6268 #ifdef MAKE_FS1
6269                 419, 366, 39, 9
6270 #else
6271                 467, 332, 55, 15
6272 #endif
6273         },
6274         { // GR_1024
6275                 750, 534, 85, 28
6276         }
6277 };
6278
6279 // observer count input box
6280 int Ho_obs_coords[GR_NUM_RESOLUTIONS][4] = {
6281         { // GR_640
6282 #ifdef MAKE_FS1
6283                 217, 176, 70, 9
6284 #else
6285                 36, 457, 45, 17
6286 #endif
6287         },
6288         { // GR_1024
6289                 58, 733, 72, 27
6290         }
6291 };
6292
6293 // skill text description area
6294 int Ho_st_coords[GR_NUM_RESOLUTIONS][4] = {
6295         { // GR_640
6296 #ifdef MAKE_FS1
6297                 40, 426, 167, 9
6298 #else
6299                 249, 435, 172, 10
6300 #endif
6301         },
6302         { // GR_1024
6303                 403, 699, 172, 10
6304         }
6305 };
6306
6307 // respawn input box
6308 int Ho_rsp_coords[GR_NUM_RESOLUTIONS][6] = {
6309         { // GR_640
6310 #ifdef MAKE_FS1
6311                 217, 301, 70, 9,
6312 #else
6313                 36, 394, 45, 17, 
6314 #endif
6315         },
6316         { // GR_1024
6317                 58, 632, 72, 27
6318         }
6319 };
6320
6321 // respawn max text area
6322 int Ho_max_rsp_coords[GR_NUM_RESOLUTIONS][2] = {
6323         { // GR_640
6324 #ifdef MAKE_FS1
6325                 305, 301
6326 #else
6327                 150, 378
6328 #endif
6329         },
6330         { // GR_1024
6331                 190, 618
6332         }
6333 };
6334
6335 // maximum values for various input boxes (to notify user of overruns)
6336 #define MULTI_HO_MAX_TIME_LIMIT                         500
6337 #define MULTI_HO_MAX_TOKEN_WAIT                         5
6338 #define MULTI_HO_MAX_KILL_LIMIT                         9999
6339 #define MULTI_HO_MAX_OBS                                                4
6340
6341 // LOCAL function definitions
6342 void multi_ho_check_buttons();
6343 void multi_ho_button_pressed(int n);
6344 void multi_ho_draw_radio_groups();
6345 void multi_ho_accept_hit();
6346 void multi_ho_get_options();
6347 void multi_ho_apply_options();
6348 void multi_ho_display_record_time();
6349 int multi_ho_check_values();
6350 void multi_ho_check_focus();
6351 void multi_ho_blit_max_respawns();
6352 void multi_ho_display_skill_level();
6353
6354 void multi_host_options_init()
6355 {
6356         int idx;
6357
6358         // create the interface window
6359         Multi_ho_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
6360         Multi_ho_window.set_mask_bmap(Multi_ho_bitmap_mask_fname[gr_screen.res]);
6361
6362         // load the background bitmap
6363         Multi_ho_bitmap = bm_load(Multi_ho_bitmap_fname[gr_screen.res]);
6364         if(Multi_ho_bitmap < 0){
6365                 // we failed to load the bitmap - this is very bad
6366                 Int3();
6367         }               
6368
6369         // initialize the common notification messaging
6370         multi_common_notify_init();     
6371
6372         // use the common interface palette
6373         multi_common_set_palette();     
6374
6375         // create the interface buttons
6376         for(idx=0;idx<MULTI_HO_NUM_BUTTONS;idx++){
6377                 // create the object
6378                 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);
6379
6380                 // set the sound to play when highlighted
6381                 Multi_ho_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
6382
6383                 // set the ani for the button
6384                 Multi_ho_buttons[gr_screen.res][idx].button.set_bmaps(Multi_ho_buttons[gr_screen.res][idx].filename);
6385
6386                 // set the hotspot, ignoring the skill level button             
6387                 Multi_ho_buttons[gr_screen.res][idx].button.link_hotspot(Multi_ho_buttons[gr_screen.res][idx].hotspot);
6388
6389                 // add xstr text
6390 #ifndef MAKE_FS1
6391                 Multi_ho_window.add_XSTR(&Multi_ho_text[gr_screen.res][idx]);
6392 #endif
6393         }               
6394
6395 #ifndef MAKE_FS1
6396         // create misc text
6397         for(idx=0; idx<MULTI_HO_NUM_TITLES; idx++){
6398                 Multi_ho_window.add_XSTR(&Multi_ho_titles[gr_screen.res][idx]);
6399         }
6400 #endif
6401
6402         // create the interface sliders
6403         for(idx=0; idx<MULTI_HO_NUM_SLIDERS; idx++){
6404                 // create the object
6405                 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);
6406         }
6407
6408         // create the respawn count input box
6409         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);          
6410         // if we're in campaign mode, disable it
6411         if(Netgame.campaign_mode == MP_CAMPAIGN){
6412                 Multi_ho_respawns.set_text(XSTR("NA",795));  // [[ Not applicable ]]
6413                 Multi_ho_respawns.disable();
6414         } else {
6415                 Multi_ho_respawns.set_valid_chars("0123456789");
6416         }
6417
6418         // create the time limit input box
6419         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);
6420         Multi_ho_time_limit.set_valid_chars("-0123456789");
6421
6422         // create the voice token wait input box
6423         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);
6424         Multi_ho_voice_wait.set_valid_chars("01243456789");
6425
6426         // create the furball kill limit input box
6427         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);
6428         Multi_ho_kill_limit.set_valid_chars("0123456789");
6429
6430         // create the observer limit input box
6431         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);
6432         Multi_ho_obs.set_valid_chars("01234");  
6433         
6434         // load in the current netgame defaults
6435         multi_ho_get_options();
6436
6437         // whether or not any of the inputboxes on this screen had focus last frame
6438         Multi_ho_lastframe_input = 0;   
6439
6440         // get the # of respawns for the currently selected mission (if any)
6441         if(Multi_create_list_select != -1){
6442                 int abs_index = multi_create_select_to_index(Multi_create_list_select);
6443
6444                 // if he has a valid mission selected
6445                 if(abs_index >= 0){
6446                         Multi_ho_mission_respawn = (int)Multi_create_file_list[abs_index].respawn;
6447                 } else {
6448                         Multi_ho_mission_respawn = -1;
6449                 }
6450         } else {
6451                 Multi_ho_mission_respawn = -1;
6452         }
6453 }
6454
6455 void multi_ho_update_sliders()
6456 {
6457         // game skill slider
6458         if (Game_skill_level != Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos) {
6459                 if ( !(Netgame.type_flags & NG_TYPE_TEAM) ){            
6460                         Game_skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
6461                         gamesnd_play_iface(SND_USER_SELECT);
6462                 } else {        
6463                         Game_skill_level = NUM_SKILL_LEVELS / 2;
6464                 }
6465         }
6466
6467         // get the voice qos options
6468         if (Netgame.options.voice_qos != (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1)) {
6469                 Netgame.options.voice_qos = (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1); 
6470                 gamesnd_play_iface(SND_USER_SELECT);
6471         }
6472
6473         // get the voice duration options
6474         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)) {
6475                 Netgame.options.voice_record_time = (int)(0.5f * (float)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 1000.0f);
6476                 gamesnd_play_iface(SND_USER_SELECT);
6477         }
6478
6479 }
6480
6481 void multi_host_options_do()
6482 {
6483         int k;  
6484         
6485         // process stuff
6486         k = Multi_ho_window.process();          
6487         chatbox_process(k);     
6488
6489         // process any keypresses
6490         switch(k){
6491         case SDLK_ESCAPE :
6492                 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
6493                 break;
6494         // same as ACCEPT
6495         case KEY_CTRLED + SDLK_RETURN :
6496                 gamesnd_play_iface(SND_COMMIT_PRESSED);
6497                 multi_ho_accept_hit();
6498                 break;
6499         }
6500
6501         // process any button clicks
6502         multi_ho_check_buttons();       
6503
6504         // update the sliders
6505         multi_ho_update_sliders();
6506
6507         // make sure that the chatbox inputbox and any inputbox on this screen are mutually exclusive in terms of focus
6508         multi_ho_check_focus();
6509
6510         // draw the background, etc
6511         gr_reset_clip();
6512         GR_MAYBE_CLEAR_RES(Multi_ho_bitmap);
6513         if(Multi_ho_bitmap != -1){
6514                 gr_set_bitmap(Multi_ho_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
6515                 gr_bitmap(0,0);
6516         }
6517         Multi_ho_window.draw();
6518         
6519         // draw all the radio buttons properly
6520         multi_ho_draw_radio_groups();
6521                         
6522         // display any pending notification messages
6523         multi_common_notify_do();               
6524
6525         // display the voice record time settings
6526         multi_ho_display_record_time(); 
6527
6528         // maybe display the max # of respawns next to the respawn input box
6529         multi_ho_blit_max_respawns();
6530
6531         // blit the proper skill level  
6532         multi_ho_display_skill_level();
6533
6534         // blit the "host modifies button"
6535         if(Multi_ho_host_modifies){
6536                 Multi_ho_buttons[gr_screen.res][MULTI_HO_HOST_MODIFIES].button.draw_forced(2);
6537         }
6538
6539         // process and show the chatbox thingie 
6540         chatbox_render();
6541
6542         // draw tooltips
6543         Multi_ho_window.draw_tooltip(); 
6544
6545         // display the voice status indicator
6546         multi_common_voice_display_status();
6547
6548         // flip the buffer
6549         gr_flip();
6550 }
6551
6552 void multi_host_options_close()
6553 {
6554         // unload any bitmaps
6555         if(!bm_unload(Multi_ho_bitmap)){
6556                 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_ho_bitmap_fname[gr_screen.res]));
6557         }       
6558         
6559         // destroy the UI_WINDOW
6560         Multi_ho_window.destroy();
6561 }
6562
6563 void multi_ho_check_buttons()
6564 {
6565         int idx;
6566         for(idx=0;idx<MULTI_HO_NUM_BUTTONS;idx++){
6567                 // we only really need to check for one button pressed at a time, so we can break after 
6568                 // finding one.
6569                 if(Multi_ho_buttons[gr_screen.res][idx].button.pressed()){
6570                         multi_ho_button_pressed(idx);
6571                         break;
6572                 }
6573         }
6574 }
6575
6576 void multi_ho_button_pressed(int n)
6577 {
6578         int radio_index,idx;
6579         int x_pixel,y_pixel;
6580
6581         // get the pixel position of the click
6582         Multi_ho_buttons[gr_screen.res][n].button.get_mouse_pos(&x_pixel,&y_pixel);
6583                 
6584         switch(n){              
6585         // clicked on the accept button
6586         case MULTI_HO_ACCEPT:
6587                 gamesnd_play_iface(SND_COMMIT_PRESSED);
6588                 multi_ho_accept_hit();
6589                 return; 
6590         
6591         // clicked on the host/captains only modify button
6592         case MULTI_HO_HOST_MODIFIES:
6593                 // toggle it on or off
6594                 Multi_ho_host_modifies = !Multi_ho_host_modifies;
6595                 gamesnd_play_iface(SND_USER_SELECT);
6596                 return;
6597         }
6598
6599         // look through the radio buttons and see which one this corresponds to
6600         radio_index = -1;
6601         for(idx=0;idx<MULTI_HO_NUM_RADIO_BUTTONS;idx++){
6602                 if(Multi_ho_radio_info[idx][2] == n){
6603                         radio_index = idx;
6604                         break;
6605                 }
6606         }
6607         SDL_assert(radio_index != -1);
6608
6609         // check to see if a radio button was pressed
6610         if(radio_index < MULTI_HO_NUM_RADIO_BUTTONS){
6611                 // see if this value is already picked for this radio group
6612                 if(Multi_ho_radio_groups[Multi_ho_radio_info[radio_index][0]] != Multi_ho_radio_info[radio_index][1]){
6613                         gamesnd_play_iface(SND_USER_SELECT);
6614                         Multi_ho_radio_groups[Multi_ho_radio_info[radio_index][0]] = Multi_ho_radio_info[radio_index][1];
6615                 } else {
6616                         gamesnd_play_iface(SND_GENERAL_FAIL);
6617                 }
6618         }
6619 }
6620
6621 void multi_ho_draw_radio_groups()
6622 {
6623         int idx;
6624         
6625         // go through each item and draw it if it is the selected button in its respective group
6626         for(idx=0;idx<MULTI_HO_NUM_RADIO_BUTTONS;idx++){
6627                 /// if this button is the currently selected one in its group
6628                 if(Multi_ho_radio_info[idx][1] == Multi_ho_radio_groups[Multi_ho_radio_info[idx][0]]){
6629                         Multi_ho_buttons[gr_screen.res][Multi_ho_radio_info[idx][2]].button.draw_forced(2);
6630                 }
6631         }
6632 }
6633
6634 void multi_ho_accept_hit()
6635 {
6636         char resp_str[10];
6637
6638         // check the values in the input boxes
6639         if(!multi_ho_check_values()){
6640                 return;
6641         }
6642         
6643         // zero out the netgame flags
6644         Netgame.flags = 0;
6645         
6646         // set default options
6647         Netgame.options.flags = (MSO_FLAG_INGAME_XFER | MSO_FLAG_ACCEPT_PIX);
6648
6649         // set the squadmate messaging flags
6650         switch(Multi_ho_radio_groups[MULTI_HO_MSG_GROUP]){
6651         case 0 : 
6652                 Netgame.options.squad_set = MSO_SQUAD_RANK;
6653                 break;
6654         case 1 :
6655                 Netgame.options.squad_set = MSO_SQUAD_LEADER;           
6656                 break;
6657         case 2 :
6658                 Netgame.options.squad_set = MSO_SQUAD_ANY;              
6659                 break;
6660         case 3 :
6661                 Netgame.options.squad_set = MSO_SQUAD_HOST;
6662                 break;
6663         default : 
6664                 Int3(); 
6665         }
6666
6667         // set the end mission flags
6668         switch(Multi_ho_radio_groups[MULTI_HO_END_GROUP]){
6669         case 0 : 
6670                 Netgame.options.endgame_set = MSO_END_RANK;             
6671                 break;
6672         case 1 :
6673                 Netgame.options.endgame_set = MSO_END_LEADER;           
6674                 break;                  
6675         case 2 : 
6676                 Netgame.options.endgame_set = MSO_END_ANY;              
6677                 break;
6678         case 3 :
6679                 Netgame.options.endgame_set = MSO_END_HOST;             
6680                 break;  
6681         default : 
6682                 Int3(); 
6683         }
6684         
6685         // set the voice toggle
6686         switch(Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP]){
6687         case 0 :
6688                 Netgame.options.flags &= ~(MSO_FLAG_NO_VOICE);
6689                 break;
6690         case 1 :
6691                 Netgame.options.flags |= MSO_FLAG_NO_VOICE;
6692                 break;
6693         default : 
6694                 Int3();
6695         }       
6696
6697         // get the voice qos options
6698         Netgame.options.voice_qos = (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1); 
6699
6700         // get the voice duration options
6701         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);
6702         
6703         // set the skill level.  If in team vs. team mode, preserve the old setting before saving
6704         // the pilot file.  I'll bet that this doesn't work though because the pilot file gets
6705         // written in a bunch of locations....sigh.
6706         if ( !(Netgame.type_flags & NG_TYPE_TEAM) ){            
6707                 Game_skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
6708         } else {        
6709                 Game_skill_level = NUM_SKILL_LEVELS / 2;
6710         }
6711
6712         // set the netgame respawn count
6713         // maybe warn the user that respawns will not be used for a campaign mission
6714         if(Netgame.campaign_mode == MP_SINGLE){
6715                 Multi_ho_respawns.get_text(resp_str);
6716                 uint temp_respawn = (uint)atoi(resp_str);
6717                 // if he currently has no mission selected, let the user set any # of respawns
6718                 if((int)temp_respawn > Multi_ho_mission_respawn){
6719                         if(Multi_ho_mission_respawn == -1){     
6720                                 Netgame.respawn = temp_respawn;         
6721                                 Netgame.options.respawn = temp_respawn;
6722                         }
6723                         // this should have been taken care of by the interface code
6724                         else {
6725                                 Int3();
6726                         }
6727                 } else {        
6728                         Netgame.options.respawn = temp_respawn;
6729                         Netgame.respawn = temp_respawn;
6730                 }
6731         }
6732
6733         // get the mission time limit
6734         Multi_ho_time_limit.get_text(resp_str);
6735         int temp_time = atoi(resp_str);
6736         if(temp_time <= 0){
6737                 Netgame.options.mission_time_limit = fl2f(-1.0f);
6738         } else if(temp_time > MULTI_HO_MAX_TIME_LIMIT){
6739                 Int3();
6740         } else {
6741                 Netgame.options.mission_time_limit = fl2f(60.0f * (float)temp_time);    
6742         }
6743
6744         // get observer count options
6745         Multi_ho_obs.get_text(resp_str);
6746         int temp_obs = atoi(resp_str);
6747         if(temp_obs > MULTI_HO_MAX_OBS){
6748                 Int3();
6749         } 
6750         Netgame.options.max_observers = (ubyte)temp_obs;        
6751
6752         // get the furball kill limit
6753         Multi_ho_kill_limit.get_text(resp_str);
6754         int temp_kills = atoi(resp_str);
6755         if(temp_kills > MULTI_HO_MAX_KILL_LIMIT){
6756                 Int3();
6757         }
6758         Netgame.options.kill_limit = temp_kills;
6759
6760         // get the token wait limit
6761         Multi_ho_voice_wait.get_text(resp_str);
6762         int temp_wait = atoi(resp_str);
6763         if(temp_wait > MULTI_HO_MAX_TOKEN_WAIT){
6764                 Int3();
6765         } 
6766         Netgame.options.voice_token_wait = (temp_wait * 1000);          
6767
6768         // set the netgame option
6769         Netgame.options.skill_level = (ubyte)Game_skill_level;
6770
6771         // get whether we're in host/captains only modify mode
6772         Netgame.options.flags &= ~(MSO_FLAG_SS_LEADERS);
6773         if(Multi_ho_host_modifies){
6774                 Netgame.options.flags |= MSO_FLAG_SS_LEADERS;
6775         }       
6776
6777         // store these values locally
6778         memcpy(&Player->m_local_options,&Net_player->p_info.options,sizeof(multi_local_options));
6779         memcpy(&Player->m_server_options,&Netgame.options,sizeof(multi_server_options));
6780         write_pilot_file(Player);       
6781
6782         // apply any changes in settings (notify everyone of voice qos changes, etc)
6783         multi_ho_apply_options();
6784
6785         // move back to the create game screen
6786         gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
6787 }
6788
6789 void multi_ho_get_options()
6790 {       
6791         char resp_str[10];
6792         
6793         // set the squadmate messaging buttons  
6794         switch(Netgame.options.squad_set){
6795         case MSO_SQUAD_RANK :           
6796                 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 0;
6797                 break;
6798         case MSO_SQUAD_LEADER:                          
6799                 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 1;
6800                 break;
6801         case MSO_SQUAD_ANY:                     
6802                 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 2;
6803                 break;
6804         case MSO_SQUAD_HOST:            
6805                 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 3;
6806                 break;
6807         default : 
6808                 Int3();         
6809         }
6810         
6811         // set the mission end buttons  
6812         switch(Netgame.options.endgame_set){
6813         case MSO_END_RANK:                      
6814                 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 0;
6815                 break;
6816         case MSO_END_LEADER:                    
6817                 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 1;
6818                 break;
6819         case MSO_END_ANY:
6820                 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 2;
6821                 break;
6822         case MSO_END_HOST:
6823                 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 3;
6824                 break;
6825         default : 
6826                 Int3();
6827         }                       
6828
6829         // set the voice toggle buttons
6830         if(Netgame.options.flags & MSO_FLAG_NO_VOICE){
6831                 Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP] = 1;
6832         } else {
6833                 Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP] = 0;
6834         }       
6835
6836         // get the voice qos options
6837         SDL_assert((Netgame.options.voice_qos >= 1) && (Netgame.options.voice_qos <= 10));
6838         Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos = (Netgame.options.voice_qos - 1);
6839
6840         // get the voice duration options
6841         SDL_assert((Netgame.options.voice_record_time > 0) && (Netgame.options.voice_record_time <= MULTI_VOICE_MAX_TIME));
6842         Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos = ((int)((float)Netgame.options.voice_record_time / 500.0f)) - 1; 
6843
6844         // get the current skill level
6845         SDL_assert((Game_skill_level >= 0) && (Game_skill_level < NUM_SKILL_LEVELS));
6846         Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos = Game_skill_level;   
6847
6848         // get the # of observers
6849         memset(resp_str,0,10);
6850         SDL_snprintf(resp_str,SDL_arraysize(resp_str),"%d",Netgame.options.max_observers);
6851         Multi_ho_obs.set_text(resp_str);
6852
6853         // set the respawn count
6854         if(Netgame.campaign_mode == MP_SINGLE){
6855                 memset(resp_str,0,10);
6856                 SDL_snprintf(resp_str,SDL_arraysize(resp_str),"%d",Netgame.respawn);
6857                 Multi_ho_respawns.set_text(resp_str);   
6858         }
6859
6860         // set the mission time limit
6861         memset(resp_str,0,10);
6862         float tl = f2fl(Netgame.options.mission_time_limit);
6863         SDL_snprintf(resp_str,SDL_arraysize(resp_str),"%d",(int)(tl / 60.0f));
6864         Multi_ho_time_limit.set_text(resp_str);
6865
6866         // set the furball kill limit
6867         memset(resp_str,0,10);
6868         SDL_snprintf(resp_str,SDL_arraysize(resp_str),"%d",Netgame.options.kill_limit);
6869         Multi_ho_kill_limit.set_text(resp_str);
6870
6871         // set the token wait time
6872         memset(resp_str,0,10);
6873         SDL_snprintf(resp_str,SDL_arraysize(resp_str),"%d",Netgame.options.voice_token_wait / 1000);
6874         Multi_ho_voice_wait.set_text(resp_str); 
6875
6876         // get whether we're in host/captains only modify mode
6877         if(Netgame.options.flags & MSO_FLAG_SS_LEADERS){
6878                 Multi_ho_host_modifies = 1;
6879         } else {
6880                 Multi_ho_host_modifies = 0;
6881         }
6882 }
6883
6884 void multi_ho_apply_options()
6885 {
6886         // if the voice qos or duration has changed, apply the change
6887         multi_voice_maybe_update_vars(Netgame.options.voice_qos,Netgame.options.voice_record_time);             
6888
6889         // send an options update
6890         multi_options_update_netgame();
6891 }
6892
6893 // display the voice record time settings
6894 void multi_ho_display_record_time()
6895 {
6896         char time_str[30];
6897         int full_seconds, half_seconds;
6898
6899         // clear the string
6900         memset(time_str,0,30);
6901
6902         // get the seconds
6903         full_seconds = (((Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 500) / 1000);
6904         
6905         // get the half-seconds
6906         half_seconds = ((((Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 500) % 1000) / 500) * 5;
6907
6908         // format the string
6909         SDL_snprintf(time_str,SDL_arraysize(time_str),"%d.%d",full_seconds,half_seconds);
6910         gr_set_color_fast(&Color_bright);
6911         gr_string(Ho_vd_coords[gr_screen.res][MULTI_HO_X_COORD],Ho_vd_coords[gr_screen.res][MULTI_HO_Y_COORD],time_str);
6912 }
6913
6914 int multi_ho_check_values()
6915 {
6916         char val_txt[255];
6917
6918         memset(val_txt,0,255);
6919
6920         // check against respawn settings       
6921         if(Multi_ho_mission_respawn != -1){
6922                 Multi_ho_respawns.get_text(val_txt);
6923                 // if the value is invalid, let the user know
6924                 if(atoi(val_txt) > Multi_ho_mission_respawn){
6925                         memset(val_txt,0,255);
6926                         SDL_snprintf(val_txt,SDL_arraysize(val_txt),XSTR("Warning\nRespawn count in greater than mission specified max (%d)",796),Multi_ho_mission_respawn);
6927                         popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6928                         return 0;
6929                 }
6930         }
6931
6932         // check against mission time limit max
6933         Multi_ho_time_limit.get_text(val_txt);
6934         // if the value is invalid, force it to be valid
6935         if(atoi(val_txt) > MULTI_HO_MAX_TIME_LIMIT){
6936                 memset(val_txt,0,255);
6937                 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);
6938                 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6939                 return 0;
6940         }
6941
6942         // check against max observer limit     
6943         Multi_ho_obs.get_text(val_txt);
6944         // if the value is invalid, force it to be valid
6945         if(atoi(val_txt) > MULTI_HO_MAX_OBS){
6946                 memset(val_txt,0,255);
6947                 SDL_snprintf(val_txt,SDL_arraysize(val_txt),XSTR("Warning\nObserver count is greater than max allowed (%d)",798),MULTI_HO_MAX_OBS);
6948                 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6949                 return 0;
6950         }
6951
6952         // check against furball kill limit     
6953         Multi_ho_kill_limit.get_text(val_txt);
6954         // if the value is invalid, force it to be valid
6955         if(atoi(val_txt) > MULTI_HO_MAX_KILL_LIMIT){
6956                 memset(val_txt,0,255);
6957                 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);
6958                 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6959                 return 0;
6960         }
6961
6962         // check against the token wait limit   
6963         Multi_ho_voice_wait.get_text(val_txt);
6964         if(atoi(val_txt) > MULTI_HO_MAX_TOKEN_WAIT){
6965                 memset(val_txt,0,255);
6966                 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);
6967                 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6968                 return 0;
6969         }
6970
6971         // all values are valid
6972         return 1;
6973 }
6974
6975 void multi_ho_check_focus()
6976 {
6977         // if an inputbox has been pressed (hit enter), lose its focus
6978         if (Multi_ho_respawns.pressed() || Multi_ho_time_limit.pressed() || Multi_ho_voice_wait.pressed() || Multi_ho_kill_limit.pressed() || Multi_ho_obs.pressed()) {
6979                 Multi_ho_respawns.clear_focus();
6980                 Multi_ho_time_limit.clear_focus();      
6981                 Multi_ho_voice_wait.clear_focus();                      
6982                 Multi_ho_kill_limit.clear_focus();
6983                 Multi_ho_obs.clear_focus();
6984                 gamesnd_play_iface(SND_COMMIT_PRESSED);
6985                 chatbox_set_focus();
6986                 Multi_ho_lastframe_input = 0;
6987
6988         } else if(!Multi_ho_lastframe_input) {
6989                 // if we didn't have focus last frame
6990                 if(Multi_ho_respawns.has_focus() || Multi_ho_time_limit.has_focus() || Multi_ho_kill_limit.has_focus() || Multi_ho_voice_wait.has_focus() ){
6991                         chatbox_lose_focus();
6992
6993                         Multi_ho_lastframe_input = 1;
6994                 }       
6995         } 
6996         // if we _did_ have focus last frame
6997         else {
6998                 // if we no longer have focus on any of the input boxes, set the focus on the chatbox
6999                 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()){
7000                         chatbox_set_focus();
7001                 }                       
7002                 // if the chatbox now has focus, clear all focus from our inputboxes
7003                 else if (chatbox_has_focus()) {
7004                         Multi_ho_respawns.clear_focus();
7005                         Multi_ho_time_limit.clear_focus();
7006                         Multi_ho_kill_limit.clear_focus();
7007                         Multi_ho_voice_wait.clear_focus();
7008
7009                         Multi_ho_lastframe_input = 0;
7010                 }
7011         }
7012 }
7013
7014 void multi_ho_blit_max_respawns()
7015 {
7016         char string[50];
7017         
7018         // if we're in campaign mode, do nothing
7019         if(Netgame.campaign_mode == MP_CAMPAIGN){
7020                 return;
7021         }
7022         
7023         // otherwise blit the max as specified by the current mission file      
7024         SDL_snprintf(string,SDL_arraysize(string),"(%d)",Multi_ho_mission_respawn);
7025         gr_set_color_fast(&Color_normal);
7026         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);
7027 }
7028
7029 void multi_ho_display_skill_level()
7030 {
7031         int skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
7032
7033         // sanity
7034         SDL_assert((skill_level >= 0) && (skill_level < NUM_SKILL_LEVELS));
7035         if((skill_level < 0) || (skill_level >= NUM_SKILL_LEVELS)){
7036                 skill_level = 0;
7037         }
7038
7039         gr_set_color_fast(&Color_bright);
7040         gr_string(Ho_st_coords[gr_screen.res][0], Ho_st_coords[gr_screen.res][1], Skill_level_names(skill_level, 1));
7041 }
7042
7043 // -------------------------------------------------------------------------------------------------------------
7044 // 
7045 // MULTIPLAYER JOIN SCREEN 
7046 //
7047
7048 #define MULTI_JW_NUM_BUTTONS    8
7049
7050 //XSTR:OFF
7051 // bitmaps defs
7052 #define MULTI_JW_PALETTE                                "InterfacePalette"
7053
7054 static const char *Multi_jw_bitmap_fname[GR_NUM_RESOLUTIONS] = {
7055         "MultiJoinWait",                // GR_640
7056         "2_MultiJoinWait"               // GR_1024
7057 };
7058
7059 static const char *Multi_jw_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
7060         "MultiJoinWait-M",              // GR_640
7061         "2_MultiJoinWait-M"             // GR_1024
7062 };
7063
7064 //XSTR:ON
7065
7066 // button defs
7067 #define MJW_SCROLL_PLAYERS_UP           0
7068 #define MJW_SCROLL_PLAYERS_DOWN 1
7069 #define MJW_TEAM0                                               2
7070 #define MJW_TEAM1                                               3
7071 #define MJW_PILOT_INFO                          4
7072 #define MJW_SCROLL_INFO_UP                      5
7073 #define MJW_SCROLL_INFO_DOWN            6
7074 #define MJW_CANCEL                                      7
7075
7076 UI_WINDOW Multi_jw_window;                                                                                              // the window object for the join screen
7077 int Multi_jw_bitmap;                                                                                                            // the background bitmap
7078
7079 // constants for coordinate lookup
7080 #define MJW_X_COORD 0
7081 #define MJW_Y_COORD 1
7082 #define MJW_W_COORD 2
7083 #define MJW_H_COORD 3
7084
7085 ui_button_info Multi_jw_buttons[GR_NUM_RESOLUTIONS][MULTI_JW_NUM_BUTTONS] = {
7086         { // GR_640
7087 #ifdef MAKE_FS1
7088                 ui_button_info("MJW_00",        0,              50,             -1,     -1,     0),
7089                 ui_button_info("MJW_01",        0,              87,             -1,     -1,     1),
7090                 ui_button_info("MJW_02",        20,             219,    -1,     -1,     2),
7091                 ui_button_info("MJW_03",        73,             219,    -1,     -1,     3),
7092                 ui_button_info("MJW_09",        131,    213,    -1,     -1,     9),
7093                 ui_button_info("MJW_05",        0,              398,    -1,     -1,     5),
7094                 ui_button_info("MJW_06",        0,              435,    -1,     -1,     6),
7095                 ui_button_info("MJW_04",        559,    411,    -1,     -1,     4),
7096 #else
7097                 ui_button_info("MJW_00",        1,              24,     -1,     -1,     0),
7098                 ui_button_info("MJW_01",        1,              66,     -1,     -1,     1),
7099                 ui_button_info("MJW_02",        30,     244,    20,     272,    2),
7100                 ui_button_info("MJW_03",        84,     244,    73,     272,    3),
7101                 ui_button_info("MJW_04",        139,    242,    134,    272,    4),
7102                 ui_button_info("MJW_05",        1,              406,    -1,     -1,     5),
7103                 ui_button_info("MJW_06",        1,              447,    -1,     -1,     6),
7104                 ui_button_info("MJW_07",        577,    428,    570,    414,    7),
7105 #endif
7106         },
7107         { // GR_1024
7108                 ui_button_info("2_MJW_00",      2,              38,     -1,     -1,     0),
7109                 ui_button_info("2_MJW_01",      2,              106,    -1,     -1,     1),
7110                 ui_button_info("2_MJW_02",      48,     390,    47,     435,    2),
7111                 ui_button_info("2_MJW_03",      134,    390,    133,    435,    3),
7112                 ui_button_info("2_MJW_04",      223,    388,    225,    435,    4),
7113                 ui_button_info("2_MJW_05",      2,              649,    -1,     -1,     5),
7114                 ui_button_info("2_MJW_06",      2,              715,    -1,     -1,     6),
7115                 ui_button_info("2_MJW_07",      923,    685,    931,    667,    7),
7116         }
7117 };
7118
7119 #ifndef MAKE_FS1
7120 #define MULTI_JW_NUM_TEXT                       7
7121
7122 UI_XSTR Multi_jw_text[GR_NUM_RESOLUTIONS][MULTI_JW_NUM_TEXT] = {
7123         { // GR_640
7124                 { "Team 1",                             1308,           20,     272,    UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_TEAM0].button },
7125                 { "Team 2",                             1309,           73,     272,    UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_TEAM1].button },
7126                 { "Pilot",                              1310,           134,    272,    UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_PILOT_INFO].button },
7127                 { "Info",                               1311,           134,    283,    UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_PILOT_INFO].button },
7128                 { "Cancel",                             387,            570,    414,    UI_XSTR_COLOR_PINK, -1, &Multi_jw_buttons[0][MJW_CANCEL].button },
7129                 { "Players",                    1269,           38,     8,              UI_XSTR_COLOR_GREEN, -1, NULL },
7130                 { "Choose Team",                1312,           27,     231,    UI_XSTR_COLOR_GREEN, -1, NULL },
7131         },
7132         { // GR_1024
7133                 { "Team 1",                             1308,           47,     435,    UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_TEAM0].button },
7134                 { "Team 2",                             1309,           133,    435,    UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_TEAM1].button },
7135                 { "Pilot",                              1310,           225,    435,    UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_PILOT_INFO].button },
7136                 { "Info",                               1311,           225,    446,    UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_PILOT_INFO].button },
7137                 { "Cancel",                             387,            931,    667,    UI_XSTR_COLOR_PINK, -1, &Multi_jw_buttons[1][MJW_CANCEL].button },
7138                 { "Players",                    1269,           165,    12,     UI_XSTR_COLOR_GREEN, -1, NULL },
7139                 { "Choose Team",                1312,           45,     373,    UI_XSTR_COLOR_GREEN, -1, NULL },
7140         }
7141 };
7142 #endif
7143
7144 int Mjw_players_coords[GR_NUM_RESOLUTIONS][4] = {
7145         { // GR_640
7146 #ifdef MAKE_FS1
7147                 36, 31, 173, 204
7148 #else
7149                 29, 18, 153, 210
7150 #endif
7151         },
7152         { // GR_1024
7153                 46, 29, 254, 336
7154         }
7155 };
7156
7157 int Mjw_mission_name_coords[GR_NUM_RESOLUTIONS][2] = {
7158         { // GR_640
7159                 5, 380
7160         },
7161         { // GR_1024
7162                 47, 618
7163         }
7164 };
7165
7166 #ifndef MAKE_FS1
7167 // squad war checkbox
7168 UI_CHECKBOX     Multi_jw_sw_checkbox;
7169 const char *Multi_jw_sw_checkbox_fname[GR_NUM_RESOLUTIONS] = {
7170         "MC_SW_00",
7171         "MC_SW_00",
7172 };
7173 int Multi_jw_sw_checkbox_coords[GR_NUM_RESOLUTIONS][2] = {
7174         { // GR_640
7175                 6, 285
7176         },
7177         { // GR_1024
7178                 18, 450
7179         }
7180 };
7181 int Multi_jw_sw_checkbox_text[GR_NUM_RESOLUTIONS][2] = {
7182         { // GR_640
7183                 6, 305
7184         },
7185         { // GR_640
7186                 18, 470
7187         },
7188 };
7189 #endif
7190
7191
7192 // player list control thingie defs
7193 #define MULTI_JW_PLIST_MAX_DISPLAY              19
7194 int Multi_jw_plist_select_flag;                                 // indicates whether we currently have a selected player
7195 short Multi_jw_plist_select_id;                         // id of the current selected player
7196 UI_BUTTON Multi_jw_plist_select_button;         // for selecting a player
7197
7198 int Multi_jw_should_show_popup = 0;
7199
7200 // LOCAL function definitions
7201 void multi_jw_check_buttons();
7202 void multi_jw_button_pressed(int n);
7203 void multi_jw_do_netstuff();
7204 void multi_jw_scroll_players_up();
7205 void multi_jw_scroll_players_down();
7206 void multi_jw_plist_process();
7207 void multi_jw_plist_blit_normal();
7208 void multi_jw_plist_blit_team();
7209 short multi_jw_get_mouse_id();
7210
7211 void multi_game_client_setup_init()
7212 {
7213         int idx;
7214
7215         // create the interface window
7216         Multi_jw_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
7217         Multi_jw_window.set_mask_bmap(Multi_jw_bitmap_mask_fname[gr_screen.res]);
7218
7219         // load the background bitmap
7220         Multi_jw_bitmap = bm_load(Multi_jw_bitmap_fname[gr_screen.res]);
7221         if(Multi_jw_bitmap < 0){
7222                 // we failed to load the bitmap - this is very bad
7223                 Int3();
7224         }
7225
7226         // initialize the player list data      
7227         Multi_jw_plist_select_flag = 0;
7228         Multi_jw_plist_select_id = -1;  
7229         
7230         // kill any old instances of the chatbox and create a new one
7231         chatbox_close();
7232         chatbox_create(CHATBOX_FLAG_BIG | CHATBOX_FLAG_DRAW_BOX | CHATBOX_FLAG_BUTTONS);
7233
7234         // initialize the common notification messaging
7235         multi_common_notify_init();
7236
7237         // initialize the common mission info display area.
7238         multi_common_set_text("");      
7239
7240         // use the common interface palette
7241         multi_common_set_palette();     
7242
7243         // create the interface buttons
7244         for(idx=0; idx<MULTI_JW_NUM_BUTTONS; idx++){
7245                 // create the object
7246                 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);
7247
7248                 // set the sound to play when highlighted
7249                 Multi_jw_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
7250
7251                 // set the ani for the button
7252                 Multi_jw_buttons[gr_screen.res][idx].button.set_bmaps(Multi_jw_buttons[gr_screen.res][idx].filename);
7253
7254                 // set the hotspot
7255                 Multi_jw_buttons[gr_screen.res][idx].button.link_hotspot(Multi_jw_buttons[gr_screen.res][idx].hotspot);
7256         }               
7257
7258 #ifndef MAKE_FS1
7259         // if this is a PXO game, enable the squadwar checkbox  
7260         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);
7261         Multi_jw_sw_checkbox.set_bmaps(Multi_jw_sw_checkbox_fname[gr_screen.res], 6, 0);
7262         Multi_jw_sw_checkbox.disable();
7263         if(!MULTI_IS_TRACKER_GAME){
7264                 Multi_jw_sw_checkbox.hide();            
7265         }
7266 #endif
7267
7268 #ifndef MAKE_FS1
7269         // create all xstrs
7270         for(idx=0; idx<MULTI_JW_NUM_TEXT; idx++){
7271                 Multi_jw_window.add_XSTR(&Multi_jw_text[gr_screen.res][idx]);
7272         }
7273 #endif
7274         
7275         // create the player select list button and hide it
7276         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);
7277         Multi_jw_plist_select_button.hide();
7278
7279         // set hotkeys
7280         Multi_jw_buttons[gr_screen.res][MJW_CANCEL].button.set_hotkey(SDLK_ESCAPE);
7281
7282         // remove campaign flags
7283         Game_mode &= ~(GM_CAMPAIGN_MODE);
7284
7285         // tell the server we have finished joining
7286         Net_player->state = NETPLAYER_STATE_JOINED;
7287         send_netplayer_update_packet(); 
7288
7289         // NETLOG
7290         ml_printf(NOX("Joined netgame %s"), Netgame.name);
7291
7292         // send any appropriate files
7293         multi_data_send_my_junk();      
7294 }
7295
7296 void multi_game_client_setup_do_frame()
7297 {
7298         int player_index;
7299         int k = chatbox_process();
7300         char mission_text[255];
7301         /*k =*/ Multi_jw_window.process(k,0);
7302
7303         Multi_jw_should_show_popup = 0;
7304
7305         // process any button clicks
7306         multi_jw_check_buttons();
7307
7308         // do any network related stuff
7309         multi_jw_do_netstuff();                 
7310
7311         // draw the background, etc
7312         gr_reset_clip();
7313         GR_MAYBE_CLEAR_RES(Multi_jw_bitmap);
7314         if(Multi_jw_bitmap != -1){              
7315                 gr_set_bitmap(Multi_jw_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7316                 gr_bitmap(0,0);
7317         }
7318
7319         // if we're not in team vs. team mode, don't draw the team buttons
7320         if(!(Netgame.type_flags & NG_TYPE_TEAM)){
7321                 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.hide();
7322                 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.hide();
7323                 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.disable();
7324                 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.disable();
7325         } else {
7326                 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.enable();
7327                 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.enable();
7328                 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.unhide();
7329                 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.unhide();             
7330         }
7331
7332 #ifndef MAKE_FS1
7333         if(MULTI_IS_TRACKER_GAME){
7334                 // maybe check the squadwar button
7335                 if(Netgame.type_flags & NG_TYPE_SW){
7336                         Multi_jw_sw_checkbox.set_state(1);
7337                         gr_set_color_fast(&Color_bright);
7338                 } else {
7339                         Multi_jw_sw_checkbox.set_state(0);
7340                         gr_set_color_fast(&Color_normal);
7341                 }
7342                                 
7343                 gr_string(Multi_jw_sw_checkbox_text[gr_screen.res][0], Multi_jw_sw_checkbox_text[gr_screen.res][1], "SquadWar");
7344         }
7345 #endif
7346
7347         // draw the UI window
7348         Multi_jw_window.draw(); 
7349
7350         // process and display the player list  
7351         // NOTE : this must be done before the buttons are checked to insure that a player hasn't dropped 
7352         multi_jw_plist_process();
7353         if(Netgame.type_flags & NG_TYPE_TEAM){
7354                 multi_jw_plist_blit_team();
7355         } else {
7356                 multi_jw_plist_blit_normal();
7357         }
7358                 
7359         // display any text in the info area
7360         multi_common_render_text();
7361
7362         // display any pending notification messages
7363         multi_common_notify_do();
7364
7365         // blit the mission filename if possible
7366         if(Netgame.campaign_mode){
7367                 if(strlen(Netgame.campaign_name) > 0){                  
7368                         SDL_strlcpy(mission_text, Netgame.campaign_name, SDL_arraysize(mission_text));
7369                         
7370                         if(strlen(Netgame.title) > 0){
7371                                 SDL_strlcat(mission_text, ", ", SDL_arraysize(mission_text));
7372                                 SDL_strlcat(mission_text, Netgame.title, SDL_arraysize(mission_text));
7373                         }
7374
7375                         gr_set_color_fast(&Color_bright_white);
7376                         gr_string(Mjw_mission_name_coords[gr_screen.res][MJW_X_COORD],Mjw_mission_name_coords[gr_screen.res][MJW_Y_COORD],mission_text);
7377                 }                                                               
7378         } else {
7379                 if(strlen(Netgame.mission_name) > 0){                   
7380                         SDL_strlcpy(mission_text, Netgame.mission_name, SDL_arraysize(mission_text));
7381
7382                         if(strlen(Netgame.title) > 0){
7383                                 SDL_strlcat(mission_text, ", ", SDL_arraysize(mission_text));
7384                                 SDL_strlcat(mission_text, Netgame.title, SDL_arraysize(mission_text));
7385                         }                       
7386
7387                         gr_set_color_fast(&Color_bright_white);
7388                         gr_string(Mjw_mission_name_coords[gr_screen.res][MJW_X_COORD],Mjw_mission_name_coords[gr_screen.res][MJW_Y_COORD],mission_text);
7389                 }
7390         }       
7391
7392         // process and show the chatbox thingie 
7393         chatbox_render();
7394
7395         // draw tooltips
7396         Multi_jw_window.draw_tooltip();
7397
7398         // display the voice status indicator
7399         multi_common_voice_display_status();
7400         
7401         // flip the buffer
7402         gr_flip();      
7403
7404         // if we're supposed to be displaying a pilot info popup
7405         if(Multi_jw_should_show_popup){
7406                 player_index = find_player_id(Multi_jw_plist_select_id);
7407                 if(player_index != -1){                 
7408                         multi_pinfo_popup(&Net_players[player_index]);
7409                 }               
7410         }
7411 }
7412
7413 void multi_game_client_setup_close()
7414 {
7415         // unload any bitmaps
7416         if(!bm_unload(Multi_jw_bitmap)){
7417                 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_jw_bitmap_fname[gr_screen.res]));
7418         }               
7419
7420         // destroy the chatbox
7421         // chatbox_close();
7422         
7423         // destroy the UI_WINDOW
7424         Multi_jw_window.destroy();
7425
7426         // play a sound.
7427         if(Netgame.game_state == NETGAME_STATE_MISSION_SYNC){
7428                 gamesnd_play_iface(SND_COMMIT_PRESSED);
7429         }
7430 }
7431
7432
7433 void multi_jw_check_buttons()
7434 {
7435         int idx;
7436         for(idx=0;idx<MULTI_JW_NUM_BUTTONS;idx++){
7437                 // we only really need to check for one button pressed at a time, so we can break after 
7438                 // finding one.
7439                 if(Multi_jw_buttons[gr_screen.res][idx].button.pressed()){
7440                         multi_jw_button_pressed(idx);
7441                         break;
7442                 }
7443         }
7444 }
7445
7446 void multi_jw_button_pressed(int n)
7447 {
7448         switch(n){      
7449         case MJW_CANCEL:
7450                 gamesnd_play_iface(SND_USER_SELECT);            
7451                 multi_quit_game(PROMPT_CLIENT);         
7452                 break;  
7453         case MJW_SCROLL_PLAYERS_UP:
7454                 multi_jw_scroll_players_up();
7455                 break;
7456         case MJW_SCROLL_PLAYERS_DOWN:
7457                 multi_jw_scroll_players_down();
7458                 break;  
7459         case MJW_SCROLL_INFO_UP:
7460                 multi_common_scroll_text_up();
7461                 break;
7462         case MJW_SCROLL_INFO_DOWN:
7463                 multi_common_scroll_text_down();
7464                 break;
7465         
7466         // request to set myself to team 0
7467         case MJW_TEAM0:
7468                 gamesnd_play_iface(SND_USER_SELECT);
7469                 multi_team_set_team(Net_player,0);
7470                 break;
7471
7472         // request to set myself to team 1
7473         case MJW_TEAM1:
7474                 gamesnd_play_iface(SND_USER_SELECT);
7475                 multi_team_set_team(Net_player,1);
7476                 break;
7477
7478         // pilot info popup
7479         case MJW_PILOT_INFO:
7480                 Multi_jw_should_show_popup = 1;
7481                 break;
7482
7483         default :
7484                 multi_common_add_notify(XSTR("Not implemented yet!",760));
7485                 break;
7486         }
7487 }
7488
7489 // do stuff like pinging servers, sending out requests, etc
7490 void multi_jw_do_netstuff()
7491 {
7492 }
7493
7494 void multi_jw_scroll_players_up()
7495 {
7496         gamesnd_play_iface(SND_GENERAL_FAIL);
7497 }
7498
7499 // scroll down through the player list
7500 void multi_jw_scroll_players_down()
7501 {       
7502         gamesnd_play_iface(SND_GENERAL_FAIL);
7503 }
7504
7505 void multi_jw_plist_process()
7506 {
7507         int test_count,player_index,idx;
7508         
7509         // first determine if there are 0 players in the game. This should never happen since the host is _always_ in the game
7510         test_count = 0;
7511         for(idx=0;idx<MAX_PLAYERS;idx++){
7512                 // count anyone except the standalone server (if applicable)
7513                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7514                         test_count++;
7515                 }
7516         }
7517         if(test_count <= 0){
7518                 return;
7519         }
7520         
7521         // if we had a selected item but that player has left, select myself instead
7522         if(Multi_jw_plist_select_flag){
7523                 player_index = find_player_id(Multi_jw_plist_select_id);
7524                 if(player_index == -1){
7525                         Multi_jw_plist_select_id = Net_player->player_id;                                               
7526                 }
7527         } else {
7528                 Multi_jw_plist_select_flag = 1;
7529                 Multi_jw_plist_select_id = Net_player->player_id;               
7530         }
7531                 
7532         // if the player has clicked somewhere in the player list area
7533         if(Multi_jw_plist_select_button.pressed()){
7534                 short player_id;
7535         
7536                 player_id = multi_jw_get_mouse_id();
7537                 player_index = find_player_id(player_id);
7538                 if(player_index != -1){
7539                         Multi_jw_plist_select_id = player_id;
7540                         Multi_jw_plist_select_flag = 1;
7541                 }
7542         }
7543 }
7544
7545 void multi_jw_plist_blit_normal()
7546 {
7547         int idx;                
7548         char str[CALLSIGN_LEN+1];
7549         int y_start = Mjw_players_coords[gr_screen.res][MJW_Y_COORD];   
7550         int total_offset;
7551
7552         // display all the players      
7553         for(idx=0;idx<MAX_PLAYERS;idx++){               
7554                 // reset total offset
7555                 total_offset = 0;
7556
7557                 // count anyone except the standalone server (if applicable)
7558                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7559                         // highlight him if he's the host                       
7560                         if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7561                                 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7562                                         gr_set_color_fast(&Color_text_active_hi);
7563                                 } else {
7564                                         gr_set_color_fast(&Color_bright);
7565                                 }
7566                         } else {
7567                                 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7568                                         gr_set_color_fast(&Color_text_active);
7569                                 } else {
7570                                         gr_set_color_fast(&Color_text_normal);
7571                                 }
7572                         }
7573
7574                         // optionally draw his CD status
7575                         if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7576                                 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7577                                 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7578
7579                                 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7580                         }                       
7581                         
7582                         // make sure the string will fit, then display it
7583                         SDL_strlcpy(str, Net_players[idx].player->callsign, SDL_arraysize(str));
7584                         if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
7585                                 SDL_strlcat(str, "(0)", SDL_arraysize(str));
7586                         }
7587                         gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7588                         gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7589
7590                         y_start += 10;                  
7591                 }
7592         }               
7593 }
7594
7595 void multi_jw_plist_blit_team()
7596 {
7597         int idx;                
7598         char str[CALLSIGN_LEN+1];
7599         int y_start = Mjw_players_coords[gr_screen.res][MJW_Y_COORD];   
7600         int total_offset;
7601
7602         // always blit the proper team button based on _my_ team status
7603         if(Net_player->p_info.team == 0){
7604                 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.draw_forced(2);
7605         } else {
7606                 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.draw_forced(2);
7607         }
7608
7609         // display all the red players first
7610         for(idx=0;idx<MAX_PLAYERS;idx++){
7611                 // reset total offset
7612                 total_offset = 0;
7613
7614                 // count anyone except the standalone server (if applicable)
7615                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
7616                         // highlight him if he's the host                       
7617                         if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7618                                 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7619                                         gr_set_color_fast(&Color_text_active_hi);                                       
7620                                 } else {
7621                                         gr_set_color_fast(&Color_bright);
7622                                 }
7623                         } else {
7624                                 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7625                                         gr_set_color_fast(&Color_text_active);                                  
7626                                 } else {
7627                                         gr_set_color_fast(&Color_text_normal);
7628                                 }
7629                         }
7630
7631                         // optionally draw his CD status
7632                         if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7633                                 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7634                                 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7635
7636                                 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7637                         }                       
7638
7639                         // blit the red team indicator
7640                         if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
7641                                 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
7642                                         gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7643                                         gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7644
7645                                         total_offset += Multi_common_icon_dims[MICON_TEAM0_SELECT][0] + 1;
7646                                 }                               
7647                         } else {
7648                                 if(Multi_common_icons[MICON_TEAM0] != -1){
7649                                         gr_set_bitmap(Multi_common_icons[MICON_TEAM0], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7650                                         gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7651
7652                                         total_offset += Multi_common_icon_dims[MICON_TEAM0][0] + 1;
7653                                 }
7654                         }
7655
7656                         // make sure the string will fit
7657                         SDL_strlcpy(str, Net_players[idx].player->callsign, SDL_arraysize(str));
7658                         gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7659
7660                         // display him in the correct half of the list depending on his team
7661                         gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7662                         y_start += 10;
7663                 }
7664         }       
7665         
7666         // display all the green players next
7667         for(idx=0;idx<MAX_PLAYERS;idx++){
7668                 // reset total offset
7669                 total_offset = 0;
7670
7671                 // count anyone except the standalone server (if applicable)
7672                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
7673                         // highlight him if he's the host                       
7674                         if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7675                                 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7676                                         gr_set_color_fast(&Color_text_active_hi);                               
7677                                 } else {
7678                                         gr_set_color_fast(&Color_bright);
7679                                 }
7680                         } else {
7681                                 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7682                                         gr_set_color_fast(&Color_text_active);                                  
7683                                 } else {
7684                                         gr_set_color_fast(&Color_text_normal);
7685                                 }
7686                         }
7687
7688                         // optionally draw his CD status
7689                         if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7690                                 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7691                                 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7692
7693                                 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7694                         }                       
7695
7696                         // blit the red team indicator
7697                         if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
7698                                 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
7699                                         gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7700                                         gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7701
7702                                         total_offset += Multi_common_icon_dims[MICON_TEAM1_SELECT][0] + 1;
7703                                 }
7704                         } else {
7705                                 if(Multi_common_icons[MICON_TEAM1] != -1){
7706                                         gr_set_bitmap(Multi_common_icons[MICON_TEAM1], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7707                                         gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7708
7709                                         total_offset += Multi_common_icon_dims[MICON_TEAM1][0] + 1;
7710                                 }
7711                         }
7712
7713                         // make sure the string will fit
7714                         SDL_strlcpy(str, Net_players[idx].player->callsign, SDL_arraysize(str));
7715                         if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
7716                                 SDL_strlcat(str, "(0)", SDL_arraysize(str));
7717                         }
7718                         gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7719
7720                         // display him in the correct half of the list depending on his team
7721                         gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7722                         y_start += 10;
7723                 }
7724         }                       
7725 }
7726
7727 void multi_jw_handle_join(net_player *pl)
7728 {
7729         // for now just play a bloop sound
7730         gamesnd_play_iface(SND_ICON_DROP_ON_WING);
7731 }
7732
7733 short multi_jw_get_mouse_id()
7734 {
7735         // determine where he clicked (y pixel value)
7736         int y,nth,idx;          
7737         Multi_jw_plist_select_button.get_mouse_pos(NULL,&y);
7738
7739         // select things a little differently if we're in team vs. team or non-team vs. team mode                       
7740         nth = (y / 10);                 
7741         if(Netgame.type_flags & NG_TYPE_TEAM){
7742                 int player_index = -1;
7743
7744                 // look through all of team red first
7745                 for(idx=0;idx<MAX_PLAYERS;idx++){
7746                         if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){                            
7747                                 nth--;
7748
7749                                 // if this is the _nth_ guy 
7750                                 if(nth < 0){
7751                                         player_index = idx;                                             
7752                                         break;
7753                                 }
7754                         }
7755                 }
7756                         
7757                 // if we still haven't found him yet, look through the green team
7758                 if(player_index == -1){
7759                         for(idx=0;idx<MAX_PLAYERS;idx++){
7760                                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){                                    
7761                                         nth--;
7762                                         // if this is the _nth_ guy 
7763                                         if(nth < 0){
7764                                                 player_index = idx;                                             
7765                                                 break;
7766                                         }
7767                                 }
7768                         }
7769                 }
7770                 if(player_index != -1){
7771                         return Net_players[idx].player_id;                      
7772                 }               
7773         } else {
7774                 // select the nth active player if possible, disregarding the standalone server
7775                 for(idx=0;idx<MAX_PLAYERS;idx++){
7776                         if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7777                                 nth--;
7778
7779                                 // if this is the _nth_ guy 
7780                                 if(nth < 0){
7781                                         return Net_players[idx].player_id;                                      
7782                                 }
7783                         }
7784                 }
7785                 return -1;
7786         }                               
7787
7788         return -1;
7789 }
7790
7791
7792 // -------------------------------------------------------------------------------------------------------------
7793 // 
7794 // MULTIPLAYER WAIT/SYNCH SCREEN 
7795 //
7796
7797 #define MULTI_SYNC_HOST_COUNT                           4               // host uses 4 buttons (and sometimes 5)
7798 #define MULTI_SYNC_CLIENT_COUNT                 3               // client only uses 3 buttons
7799
7800 const char *Multi_sync_bitmap_fname[GR_NUM_RESOLUTIONS] = {
7801         "MultiSynch",           // GR_640
7802         "2_MultiSynch"          // GR_1024
7803 };
7804
7805 const char *Multi_sync_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
7806         "MultiSynch-M",         // GR_640
7807         "2_MultiSynch-M"                        // GR_1024
7808 };
7809
7810 //XSTR:ON
7811
7812 // constants for coordinate lookup
7813 #define MS_X_COORD 0
7814 #define MS_Y_COORD 1
7815 #define MS_W_COORD 2
7816 #define MS_H_COORD 3
7817
7818 UI_WINDOW Multi_sync_window;                                                                                            // the window object for the join screen
7819 int Multi_sync_button_count;                                                                                            // the # of buttons to use for this instance of mission sync
7820 int Multi_sync_bitmap;                                                                                                          // the background bitmap
7821
7822 // button defs
7823 #define MULTI_SYNC_NUM_BUTTONS                  5
7824 #define MS_SCROLL_INFO_UP                                       0
7825 #define MS_SCROLL_INFO_DOWN                             1
7826 #define MS_CANCEL                                                               2
7827 #define MS_KICK                                                         3
7828 #define MS_LAUNCH                                                               4
7829 ui_button_info Multi_sync_buttons[GR_NUM_RESOLUTIONS][MULTI_SYNC_NUM_BUTTONS] = {
7830         { // GR_640
7831 #ifdef MAKE_FS1
7832                 ui_button_info("MS_00",         0,              396,    -1,     -1,     0),
7833                 ui_button_info("MS_01",         0,              435,    -1,     -1,     1),
7834                 ui_button_info("MS_02",         496,    411,    -1,     -1,     2),
7835                 ui_button_info("MS_04",         436,    403,    -1,     -1,     4),
7836                 ui_button_info("MS_03",         556,    398,    -1,     -1,     3),
7837 #else
7838                 ui_button_info("MS_00",         1,              404,    -1,     -1,     0),
7839                 ui_button_info("MS_01",         1,              446,    -1,     -1,     1),
7840                 ui_button_info("MS_03",         518,    426,    519,    416,    3),
7841                 ui_button_info("MS_02",         469,    426,    479,    416,    2),             
7842                 ui_button_info("MS_04",         571,    420,    577,    416,    4),
7843 #endif
7844         },
7845         { // GR_1024
7846                 ui_button_info("2_MS_00",               2,              647,    -1,     -1,     0),
7847                 ui_button_info("2_MS_01",               2,              713,    -1,     -1,     1),
7848                 ui_button_info("2_MS_03",               829,    682,    831,    667,    3),
7849                 ui_button_info("2_MS_02",               751,    682,    766,    667,    2),             
7850                 ui_button_info("2_MS_04",               914,    672,    924,    667,    4),
7851         }
7852 };
7853
7854 // text
7855 #ifndef MAKE_FS1
7856 #define MULTI_SYNC_NUM_TEXT                             5
7857 #define MST_KICK                                                                0
7858 #define MST_LAUNCH                                                      2
7859 UI_XSTR Multi_sync_text[GR_NUM_RESOLUTIONS][MULTI_SYNC_NUM_TEXT] = {
7860         { // GR_640
7861                 { "Kick",               1266,           479,    416,    UI_XSTR_COLOR_PINK,     -1, &Multi_sync_buttons[0][MS_KICK].button },
7862                 { "Cancel",             387,            519,    416,    UI_XSTR_COLOR_PINK,     -1, &Multi_sync_buttons[0][MS_CANCEL].button },
7863                 { "Launch",             801,            577,    416,    UI_XSTR_COLOR_PINK,     -1, &Multi_sync_buttons[0][MS_LAUNCH].button },
7864                 { "Players",    1269,           23,     133,    UI_XSTR_COLOR_GREEN,    -1, NULL },
7865                 { "Status",             1304,           228,    133,    UI_XSTR_COLOR_GREEN,    -1, NULL }
7866         },
7867         { // GR_1024
7868                 { "Kick",               1266,           766,    667,    UI_XSTR_COLOR_PINK,     -1, &Multi_sync_buttons[1][MS_KICK].button },
7869                 { "Cancel",             387,            831,    667,    UI_XSTR_COLOR_PINK,     -1, &Multi_sync_buttons[1][MS_CANCEL].button },
7870                 { "Launch",             801,            924,    667,    UI_XSTR_COLOR_PINK,     -1, &Multi_sync_buttons[1][MS_LAUNCH].button },
7871                 { "Players",    1269,           38,     214,    UI_XSTR_COLOR_GREEN,    -1, NULL },
7872                 { "Status",             1304,           366,    214,    UI_XSTR_COLOR_GREEN,    -1, NULL }
7873         }
7874 };
7875 #endif
7876
7877 // player name
7878 int Ms_status_coords[GR_NUM_RESOLUTIONS][4] = {
7879         { // GR_640
7880 #ifdef MAKE_FS1
7881                 40, 156, 560, 200
7882 #else
7883                 38, 150, 581, 220
7884 #endif
7885         },
7886         { // GR_1024
7887                 38, 228, 958, 367
7888         }
7889 };
7890
7891 // player status coords
7892 int Ms_status2_coords[GR_NUM_RESOLUTIONS][4] = {
7893         { // GR_640
7894 #ifdef MAKE_FS1
7895                 246, 156, 354, 200
7896 #else
7897                 228, 150, 391, 220
7898 #endif
7899         },
7900         { // GR_1024
7901                 370, 228, 626, 367
7902         }
7903 };
7904
7905 int Ms_cd_icon_offset[GR_NUM_RESOLUTIONS] = {
7906         10,             // GR_640
7907         10                      // GR_1024
7908 };
7909
7910 int Ms_team_icon_offset[GR_NUM_RESOLUTIONS] = {
7911         38,             // GR_640
7912         38                      // GR_1024
7913 };
7914
7915 // player currently selected, index into Net_players[]
7916 int Multi_sync_player_select = -1;
7917
7918 // player list control thingie defs
7919 #define MULTI_SYNC_PLIST_MAX_DISPLAY    15
7920 int Multi_sync_plist_start;             // where to start displaying from
7921 int Multi_sync_plist_count;             // how many we have
7922
7923 // list select button
7924 UI_BUTTON Multi_sync_plist_button;
7925
7926 int Multi_sync_mode = -1;
7927
7928 #define MULTI_SYNC_COUNTDOWN_TIME                       5                               // in seconds
7929 float Multi_sync_countdown_timer;
7930 int Multi_sync_countdown = -1;
7931
7932 int Multi_launch_button_created;
7933
7934 //XSTR:OFF
7935 // countdown animation timer
7936 const char* Multi_sync_countdown_fname[GR_NUM_RESOLUTIONS] = {
7937         "Count",                // GR_640
7938         "2_Count"               // GR_1024
7939 };
7940
7941 int Multi_sync_countdown_coords[GR_NUM_RESOLUTIONS][2] = {
7942         {
7943                 0, 0            // GR_640
7944         },              
7945         {
7946                 0, 0            // GR_1024
7947         }
7948 };
7949
7950 //XSTR:ON
7951
7952 anim *Multi_sync_countdown_anim = NULL;
7953 anim_instance *Multi_sync_countdown_instance = NULL;
7954
7955
7956 // PREBRIEFING STUFF
7957 // syncing flags used by the server
7958 int Mission_sync_flags = 0;
7959 #define MS_FLAG_SENT_FILESIG                    (1<<0)  // sent filesig requests
7960 #define MS_FLAG_SENT_LOAD                               (1<<1)  // sent load packets
7961 #define MS_FLAG_PUSHED_BRIEFING         (1<<2)  // pushed everyone else into the briefing
7962 #define MS_FLAG_POST_DATA                               (1<<3)  // sent the post data block
7963 #define MS_FLAG_WSS_SLOTS                               (1<<4)  // all players have received wss slot data
7964 #define MS_FLAG_PSETTINGS                               (1<<5)  // send the player settings packet
7965 #define MS_FLAG_MT_STATS_START          (1<<6)  // server has started getting player stats from the tracker
7966 #define MS_FLAG_MT_STATS_DONE                   (1<<7)  // server has finished getting player stats from the tracker (success or fail)
7967 #define MS_FLAG_TS_SLOTS                                (1<<8)  // team/ship slots have been sent
7968 #define MS_FLAG_DATA_DONE                               (1<<9)  // done transferring all necessary data
7969 #define MS_FLAG_CAMP_DONE                               (1<<10) // send campaign pool/goal/event stuff
7970
7971 // POSTBRIEFING STUFF
7972 int Multi_state_timestamp;
7973 int Multi_sync_launch_pressed;
7974
7975 // LOCAL function definitions
7976 void multi_sync_check_buttons();
7977 void multi_sync_button_pressed(int n);
7978 void multi_sync_scroll_info_up();
7979 void multi_sync_scroll_info_down();
7980 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
7981 void multi_sync_display_status(const char *status, int index);                                  // display info on the right hand portion of the status window thingie
7982 void multi_sync_force_start_pre();
7983 void multi_sync_force_start_post();
7984 void multi_sync_launch();
7985 void multi_sync_create_launch_button();
7986 void multi_sync_blit_screen_all();
7987 void multi_sync_handle_plist();
7988
7989 void multi_sync_common_init();
7990 void multi_sync_common_do();
7991 void multi_sync_common_close();
7992
7993 void multi_sync_pre_init();
7994 void multi_sync_pre_do();
7995 void multi_sync_pre_close();
7996
7997 void multi_sync_post_init();
7998 void multi_sync_post_do();
7999 void multi_sync_post_close();
8000
8001 int Sync_test = 1;
8002
8003
8004 // perform the correct init functions
8005 void multi_sync_init()
8006 {       
8007         Multi_sync_countdown = -1;
8008
8009         Sync_test = 1;
8010
8011         // reset all timestamp
8012         multi_reset_timestamps();
8013
8014         extern time_t Player_multi_died_check;
8015         Player_multi_died_check = -1;
8016
8017         if(!(Game_mode & GM_STANDALONE_SERVER)){
8018                 multi_sync_common_init();
8019         }
8020         
8021         switch(Multi_sync_mode){
8022         case MULTI_SYNC_PRE_BRIEFING:
8023                 multi_sync_pre_init();
8024                 break;
8025         case MULTI_SYNC_POST_BRIEFING:
8026                 multi_sync_post_init();
8027                 break;
8028         case MULTI_SYNC_INGAME:
8029                 multi_ingame_sync_init();
8030                 break;
8031         }
8032 }
8033
8034 // perform the correct do frame functions
8035 void multi_sync_do()
8036 {
8037         if(!(Game_mode & GM_STANDALONE_SERVER)){
8038                 multi_sync_common_do();
8039         }
8040
8041         // if the netgame is ending, don't do any sync processing
8042         if(multi_endgame_ending()){
8043                 return;
8044         }
8045
8046         // process appropriateliy
8047         switch(Multi_sync_mode){
8048         case MULTI_SYNC_PRE_BRIEFING:           
8049                 multi_sync_pre_do();            
8050                 break;
8051         case MULTI_SYNC_POST_BRIEFING:
8052                 multi_sync_post_do();
8053                 break;
8054         case MULTI_SYNC_INGAME:
8055                 multi_ingame_sync_do();
8056
8057                 gr_reset_clip();                
8058                 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8059                 if(Multi_sync_bitmap != -1){
8060                         gr_set_bitmap(Multi_sync_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8061                         gr_bitmap(0,0);
8062                 }
8063                 Multi_sync_window.draw();
8064
8065                 multi_sync_blit_screen_all();
8066
8067                 gr_flip();
8068                 break;
8069         }       
8070 }
8071
8072 // perform the correct close functions
8073 void multi_sync_close()
8074 {
8075         switch(Multi_sync_mode){
8076         case MULTI_SYNC_PRE_BRIEFING:
8077                 multi_sync_pre_close();
8078                 break;
8079         case MULTI_SYNC_POST_BRIEFING:
8080                 multi_sync_post_close();
8081                 break;
8082         case MULTI_SYNC_INGAME:
8083                 multi_ingame_sync_close();
8084                 break;
8085         }
8086         
8087         if(!(Game_mode & GM_STANDALONE_SERVER)){
8088                 multi_sync_common_close();
8089         }
8090 }
8091
8092 const char *multi_sync_tooltip_handler(const char *str)
8093 {
8094         if (!SDL_strcasecmp(str, NOX("@launch"))) {
8095                 if (Multi_launch_button_created){
8096                         return XSTR("Launch",801);
8097                 }
8098         }
8099
8100         return NULL;
8101 }
8102
8103 void multi_sync_common_init()
8104 {
8105         int idx;
8106
8107         // create the interface window
8108         Multi_sync_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
8109         Multi_sync_window.set_mask_bmap(Multi_sync_bitmap_mask_fname[gr_screen.res]);
8110         Multi_sync_window.tooltip_handler = multi_sync_tooltip_handler;
8111
8112         // load the background bitmap
8113         Multi_sync_bitmap = bm_load(Multi_sync_bitmap_fname[gr_screen.res]);
8114         if (Multi_sync_bitmap < 0) {
8115                 // we failed to load the bitmap - this is very bad
8116                 Int3();
8117         }
8118
8119         // initialize the player list data
8120         Multi_sync_plist_start = 0;
8121         Multi_sync_plist_count = 1;                     // we can pretty safely assume that there's one player in the game - me.        
8122
8123         Multi_launch_button_created = 0;        
8124
8125         // create the chatbox thingie   (shouldn't be necesary to do this, but we'll put it in for good measure)
8126         chatbox_create();
8127
8128         // force the chatbox to be small
8129         chatbox_force_small();
8130
8131         // initialize the common notification messaging
8132         multi_common_notify_init();
8133
8134         // initialize the common mission info display area.
8135         multi_common_set_text("");      
8136
8137         // use the common interface palette
8138         multi_common_set_palette();
8139
8140         // don't select any player yet.
8141         Multi_sync_player_select = -1;
8142         
8143         // determine how many of the 5 buttons to create
8144         if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8145                 Multi_sync_button_count = MULTI_SYNC_HOST_COUNT;                
8146         } else {
8147                 Multi_sync_button_count = MULTI_SYNC_CLIENT_COUNT;
8148         }
8149         // create the interface buttons 
8150         for(idx=0; idx<Multi_sync_button_count; idx++){
8151                 // create the object
8152                 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);
8153
8154                 // set the sound to play when highlighted
8155                 Multi_sync_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
8156
8157                 // set the ani for the button
8158                 // this wierdness is necessary because cancel and kick buttons aren't drawn on the background bitmap,
8159                 //   so we have to load in frame 0, too (the file should exist)
8160                 if ((idx == MS_CANCEL) || (idx == MS_KICK) || (idx == MS_LAUNCH)) {
8161                         Multi_sync_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sync_buttons[gr_screen.res][idx].filename, 3, 0);
8162                 } else {
8163                         Multi_sync_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sync_buttons[gr_screen.res][idx].filename);
8164                 }
8165
8166                 // set the hotspot
8167                 Multi_sync_buttons[gr_screen.res][idx].button.link_hotspot(Multi_sync_buttons[gr_screen.res][idx].hotspot);
8168         }               
8169
8170 #ifndef MAKE_FS1
8171         // add xstrs
8172         for(idx=0; idx<MULTI_SYNC_NUM_TEXT; idx++) {
8173                 // don't create the "launch" button text just yet
8174                 if(idx == MST_LAUNCH) {
8175                         continue;
8176                 }
8177                 // multiplayer clients should ignore the kick button
8178                 if(!MULTIPLAYER_MASTER && !MULTIPLAYER_HOST && (idx == MST_KICK)) {
8179                         continue;
8180                 }
8181
8182                 Multi_sync_window.add_XSTR(&Multi_sync_text[gr_screen.res][idx]);
8183         }
8184 #endif
8185
8186         // create the player list select button and hide it
8187         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);
8188         Multi_sync_plist_button.hide();
8189
8190         // set up hotkeys for certain common functions
8191         Multi_sync_buttons[gr_screen.res][MS_CANCEL].button.set_hotkey(SDLK_ESCAPE);
8192 }
8193
8194 void multi_sync_common_do()
8195 {       
8196         int k = chatbox_process();
8197         k = Multi_sync_window.process(k);       
8198
8199         // process the player list
8200         multi_sync_handle_plist();
8201
8202         // process any button clicks
8203         multi_sync_check_buttons();     
8204
8205         // process any keypresses
8206         switch(k){
8207         case SDLK_ESCAPE :
8208                 // Sync_test = 1;
8209                 gamesnd_play_iface(SND_USER_SELECT);
8210                 multi_quit_game(PROMPT_ALL);            
8211                 break;  
8212         }                               
8213 }
8214
8215 void multi_sync_common_close()
8216 {
8217         // unload any bitmaps
8218         if(!bm_unload(Multi_sync_bitmap)){
8219                 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_sync_bitmap_fname[gr_screen.res]));
8220         }       
8221
8222         extern time_t Player_multi_died_check;
8223         Player_multi_died_check = -1;
8224         
8225         // destroy the UI_WINDOW
8226         Multi_sync_window.destroy();
8227 }
8228
8229 void multi_sync_blit_screen_all()
8230 {
8231         int count,idx;  
8232         int state;
8233         float pct_complete;
8234         char txt[255];
8235         
8236         // display any text in the info area
8237         multi_common_render_text();
8238
8239         // display any pending notification messages
8240         multi_common_notify_do();
8241         
8242         // display any info about visible players
8243         count = 0;      
8244         for(idx=0;idx<MAX_PLAYERS;idx++){
8245                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
8246                         // display his name and status
8247                         multi_sync_display_name(Net_players[idx].player->callsign,count,idx);
8248         
8249                         // get the player state
8250                         state = Net_players[idx].state;
8251
8252                         // if we're ingame joining, show all other players except myself as "playing"
8253                         if((&Net_players[idx] != Net_player) && ((Multi_sync_mode == MULTI_SYNC_INGAME) || (Net_player->flags & NETINFO_FLAG_INGAME_JOIN)) ){
8254                                 state = NETPLAYER_STATE_IN_MISSION;
8255                         }
8256
8257                         switch(state){                          
8258                         case NETPLAYER_STATE_MISSION_LOADING:
8259                                 multi_sync_display_status(XSTR("Mission Loading",802),count);
8260                                 break;
8261                         case NETPLAYER_STATE_INGAME_SHIP_SELECT:                                                                        // I don't think its possible to see this state, but...
8262                                 multi_sync_display_status(XSTR("Ingame Ship Select",803),count);
8263                                 break;
8264                         case NETPLAYER_STATE_DEBRIEF:
8265                                 multi_sync_display_status(XSTR("Debriefing",804),count);
8266                                 break;
8267                         case NETPLAYER_STATE_MISSION_SYNC:
8268                                 multi_sync_display_status(XSTR("Mission Sync",805),count);
8269                                 break;
8270                         case NETPLAYER_STATE_JOINING:                                                           
8271                                 multi_sync_display_status(XSTR("Joining",806),count);
8272                                 break;
8273                         case NETPLAYER_STATE_JOINED:                            
8274                                 multi_sync_display_status(XSTR("Joined",807),count);
8275                                 break;
8276                         case NETPLAYER_STATE_SLOT_ACK :                         
8277                                 multi_sync_display_status(XSTR("Slot Ack",808),count);
8278                                 break;                  
8279                         case NETPLAYER_STATE_BRIEFING:                          
8280                                 multi_sync_display_status(XSTR("Briefing",765),count);
8281                                 break;
8282                         case NETPLAYER_STATE_SHIP_SELECT:                               
8283                                 multi_sync_display_status(XSTR("Ship Select",809),count);
8284                                 break;
8285                         case NETPLAYER_STATE_WEAPON_SELECT:                             
8286                                 multi_sync_display_status(XSTR("Weapon Select",810),count);
8287                                 break;
8288                         case NETPLAYER_STATE_WAITING:                           
8289                                 multi_sync_display_status(XSTR("Waiting",811),count);
8290                                 break;
8291                         case NETPLAYER_STATE_IN_MISSION:                                
8292                                 multi_sync_display_status(XSTR("In Mission",812),count);
8293                                 break;                  
8294                         case NETPLAYER_STATE_MISSION_LOADED:                    
8295                                 multi_sync_display_status(XSTR("Mission Loaded",813),count);
8296                                 break;                  
8297                         case NETPLAYER_STATE_DATA_LOAD:
8298                                 multi_sync_display_status(XSTR("Data loading",814),count);
8299                                 break;
8300                         case NETPLAYER_STATE_SETTINGS_ACK:
8301                                 multi_sync_display_status(XSTR("Ready To Enter Mission",815),count);
8302                                 break;                                  
8303                         case NETPLAYER_STATE_INGAME_SHIPS:
8304                                 multi_sync_display_status(XSTR("Ingame Ships Packet Ack",816),count);
8305                                 break;
8306                         case NETPLAYER_STATE_INGAME_WINGS:
8307                                 multi_sync_display_status(XSTR("Ingame Wings Packet Ack",817),count);
8308                                 break;          
8309                         case NETPLAYER_STATE_INGAME_RPTS:
8310                                 multi_sync_display_status(XSTR("Ingame Respawn Points Ack",818),count);
8311                                 break;
8312                         case NETPLAYER_STATE_SLOTS_ACK:
8313                                 multi_sync_display_status(XSTR("Ingame Weapon Slots Ack",819),count);
8314                                 break;                  
8315                         case NETPLAYER_STATE_POST_DATA_ACK:
8316                                 multi_sync_display_status(XSTR("Post Briefing Data Block Ack",820),count);
8317                                 break;
8318                         case NETPLAYER_STATE_FLAG_ACK :
8319                                 multi_sync_display_status(XSTR("Flags Ack",821),count);
8320                                 break;
8321                         case NETPLAYER_STATE_MT_STATS :
8322                                 multi_sync_display_status(XSTR("Parallax Online Stats Updating",822),count);
8323                                 break;
8324                         case NETPLAYER_STATE_WSS_ACK :
8325                                 multi_sync_display_status(XSTR("Weapon Slots Ack",823),count);
8326                                 break;
8327                         case NETPLAYER_STATE_HOST_SETUP :
8328                                 multi_sync_display_status(XSTR("Host setup",824),count);
8329                                 break;
8330                         case NETPLAYER_STATE_DEBRIEF_ACCEPT:
8331                                 multi_sync_display_status(XSTR("Debrief accept",825),count);
8332                                 break;
8333                         case NETPLAYER_STATE_DEBRIEF_REPLAY:
8334                                 multi_sync_display_status(XSTR("Debrief replay",826),count);
8335                                 break;
8336                         case NETPLAYER_STATE_CPOOL_ACK:
8337                                 multi_sync_display_status(XSTR("Campaign ship/weapon ack",827),count);
8338                                 break;                          
8339                         case NETPLAYER_STATE_MISSION_XFER :                             
8340                                 memset(txt,0,255);
8341                                 // server should display the pct completion of all clients                              
8342                                 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8343                                         if(Net_players[idx].s_info.xfer_handle != -1){                                  
8344                                                 pct_complete = multi_xfer_pct_complete(Net_players[idx].s_info.xfer_handle);
8345
8346                                                 // if we've got a valid xfer handle
8347                                                 if((pct_complete >= 0.0) && (pct_complete <= 1.0)){                                             
8348                                                         SDL_snprintf(txt,SDL_arraysize(txt),XSTR("Mission file xfer %d%%",828),(int)(pct_complete * 100.0f));
8349                                                 }
8350                                                 // otherwise
8351                                                 else {
8352                                                         SDL_strlcpy(txt, XSTR("Mission file xfer", 829), SDL_arraysize(txt));
8353                                                 }                                       
8354                                         } else {
8355                                                 SDL_strlcpy(txt, XSTR("Mission file xfer", 829), SDL_arraysize(txt));
8356                                         }
8357                                 }
8358                                 // clients should display only for themselves (which is the only thing they know)
8359                                 else {
8360                                         // if we've got a valid file xfer handle
8361                                         if((&Net_players[idx] == Net_player) && (Net_player->s_info.xfer_handle != -1)){
8362                                                 pct_complete = multi_xfer_pct_complete(Net_player->s_info.xfer_handle);
8363
8364                                                 // if we've got a valid xfer handle
8365                                                 if((pct_complete >= 0.0) && (pct_complete <= 1.0)){                                             
8366                                                         SDL_snprintf(txt,SDL_arraysize(txt),XSTR("Mission file xfer %d%%",828),(int)(pct_complete * 100.0f));
8367                                                 }
8368                                                 // otherwise
8369                                                 else {
8370                                                         SDL_strlcpy(txt, XSTR("Mission file xfer", 829), SDL_arraysize(txt));
8371                                                 }
8372                                         }
8373                                         // otherwise
8374                                         else {
8375                                                 SDL_strlcpy(txt, XSTR("Mission file xfer", 829), SDL_arraysize(txt));
8376                                         }
8377                                 }
8378
8379                                 // display the text
8380                                 multi_sync_display_status(txt,count);
8381                                 break;
8382                         default :
8383                                 nprintf(("Network","Unhandled player state : %d !\n",Net_players[idx].state));
8384                                 break;
8385                         }
8386                         count++;
8387                 }
8388         }       
8389
8390         // display the mission start countdown timer (if any)
8391         anim_render_all(GS_STATE_MULTI_MISSION_SYNC,flFrametime);       
8392
8393         // process and show the chatbox thingie 
8394         chatbox_render();
8395
8396         // draw tooltips
8397         Multi_sync_window.draw_tooltip();
8398
8399         // display the voice status indicator
8400         multi_common_voice_display_status();
8401 }
8402
8403 void multi_sync_check_buttons()
8404 {
8405         int idx;
8406         for(idx=0;idx<Multi_sync_button_count;idx++){
8407                 // we only really need to check for one button pressed at a time, so we can break after 
8408                 // finding one.
8409                 if(Multi_sync_buttons[gr_screen.res][idx].button.pressed()){
8410                         multi_sync_button_pressed(idx);
8411                         break;
8412                 }
8413         }
8414 }
8415
8416 void multi_sync_button_pressed(int n)
8417 {
8418         switch(n){      
8419         // exit the game
8420         case MS_CANCEL:
8421                 gamesnd_play_iface(SND_USER_SELECT);            
8422                 multi_quit_game(PROMPT_ALL);            
8423                 break;  
8424         
8425         // scroll the info box up
8426         case MS_SCROLL_INFO_UP:
8427                 multi_common_scroll_text_up();          
8428                 break;
8429         
8430         // scroll the info box down
8431         case MS_SCROLL_INFO_DOWN:
8432                 multi_common_scroll_text_down();
8433                 break;  
8434
8435         // KICK (host only)
8436         case MS_KICK:
8437                 // if we have a currently selected player, kick him
8438                 if(Multi_sync_player_select >= 0){
8439                         multi_kick_player(Multi_sync_player_select);
8440                 }
8441                 break;
8442         
8443         // start the final launch countdown (post-sync only)
8444         case MS_LAUNCH:
8445                 multi_sync_start_countdown();
8446                 break;
8447         
8448         // doesn't do anything
8449         default :
8450                 Int3();         
8451         }
8452 }
8453
8454 void multi_sync_pre_init()
8455 {
8456         int idx;
8457
8458         Netgame.game_state = NETGAME_STATE_MISSION_SYNC;
8459
8460         // if we're in teamplay mode, always force skill level to be medium
8461         if((Netgame.type_flags & NG_TYPE_TEAM) && (Net_player->flags & NETINFO_FLAG_GAME_HOST)){
8462                 Netgame.options.skill_level = NUM_SKILL_LEVELS / 2;
8463                 Game_skill_level = NUM_SKILL_LEVELS / 2;
8464                 multi_options_update_netgame();
8465         }
8466
8467         // notify everyone of when we get here
8468         if(!(Game_mode & GM_STANDALONE_SERVER)){
8469                 Net_player->state = NETPLAYER_STATE_MISSION_SYNC;
8470                 send_netplayer_update_packet();
8471         }
8472         
8473         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){ 
8474                 // NETLOG
8475                 ml_string(NOX("Server performing pre-briefing data sync"));
8476
8477                 if(!(Game_mode & GM_STANDALONE_SERVER)){
8478                         multi_common_set_text(XSTR("Server performing sync\n",830),1);
8479                 }
8480                 
8481                 // maybe initialize tvt and squad war stuff
8482                 if(Netgame.type_flags & NG_TYPE_TEAM){
8483                         multi_team_level_init();
8484                 }       
8485
8486                 // force everyone into this state               
8487                 send_netgame_update_packet();           
8488
8489                 if(!(Game_mode & GM_STANDALONE_SERVER)){
8490                         multi_common_add_text(XSTR("Send update packet\n",831),1);
8491                 }
8492
8493                 // setup some of my own data
8494                 Net_player->flags |= NETINFO_FLAG_MISSION_OK;           
8495
8496                 // do any output stuff
8497                 if(Game_mode & GM_STANDALONE_SERVER){
8498                         std_debug_set_standalone_state_string("Mission Sync");                  
8499                 }               
8500
8501                 // do this here to insure we have the most up to date file checksum info
8502                 multi_get_mission_checksum(Game_current_mission_filename);
8503                 // parse_get_file_signature(Game_current_mission_filename);
8504                 
8505                 if(!(Game_mode & GM_STANDALONE_SERVER)){
8506                         multi_common_add_text(XSTR("Got file signatures\n",832),1);
8507                 }
8508         } else {
8509                 if(!(Game_mode & GM_STANDALONE_SERVER)){
8510                         multi_common_add_text(XSTR("Sending update state packet\n",833),1);
8511                 }
8512         }
8513
8514         // if we're not in team vs. team mode - set all player teams to be 0, and unset all captaincy bits
8515         if(!(Netgame.type_flags & NG_TYPE_TEAM)){
8516                 for(idx=0;idx<MAX_PLAYERS;idx++){
8517                         Net_players[idx].p_info.team = 0;
8518                         Net_players[idx].flags &= ~(NETINFO_FLAG_TEAM_CAPTAIN);
8519                 }
8520         }
8521
8522         // we aren't necessarily xferring the mission file yet  
8523         SDL_assert(Net_player->s_info.xfer_handle == -1);
8524
8525         // always call this for good measure
8526         multi_campaign_flush_data();
8527
8528         Mission_sync_flags = 0;
8529         Multi_mission_loaded = 0;
8530 }
8531
8532 void multi_sync_pre_do()
8533 {               
8534         int idx;
8535         
8536         // If I'm the server, wait for everyone to arrive in this state, then begin transferring data, etc.
8537         // all servers (standalone or no, go through this)
8538         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8539                 // wait for everyone to arrive, then request filesig from all of them
8540                 if(multi_netplayer_state_check(NETPLAYER_STATE_MISSION_SYNC) && !(Mission_sync_flags & MS_FLAG_SENT_FILESIG) && !multi_endgame_ending()){
8541                         send_file_sig_request(Netgame.mission_name);
8542                         Mission_sync_flags |= MS_FLAG_SENT_FILESIG;
8543
8544                         if(!(Game_mode & GM_STANDALONE_SERVER)){
8545                                 multi_common_add_text(XSTR("Sent filesig request\n",834),1);
8546                         }
8547                 }               
8548
8549                 // if we're waiting for players to receive files, then check on their status
8550                 if((Mission_sync_flags & MS_FLAG_SENT_FILESIG) && !multi_netplayer_flag_check(NETINFO_FLAG_MISSION_OK) && !multi_endgame_ending()){
8551                         for(idx=0;idx<MAX_PLAYERS;idx++){
8552                                 // if this player is in the process of xferring a file
8553                                 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].s_info.xfer_handle != -1)){
8554                                         switch(multi_xfer_get_status(Net_players[idx].s_info.xfer_handle)){
8555                                         // if it has successfully completed, set his ok flag
8556                                         case MULTI_XFER_SUCCESS :
8557                                                 // set his ok flag
8558                                                 Net_players[idx].flags |= NETINFO_FLAG_MISSION_OK;
8559
8560                                                 // release the xfer instance handle
8561                                                 multi_xfer_release_handle(Net_players[idx].s_info.xfer_handle);
8562                                                 Net_players[idx].s_info.xfer_handle = -1;
8563                                                 break;
8564                                         // if it has failed or timed-out, kick the player
8565                                         case MULTI_XFER_TIMEDOUT:
8566                                         case MULTI_XFER_FAIL:
8567                                                 // release the xfer handle
8568                                                 multi_xfer_release_handle(Net_players[idx].s_info.xfer_handle);
8569                                                 Net_players[idx].s_info.xfer_handle = -1;
8570                                                 
8571                                                 // kick the loser
8572                                                 multi_kick_player(idx, 0, KICK_REASON_BAD_XFER);
8573                                                 break;
8574                                         }
8575                                 }
8576                         }
8577                 }
8578
8579                 // NOTE : this is now obsolete
8580                 // once everyone is verified, do any data transfer necessary
8581                 if(multi_netplayer_flag_check(NETINFO_FLAG_MISSION_OK) && !(Mission_sync_flags & MS_FLAG_DATA_DONE) && !multi_endgame_ending()){
8582                         // do nothing for now
8583                         Mission_sync_flags |= MS_FLAG_DATA_DONE;                                                
8584                         
8585                         // send campaign pool data
8586                         multi_campaign_send_pool_status();
8587                 }
8588
8589                 // wait for everyone to ack on campaign pool data (even in non-campaign situations)
8590                 if((Mission_sync_flags & MS_FLAG_DATA_DONE) && !(Mission_sync_flags & MS_FLAG_CAMP_DONE) && !multi_endgame_ending()){
8591                         // check to see if everyone has acked the campaign pool data
8592                         if(multi_netplayer_state_check(NETPLAYER_STATE_CPOOL_ACK)){
8593                                 Mission_sync_flags |= MS_FLAG_CAMP_DONE;
8594                         }
8595                 }
8596                                 
8597                 // once everyone is verified, tell them to load the mission
8598                 // also make sure to load the mission myself _AFTER_ telling everyone to do so. This makes the whole process
8599                 // move along faster
8600                 if((Mission_sync_flags & MS_FLAG_CAMP_DONE) && !(Mission_sync_flags & MS_FLAG_SENT_LOAD) && !multi_endgame_ending()){           
8601                         send_netplayer_load_packet(NULL);                       
8602                         Mission_sync_flags |= MS_FLAG_SENT_LOAD;
8603
8604                         if(!(Game_mode & GM_STANDALONE_SERVER)){
8605                                 multi_common_add_text(XSTR("Sent load packet\n",835),1);
8606                         }
8607
8608                         // load the mission myself, as soon as possible
8609                         if(!Multi_mission_loaded){
8610                                 nprintf(("Network","Server loading mission..."));
8611         
8612                                 // update everyone about my status
8613                                 Net_player->state = NETPLAYER_STATE_MISSION_LOADING;
8614                                 send_netplayer_update_packet();
8615
8616                                 game_start_mission();
8617                                 psnet_flush();
8618                                 nprintf(("Network","Done\n"));
8619                                 Multi_mission_loaded = 1;                               
8620
8621                                 // update everyone about my status
8622                                 Net_player->state = NETPLAYER_STATE_MISSION_LOADED;
8623                                 send_netplayer_update_packet();
8624
8625                                 if(!(Game_mode & GM_STANDALONE_SERVER)){
8626                                         multi_common_add_text(XSTR("Loaded mission locally\n",836),1);
8627                                 }
8628                         }
8629                 }
8630                 
8631                 // if everyone has loaded the mission, randomly assign players to ships 
8632                 if(multi_netplayer_state_check(NETPLAYER_STATE_MISSION_LOADED) && !(Mission_sync_flags & MS_FLAG_TS_SLOTS) && !multi_endgame_ending()){
8633                         // call the team select function to assign players to their ships, wings, etc
8634                         multi_ts_assign_players_all();
8635                         send_netplayer_slot_packet();                                                                           
8636
8637                         // mark this flag
8638                         Mission_sync_flags |= MS_FLAG_TS_SLOTS;
8639                 }
8640
8641                 // if everyone has loaded the mission, move to the team select stage
8642                 if(Sync_test && multi_netplayer_state_check(NETPLAYER_STATE_SLOT_ACK) && !(Mission_sync_flags & MS_FLAG_PUSHED_BRIEFING) && !multi_endgame_ending()){
8643                         Netgame.game_state = NETGAME_STATE_BRIEFING;
8644                         send_netgame_update_packet();   // this will push everyone into the next state
8645
8646                         // the standalone moves to his own wait state, whereas in the normal game mode, the server/host moves in to the
8647                         // team select state
8648                         if(Game_mode & GM_STANDALONE_SERVER){
8649                                 gameseq_post_event(GS_EVENT_MULTI_STD_WAIT);
8650                         } else {
8651                                 gameseq_post_event(GS_EVENT_START_GAME);                                        
8652                         }
8653
8654                         Mission_sync_flags |= MS_FLAG_PUSHED_BRIEFING;
8655
8656                         if(!(Game_mode & GM_STANDALONE_SERVER)){
8657                                 multi_common_add_text(XSTR("Moving to team select\n",837),1);
8658                         }
8659                 }               
8660         } else {
8661                 // clients should detect here if they are doing a file xfer and do error processing
8662                 if((Net_player->state == NETPLAYER_STATE_MISSION_XFER) && (Net_player->s_info.xfer_handle != -1) && !multi_endgame_ending()){
8663                         switch(multi_xfer_get_status(Net_player->s_info.xfer_handle)){
8664                         // if it has successfully completed, set his ok flag
8665                         case MULTI_XFER_SUCCESS :       
8666                                 // release my xfer handle
8667                                 multi_xfer_release_handle(Net_player->s_info.xfer_handle);
8668                                 Net_player->s_info.xfer_handle = -1;                            
8669                                 break;
8670                                 
8671                         // if it has failed or timed-out, kick the player
8672                         case MULTI_XFER_TIMEDOUT:
8673                         case MULTI_XFER_FAIL:
8674                                 // release my xfer handle
8675                                 multi_xfer_release_handle(Net_player->s_info.xfer_handle);
8676                                 Net_player->s_info.xfer_handle = -1;
8677
8678                                 // leave the game qith an error code
8679                                 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_XFER_FAIL);
8680                                 break;
8681                         }
8682                 }
8683         }
8684
8685         // blit stuff
8686         if(!(Game_mode & GM_STANDALONE_SERVER)){
8687                 gr_reset_clip();
8688                 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8689                 if(Multi_sync_bitmap != -1){
8690                         gr_set_bitmap(Multi_sync_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8691                         gr_bitmap(0,0);
8692                 }
8693                 Multi_sync_window.draw();
8694
8695                 multi_sync_blit_screen_all();
8696
8697                 gr_flip();
8698         }
8699 }
8700
8701 void multi_sync_pre_close()
8702 {
8703         // at this point, we should shut down any file xfers...
8704         if(Net_player->s_info.xfer_handle != -1){
8705                 nprintf(("Network","WARNING - killing file xfer while leaving mission sync state!!!\n"));
8706
8707                 multi_xfer_abort(Net_player->s_info.xfer_handle);
8708                 Net_player->s_info.xfer_handle = -1;
8709         }
8710 }
8711
8712 void multi_sync_post_init()
8713 {       
8714         multi_reset_timestamps();
8715
8716         Multi_state_timestamp = timestamp(0);
8717
8718         // NETLOG
8719         ml_string(NOX("Performing post-briefing data sync"));
8720         
8721         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8722                 multi_common_add_text(XSTR("Server performing sync\n",830),1);
8723         } else {
8724                 multi_common_add_text(XSTR("Client performing sync\n",838),1);
8725         }
8726
8727         // everyone should re-initialize these 
8728         init_multiplayer_stats();
8729
8730         // reset all sequencing info
8731         multi_oo_reset_sequencing();
8732
8733         // if I am not the master of the game, then send the firing information for my ship
8734         // to the host
8735         if ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) ){
8736                 send_firing_info_packet();
8737         }       
8738
8739         // if I'm not a standalone server, load up the countdown stuff
8740         if(!(Game_mode & GM_STANDALONE_SERVER)){                                
8741                 Multi_sync_countdown_anim = NULL;
8742                 Multi_sync_countdown_instance = NULL;
8743                 Multi_sync_countdown_anim = anim_load(Multi_sync_countdown_fname[gr_screen.res]);                               
8744                 if(Multi_sync_countdown_anim == NULL){
8745                         nprintf(("General","WARNING!, Could not load countdown animation %s!\n",Multi_sync_countdown_fname[gr_screen.res]));
8746                 }
8747         }
8748
8749         // create objects for all permanent observers
8750         multi_obs_level_init();
8751
8752         // clear the game start countdown timer
8753         Multi_sync_countdown_timer = -1.0f;     
8754         Multi_sync_countdown = -1;
8755
8756         // if this is a team vs. team mission, mark all ship teams appropriately
8757         if(Netgame.type_flags & NG_TYPE_TEAM){
8758                 multi_team_mark_all_ships();
8759         }
8760
8761         Mission_sync_flags = 0;
8762         Multi_sync_launch_pressed = 0;
8763 }
8764
8765 #define MULTI_POST_TIMESTAMP                    7000
8766
8767 extern int create_wings();
8768
8769 void multi_sync_post_do()
8770 {       
8771         int idx;
8772         
8773         // only if the host is also the master should he be doing this (non-standalone situation)
8774         if ( Net_player->flags & NETINFO_FLAG_AM_MASTER ) {
8775
8776                 // once everyone gets to this screen, send them the ship classes of all ships.
8777                 if(multi_netplayer_state_check(NETPLAYER_STATE_WAITING) && !(Mission_sync_flags & MS_FLAG_POST_DATA)) {                 
8778                         // only the host should ever do this
8779                         if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8780                                 // at this point we want to delete all necessary ships, change all necessary ship classes, and set all weapons up
8781                                 multi_ts_create_wings();
8782
8783                                 // update player ets settings
8784                                 for(idx=0;idx<MAX_PLAYERS;idx++){
8785                                         if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].player->objnum != -1)){
8786                                                 multi_server_update_player_weapons(&Net_players[idx],&Ships[Objects[Net_players[idx].player->objnum].instance]);
8787                                         }
8788                                 }                       
8789                         }                       
8790
8791                         // note that this is done a little differently for standalones and nonstandalones
8792                         send_post_sync_data_packet();
8793                         
8794                         multi_common_add_text(XSTR("Sending post briefing block information\n",839),1);                 
8795
8796                         Mission_sync_flags |= MS_FLAG_POST_DATA;
8797                 }
8798
8799                 // send weapon slots data
8800                 if(multi_netplayer_state_check(NETPLAYER_STATE_POST_DATA_ACK) && !(Mission_sync_flags & MS_FLAG_WSS_SLOTS)) {                   
8801                         // note that this is done a little differently for standalones and nonstandalones
8802                         if(Netgame.type_flags & NG_TYPE_TEAM){
8803                                 send_wss_slots_data_packet(0,0);
8804                                 send_wss_slots_data_packet(1,1);
8805                         } else {
8806                                 send_wss_slots_data_packet(0,1);
8807                         }
8808                         
8809                         multi_common_add_text(XSTR("Sending weapon slots information\n",840),1);                        
8810
8811                         Mission_sync_flags |= MS_FLAG_WSS_SLOTS;
8812                 }
8813                         
8814                 // once weapon information is received, send player settings info
8815                 if ( multi_netplayer_state_check(NETPLAYER_STATE_WSS_ACK) && !(Mission_sync_flags & MS_FLAG_PSETTINGS)) {                                                                       
8816                         send_player_settings_packet();
8817                         
8818                         // server (specifically, the standalone), should set this here
8819                         Net_player->state = NETPLAYER_STATE_SETTINGS_ACK;
8820                         send_netplayer_update_packet();
8821                         
8822                         multi_common_add_text(XSTR("Sending player settings packets\n",841),1);                         
8823
8824                         Mission_sync_flags |= MS_FLAG_PSETTINGS;                        
8825                 }                                       
8826
8827                 // check to see if the countdown timer has started and act appropriately
8828                 if( Multi_sync_countdown_timer > -1.0f ) {
8829
8830                         // increment by frametime.
8831                         Multi_sync_countdown_timer += flFrametime;
8832
8833                         // if the animation is not playing, start it
8834                         if(!(Game_mode & GM_STANDALONE_SERVER) && (Multi_sync_countdown_instance == NULL) && (Multi_sync_countdown_anim != NULL)){
8835                                 anim_play_struct aps;                           
8836
8837                                 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]);
8838                                 aps.screen_id = GS_STATE_MULTI_MISSION_SYNC;
8839                                 aps.framerate_independent = 1;
8840
8841                                 Multi_sync_countdown_instance = anim_play(&aps);
8842                         }
8843
8844                         // if the next second has expired
8845                         if( Multi_sync_countdown_timer >= 1.0f ) {
8846
8847                                 Multi_sync_countdown--;
8848                                 Multi_sync_countdown_timer = 0.0f;
8849
8850                                 // if the countdown has reached 0, launch the mission
8851                                 if(Multi_sync_countdown == 0){
8852                                         Multi_sync_countdown_timer = -1.0f;
8853
8854                                         Multi_sync_launch_pressed = 0;
8855                                         multi_sync_launch();
8856                                 }
8857                                 // otherwise send a countdown packet
8858                                 else {
8859                                         send_countdown_packet(Multi_sync_countdown);
8860                                 }
8861                         }
8862                 }
8863                         
8864                 // jump into the mission myself
8865                 if((Multi_sync_countdown == 0) && multi_netplayer_state_check(NETPLAYER_STATE_IN_MISSION)){
8866                         if(!((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !Multi_sync_launch_pressed)){                                                      
8867                                 Net_player->state = NETPLAYER_STATE_IN_MISSION;
8868                                 Netgame.game_state = NETGAME_STATE_IN_MISSION;                          
8869                                 gameseq_post_event(GS_EVENT_ENTER_GAME);
8870                         
8871                                 multi_common_add_text(XSTR("Moving into game\n",842),1);                        
8872                         }
8873                 }
8874         } else {
8875                 // maybe start the animation countdown 
8876                 if((Multi_sync_countdown >= 0) && (Multi_sync_countdown_instance == NULL) && (Multi_sync_countdown_anim != NULL)){
8877                         anim_play_struct aps;                           
8878
8879                         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]);
8880                         aps.screen_id = GS_STATE_MULTI_MISSION_SYNC;
8881                         aps.framerate_independent = 1;
8882
8883                         Multi_sync_countdown_instance = anim_play(&aps);
8884                 }
8885         }
8886
8887         // host - specific stuff
8888         if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8889                 // create the launch button so the host can click
8890                 if( Sync_test && multi_netplayer_state_check(NETPLAYER_STATE_SETTINGS_ACK) ){
8891                         multi_sync_create_launch_button();
8892                 }
8893         }
8894
8895         // blit stuff
8896         if(!(Game_mode & GM_STANDALONE_SERVER)){
8897                 gr_reset_clip();        
8898                 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8899                 if(Multi_sync_bitmap != -1){
8900                         gr_set_bitmap(Multi_sync_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8901                         gr_bitmap(0,0);
8902                 }
8903                 Multi_sync_window.draw();
8904
8905                 multi_sync_blit_screen_all();
8906
8907                 gr_flip();
8908         }
8909 }
8910
8911 void multi_sync_post_close()
8912 {
8913         int idx;
8914
8915         // if I'm not a standalone server, unload up the countdown stuff
8916         if(!(Game_mode & GM_STANDALONE_SERVER)){                                
8917                 // release all rendering animation instances (should only be 1)
8918                 anim_release_all_instances(GS_STATE_MULTI_MISSION_SYNC);
8919                 Multi_sync_countdown_instance = NULL;
8920
8921                 // free up the countdown animation
8922                 if(Multi_sync_countdown_anim != NULL){
8923                         anim_free(Multi_sync_countdown_anim);
8924                         Multi_sync_countdown_anim = NULL;
8925                 }
8926         }
8927         
8928         // all players should reset sequencing
8929         for(idx=0;idx<MAX_PLAYERS;idx++){
8930                 if(Net_player->flags & NETINFO_FLAG_CONNECTED){
8931                         Net_players[idx].client_cinfo_seq = 0;
8932                         Net_players[idx].client_server_seq = 0; 
8933                 }
8934         }
8935
8936         // multiplayer dogfight
8937         multi_df_level_pre_enter();                             
8938
8939         // clients should clear obj_pair array and add pair for themselves
8940         /*
8941         if ( MULTIPLAYER_CLIENT ) {
8942                 obj_reset_pairs();
8943                 obj_add_pairs( OBJ_INDEX(Player_obj) ); 
8944         }
8945         */
8946 }
8947
8948 void multi_sync_display_name(const char *name, int index, int np_index)
8949 {
8950         char fit[CALLSIGN_LEN]; 
8951         
8952         // make sure the string actually fits
8953         SDL_strlcpy(fit, name, SDL_arraysize(fit));
8954
8955         // if we're in team vs. team mode
8956         if(Netgame.type_flags & NG_TYPE_TEAM){
8957                 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]);                 
8958
8959                 // if this is the currently selected player, draw him highlighted
8960                 if(np_index == Multi_sync_player_select){
8961                         gr_set_color_fast(&Color_text_selected);
8962                 } else {
8963                         gr_set_color_fast(&Color_text_normal);
8964                 }
8965
8966                 // blit the string
8967                 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);
8968
8969                 // blit his team icon 
8970                 // team 0               
8971                 if(Net_players[np_index].p_info.team == 0){
8972                         // blit the team captain icon
8973                         if(Net_players[np_index].flags & NETINFO_FLAG_TEAM_CAPTAIN){                            
8974                                 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
8975                                         gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8976                                         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);
8977                                 } 
8978                         }
8979                         // normal team member icon
8980                         else {
8981                                 if(Multi_common_icons[MICON_TEAM0] != -1){
8982                                         gr_set_bitmap(Multi_common_icons[MICON_TEAM0], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8983                                         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);
8984                                 }
8985                         }
8986                 }
8987                 // team 1
8988                 else if(Net_players[np_index].p_info.team == 1){                        
8989                         // blit the team captain icon
8990                         if(Net_players[np_index].flags & NETINFO_FLAG_TEAM_CAPTAIN){
8991                                 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
8992                                         gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8993                                         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);
8994                                 }
8995                         }
8996                         // normal team member icon
8997                         else {
8998                                 if(Multi_common_icons[MICON_TEAM1] != -1){
8999                                         gr_set_bitmap(Multi_common_icons[MICON_TEAM1], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
9000                                         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);
9001                                 }
9002                         }
9003                 }               
9004         } else {
9005                 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]);
9006
9007                 // if this is the currently selected player, draw him highlighted
9008                 if(np_index == Multi_sync_player_select){
9009                         gr_set_color_fast(&Color_text_selected);
9010                 } else {
9011                         gr_set_color_fast(&Color_text_normal);
9012                 }
9013
9014                 // blit the string
9015                 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);
9016         }
9017
9018         // maybe blit his CD status icon
9019         if((Net_players[np_index].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
9020                 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
9021                 gr_bitmap(Ms_status_coords[gr_screen.res][MS_X_COORD], Ms_status_coords[gr_screen.res][MS_Y_COORD] + (index * 10));
9022         }
9023 }
9024
9025 void multi_sync_display_status(const char *status, int index)
9026 {
9027         char fit[250];
9028
9029         // make sure the string actually fits
9030         SDL_strlcpy(fit, status, SDL_arraysize(fit));
9031         gr_force_fit_string(fit, 250, Ms_status2_coords[gr_screen.res][MS_W_COORD] - 20);
9032         gr_set_color_fast(&Color_bright);       
9033         gr_string(Ms_status2_coords[gr_screen.res][MS_X_COORD], Ms_status2_coords[gr_screen.res][MS_Y_COORD] + (index * 10), fit);              
9034 }
9035
9036 void multi_sync_force_start_pre()
9037 {
9038         int idx;
9039         int want_state = NETPLAYER_STATE_SLOT_ACK;      // kick any players who are still in this state
9040
9041         // go through the player list and boot anyone who isn't in the right state
9042         for(idx=0;idx<MAX_PLAYERS;idx++){
9043                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_player != &Net_players[idx]) && (Net_players[idx].state == want_state)){            
9044                         multi_kick_player(idx,0);                               
9045                 }
9046         }
9047 }
9048
9049 void multi_sync_force_start_post()
9050 {
9051         int idx,idx2;
9052         int kill_state[3];      
9053         int num_kill_states;
9054         
9055         // determine the state we want all players in so that we can find those who are not in the state        
9056         kill_state[0] = NETPLAYER_STATE_BRIEFING;
9057         kill_state[1] = NETPLAYER_STATE_SHIP_SELECT;
9058         kill_state[2] = NETPLAYER_STATE_WEAPON_SELECT;
9059         num_kill_states = 3;    
9060
9061         // go through the player list and boot anyone who isn't in the right state
9062         for(idx=0;idx<MAX_PLAYERS;idx++){
9063                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_player != &Net_players[idx])){
9064                         // check against all kill state
9065                         for(idx2 = 0;idx2<num_kill_states;idx2++){
9066                                 if(Net_players[idx].state == kill_state[idx2]){
9067                                         multi_kick_player(idx,0);
9068                                         break;
9069                                 }
9070                         }
9071                 }
9072         }
9073 }
9074
9075 void multi_sync_start_countdown()
9076 {
9077         // don't allow repeat button presses
9078         if(Multi_sync_launch_pressed){
9079                 return;
9080         }
9081         
9082         Multi_sync_launch_pressed = 1;
9083
9084         // if I'm the server, begin the countdown
9085         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
9086                 gamesnd_play_iface(SND_COMMIT_PRESSED);
9087                 Multi_sync_countdown_timer = 0.0f;
9088                 Multi_sync_countdown = MULTI_SYNC_COUNTDOWN_TIME;
9089
9090                 // send an initial countdown value
9091                 send_countdown_packet(Multi_sync_countdown);
9092         }
9093         // otherwise send the "start countdown" packet to the standalone
9094         else {
9095                 SDL_assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
9096                 send_countdown_packet(-1);
9097         }
9098 }
9099
9100 void multi_sync_launch()
9101 {       
9102         // don't allow repeat button presses
9103         if(Multi_sync_launch_pressed){
9104                 return;
9105         }
9106
9107         Multi_sync_launch_pressed = 1;
9108
9109         // NETLOG
9110         ml_printf(NOX("Entering mission %s"), Game_current_mission_filename);
9111
9112         // tell everyone to jump into the mission
9113         send_jump_into_mission_packet();
9114         Multi_state_timestamp = timestamp(MULTI_POST_TIMESTAMP);
9115
9116         // set the # of players at the start of the mission
9117         Multi_num_players_at_start = multi_num_players();
9118         nprintf(("Network","# of players at start of mission : %d\n", Multi_num_players_at_start));
9119         
9120         // initialize datarate limiting for all clients
9121         multi_oo_rate_init_all();       
9122                                 
9123         multi_common_add_text(XSTR("Sending mission start packet\n",843),1);                            
9124 }
9125
9126 void multi_sync_create_launch_button()
9127 {
9128         if (!Multi_launch_button_created) {             
9129                 // create the object
9130                 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);
9131
9132                 // set the sound to play when highlighted
9133                 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_highlight_action(common_play_highlight_sound);
9134
9135                 // set the ani for the button
9136                 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_bmaps(Multi_sync_buttons[gr_screen.res][MS_LAUNCH].filename, 3, 0);
9137
9138                 // set the hotspot
9139                 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.link_hotspot(Multi_sync_buttons[gr_screen.res][MS_LAUNCH].hotspot);
9140
9141                 // hotkey
9142                 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_hotkey(KEY_CTRLED+SDLK_RETURN);
9143
9144 #ifndef MAKE_FS1
9145                 // create the text for the button
9146                 Multi_sync_window.add_XSTR(&Multi_sync_text[gr_screen.res][MST_LAUNCH]);
9147 #endif
9148
9149                 // increment the button count so we start checking this one
9150                 Multi_sync_button_count++;
9151
9152                 Multi_launch_button_created = 1;
9153         }
9154 }
9155
9156 void multi_sync_handle_plist()
9157 {
9158         int idx;
9159         int my;
9160         int select_index;
9161         
9162         // if we don't have a currently selected player, select one
9163         if((Multi_sync_player_select < 0) || !MULTI_CONNECTED(Net_players[Multi_sync_player_select])){
9164                 for(idx=0;idx<MAX_PLAYERS;idx++){
9165                         if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
9166                                 Multi_sync_player_select = idx;
9167                                 break;
9168                         }
9169                 }
9170         }
9171         
9172         // check for button list presses
9173         if(Multi_sync_plist_button.pressed()){
9174                 // get the y mouse coords
9175                 Multi_sync_plist_button.get_mouse_pos(NULL,&my);
9176
9177                 // get the index of the item selected
9178                 select_index = my / 10;
9179
9180                 // if the index is greater than the current # connections, do nothing
9181                 if(select_index > (multi_num_connections() - 1)){
9182                         return;
9183                 }
9184
9185                 // translate into an absolute Net_players[] index (get the Nth net player)
9186                 Multi_sync_player_select = -1;
9187                 for(idx=0;idx<MAX_PLAYERS;idx++){
9188                         if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
9189                                 select_index--;
9190                         }
9191
9192                         // if we've found the item we're looking for
9193                         if(select_index < 0){
9194                                 Multi_sync_player_select = idx;
9195                                 break;
9196                         }
9197                 }
9198
9199                 // if for some bizarre reason, this is an invalid player, unselect him and wait for the next interation
9200                 // of the loop
9201                 if((Multi_sync_player_select >= 0) && (!MULTI_CONNECTED(Net_players[Multi_sync_player_select]) || MULTI_STANDALONE(Net_players[Multi_sync_player_select])) ){
9202                         Multi_sync_player_select = -1;
9203                 }
9204         }
9205 }
9206
9207
9208 // -------------------------------------------------------------------------------------------------------------
9209 // 
9210 // MULTIPLAYER DEBRIEF SCREEN 
9211 //
9212
9213 // other relevant data
9214 int Multi_debrief_accept_hit;
9215 int Multi_debrief_replay_hit;
9216
9217 // set if the server has left the game
9218 int Multi_debrief_server_left = 0;
9219
9220 // if we've reported on TvT status all players are in the debrief
9221 int Multi_debrief_reported_tvt = 0;
9222
9223 // whether stats are being accepted
9224 // -1 == no decision yet
9225 // 1 == accepted
9226 // 0 == tossed
9227 int Multi_debrief_stats_accept_code = -1;
9228
9229 int Multi_debrief_server_framecount = 0;
9230
9231 float Multi_debrief_time = 0.0f;
9232 float Multi_debrief_resend_time = 10.0f;
9233
9234 void multi_debrief_init()
9235 {                       
9236         int idx;
9237
9238         Multi_debrief_time = 0.0f;
9239         Multi_debrief_resend_time = 10.0f;
9240         
9241         // do this to notify the standalone or the normal server that we're in the debrief state and ready to receive packets
9242         if (!(Net_player->flags & NETINFO_FLAG_AM_MASTER)) {
9243                 Net_player->state = NETPLAYER_STATE_DEBRIEF;
9244                 send_netplayer_update_packet();
9245         }       
9246
9247         // unflag some stuff
9248         for(idx=0;idx<MAX_PLAYERS;idx++){
9249                 if(MULTI_CONNECTED(Net_players[idx])){
9250                         Net_players[idx].flags &= ~(NETINFO_FLAG_RESPAWNING | NETINFO_FLAG_LIMBO | NETINFO_FLAG_WARPING_OUT);
9251                 }
9252         }
9253         
9254         // if text input mode is active, clear it
9255         multi_msg_text_flush(); 
9256         
9257         // the server has not left yet
9258         Multi_debrief_server_left = 0;
9259
9260         // have not hit accept or replay yet
9261         Multi_debrief_accept_hit = 0;
9262         Multi_debrief_replay_hit = 0;
9263
9264         // stats have not been accepted yet
9265         Multi_debrief_stats_accept_code = -1;
9266
9267         // mark stats as not being store yet
9268         Netgame.flags &= ~(NG_FLAG_STORED_MT_STATS);
9269
9270         // no report on TvT yet
9271         Multi_debrief_reported_tvt = 0;
9272
9273         Multi_debrief_server_framecount = 0;
9274 }
9275
9276 void multi_debrief_do_frame()
9277 {
9278         Multi_debrief_time += flFrametime;
9279
9280         // set the netgame state to be debriefing when appropriate
9281         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)){
9282                 Netgame.game_state = NETGAME_STATE_DEBRIEF;
9283                 send_netgame_update_packet();
9284         }
9285         
9286         // evaluate all server stuff
9287         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
9288                 multi_debrief_server_process();
9289         }                       
9290 }
9291
9292 void multi_debrief_close()
9293 {       
9294         if ( MULTIPLAYER_CLIENT && (Netgame.game_state == NETGAME_STATE_MISSION_SYNC) ){
9295                 gamesnd_play_iface( SND_COMMIT_PRESSED );
9296         }
9297 }
9298
9299 // handle optional mission loop
9300 void multi_maybe_set_mission_loop()
9301 {
9302         int cur = Campaign.current_mission;
9303         if (Campaign.missions[cur].has_mission_loop) {
9304                 SDL_assert(Campaign.loop_mission != CAMPAIGN_LOOP_MISSION_UNINITIALIZED);
9305         }
9306         bool require_repeat_mission = (Campaign.current_mission == Campaign.next_mission);
9307
9308         // check for (1) mission loop available, (2) dont have to repeat last mission
9309         if ( (Campaign.missions[cur].has_mission_loop && (Campaign.loop_mission != -1)) && !require_repeat_mission ) {
9310
9311                 char buffer[512];
9312                 debrief_assemble_optional_mission_popup_text(buffer, SDL_arraysize(buffer), Campaign.missions[cur].mission_loop_desc);
9313
9314                 int choice = popup(0 , 2, POPUP_NO, POPUP_YES, buffer);
9315                 if (choice == 1) {
9316                         Campaign.loop_enabled = 1;
9317                         Campaign.next_mission = Campaign.loop_mission;
9318                 }
9319         }
9320 }
9321
9322 // handle all cases for when the accept key is hit in a multiplayer debriefing
9323 void multi_debrief_accept_hit()
9324 {
9325         // if we already accepted, do nothing
9326         // but he may need to hit accept again after the server has left the game, so allow this
9327         if(Multi_debrief_accept_hit){
9328                 return;
9329         }
9330         
9331         // mark this so that we don't hit it again
9332         Multi_debrief_accept_hit = 1;
9333
9334         gamesnd_play_iface(SND_COMMIT_PRESSED);
9335
9336         // if the server has left the game, always just end the game. 
9337         if(Multi_debrief_server_left){
9338                 if(!multi_quit_game(PROMPT_ALL)){
9339                         Multi_debrief_server_left = 1;
9340                         Multi_debrief_accept_hit = 0;
9341                 } else {
9342                         Multi_debrief_server_left = 0;
9343                 }                       
9344         } else {                
9345                 // query the host and see if he wants to accept stats
9346                 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
9347                         // if we're on a tracker game, he gets no choice for storing stats
9348                         if (MULTI_IS_TRACKER_GAME) {
9349                                 // if not on standalone, send stats
9350                                 if (Net_player->flags & NETINFO_FLAG_AM_MASTER) {
9351                                         if ( !(Netgame.flags & NG_FLAG_STORED_MT_STATS) ) {
9352                                                 int stats_saved = multi_fs_tracker_store_stats();
9353
9354                                                 if (stats_saved) {
9355                                                         Netgame.flags |= NG_FLAG_STORED_MT_STATS;
9356                                                         send_netgame_update_packet();
9357                                                 } else {
9358                                                         send_store_stats_packet(0);
9359                                                 }
9360
9361 #ifndef MAKE_FS1
9362                                                 if (Netgame.type_flags & NG_TYPE_SW) {
9363                                                         multi_sw_report(stats_saved);
9364                                                 }
9365 #endif
9366                                         }
9367                                 }
9368
9369                                 multi_maybe_set_mission_loop();
9370                         } else {
9371                                 int res = popup(PF_TITLE | PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON | PF_IGNORE_ESC,3,XSTR("&Cancel",779),XSTR("&Accept",844),XSTR("&Toss",845),XSTR("(Continue Netgame)\nDo you wish to accept these stats?",846));
9372                 
9373                                 // evaluate the result
9374                                 switch(res){
9375                                 // undo the accept
9376                                 case -1:
9377                                 case 0:
9378                                         Multi_debrief_accept_hit = 0;
9379                                         return;
9380
9381                                 // set the accept code to be "not accepting"
9382                                 case 2 :
9383                                         multi_debrief_stats_toss();
9384                                         multi_maybe_set_mission_loop();
9385                                         break;
9386                                 
9387                                 // accept the stats and continue
9388                                 case 1 :
9389                                         multi_debrief_stats_accept();
9390                                         multi_maybe_set_mission_loop();
9391                                         break;
9392                                 }
9393                         }
9394                 }
9395
9396                 // set my netplayer state to be "debrief_accept", and be done with it
9397                 Net_player->state = NETPLAYER_STATE_DEBRIEF_ACCEPT;
9398                 send_netplayer_update_packet();
9399         }
9400 }
9401
9402 // handle all cases for when the escape key is hit in a multiplayer debriefing
9403 void multi_debrief_esc_hit()
9404 {
9405         int res;
9406
9407         // if the server has left
9408         if(Multi_debrief_server_left){
9409                 multi_quit_game(PROMPT_ALL);
9410                 return;
9411         }
9412         
9413         // display a popup
9414         if(Net_player->flags & NETINFO_FLAG_GAME_HOST){         
9415                 // if the stats have already been accepted
9416                 if((Multi_debrief_stats_accept_code != -1) || (MULTI_IS_TRACKER_GAME)){
9417                         // if not on standalone, maybe send stats
9418                         if ( (Net_player->flags & NETINFO_FLAG_AM_MASTER) && MULTI_IS_TRACKER_GAME ) {
9419                                 if ( !(Netgame.flags & NG_FLAG_STORED_MT_STATS) ) {
9420                                         int stats_saved = multi_fs_tracker_store_stats();
9421
9422                                         if (stats_saved) {
9423                                                 Netgame.flags |= NG_FLAG_STORED_MT_STATS;
9424                                                 send_netgame_update_packet();
9425                                         } else {
9426                                                 send_store_stats_packet(0);
9427                                         }
9428
9429 #ifndef MAKE_FS1
9430                                         if (Netgame.type_flags & NG_TYPE_SW) {
9431                                                 multi_sw_report(stats_saved);
9432                                         }
9433 #endif
9434                                 }
9435                         }
9436
9437                         multi_quit_game(PROMPT_HOST);
9438                 } else {
9439                         res = popup(PF_TITLE | PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON | PF_IGNORE_ESC,3,XSTR("&Cancel",779),XSTR("&Accept",844),XSTR("&Toss",845),XSTR("(Exit Netgame)\nDo you wish to accept these stats?",847));
9440                 
9441                         // evaluate the result
9442                         switch(res){                    
9443                         // undo the accept
9444                         case -1:
9445                         case 0:                         
9446                                 break;
9447
9448                         // set the accept code to be "not accepting"
9449                         case 2 :
9450                                 multi_debrief_stats_toss();
9451                                 multi_quit_game(PROMPT_NONE);
9452                                 break;
9453                                 
9454                         // accept the stats and continue
9455                         case 1 :
9456                                 multi_debrief_stats_accept();
9457                                 multi_quit_game(PROMPT_NONE);
9458                                 break;                                          
9459                         }               
9460                 }
9461         } else {                
9462                 // if the stats haven't been accepted yet, or this is a tracker game
9463                 if((Multi_debrief_stats_accept_code == -1) && !(MULTI_IS_TRACKER_GAME)){
9464                         res = popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON,2,XSTR("&Cancel",779),XSTR("&Leave",848),XSTR("Are you sure you want to leave the netgame before stats are stored?",849));
9465
9466                         // evaluate the result
9467                         if(res == 1){
9468                                 multi_quit_game(PROMPT_NONE);
9469                         }
9470                 }
9471                 // otherwise go through the normal endgame channels
9472                 else {
9473                         multi_quit_game(PROMPT_ALL);
9474                 }               
9475         }
9476 }
9477
9478 void multi_debrief_replay_hit()
9479 {
9480         // only the host should ever get here
9481         SDL_assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
9482
9483         // if the button was already pressed, do nothing
9484         if(Multi_debrief_accept_hit){
9485                 return;
9486         }
9487         
9488         // same as hittin the except button except no stats are kept
9489         Multi_debrief_accept_hit = 1;
9490
9491         // mark myself as being in the replay state so we know what to do next
9492         Net_player->state = NETPLAYER_STATE_DEBRIEF_REPLAY;
9493         send_netplayer_update_packet();
9494 }
9495
9496 // call this when the server has left and we would otherwise be saying "contact lost with server
9497 void multi_debrief_server_left()
9498 {
9499         // the server left
9500         Multi_debrief_server_left = 1;
9501
9502         // undo any "accept" hit so that clients can hit accept again to leave
9503         Multi_debrief_accept_hit = 0;
9504 }
9505
9506 void multi_debrief_stats_accept()
9507 {
9508         // don't do anything if we've already accepted
9509         if(Multi_debrief_stats_accept_code != -1){
9510                 return;
9511         }
9512         
9513         Multi_debrief_stats_accept_code = 1;
9514
9515         // if we're the host, and we're on a standalone, tell the standalone to begin the stats storing process
9516         if((Net_player->flags & NETINFO_FLAG_GAME_HOST) || (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
9517                 // send a packet to the players telling them to store their stats
9518                 send_store_stats_packet(1);
9519         } 
9520
9521         // add a chat line saying "stats have been accepted"
9522         multi_display_chat_msg(XSTR("<stats have been accepted>",850),0,0);
9523
9524         // NETLOG
9525         ml_string(NOX("Stats stored"));
9526 }
9527
9528 void multi_debrief_stats_toss()
9529 {
9530         // don't do anything if we've already accepted
9531         if(Multi_debrief_stats_accept_code != -1){
9532                 return;
9533         }
9534         
9535         Multi_debrief_stats_accept_code = 0;
9536
9537         // if we're the host, and we're on a standalone, tell everyone to "toss" stats
9538         if((Net_player->flags & NETINFO_FLAG_GAME_HOST) || (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
9539                 // send a packet to the players telling them to store their stats
9540                 send_store_stats_packet(0);
9541         } 
9542
9543         // add a chat line saying "stats have been accepted"
9544         multi_display_chat_msg(XSTR("<stats have been tossed>",851),0,0);
9545
9546         // NETLOG
9547         ml_string(NOX("Stats tossed"));
9548 }
9549
9550 int multi_debrief_stats_accept_code()
9551 {
9552         return Multi_debrief_stats_accept_code;
9553 }
9554
9555 void multi_debrief_server_process()
9556 {       
9557         int idx;
9558         int player_status,other_status;
9559
9560         Multi_debrief_server_framecount++;
9561
9562         // if we're > 10 seconds into the debrief and not everyone is here, try warping everyone out again
9563         if((Multi_debrief_time >= Multi_debrief_resend_time) && !multi_netplayer_state_check3(NETPLAYER_STATE_DEBRIEF, NETPLAYER_STATE_DEBRIEF_ACCEPT, NETPLAYER_STATE_DEBRIEF_REPLAY, 1)){
9564                 // find all players who are not in the debrief state and hit them with the endgame packet
9565                 for(idx=0; idx<MAX_PLAYERS; idx++){
9566                         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)) ){
9567                                 send_endgame_packet(&Net_players[idx]);
9568                         }
9569                 }
9570
9571                 // next check time
9572                 Multi_debrief_resend_time += 7.0f;
9573         }
9574
9575         // evaluate the status of all players in the game (0 == not ready, 1 == ready to continue, 2 == ready to replay)
9576         other_status = 1;
9577
9578         // check all players
9579         for(idx=0;idx<MAX_PLAYERS;idx++){
9580                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_HOST(Net_players[idx])){                                  
9581                         if(Net_players[idx].state != NETPLAYER_STATE_DEBRIEF_ACCEPT){
9582                                 other_status = 0;
9583                                 break;
9584                         }
9585                 }
9586         }
9587
9588         // if we haven't already reported TvT results
9589         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)){
9590                 multi_team_report();
9591                 Multi_debrief_reported_tvt = 1;
9592         }       
9593
9594         // if all other players are good to go, check the host
9595         if(other_status){
9596                 // if he is ready to continue
9597                 if(Netgame.host->state == NETPLAYER_STATE_DEBRIEF_ACCEPT){                                      
9598                         player_status = 1;                      
9599                 } 
9600                 // if he wants to replay the mission
9601                 else if(Netgame.host->state == NETPLAYER_STATE_DEBRIEF_REPLAY){
9602                         player_status = 2;                      
9603                 } 
9604                 // if he is not ready
9605                 else {
9606                         player_status = 0;
9607                 }
9608         }
9609         // if all players are _not_ good to go
9610         else {
9611                 player_status = 0;
9612         }
9613                 
9614         // if we're in the debriefing state in a campaign mode, process accordingly
9615         if(Netgame.campaign_mode == MP_CAMPAIGN){
9616                 multi_campaign_do_debrief(player_status);
9617         }
9618         // otherwise process as normal (looking for all players to be ready to go to the next mission
9619         else {
9620                 if(player_status == 1){
9621                         multi_flush_mission_stuff();
9622
9623                         // set the netgame state to be forming and continue
9624                         Netgame.game_state = NETGAME_STATE_FORMING;
9625                         send_netgame_update_packet();
9626
9627                         // move to the proper state
9628                         if(Game_mode & GM_STANDALONE_SERVER){
9629                                 gameseq_post_event(GS_EVENT_STANDALONE_MAIN);
9630                         } else {
9631                                 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
9632                         }
9633
9634                         multi_reset_timestamps();
9635                 } else if(player_status == 2){
9636                         multi_flush_mission_stuff();
9637
9638                         // tell everyone to move into the pre-briefing sync state
9639                         Netgame.game_state = NETGAME_STATE_MISSION_SYNC;
9640                         send_netgame_update_packet();
9641
9642                         // move back to the mission sync screen for the same mission again
9643                         Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
9644                         gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
9645
9646                         multi_reset_timestamps();
9647                 }
9648         }       
9649 }
9650
9651
9652 // -------------------------------------------------------------------------------------------------------------
9653 // 
9654 // MULTIPLAYER PASSWORD POPUP
9655 //
9656
9657 //XSTR:OFF
9658 // bitmaps defs
9659 static const char *Multi_pwd_bitmap_fname[GR_NUM_RESOLUTIONS] = {
9660         "Password",                     // GR_640
9661         "2_Password"            // GR_1024
9662 };
9663
9664 static const char *Multi_pwd_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
9665         "Password-M",           // GR_640
9666         "2_Password-M"          // GR_1024
9667 };
9668
9669 //XSTR:ON
9670
9671 // constants for coordinate lookup
9672 #define MPWD_X_COORD 0
9673 #define MPWD_Y_COORD 1
9674 #define MPWD_W_COORD 2
9675 #define MPWD_H_COORD 3
9676
9677 // button defs
9678 #define MULTI_PWD_NUM_BUTTONS                   2
9679 #define MPWD_CANCEL                     0
9680 #define MPWD_COMMIT                     1
9681
9682 // password area defs
9683 int Mpwd_coords[GR_NUM_RESOLUTIONS][4] = {
9684         { // GR_640
9685 #ifdef MAKE_FS1
9686                 147, 114, 343, 13
9687 #else
9688                 134, 112, 367, 15
9689 #endif
9690         },
9691         { // GR_1024
9692                 215, 190, 587, 24
9693         }
9694 };
9695
9696 UI_WINDOW Multi_pwd_window;                                                                                             // the window object for the join screen
9697 UI_INPUTBOX     Multi_pwd_passwd;                                                                                               // for Netgame.passwd
9698 int Multi_pwd_bitmap;                                                                                                           // the background bitmap
9699 int Multi_passwd_background = -1;
9700 int Multi_passwd_done = -1;
9701 int Multi_passwd_running = 0;
9702
9703 // password buttons
9704 ui_button_info Multi_pwd_buttons[GR_NUM_RESOLUTIONS][MULTI_PWD_NUM_BUTTONS] = {
9705         { // GR_640
9706 #ifdef MAKE_FS1
9707                 ui_button_info("PWB_00",        402,    134,    -1,     -1,     0),
9708                 ui_button_info("PWB_01",        450,    134,    -1,     -1,     1),
9709 #else
9710                 ui_button_info("PWB_00",        411,    151,    405,    141,    0),
9711                 ui_button_info("PWB_01",        460,    151,    465,    141,    1),
9712 #endif
9713         }, 
9714         { // GR_1024
9715                 ui_button_info("2_PWB_00",      659,    242,    649,    225,    0),
9716                 ui_button_info("2_PWB_01",      737,    242,    736,    225,    1),
9717         }, 
9718 };
9719
9720 // text
9721 #ifndef MAKE_FS1
9722 #define MULTI_PWD_NUM_TEXT                              3
9723
9724 UI_XSTR Multi_pwd_text[GR_NUM_RESOLUTIONS][MULTI_PWD_NUM_TEXT] = {
9725         { // GR_640
9726                 { "Cancel",                     387,    400,    141,    UI_XSTR_COLOR_GREEN, -1,        &Multi_pwd_buttons[0][MPWD_CANCEL].button},
9727                 { "Commit",                     1062,   455,    141,    UI_XSTR_COLOR_GREEN, -1,        &Multi_pwd_buttons[0][MPWD_COMMIT].button},
9728                 { "Enter Password",     1332,   149,    92,     UI_XSTR_COLOR_GREEN, -1,        NULL},
9729         },
9730         { // GR_1024
9731                 { "Cancel",                     387,    649,    225,    UI_XSTR_COLOR_GREEN, -1,        &Multi_pwd_buttons[1][MPWD_CANCEL].button},
9732                 { "Commit",                     1062,   736,    225,    UI_XSTR_COLOR_GREEN, -1,        &Multi_pwd_buttons[1][MPWD_COMMIT].button},
9733                 { "Enter Password",     1332,   239,    148,    UI_XSTR_COLOR_GREEN, -1,        NULL},
9734         }
9735 };
9736 #endif
9737
9738 // initialize all graphics, etc
9739 void multi_passwd_init()
9740 {
9741         int idx;
9742
9743         // store the background as it currently is
9744         Multi_passwd_background = gr_save_screen();     
9745         
9746         // create the interface window
9747         Multi_pwd_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
9748         Multi_pwd_window.set_mask_bmap(Multi_pwd_bitmap_mask_fname[gr_screen.res]);
9749
9750         // load the background bitmap
9751         Multi_pwd_bitmap = bm_load(Multi_pwd_bitmap_fname[gr_screen.res]);
9752         if(Multi_pwd_bitmap < 0){
9753                 // we failed to load the bitmap - this is very bad
9754                 Int3();
9755         }
9756                         
9757         // create the interface buttons
9758         for(idx=0; idx<MULTI_PWD_NUM_BUTTONS; idx++){
9759                 // create the object
9760                 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);
9761
9762                 // set the sound to play when highlighted
9763                 Multi_pwd_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
9764
9765                 // set the ani for the button
9766                 Multi_pwd_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pwd_buttons[gr_screen.res][idx].filename);
9767
9768                 // set the hotspot
9769                 Multi_pwd_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pwd_buttons[gr_screen.res][idx].hotspot);
9770         }       
9771
9772 #ifndef MAKE_FS1
9773         // add all xstrs
9774         for(idx=0; idx<MULTI_PWD_NUM_TEXT; idx++){
9775                 Multi_pwd_window.add_XSTR(&Multi_pwd_text[gr_screen.res][idx]);
9776         }
9777 #endif
9778         
9779         // create the password input box
9780         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);
9781         Multi_pwd_passwd.set_focus();
9782         
9783         // link the enter key to ACCEPT
9784         Multi_pwd_buttons[gr_screen.res][MPWD_COMMIT].button.set_hotkey(SDLK_RETURN);
9785
9786         Multi_passwd_done = -1;
9787         Multi_passwd_running = 1;
9788 }
9789
9790 // close down all graphics, etc
9791 void multi_passwd_close()
9792 {
9793         // unload any bitmaps
9794         bm_release(Multi_pwd_bitmap);           
9795                 
9796         // destroy the UI_WINDOW
9797         Multi_pwd_window.destroy();
9798
9799         // free up the saved background screen
9800         if(Multi_passwd_background >= 0){
9801                 gr_free_screen(Multi_passwd_background);        
9802                 Multi_passwd_background = -1;
9803         }
9804
9805         Multi_passwd_running = 0;
9806 }
9807
9808 // process any button pressed
9809 void multi_passwd_process_buttons()
9810 {
9811         // if the accept button was pressed
9812         if(Multi_pwd_buttons[gr_screen.res][MPWD_COMMIT].button.pressed()){
9813                 gamesnd_play_iface(SND_USER_SELECT);
9814                 Multi_passwd_done = 1;
9815         }
9816
9817         // if the cancel button was pressed
9818         if(Multi_pwd_buttons[gr_screen.res][MPWD_CANCEL].button.pressed()){
9819                 gamesnd_play_iface(SND_USER_SELECT);
9820                 Multi_passwd_done = 0;
9821         }
9822 }
9823
9824 // run the passwd popup
9825 void multi_passwd_do(char *passwd, const int max_passlen)
9826 {
9827         int k;
9828
9829         while(Multi_passwd_done == -1){ 
9830                 // set frametime and run background stuff
9831                 game_set_frametime(-1);
9832                 game_do_state_common(gameseq_get_state());
9833
9834                 k = Multi_pwd_window.process();
9835
9836                 // process any keypresses
9837                 switch(k){
9838                 case SDLK_ESCAPE :
9839                         // set this to indicate the user has cancelled for one reason or another
9840                         Multi_passwd_done = 0;
9841                         break;          
9842                 }       
9843
9844                 // if the input box text has changed
9845                 if(Multi_pwd_passwd.changed()){
9846                         SDL_strlcpy(passwd, "", max_passlen);
9847                         Multi_pwd_passwd.get_text(passwd);
9848                 }
9849
9850                 // process any button pressed
9851                 multi_passwd_process_buttons();
9852         
9853                 // draw the background, etc
9854                 gr_reset_clip();
9855                 gr_clear();
9856                 if(Multi_passwd_background >= 0){
9857                         gr_restore_screen(Multi_passwd_background);             
9858                 }
9859                 gr_set_bitmap(Multi_pwd_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
9860                 gr_bitmap(0,0);
9861                 Multi_pwd_window.draw();
9862                         
9863                 // flip the buffer
9864                 gr_flip();
9865         }
9866 }
9867
9868 // bring up the password string popup, fill in passwd (return 1 if accept was pressed, 0 if cancel was pressed)
9869 int multi_passwd_popup(char *passwd, const int max_plen)
9870 {
9871         // if the popup is already running for some reason, don't do anything
9872         if(Multi_passwd_running){
9873                 return 0;
9874         }
9875
9876         // initialize all graphics
9877         multi_passwd_init();
9878
9879         // run the popup
9880         multi_passwd_do(passwd, max_plen);
9881
9882         // shut everything down
9883         multi_passwd_close();
9884
9885         return Multi_passwd_done;
9886 }