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