]> icculus.org git repositories - taylor/freespace2.git/blob - src/network/multiui.cpp
use SDL_arraysize() instead of sizeof() where appropriate
[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
531 // -------------------------------------------------------------------------------------------------------------
532 // 
533 // MULTIPLAYER COMMON interface controls
534 //
535
536 // the common text info box stuff. This is lifted almost directly from Alans briefing code (minus the spiffy colored, scrolling
537 // text crap :)
538 int Multi_common_text_coords[GR_NUM_RESOLUTIONS][4] = {
539         { // GR_640
540 #ifdef MAKE_FS1
541                 47, 405, 374, 58
542 #else
543                 29, 396, 393, 76
544 #endif
545         },
546         { // GR_1024
547                 47, 634, 630, 122
548         }
549 };
550
551 int Multi_common_text_max_display[GR_NUM_RESOLUTIONS] = {
552 #ifdef MAKE_FS1
553         6,
554 #else
555         8,              // GR_640
556 #endif
557         12,     // GR_1024
558 };
559
560 #define MULTI_COMMON_TEXT_META_CHAR                             '$'
561 #define MULTI_COMMON_TEXT_MAX_LINE_LENGTH               100
562 #define MULTI_COMMON_TEXT_MAX_LINES                             20
563 #define MULTI_COMMON_MAX_TEXT                                           (MULTI_COMMON_TEXT_MAX_LINES * MULTI_COMMON_TEXT_MAX_LINE_LENGTH)
564
565 char Multi_common_all_text[MULTI_COMMON_MAX_TEXT];
566 char Multi_common_text[MULTI_COMMON_TEXT_MAX_LINES][MULTI_COMMON_TEXT_MAX_LINE_LENGTH];
567
568 int Multi_common_top_text_line = -1;            // where to start displaying from
569 int Multi_common_num_text_lines = 0;            // how many lines we have
570
571 void multi_common_scroll_text_up();
572 void multi_common_scroll_text_down();
573 void multi_common_move_to_bottom();
574 void multi_common_render_text();
575 void multi_common_split_text();
576
577 #define MAX_IP_STRING           255                             // maximum length for ip string
578
579 void multi_common_scroll_text_up()
580 {
581         Multi_common_top_text_line--;
582         if ( Multi_common_top_text_line < 0 ) {
583                 Multi_common_top_text_line = 0;
584                 if ( !mouse_down(MOUSE_LEFT_BUTTON) )
585                         gamesnd_play_iface(SND_GENERAL_FAIL);
586
587         } else {
588                 gamesnd_play_iface(SND_SCROLL);
589         }
590 }
591
592 void multi_common_scroll_text_down()
593 {
594         Multi_common_top_text_line++;
595         if ( (Multi_common_num_text_lines - Multi_common_top_text_line) < Multi_common_text_max_display[gr_screen.res] ) {
596                 Multi_common_top_text_line--;
597                 if ( !mouse_down(MOUSE_LEFT_BUTTON) ){
598                         gamesnd_play_iface(SND_GENERAL_FAIL);
599                 }
600         } else {
601                 gamesnd_play_iface(SND_SCROLL);
602         }
603 }
604
605 void multi_common_move_to_bottom()
606 {
607         // if there's nowhere to scroll down, do nothing
608         if(Multi_common_num_text_lines <= Multi_common_text_max_display[gr_screen.res]){
609                 return;
610         }
611                 
612         Multi_common_top_text_line = Multi_common_num_text_lines - Multi_common_text_max_display[gr_screen.res];
613 }
614
615 void multi_common_set_text(const char *str, int auto_scroll)
616 {
617         // make sure it fits
618         // store the entire string as well
619         if(strlen(str) > MULTI_COMMON_MAX_TEXT){
620                 return ;
621         } else {
622                 SDL_strlcpy(Multi_common_all_text, str, SDL_arraysize(Multi_common_all_text));
623         }
624         
625         // split the whole thing up
626         multi_common_split_text();
627
628         // scroll to the bottom if we're supposed to
629         if(auto_scroll){
630                 multi_common_move_to_bottom();
631         }
632 }
633
634 void multi_common_add_text(const char *str, int auto_scroll)
635 {
636         // make sure it fits
637         // store the entire string as well
638         if((strlen(str) + strlen(Multi_common_all_text)) > MULTI_COMMON_MAX_TEXT){
639                 return ;
640         } else {
641                 SDL_strlcat(Multi_common_all_text, str, SDL_arraysize(Multi_common_all_text));
642         }
643         
644         // split the whole thing up
645         multi_common_split_text();
646
647         // scroll to the bottom if we're supposed to
648         if(auto_scroll){
649                 multi_common_move_to_bottom();
650         }
651 }
652
653 void multi_common_split_text()
654 {
655         int     n_lines, i;
656         int     n_chars[MAX_BRIEF_LINES];
657         char    *p_str[MAX_BRIEF_LINES];
658
659         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);
660         SDL_assert(n_lines != -1);
661
662         for ( i = 0; i < n_lines; i++ ) {
663                 SDL_assert(n_chars[i] < MULTI_COMMON_TEXT_MAX_LINE_LENGTH);
664                 int len = min(n_chars[i] + 1, MULTI_COMMON_TEXT_MAX_LINE_LENGTH);
665                 SDL_strlcpy(Multi_common_text[i], p_str[i], len);
666                 Multi_common_text[i][n_chars[i]] = 0;
667                 drop_leading_white_space(Multi_common_text[i]);         
668         }
669
670         Multi_common_top_text_line = 0;
671         Multi_common_num_text_lines = n_lines;  
672 }
673
674 void multi_common_render_text()
675 {
676         int i, fh, line_count;
677
678         fh = gr_get_font_height();
679         
680         line_count = 0;
681         gr_set_color_fast(&Color_text_normal);
682         for ( i = Multi_common_top_text_line; i < Multi_common_num_text_lines; i++ ) {
683                 if ( line_count >= Multi_common_text_max_display[gr_screen.res] ){
684                         break;  
685                 }
686                 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]);              
687                 line_count++;
688         }
689
690         if ( (Multi_common_num_text_lines - Multi_common_top_text_line) > Multi_common_text_max_display[gr_screen.res] ) {
691                 gr_set_color_fast(&Color_bright_red);
692                 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));
693         }
694 }
695
696 // common notification messaging stuff
697 #define MULTI_COMMON_NOTIFY_TIME                3500
698 int Multi_common_join_y[GR_NUM_RESOLUTIONS] = {
699         375,            // GR_640
700         605             // GR_1024
701 };
702 int Multi_common_create_y[GR_NUM_RESOLUTIONS] = {
703         380,            // GR_640
704         610             // GR_1024
705 };
706
707 int Multi_common_jw_y[GR_NUM_RESOLUTIONS] = {
708         380,            // GR_640
709         610             // GR_1024
710 };
711
712 int Multi_common_msg_y[GR_NUM_RESOLUTIONS] = {
713         380,            // GR_640
714         610             // GR_1024
715 };
716
717 char Multi_common_notify_text[200];
718 int Multi_common_notify_stamp;
719
720 void multi_common_notify_init()
721 {
722         SDL_strlcpy(Multi_common_notify_text, "", SDL_arraysize(Multi_common_notify_text));
723         Multi_common_notify_stamp = -1;
724 }
725
726 // add a notification string, drawing appropriately depending on the state/screen we're in
727 void multi_common_add_notify(const char *str)
728 {
729         if(str){
730                 SDL_strlcpy(Multi_common_notify_text, str, SDL_arraysize(Multi_common_notify_text));
731                 Multi_common_notify_stamp = timestamp(MULTI_COMMON_NOTIFY_TIME);
732         }
733 }
734
735 // process/display notification messages
736 void multi_common_notify_do()
737 {
738         if(Multi_common_notify_stamp != -1){
739                 if(timestamp_elapsed(Multi_common_notify_stamp)){
740                         Multi_common_notify_stamp = -1;
741                 } else {
742                         int w,h,y;
743                         gr_get_string_size(&w,&h,Multi_common_notify_text);
744                         gr_set_color_fast(&Color_white);
745                         
746                         // determine where it should be placed based upon which screen we're on
747                         y = -1;
748                         switch(gameseq_get_state()){
749                         case GS_STATE_MULTI_JOIN_GAME :
750                                 y = Multi_common_join_y[gr_screen.res];
751                                 break;
752                         case GS_STATE_MULTI_HOST_SETUP :
753                                 y = Multi_common_create_y[gr_screen.res];
754                                 break;
755                         case GS_STATE_MULTI_CLIENT_SETUP :
756                                 y = Multi_common_jw_y[gr_screen.res];
757                                 break;
758                         case GS_STATE_MULTI_START_GAME :
759                                 y = Multi_common_msg_y[gr_screen.res];
760                                 break;                  
761                         }
762                         if(y != -1){
763                                 gr_string((gr_screen.max_w - w)/2, y, Multi_common_notify_text);
764                         }
765                 }
766         }
767 }
768
769 // common icon stuff
770 int Multi_common_icons[MULTI_NUM_COMMON_ICONS];
771 //XSTR:OFF
772 const char *Multi_common_icon_names[MULTI_NUM_COMMON_ICONS] = {
773         "DotRed",                               // voice denied
774         "DotGreen",                             // voice recording
775         "OvalGreen",                    // team 0
776         "OvalGreen01",                  // team 0 select
777         "OvalRed",                              // team 1
778         "OvalRed01",                    // team 1 select
779         "mp_coop",                              // coop mission
780         "mp_teams",                             // TvT mission
781         "mp_furball",                   // furball mission
782         "icon-volition",                // volition mission     
783         "icon-valid",                   // mission is valid
784 #ifndef MAKE_FS1
785         "cd"                                            // cd icon
786 #else
787         "cd",                                   // cd icon
788         "icon-silent"                   // SilentThreat
789 #endif
790 };
791 //XSTR:ON
792 // width and height of the icons
793 int Multi_common_icon_dims[MULTI_NUM_COMMON_ICONS][2] = {
794         {11, 11},                               // voice denied
795         {11, 11},                               // voice recording
796         {11, 11},                               // team 0
797         {11, 11},                               // team 0 select
798         {11, 11},                               // team 1
799         {11, 11},                               // team 1 select
800         {18, 11},                               // mp coop
801         {18, 11},                               // mp TvT
802         {18, 11},                               // mp furball
803         {9, 9},                                 // volition mission     
804         {8, 8},                                 // mission is valid
805 #ifndef MAKE_FS1
806         {8, 8}                                  // cd icon
807 #else
808         {8, 8},                                 // cd icon
809         {16, 7}                                 // silent threat
810 #endif
811 };
812
813 void multi_load_common_icons()
814 {
815         int idx;
816
817         // load all icons
818         for(idx=0; idx<MULTI_NUM_COMMON_ICONS; idx++){
819                 Multi_common_icons[idx] = -1;
820                 Multi_common_icons[idx] = bm_load(Multi_common_icon_names[idx]);
821         }
822 }
823
824 void multi_unload_common_icons()
825 {
826         int idx;
827
828         // unload all icons
829         for(idx=0; idx<MULTI_NUM_COMMON_ICONS; idx++){
830                 if(Multi_common_icons[idx] != -1){
831                         bm_unload(Multi_common_icons[idx]);
832                         Multi_common_icons[idx] = -1;
833                 }
834         }
835 }
836
837 // display any relevant voice status icons
838 void multi_common_voice_display_status()
839 {       
840         switch(multi_voice_status()){
841         // i have been denied the voice token
842         case MULTI_VOICE_STATUS_DENIED:
843                 if(Multi_common_icons[MICON_VOICE_DENIED] != -1){
844                         gr_set_bitmap(Multi_common_icons[MICON_VOICE_DENIED], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
845                         gr_bitmap(0,0);
846                 }
847                 break;
848
849         // i am currently recording
850         case MULTI_VOICE_STATUS_RECORDING:
851                 if(Multi_common_icons[MICON_VOICE_RECORDING] != -1){
852                         gr_set_bitmap(Multi_common_icons[MICON_VOICE_RECORDING], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
853                         gr_bitmap(0,0);
854                 }
855                 break;
856
857         // i am currently playing back sound
858         case MULTI_VOICE_STATUS_PLAYING:
859                 break;
860
861         // the system is currently idle
862         case MULTI_VOICE_STATUS_IDLE:
863                 break;
864         }
865 }
866
867 //XSTR:OFF
868 // palette initialization stuff
869 #define MULTI_COMMON_PALETTE_FNAME                      "InterfacePalette"
870 //XSTR:ON
871
872 int Multi_common_interface_palette = -1;
873
874 void multi_common_load_palette();
875 void multi_common_set_palette();
876 void multi_common_unload_palette();
877
878 // load in the palette if it doesn't already exist
879 void multi_common_load_palette()
880 {
881         if(Multi_common_interface_palette != -1){
882                 return;
883         }
884
885         Multi_common_interface_palette = bm_load(MULTI_COMMON_PALETTE_FNAME);
886         if(Multi_common_interface_palette == -1){
887                 nprintf(("Network","Error loading multiplayer common palette!\n"));
888         }
889 }
890
891 // set the common palette to be the active one
892 void multi_common_set_palette()
893 {
894         // if the palette is not loaded yet, do so now
895         if(Multi_common_interface_palette == -1){
896                 multi_common_load_palette();
897         }
898 }
899
900 // unload the bitmap palette
901 void multi_common_unload_palette()
902 {
903         if(Multi_common_interface_palette != -1){
904                 bm_unload(Multi_common_interface_palette);
905                 Multi_common_interface_palette = -1;
906         }
907 }
908
909 void multi_common_verify_cd()
910 {
911         Multi_has_cd = 1;
912 }
913
914
915 // -------------------------------------------------------------------------------------------------------------
916 // 
917 // MULTIPLAYER JOIN SCREEN 
918 //
919
920 #define MULTI_JOIN_NUM_BUTTONS  11
921
922 //XSTR:OFF
923 // bitmaps defs
924 #define MULTI_JOIN_PALETTE                              "InterfacePalette"
925
926 static const char *Multi_join_bitmap_fname[GR_NUM_RESOLUTIONS] = {
927         "MultiJoin",            // GR_640
928         "2_MultiJoin"                   // GR_1024
929 };
930
931 static const char *Multi_join_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
932         "MultiJoin-M",          // GR_640
933         "2_MultiJoin-M"         // GR_1024
934 };
935 //XSTR:ON
936
937 // slider
938 #ifndef MAKE_FS1
939 const char *Mj_slider_name[GR_NUM_RESOLUTIONS] = {
940         "slider",
941         "2_slider"
942 };
943 int Mj_slider_coords[GR_NUM_RESOLUTIONS][4] = {
944         { // GR_640
945                 2, 91, 15, 202
946         },
947         { // GR_1024
948                 8, 147, 30, 322
949         }
950 };
951 #endif
952
953 // button defs
954 #define MJ_SCROLL_UP                            0
955 #define MJ_SCROLL_DOWN                  1
956 #define MJ_REFRESH                              2
957 #define MJ_SCROLL_INFO_UP               3
958 #define MJ_SCROLL_INFO_DOWN     4
959 #define MJ_JOIN_OBSERVER                5
960 #define MJ_START_GAME                   6
961 #define MJ_CANCEL                                       7
962 #define MJ_HELP                                 8
963 #define MJ_OPTIONS                              9
964 #define MJ_ACCEPT                                       10
965
966 // uses MULTI_JOIN_REFRESH_TIME as its timestamp
967 int Multi_join_glr_stamp;
968
969 #define MULTI_JOIN_PING_TIME     15000        // how often we ping all the known servers
970 int Multi_join_ping_stamp;
971 UI_WINDOW Multi_join_window;                                                                                    // the window object for the join screen
972 UI_BUTTON Multi_join_select_button;                                                                     // for selecting list items
973 #ifndef MAKE_FS1
974 UI_SLIDER2 Multi_join_slider;                                                                                   // handy dandy slider
975 #endif
976 int Multi_join_bitmap;                                                                                                  // the background bitmap
977
978 ui_button_info Multi_join_buttons[GR_NUM_RESOLUTIONS][MULTI_JOIN_NUM_BUTTONS] = {
979         { // GR_640
980 #ifdef MAKE_FS1
981                 ui_button_info("MJ_00",         0,              85,             -1,     -1,     0),
982                 ui_button_info("MJ_01",         0,              125,    -1,     -1,     1),
983                 ui_button_info("MJ_03",         20,             324,    -1,     -1,     3),
984                 ui_button_info("MJ_04",         0,              399,    -1,     -1,     4),
985                 ui_button_info("MJ_05",         0,              436,    -1,     -1,     5),
986                 ui_button_info("MJ_15",         450,    323,    -1,     -1,     15),
987                 ui_button_info("MJ_10",         519,    323,    -1,     -1,     10),
988                 ui_button_info("MJ_06",         574,    323,    -1,     -1,     6),
989                 ui_button_info("MJ_08",         470,    427,    -1,     -1,     8),
990                 ui_button_info("MJ_09",         448,    454,    -1,     -1,     9),
991                 ui_button_info("MJ_07",         563,    411,    -1,     -1,     7),
992 #else
993                 ui_button_info( "MJ_00",        1,              57,     -1,     -1,     0 ),                                            // scroll up
994                 ui_button_info( "MJ_02",        1,              297,    -1,     -1,     2 ),                                            // scroll down
995                 ui_button_info( "MJ_03",        10,     338,    65,     364,    3 ),                                            // refresh
996                 ui_button_info( "MJ_04",        1,              405,    -1,     -1,     4 ),                                            // scroll info up
997                 ui_button_info( "MJ_05",        1,              446,    -1,     -1,     5 ),                                            // scroll info down
998                 ui_button_info( "MJ_06",        489,    339,    -1,     -1,     6 ),                                            // join as observer
999                 ui_button_info( "MJ_07",        538,    339,    -1,     -1,     7 ),                                            // create game
1000                 ui_button_info( "MJ_08",        583,    339,    588,    376,    8 ),                                            // cancel
1001                 ui_button_info( "MJ_09",        534,    426,    -1,     -1,     9 ),                                            // help
1002                 ui_button_info( "MJ_10",        534,    454,    -1,     -1,     10 ),                                           // options
1003                 ui_button_info( "MJ_11",        571,    426,    589,    416,    11 ),                                           // join
1004 #endif
1005         },
1006         { // GR_1024
1007                 ui_button_info( "2_MJ_00",      2,              92,     -1,     -1,     0 ),                                            // scroll up
1008                 ui_button_info( "2_MJ_02",      2,              475,    -1,     -1,     2 ),                                            // scroll down
1009                 ui_button_info( "2_MJ_03",      16,     541,    104,    582,    3 ),                                            // refresh
1010                 ui_button_info( "2_MJ_04",      2,              648,    -1,     -1,     4 ),                                            // scroll info up
1011                 ui_button_info( "2_MJ_05",      2,              713,    -1,     -1,     5 ),                                            // scroll info down
1012                 ui_button_info( "2_MJ_06",      783,    542,    -1,     -1,     6 ),                                            // join as observer
1013                 ui_button_info( "2_MJ_07",      861,    542,    -1,     -1,     7 ),                                            // create game
1014                 ui_button_info( "2_MJ_08",      933,    542,    588,    376,    8 ),                                            // cancel
1015                 ui_button_info( "2_MJ_09",      854,    681,    -1,     -1,     9 ),                                            // help
1016                 ui_button_info( "2_MJ_10",      854,    727,    -1,     -1,     10 ),                                           // options
1017                 ui_button_info( "2_MJ_11",      914,    681,    937,    668,    11 ),                                           // join
1018         }
1019 };
1020
1021 #ifndef MAKE_FS1
1022 #define MULTI_JOIN_NUM_TEXT                     13
1023
1024 UI_XSTR Multi_join_text[GR_NUM_RESOLUTIONS][MULTI_JOIN_NUM_TEXT] = {
1025         { // GR_640             
1026                 {"Refresh",                                                     1299, 65,       364,    UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_REFRESH].button},
1027                 {"Join as",                                                     1300,   476,    376,    UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_JOIN_OBSERVER].button},
1028                 {"Observer",                                            1301,   467,    385,    UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_JOIN_OBSERVER].button},
1029                 {"Create",                                                      1408,   535,    376,    UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_START_GAME].button}, 
1030                 {"Game",                                                                1302,   541,    385,    UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_START_GAME].button},
1031                 {"Cancel",                                                      387,    588,    376,    UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[0][MJ_CANCEL].button},      
1032                 {"Help",                                                                928,    479,    436,    UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_HELP].button},
1033                 {"Options",                                                     1036,   479,    460,    UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_OPTIONS].button},
1034                 {"Join",                                                                1303,   589,    416,    UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[0][MJ_ACCEPT].button},
1035                 {"Status",                                                      1304,   37,     37,     UI_XSTR_COLOR_GREEN,    -1, NULL},
1036                 {"Server",                                                      1305,   116,    37,     UI_XSTR_COLOR_GREEN, -1, NULL},
1037                 {"Players",                                                     1306,   471,    37,     UI_XSTR_COLOR_GREEN,    -1, NULL},
1038                 {"Ping",                                                                1307,   555,    37,     UI_XSTR_COLOR_GREEN, -1, NULL}
1039         },
1040         { // GR_1024            
1041                 {"Refresh",                                                     1299, 104,      582,    UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_REFRESH].button},
1042                 {"Join as",                                                     1300,   783,    602,    UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_JOIN_OBSERVER].button},
1043                 {"Observer",                                            1301,   774,    611,    UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_JOIN_OBSERVER].button},              
1044                 {"Create",                                                      1408,   868,    602,    UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_START_GAME].button},
1045                 {"Game",                                                                1302,   872,    611,    UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_START_GAME].button},
1046                 {"Cancel",                                                      387,    941,    602,    UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[1][MJ_CANCEL].button},
1047                 {"Help",                                                                928,    782,    699,    UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_HELP].button},
1048                 {"Options",                                                     1036,   782,    736,    UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_OPTIONS].button},
1049                 {"Join",                                                                1303,   937,    668,    UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[1][MJ_ACCEPT].button},
1050                 {"Status",                                                      1304,   60,     60,     UI_XSTR_COLOR_GREEN,    -1, NULL},
1051                 {"Server",                                                      1305,   186,    60,     UI_XSTR_COLOR_GREEN, -1, NULL},
1052                 {"Players",                                                     1306,   753,    60,     UI_XSTR_COLOR_GREEN,    -1, NULL},
1053                 {"Ping",                                                                1307,   888,    60,     UI_XSTR_COLOR_GREEN, -1, NULL}
1054         }
1055 };
1056 #endif
1057
1058 // constants for coordinate look ups
1059 #define MJ_X_COORD 0
1060 #define MJ_Y_COORD 1
1061 #define MJ_W_COORD 2
1062 #define MJ_H_COORD 3
1063
1064 #define MULTI_JOIN_SENT_WAIT            10000                                   // wait this long since a join was sent to allow another
1065 int Multi_join_sent_stamp;
1066
1067 // game information text areas
1068 int Mj_max_game_items[GR_NUM_RESOLUTIONS] = {
1069         28,                     // GR_640
1070         46                              // GR_1024
1071 };
1072
1073 int Mj_list_y[GR_NUM_RESOLUTIONS] = {
1074         53,                     // GR_640
1075         77                              // GR_1024
1076 };
1077
1078 int Mj_status_coords[GR_NUM_RESOLUTIONS][4] = {
1079         { // GR_640
1080                 34, 53, 55, 287
1081         },
1082         { // GR_1024
1083                 53, 77, 57, 459
1084         }
1085 };
1086
1087 int Mj_game_icon_coords[GR_NUM_RESOLUTIONS][3] = {
1088         { // GR_640
1089                 98, 53, 28
1090         },
1091         { // GR_1024
1092                 124, 77, 28
1093         }
1094 };      
1095
1096 int Mj_speed_coords[GR_NUM_RESOLUTIONS][4] = {
1097         { // GR_640
1098                 122, 53, 56, 261
1099         },
1100         { // GR_1024
1101                 152, 77, 56, 261
1102         }
1103 };      
1104
1105 int Mj_game_name_coords[GR_NUM_RESOLUTIONS][4] = {
1106         { // GR_640
1107                 186, 53, 280, 261
1108         },
1109         { // GR_1024
1110                 206, 77, 311, 261
1111         }
1112 };      
1113
1114 int Mj_players_coords[GR_NUM_RESOLUTIONS][4] = {
1115         { // GR_640
1116                 473, 53, 50, 261
1117         },
1118         { // GR_1024
1119                 748, 77, 67, 459
1120         }
1121 };      
1122
1123 int Mj_ping_coords[GR_NUM_RESOLUTIONS][4] = {
1124         { // GR_640
1125                 551, 53, 47, 261
1126         },
1127         { // GR_1024
1128                 880, 77, 48, 459
1129         }
1130 };      
1131
1132 // game speed labels
1133 #define MJ_NUM_SPEED_LABELS             5
1134 const char *Multi_join_speed_labels[MJ_NUM_SPEED_LABELS] = {
1135         "< 56k",
1136         "56k",
1137         "isdn",
1138         "cable",
1139         "t1/adsl+"
1140 };
1141 color *Multi_join_speed_colors[MJ_NUM_SPEED_LABELS] = {
1142         &Color_bright_red,
1143         &Color_bright_red,
1144         &Color_bright_green,
1145         &Color_bright_green,
1146         &Color_bright_green
1147 };
1148
1149 int Mj_cd_coords[GR_NUM_RESOLUTIONS] = {
1150         20, 30
1151 };
1152
1153 //XSTR:OFF
1154 // extents of the entire boundable game info region
1155 // NOTE : these numbers are completely empirical
1156 #define MJ_PING_GREEN                           160
1157 #define MJ_PING_YELLOW                          300
1158
1159 int Mj_list_area_coords[GR_NUM_RESOLUTIONS][4] = {
1160         { // GR_640
1161                 23, 53, 565, 279 
1162         },
1163         { // GR_1024
1164                 53, 76, 887, 461 
1165         }
1166 };      
1167
1168 // PXO channel filter
1169 #define MJ_PXO_FILTER_Y                         0
1170
1171 // special chars to indicate various status modes for servers
1172 #define MJ_CHAR_STANDALONE                      "*"
1173 #define MJ_CHAR_CAMPAIGN                        "c"
1174 //XSTR:ON
1175
1176 // various interface indices
1177 int Multi_join_list_start;                                                      // where to start displaying from
1178 active_game *Multi_join_list_start_item;                // a pointer to the corresponding active_game
1179 int Multi_join_list_selected;                                           // which item we have selected
1180 active_game *Multi_join_selected_item;                  // a pointer to the corresponding active_game
1181
1182 // use this macro to modify the list start
1183 #define MJ_LIST_START_INC()                     do { Multi_join_list_start++; } while(0);
1184 #define MJ_LIST_START_DEC()                     do { Multi_join_list_start--; } while(0);
1185 #define MJ_LIST_START_SET(vl)                   do { Multi_join_list_start = vl; } while(0);
1186
1187 // if we should be sending a join request at the end of the frame
1188 int Multi_join_should_send = -1;
1189
1190 // master tracker details
1191 int Multi_join_frame_count;                                             // keep a count of frames displayed
1192 int Multi_join_mt_tried_verify;                                 // already tried verifying the pilot with the tracker
1193
1194 // data stuff for auto joining a game
1195 #define MULTI_AUTOJOIN_JOIN_STAMP               2000
1196 #define MULTI_AUTOJOIN_QUERY_STAMP              2000
1197
1198 int Multi_did_autojoin;
1199 net_addr Multi_autojoin_addr;
1200 int Multi_autojoin_join_stamp;
1201 int Multi_autojoin_query_stamp;
1202
1203 // our join request
1204 join_request Multi_join_request;
1205
1206 // LOCAL function definitions
1207 void multi_join_check_buttons();
1208 void multi_join_button_pressed(int n);
1209 void multi_join_display_games();
1210 void multi_join_blit_game_status(active_game *game, int y);
1211 void multi_join_load_tcp_addrs();
1212 void multi_join_do_netstuff();
1213 void multi_join_ping_all();
1214 void multi_join_process_select();
1215 void multi_join_list_scroll_up();
1216 void multi_join_list_scroll_down();
1217 void multi_join_list_page_up();
1218 void multi_join_list_page_down();
1219 active_game *multi_join_get_game(int n);
1220 void multi_join_cull_timeouts();
1221 void multi_join_handle_item_cull(active_game *item, int item_index);
1222 void multi_join_send_join_request(int as_observer);
1223 void multi_join_create_game();
1224 void multi_join_blit_top_stuff();
1225 int multi_join_maybe_warn();
1226 int multi_join_warn_pxo();
1227 void multi_join_blit_protocol();
1228
1229 DCF(mj_make, "")
1230 {
1231         active_game ag, *newitem;;
1232         int idx;
1233
1234         dc_get_arg(ARG_INT);
1235         for(idx=0; idx<Dc_arg_int; idx++){
1236                 // stuff some fake info
1237                 memset(&ag, 0, sizeof(active_game));
1238                 SDL_snprintf(ag.name, SDL_arraysize(ag.name), "Game %d", idx);
1239                 ag.version = MULTI_FS_SERVER_VERSION;
1240                 ag.comp_version = MULTI_FS_SERVER_VERSION;
1241                 ag.server_addr.addr[0] = (char)idx;
1242                 ag.flags = (AG_FLAG_COOP | AG_FLAG_FORMING | AG_FLAG_STANDALONE);               
1243
1244                 // add the game
1245                 newitem = multi_update_active_games(&ag);
1246
1247                 // timestamp it so we get random timeouts
1248                 if(newitem != NULL){
1249                         // newitem->heard_from_timer = timestamp((int)frand_range(500.0f, 10000.0f));
1250                 }
1251         }       
1252 }
1253
1254 void multi_join_notify_new_game()
1255 {       
1256 #ifndef MAKE_FS1
1257         // reset the # of items 
1258         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);
1259         Multi_join_slider.force_currentItem(Multi_join_list_start);
1260 #endif
1261 }
1262
1263 int multi_join_autojoin_do()
1264 {
1265         // if we have an active game on the list, then return a positive value so that we
1266         // can join the game
1267         if ( Active_game_head && (Active_game_count > 0) ) {
1268                 Multi_join_selected_item = Active_game_head;
1269                 return 1;
1270         }
1271
1272         // send out a server_query again
1273         if ( timestamp_elapsed(Multi_autojoin_query_stamp) ) {
1274                 send_server_query(&Multi_autojoin_addr);
1275                 Multi_autojoin_query_stamp = timestamp(MULTI_AUTOJOIN_QUERY_STAMP);
1276         }
1277
1278         return -1;
1279 }
1280
1281 void multi_join_game_init()
1282 {
1283         int idx;
1284
1285         // do the multiplayer init stuff - multi_level_init() now does all net_player zeroing.
1286         // setup various multiplayer things
1287         SDL_assert( Game_mode & GM_MULTIPLAYER );
1288         SDL_assert( Net_player != NULL );
1289
1290         switch (Multi_options_g.protocol) {     
1291         case NET_IPX:
1292                 ADDRESS_LENGTH = IPX_ADDRESS_LENGTH;
1293                 PORT_LENGTH = IPX_PORT_LENGTH;
1294                 break;
1295
1296         case NET_TCP:
1297                 ADDRESS_LENGTH = IP_ADDRESS_LENGTH;             
1298                 PORT_LENGTH = IP_PORT_LENGTH;                   
1299                 break;
1300
1301         default :
1302                 Int3();
1303         } // end switch
1304         
1305         HEADER_LENGTH = 1;
1306
1307         memset( &Netgame, 0, sizeof(Netgame) );
1308
1309         multi_level_init();             
1310         Net_player->flags |= NETINFO_FLAG_DO_NETWORKING;        
1311         Net_player->player = Player;
1312         memcpy(&Net_player->p_info.addr,&Psnet_my_addr,sizeof(net_addr));
1313
1314         // check for the existence of a CD
1315         multi_common_verify_cd();
1316
1317         // load my local netplayer options
1318         multi_options_local_load(&Net_player->p_info.options, Net_player);      
1319
1320         game_flush();
1321
1322         // set the palette
1323 #ifdef MAKE_FS1
1324         common_set_interface_palette(MULTI_JOIN_PALETTE);
1325 #endif
1326
1327         // destroy any chatbox contents which previously existed (from another game)
1328         chatbox_clear();
1329
1330         // create the interface window
1331         Multi_join_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
1332         Multi_join_window.set_mask_bmap(Multi_join_bitmap_mask_fname[gr_screen.res]);
1333
1334         // load the background bitmap
1335         Multi_join_bitmap = bm_load(Multi_join_bitmap_fname[gr_screen.res]);
1336         if(Multi_join_bitmap < 0){
1337                 // we failed to load the bitmap - this is very bad
1338                 Int3();
1339         }
1340
1341         // intialize the endgame system
1342         multi_endgame_init();   
1343
1344         // initialize the common notification messaging
1345         multi_common_notify_init();
1346
1347         // initialize the common text area
1348         multi_common_set_text("");      
1349
1350         // load and use the common interface palette
1351         multi_common_load_palette();
1352         multi_common_set_palette();
1353
1354         // load the help overlay
1355         help_overlay_load(MULTI_JOIN_OVERLAY);
1356         help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1357         
1358         // do TCP and VMT specific initialization
1359         if(Multi_options_g.protocol == NET_TCP){                
1360                 // if this is a TCP (non tracker) game, we'll load up our default address list right now                
1361                 multi_join_load_tcp_addrs();            
1362         }       
1363
1364         // initialize any and all timestamps    
1365         Multi_join_glr_stamp = -1;
1366         Multi_join_ping_stamp = -1;
1367         Multi_join_sent_stamp = -1;
1368
1369         // reset frame count
1370         Multi_join_frame_count = 0;
1371
1372         // haven't tried to verify on the tracker yet.
1373         Multi_join_mt_tried_verify = 0;
1374
1375         // clear our all game lists to save hassles
1376         multi_join_clear_game_list();
1377
1378         // create the interface buttons
1379         for(idx=0; idx<MULTI_JOIN_NUM_BUTTONS; idx++){
1380                 // create the object
1381                 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);
1382
1383                 // set the sound to play when highlighted
1384                 Multi_join_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
1385
1386                 // set the ani for the button
1387                 Multi_join_buttons[gr_screen.res][idx].button.set_bmaps(Multi_join_buttons[gr_screen.res][idx].filename);
1388
1389                 // set the hotspot
1390                 Multi_join_buttons[gr_screen.res][idx].button.link_hotspot(Multi_join_buttons[gr_screen.res][idx].hotspot);
1391         }               
1392
1393 #ifndef MAKE_FS1
1394         // create all xstrs
1395         for(idx=0; idx<MULTI_JOIN_NUM_TEXT; idx++){
1396                 Multi_join_window.add_XSTR(&Multi_join_text[gr_screen.res][idx]);
1397         }
1398 #endif
1399
1400         Multi_join_should_send = -1;
1401
1402         // close any previously open chatbox
1403         chatbox_close();
1404
1405         // create the list item select button
1406         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);
1407         Multi_join_select_button.hide();
1408
1409 #ifndef MAKE_FS1
1410         // slider
1411         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);
1412 #endif
1413
1414         // if starting a network game, then go to the create game screen
1415         if ( Cmdline_start_netgame ) {
1416                 multi_join_create_game();               
1417         } else if ( Cmdline_connect_addr != NULL ) {
1418                 char *p;
1419                 short port_num;
1420                 int ip_addr;
1421
1422                 // joining a game.  Send a join request to the given IP address, and wait for the return.
1423                 memset( &Multi_autojoin_addr, 0, sizeof(net_addr) );
1424                 Multi_autojoin_addr.type = NET_TCP;
1425
1426                 // create the address, looking out for port number at the end
1427                 port_num = DEFAULT_GAME_PORT;
1428                 p = strrchr(Cmdline_connect_addr, ':');
1429                 if ( p ) {
1430                         *p = 0;
1431                         p++;
1432                         port_num = (short)atoi(p);
1433                 }
1434                 ip_addr = inet_addr(Cmdline_connect_addr);
1435                 memcpy(Multi_autojoin_addr.addr, &ip_addr, 4);
1436                 Multi_autojoin_addr.port = port_num;
1437
1438                 send_server_query(&Multi_autojoin_addr);
1439                 Multi_autojoin_query_stamp = timestamp(MULTI_AUTOJOIN_QUERY_STAMP);
1440                 Multi_did_autojoin = 0;
1441         }
1442 }
1443
1444 void multi_join_clear_game_list()
1445 {
1446         // misc data    
1447         Multi_join_list_selected = -1;  
1448         Multi_join_selected_item = NULL;        
1449         MJ_LIST_START_SET(-1);
1450         Multi_join_list_start_item = NULL;      
1451
1452         // free up the active game list
1453         multi_free_active_games();
1454
1455         // initialize the active game list
1456         Active_game_head = NULL;
1457         Active_game_count = 0;
1458 }
1459
1460 void multi_join_game_do_frame()
1461 {
1462         // check the status of our reliable socket.  If not valid, popup error and return to main menu
1463         // I put this code here to avoid nasty gameseq issues with states.  Also, we will have nice
1464         // background for the popup
1465         if ( !psnet_rel_check() ) {
1466                 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));
1467                 gameseq_post_event(GS_EVENT_MAIN_MENU);
1468                 return;
1469         }       
1470
1471         // return here since we will be moving to the next stage anyway -- I don't want to see the backgrounds of
1472         // all the screens for < 1 second for every screen we automatically move to.
1473         if ( Cmdline_start_netgame ) {
1474                 return;
1475         }
1476
1477         // when joining a network game, wait for the server query to come back, and then join the game
1478         if ( Cmdline_connect_addr != NULL ) {
1479                 int rval;
1480
1481                 if ( !Multi_did_autojoin ) {
1482                         rval = popup_till_condition(multi_join_autojoin_do, XSTR("&Cancel", 779), XSTR("Joining netgame", 1500) );
1483                         if ( rval == 0 ) {
1484                                 // cancel was hit.  Send the user back to the main hall
1485                                 gameseq_post_event(GS_EVENT_MAIN_MENU);
1486                                 Cmdline_connect_addr = NULL;            // reset this value.
1487                         }
1488
1489                         // when we get here, we have the data -- join the game.
1490                         multi_join_send_join_request(0);
1491                         Multi_autojoin_join_stamp = timestamp(MULTI_AUTOJOIN_JOIN_STAMP);
1492                         Multi_did_autojoin = 1;
1493                 }
1494
1495                 if ( timestamp_elapsed(Multi_autojoin_join_stamp) ) {
1496                         multi_join_send_join_request(0);
1497                         Multi_autojoin_join_stamp = timestamp(MULTI_AUTOJOIN_JOIN_STAMP);
1498                 }
1499                 return;
1500
1501         }       
1502
1503         // reset the should send var
1504         Multi_join_should_send = -1;
1505
1506         int k = Multi_join_window.process();
1507
1508         // process any keypresses
1509         switch(k){
1510         case SDLK_ESCAPE :
1511                 if(help_overlay_active(MULTI_JOIN_OVERLAY)){
1512                         help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1513                 } else {                
1514                         gameseq_post_event(GS_EVENT_MAIN_MENU);                 
1515                         gamesnd_play_iface(SND_USER_SELECT);
1516                 }
1517                 break;
1518
1519         // page up the game list
1520         case SDLK_PAGEUP:
1521                 multi_join_list_page_up();      
1522 #ifndef MAKE_FS1
1523                 Multi_join_slider.force_currentItem(Multi_join_list_start);
1524 #endif
1525                 break;
1526
1527         case SDLK_t:
1528                 multi_pinfo_popup(Net_player);
1529                 break;
1530
1531         // page down the game list
1532         case SDLK_PAGEDOWN:
1533                 multi_join_list_page_down();
1534 #ifndef MAKE_FS1
1535                 Multi_join_slider.force_currentItem(Multi_join_list_start);
1536 #endif
1537                 break;
1538
1539         // send out a ping-all
1540         case SDLK_p :
1541                 multi_join_ping_all();          
1542                 Multi_join_ping_stamp = timestamp(MULTI_JOIN_PING_TIME);
1543                 break;  
1544
1545         // shortcut to start a game     
1546         case SDLK_s :
1547                 multi_join_create_game();               
1548                 break;
1549
1550         // scroll the game list up
1551         case SDLK_UP:
1552                 multi_join_list_scroll_up();
1553 #ifndef MAKE_FS1
1554                 Multi_join_slider.force_currentItem(Multi_join_list_start);
1555 #endif
1556                 break;
1557
1558         // scroll the game list down
1559         case SDLK_DOWN:
1560                 multi_join_list_scroll_down();
1561 #ifndef MAKE_FS1
1562                 Multi_join_slider.force_currentItem(Multi_join_list_start);
1563 #endif
1564                 break;
1565         }       
1566
1567         if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
1568                 help_overlay_set_state(MULTI_JOIN_OVERLAY, 0);
1569         }
1570
1571         // do any network related stuff
1572         multi_join_do_netstuff(); 
1573
1574         // process any button clicks
1575         multi_join_check_buttons();
1576
1577         // process any list selection stuff
1578         multi_join_process_select();
1579
1580         // draw the background, etc
1581         gr_reset_clip();
1582         GR_MAYBE_CLEAR_RES(Multi_join_bitmap);
1583         if(Multi_join_bitmap != -1){            
1584                 gr_set_bitmap(Multi_join_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1585                 gr_bitmap(0,0);
1586         }
1587         Multi_join_window.draw();
1588
1589         // display the active games
1590         multi_join_display_games();
1591
1592         // display any text in the info area
1593         multi_common_render_text();
1594
1595         // display any pending notification messages
1596         multi_common_notify_do();
1597
1598         // blit the CD icon and any PXO filter stuff
1599         multi_join_blit_top_stuff();
1600
1601         // draw the help overlay
1602         help_overlay_maybe_blit(MULTI_JOIN_OVERLAY);
1603         
1604         // flip the buffer
1605         gr_flip();
1606
1607         // if we are supposed to be sending a join request
1608         if(Multi_join_should_send != -1){               
1609                 multi_join_send_join_request(Multi_join_should_send);
1610         }
1611         Multi_join_should_send = -1;
1612
1613         // increment the frame count
1614         Multi_join_frame_count++;
1615 }
1616
1617 void multi_join_game_close()
1618 {
1619         // unload any bitmaps
1620         if(!bm_unload(Multi_join_bitmap)){
1621                 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_join_bitmap_fname[gr_screen.res]));
1622         }
1623
1624         // unload the help overlay
1625         help_overlay_unload(MULTI_JOIN_OVERLAY);        
1626
1627         // free up the active game list
1628         multi_free_active_games();
1629         
1630         // destroy the UI_WINDOW
1631         Multi_join_window.destroy();
1632
1633 #ifdef MAKE_FS1
1634         common_free_interface_palette();
1635 #endif
1636 }
1637
1638 void multi_join_check_buttons()
1639 {
1640         int idx;
1641         for(idx=0;idx<MULTI_JOIN_NUM_BUTTONS;idx++){
1642                 // we only really need to check for one button pressed at a time, so we can break after 
1643                 // finding one.
1644                 if(Multi_join_buttons[gr_screen.res][idx].button.pressed()){
1645                         multi_join_button_pressed(idx);
1646                         break;
1647                 }
1648         }
1649 }
1650
1651 void multi_join_button_pressed(int n)
1652 {
1653         switch(n){
1654         case MJ_CANCEL :
1655                 // if we're player PXO, go back there   
1656                 gameseq_post_event(GS_EVENT_MAIN_MENU);         
1657                 gamesnd_play_iface(SND_USER_SELECT);            
1658                 break;
1659         case MJ_ACCEPT :
1660                 if(Active_game_count <= 0){
1661                         multi_common_add_notify(XSTR("No games found!",757));
1662                         gamesnd_play_iface(SND_GENERAL_FAIL);
1663                 } else if(Multi_join_list_selected == -1){
1664                         multi_common_add_notify(XSTR("No game selected!",758));
1665                         gamesnd_play_iface(SND_GENERAL_FAIL);
1666                 } else if((Multi_join_sent_stamp != -1) && !timestamp_elapsed(Multi_join_sent_stamp)){
1667                         multi_common_add_notify(XSTR("Still waiting on previous join request!",759));
1668                         gamesnd_play_iface(SND_GENERAL_FAIL);
1669                 } else {                        
1670                         // otherwise, if he's already played PXO games, warn him        
1671                         /*
1672                         if(Player->flags & PLAYER_FLAGS_HAS_PLAYED_PXO){
1673                                 if(!multi_join_warn_pxo()){
1674                                         break;
1675                                 }                       
1676                         }
1677                         */
1678
1679                         // send the join request here
1680                         SDL_assert(Multi_join_selected_item != NULL);
1681
1682                         // send a join request packet
1683                         Multi_join_should_send = 0;                     
1684                         
1685                         gamesnd_play_iface(SND_COMMIT_PRESSED);
1686                 }
1687                 break;
1688
1689         // help overlay
1690         case MJ_HELP:
1691                 if(!help_overlay_active(MULTI_JOIN_OVERLAY)){
1692                         help_overlay_set_state(MULTI_JOIN_OVERLAY,1);
1693                 } else {
1694                         help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1695                 }
1696                 break;
1697
1698         // scroll the game list up
1699         case MJ_SCROLL_UP:
1700                 multi_join_list_scroll_up();
1701 #ifndef MAKE_FS1
1702                 Multi_join_slider.force_currentItem(Multi_join_list_start);
1703 #endif
1704                 break;
1705
1706         // scroll the game list down
1707         case MJ_SCROLL_DOWN:
1708                 multi_join_list_scroll_down();
1709 #ifndef MAKE_FS1
1710                 Multi_join_slider.force_currentItem(Multi_join_list_start);
1711 #endif
1712                 break;
1713
1714         // scroll the info text box up
1715         case MJ_SCROLL_INFO_UP:
1716                 multi_common_scroll_text_up();
1717                 break;
1718
1719         // scroll the info text box down
1720         case MJ_SCROLL_INFO_DOWN:
1721                 multi_common_scroll_text_down();
1722                 break;
1723
1724         // go to the options screen
1725         case MJ_OPTIONS:
1726                 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
1727                 break;
1728
1729         // go to the start game screen  
1730         case MJ_START_GAME:
1731                 multi_join_create_game();               
1732                 break;
1733
1734         // refresh the game/server list
1735         case MJ_REFRESH:                
1736                 gamesnd_play_iface(SND_USER_SELECT);
1737                 broadcast_game_query();         
1738                 break;
1739
1740         // join a game as an observer
1741         case MJ_JOIN_OBSERVER:
1742                 if(Active_game_count <= 0){
1743                         multi_common_add_notify(XSTR("No games found!",757));
1744                         gamesnd_play_iface(SND_GENERAL_FAIL);
1745                 } else if(Multi_join_list_selected == -1){
1746                         multi_common_add_notify(XSTR("No game selected!",758));
1747                         gamesnd_play_iface(SND_GENERAL_FAIL);
1748                 } else if((Multi_join_sent_stamp != -1) && !timestamp_elapsed(Multi_join_sent_stamp)){
1749                         multi_common_add_notify(XSTR("Still waiting on previous join request!",759));
1750                         gamesnd_play_iface(SND_GENERAL_FAIL);
1751                 } else {                        
1752                         // send the join request here
1753                         SDL_assert(Multi_join_selected_item != NULL);
1754
1755                         Multi_join_should_send = 1;             
1756
1757                         gamesnd_play_iface(SND_COMMIT_PRESSED);
1758                 }
1759                 break;
1760
1761         default :
1762                 multi_common_add_notify(XSTR("Not implemented yet!",760));
1763                 gamesnd_play_iface(SND_GENERAL_FAIL);
1764                 break;
1765         }
1766 }
1767
1768 // display all relevant info for active games
1769 void multi_join_display_games()
1770 {
1771         active_game *moveup = Multi_join_list_start_item;       
1772         char str[200];          
1773         int w,h;
1774         int con_type;
1775         int y_start = Mj_list_y[gr_screen.res];
1776         int count = 0;
1777         
1778         if(moveup != NULL){
1779                 do {                    
1780                         // blit the game status (including text and type icon)
1781                         multi_join_blit_game_status(moveup,y_start);                    
1782                         
1783                         // get the connection type
1784                         con_type = (moveup->flags & AG_FLAG_CONNECTION_SPEED_MASK) >> AG_FLAG_CONNECTION_BIT;
1785                         if((con_type > 4) || (con_type < 0)){
1786                                 con_type = 0;
1787                         }
1788
1789                         // display the connection speed
1790                         str[0] = '\0';
1791                         SDL_strlcpy(str, Multi_join_speed_labels[con_type], SDL_arraysize(str));
1792                         gr_set_color_fast(Multi_join_speed_colors[con_type]);
1793                         gr_string(Mj_speed_coords[gr_screen.res][MJ_X_COORD], y_start, str);
1794
1795                         // we'll want to have different colors for highlighted items, etc.
1796                         if(moveup == Multi_join_selected_item){
1797                                 gr_set_color_fast(&Color_text_selected);
1798                         } else {
1799                                 gr_set_color_fast(&Color_text_normal);
1800                         }
1801
1802                         // display the game name, adding appropriate status chars
1803                         str[0] = '\0';
1804                         if(moveup->flags & AG_FLAG_STANDALONE){
1805                                 SDL_strlcat(str, MJ_CHAR_STANDALONE, SDL_arraysize(str));
1806                         }
1807                         if(moveup->flags & AG_FLAG_CAMPAIGN){
1808                                 SDL_strlcat(str, MJ_CHAR_CAMPAIGN, SDL_arraysize(str));
1809                         }
1810
1811                         // tack on the actual server name                       
1812                         SDL_strlcat(str, " ", SDL_arraysize(str));
1813                         SDL_strlcat(str, moveup->name, SDL_arraysize(str));
1814                         if(strlen(moveup->mission_name) > 0){
1815                                 SDL_strlcat(str, " / ", SDL_arraysize(str));
1816                                 SDL_strlcat(str, moveup->mission_name, SDL_arraysize(str));
1817                         } 
1818
1819                         // make sure the string fits in the display area and draw it
1820                         gr_force_fit_string(str,200,Mj_game_name_coords[gr_screen.res][MJ_W_COORD]);                    
1821                         gr_string(Mj_game_name_coords[gr_screen.res][MJ_X_COORD],y_start,str);
1822
1823                         // display the ping time
1824                         if(moveup->ping.ping_avg > 0){
1825                                 if(moveup->ping.ping_avg > 1000){
1826                                         gr_set_color_fast(&Color_bright_red);
1827                                         SDL_strlcpy(str, XSTR("> 1 sec",761), SDL_arraysize(str));
1828                                 } else {
1829                                         // set the appropriate ping time color indicator
1830                                         if(moveup->ping.ping_avg > MJ_PING_YELLOW){
1831                                                 gr_set_color_fast(&Color_bright_red);
1832                                         } else if(moveup->ping.ping_avg > MJ_PING_YELLOW){
1833                                                 gr_set_color_fast(&Color_bright_yellow);
1834                                         } else {
1835                                                 gr_set_color_fast(&Color_bright_green);
1836                                         }
1837
1838                                         SDL_snprintf(str, SDL_arraysize(str), "%d%s", moveup->ping.ping_avg, XSTR(" ms",762));
1839                                 }
1840
1841                                 gr_string(Mj_ping_coords[gr_screen.res][MJ_X_COORD],y_start,str);
1842                         }
1843
1844                         // display the number of players (be sure to center it)
1845                         if(moveup == Multi_join_selected_item){
1846                                 gr_set_color_fast(&Color_text_selected);
1847                         } else {
1848                                 gr_set_color_fast(&Color_text_normal);
1849                         }
1850                         SDL_snprintf(str, SDL_arraysize(str), "%d", moveup->num_players);
1851                         gr_get_string_size(&w,&h,str);
1852                         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);                     
1853
1854                         count++;
1855                         y_start += 10;
1856                         moveup = moveup->next;
1857                 } while((moveup != Active_game_head) && (count < Mj_max_game_items[gr_screen.res]));
1858         }
1859         // if there are no items on the list, display this info
1860         else {
1861                 gr_set_color_fast(&Color_bright);
1862                 gr_string(Mj_game_name_coords[gr_screen.res][MJ_X_COORD] - 30,y_start,XSTR("<No game servers found>",763));
1863         }
1864 }
1865
1866 void multi_join_blit_game_status(active_game *game, int y)
1867 {
1868         int draw,str_w;
1869         char status_text[25];
1870
1871         // blit the proper icon
1872         draw = 0;       
1873         switch( game->flags & AG_FLAG_TYPE_MASK ){
1874         // coop game
1875         case AG_FLAG_COOP:
1876                 if(Multi_common_icons[MICON_COOP] != -1){
1877                         gr_set_bitmap(Multi_common_icons[MICON_COOP], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1878                         draw = 1;
1879                 }
1880                 break;  
1881         
1882         // team vs. team game
1883         case AG_FLAG_TEAMS:
1884                 if(Multi_common_icons[MICON_TVT] != -1){
1885                         gr_set_bitmap(Multi_common_icons[MICON_TVT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1886                         draw = 1;
1887                 } 
1888                 break;  
1889
1890         // dogfight game
1891 #ifndef MAKE_FS1
1892         case AG_FLAG_DOGFIGHT:
1893                 if(Multi_common_icons[MICON_DOGFIGHT] != -1){
1894                         gr_set_bitmap(Multi_common_icons[MICON_DOGFIGHT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1895                         draw = 1;
1896                 } 
1897                 break;  
1898 #endif
1899         }
1900         // if we're supposed to draw a bitmap
1901         if(draw){
1902                 gr_bitmap(Mj_game_icon_coords[gr_screen.res][MJ_X_COORD],y-1);
1903         }
1904
1905         // blit the proper status text
1906         memset(status_text,0,25);
1907
1908         switch( game->flags & AG_FLAG_STATE_MASK ){
1909         case AG_FLAG_FORMING:
1910                 gr_set_color_fast(&Color_bright_green);
1911                 SDL_strlcpy(status_text, XSTR("Forming", 764), SDL_arraysize(status_text));
1912                 break;
1913         case AG_FLAG_BRIEFING:
1914                 gr_set_color_fast(&Color_bright_red);
1915                 SDL_strlcpy(status_text, XSTR("Briefing", 765), SDL_arraysize(status_text));
1916                 break;
1917         case AG_FLAG_DEBRIEF:
1918                 gr_set_color_fast(&Color_bright_red);
1919                 SDL_strlcpy(status_text, XSTR("Debrief", 766), SDL_arraysize(status_text));
1920                 break;
1921         case AG_FLAG_PAUSE:
1922                 gr_set_color_fast(&Color_bright_red);
1923                 SDL_strlcpy(status_text, XSTR("Paused", 767), SDL_arraysize(status_text));
1924                 break;
1925         case AG_FLAG_IN_MISSION:
1926                 gr_set_color_fast(&Color_bright_red);
1927                 SDL_strlcpy(status_text, XSTR("Playing", 768), SDL_arraysize(status_text));
1928                 break;
1929         default:
1930                 gr_set_color_fast(&Color_bright);
1931                 SDL_strlcpy(status_text, XSTR("Unknown", 769), SDL_arraysize(status_text));
1932                 break;
1933         }               
1934         gr_get_string_size(&str_w,NULL,status_text);
1935         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);
1936 }
1937
1938 // load in a list of active games from our tcp.cfg file
1939 void multi_join_load_tcp_addrs()
1940 {
1941         char line[MAX_IP_STRING];
1942         net_addr addr;  
1943         server_item *item;
1944         CFILE *file = NULL;
1945
1946         // attempt to open the ip list file
1947         file = cfopen(IP_CONFIG_FNAME,"rt",CFILE_NORMAL,CF_TYPE_DATA);  
1948         if(file == NULL){
1949                 nprintf(("Network","Error loading tcp.cfg file!\n"));
1950                 return;
1951         }
1952
1953         // free up any existing server list
1954         multi_free_server_list();
1955
1956         // read in all the strings in the file
1957         while(!cfeof(file)){
1958                 line[0] = '\0';
1959                 cfgets(line,MAX_IP_STRING,file);
1960
1961                 // strip off any newline character
1962                 if(line[strlen(line) - 1] == '\n'){
1963                         line[strlen(line) - 1] = '\0';
1964                 }
1965                 
1966                 // empty lines don't get processed
1967                 if( (line[0] == '\0') || (line[0] == '\n') ){
1968                         continue;
1969                 }
1970
1971                 if ( !psnet_is_valid_ip_string(line) ) {
1972                         nprintf(("Network","Invalid ip string (%s)\n",line));
1973                 } else {                         
1974                         // copy the server ip address
1975                         memset(&addr,0,sizeof(net_addr));
1976                         addr.type = NET_TCP;
1977                         psnet_string_to_addr(&addr, line, SDL_arraysize(line));
1978                         if ( addr.port == 0 ){
1979                                 addr.port = DEFAULT_GAME_PORT;
1980                         }
1981
1982                         // create a new server item on the list
1983                         item = multi_new_server_item();
1984                         if(item != NULL){
1985                                 memcpy(&item->server_addr,&addr,sizeof(net_addr));
1986                         }                       
1987                 }
1988         }
1989
1990         cfclose(file);  
1991 }
1992
1993 // do stuff like pinging servers, sending out requests, etc
1994 void multi_join_do_netstuff()
1995 {
1996         // handle game query stuff
1997         if(Multi_join_glr_stamp == -1){
1998                 broadcast_game_query();
1999                 
2000                 if(Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST){
2001                         Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME_LOCAL);
2002                 } else {
2003                         Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME);
2004                 }
2005         } 
2006         // otherwise send out game query and restamp
2007         else if(timestamp_elapsed(Multi_join_glr_stamp)){                       
2008                 broadcast_game_query();
2009
2010                 if(Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST){
2011                         Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME_LOCAL);
2012                 } else {
2013                         Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME);
2014                 }               
2015         }
2016
2017         // check to see if we've been accepted.  If so, put up message saying so
2018         if ( Net_player->flags & (NETINFO_FLAG_ACCEPT_INGAME|NETINFO_FLAG_ACCEPT_CLIENT|NETINFO_FLAG_ACCEPT_HOST|NETINFO_FLAG_ACCEPT_OBSERVER) ) {
2019                 multi_common_add_notify(XSTR("Accepted.  Waiting for player data.",770));
2020         }
2021
2022         // check to see if any join packets we have sent have timed out
2023         if((Multi_join_sent_stamp != -1) && (timestamp_elapsed(Multi_join_sent_stamp))){
2024                 Multi_join_sent_stamp = -1;
2025                 multi_common_add_notify(XSTR("Join request timed out!",771));
2026         }
2027
2028         // check to see if we should be pinging everyone
2029         if((Multi_join_ping_stamp == -1) || (timestamp_elapsed(Multi_join_ping_stamp))){
2030                 multi_join_ping_all();
2031                 Multi_join_ping_stamp = timestamp(MULTI_JOIN_PING_TIME);
2032         } 
2033
2034         // cull timeouts
2035         multi_join_cull_timeouts();
2036 }
2037
2038 // evaluate a returned pong.
2039 void multi_join_eval_pong(net_addr *addr, fix pong_time)
2040 {       
2041 //      int found;
2042         active_game *moveup = Active_game_head;
2043
2044 //      found = 0;
2045         if(moveup != NULL){
2046                 do {                            
2047                         if(psnet_same(&moveup->server_addr,addr)){
2048                         //      found = 1;
2049                                 multi_ping_eval_pong(&moveup->ping);
2050                                 
2051                                 break;
2052                         } else {
2053                                 moveup = moveup->next;
2054                         }
2055                 } while(moveup != Active_game_head);
2056         }       
2057
2058         // update the game's ping
2059         /*
2060         if(found && (moveup->ping_end > moveup->ping_start)){           
2061                 moveup->ping_time = f2fl(moveup->ping_end - moveup->ping_start);
2062                 moveup->ping_start = -1;
2063                 moveup->ping_end = -1;
2064         }
2065         */
2066 }
2067
2068 // ping all the server on the list
2069 void multi_join_ping_all()
2070 {
2071         active_game *moveup = Active_game_head; 
2072         
2073         if(moveup != NULL){
2074                 do {
2075                         /*
2076                         moveup->ping_start = timer_get_fixed_seconds();
2077                         moveup->ping_end = -1;
2078                         send_ping(&moveup->server_addr);
2079                         */
2080                         multi_ping_send(&moveup->server_addr,&moveup->ping);
2081         
2082                         moveup = moveup->next;
2083                 } while(moveup != Active_game_head);
2084         }
2085 }
2086
2087 void multi_join_process_select()
2088 {
2089         // if we don't have anything selected and there are items on the list - select the first one
2090         if((Multi_join_list_selected == -1) && (Active_game_count > 0)){
2091                 Multi_join_list_selected = 0;
2092                 Multi_join_selected_item = multi_join_get_game(0);                              
2093                 MJ_LIST_START_SET(0);
2094                 Multi_join_list_start_item = Multi_join_selected_item;
2095
2096                 // send a mission description request to this guy               
2097                 send_netgame_descript_packet(&Multi_join_selected_item->server_addr,0);
2098                 multi_common_set_text("");
2099
2100                 // I sure hope this doesn't happen
2101                 SDL_assert(Multi_join_selected_item != NULL);           
2102                 return;
2103         } 
2104         // otherwise see if he's clicked on an item
2105         else if(Multi_join_select_button.pressed() && (Active_game_count > 0)){          
2106                 int y,item;             
2107                 Multi_join_select_button.get_mouse_pos(NULL,&y);
2108                 item = y / 10;
2109                 if(item + Multi_join_list_start < Active_game_count){           
2110                         gamesnd_play_iface(SND_IFACE_MOUSE_CLICK);
2111
2112                         Multi_join_list_selected = item + Multi_join_list_start;
2113                         Multi_join_selected_item = multi_join_get_game(Multi_join_list_selected);
2114                         
2115                         // I sure hope this doesn't happen
2116                         SDL_assert(Multi_join_selected_item != NULL);
2117
2118                         // send a mission description request to this guy
2119                         send_netgame_descript_packet(&Multi_join_selected_item->server_addr,0);
2120                         multi_common_set_text("");                      
2121                 }               
2122         }
2123
2124         // if he's double clicked, then select it and accept            
2125         if(Multi_join_select_button.double_clicked()){                  
2126                 int y,item;             
2127                 Multi_join_select_button.get_mouse_pos(NULL,&y);
2128                 item = y / 10;
2129                 if(item == Multi_join_list_selected){           
2130                         multi_join_button_pressed(MJ_ACCEPT);
2131                 }
2132         }
2133 }
2134
2135 // return game n (0 based index)
2136 active_game *multi_join_get_game(int n)
2137 {
2138         active_game *moveup = Active_game_head;
2139
2140         if(moveup != NULL){
2141                 if(n == 0){
2142                         return moveup;
2143                 } else {
2144                         int count = 1;
2145                         moveup = moveup->next;
2146                         while((moveup != Active_game_head) && (count != n)){
2147                                 moveup = moveup->next;
2148                                 count++;
2149                         }
2150                         if(moveup == Active_game_head){
2151                                 nprintf(("Network","Warning, couldn't find game item %d!\n",n));
2152                                 return NULL;
2153                         } else {
2154                                 return moveup;
2155                         }
2156                 }
2157         } 
2158         return NULL;
2159 }
2160
2161 // scroll through the game list
2162 void multi_join_list_scroll_up()
2163 {
2164         // if we're not at the beginning of the list, scroll up 
2165         if(Multi_join_list_start_item != Active_game_head){
2166                 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2167                 
2168                 MJ_LIST_START_DEC();            
2169
2170                 gamesnd_play_iface(SND_SCROLL);
2171         } else {
2172                 gamesnd_play_iface(SND_GENERAL_FAIL);
2173         }
2174 }
2175
2176 // scroll through the game list
2177 void multi_join_list_scroll_down()
2178 {
2179         if((Active_game_count - Multi_join_list_start) > Mj_max_game_items[gr_screen.res]){
2180                 Multi_join_list_start_item = Multi_join_list_start_item->next;
2181
2182                 MJ_LIST_START_INC();            
2183
2184                 gamesnd_play_iface(SND_SCROLL);
2185         } else {
2186                 gamesnd_play_iface(SND_GENERAL_FAIL);
2187         }
2188 }
2189
2190 void multi_join_list_page_up()
2191 {
2192         // in this case, just set us to the beginning of the list
2193         if((Multi_join_list_start - Mj_max_game_items[gr_screen.res]) < 0){
2194                 Multi_join_list_start_item = Active_game_head;          
2195
2196                 MJ_LIST_START_SET(0);
2197
2198                 gamesnd_play_iface(SND_SCROLL);         
2199         } else {
2200                 // otherwise page the whole thing up
2201                 int idx;
2202                 for(idx=0; idx<Mj_max_game_items[gr_screen.res]; idx++){
2203                         Multi_join_list_start_item = Multi_join_list_start_item->prev;
2204
2205                         MJ_LIST_START_DEC();                    
2206                 }
2207                 gamesnd_play_iface(SND_SCROLL);
2208         }
2209 }
2210
2211 void multi_join_list_page_down()
2212 {       
2213         int count = 0;
2214
2215         // page the whole thing down            
2216         while((count < Mj_max_game_items[gr_screen.res]) && ((Active_game_count - Multi_join_list_start) > Mj_max_game_items[gr_screen.res])){
2217                 Multi_join_list_start_item = Multi_join_list_start_item->next;
2218                 MJ_LIST_START_INC();                    
2219
2220                 // next 
2221                 count++;
2222         }       
2223         gamesnd_play_iface(SND_SCROLL);  
2224 }
2225
2226 void multi_join_cull_timeouts()
2227 {
2228         active_game *backup;
2229         int count;
2230         active_game *moveup = Active_game_head;
2231
2232         // traverse through the entire list if any items exist  
2233         count = 0;
2234         if(moveup != NULL){
2235                 do {
2236                         if((moveup->heard_from_timer != -1) && (timestamp_elapsed(moveup->heard_from_timer))){
2237                                 Active_game_count--;
2238
2239                                 // if this is the head of the list
2240                                 if(moveup == Active_game_head){                                 
2241                                         // if this is the _only_ item on the list
2242                                         if(moveup->next == Active_game_head){
2243                                                 // handle any gui details related to deleting this item
2244                                                 multi_join_handle_item_cull(Active_game_head, count);
2245                                                 
2246                                                 free(Active_game_head);
2247                                                 Active_game_head = NULL;                                                
2248                                                 return;
2249                                         } 
2250                                         // if there are other items on the list
2251                                         else {
2252                                                 // handle any gui details related to deleting this item
2253                                                 multi_join_handle_item_cull(moveup, count);
2254                                                 
2255                                                 Active_game_head = moveup->next;
2256                                                 Active_game_head->prev = moveup->prev;
2257                                                 Active_game_head->prev->next = Active_game_head;
2258                                                 free(moveup);
2259                                                 moveup = Active_game_head;                                                                                      
2260                                         }
2261                                 }
2262                                 // if its somewhere else on the list
2263                                 else {
2264                                         // handle any gui details related to deleting this item
2265                                         multi_join_handle_item_cull(moveup, count);
2266                                         
2267                                         // if its the last item on the list                                     
2268                                         moveup->next->prev = moveup->prev;
2269                                         moveup->prev->next = moveup->next;                                      
2270                                         
2271                                         // if it was the last element on the list, return
2272                                         if(moveup->next == Active_game_head){
2273                                                 free(moveup);
2274                                                 return;
2275                                         } else {
2276                                                 backup = moveup->next;
2277                                                 free(moveup);
2278                                                 moveup = backup;                                                
2279                                         }
2280                                 }
2281                         } else {
2282                                 moveup = moveup->next;                          
2283                                 count++;
2284                         }
2285                 } while(moveup != Active_game_head);
2286         }
2287 }
2288
2289 // deep magic begins here. 
2290 void multi_join_handle_item_cull(active_game *item, int item_index)
2291 {       
2292         // if this is the only item on the list, unset everything
2293         if(item->next == item){
2294                 Multi_join_list_selected = -1;
2295                 Multi_join_selected_item = NULL;
2296         
2297 #ifndef MAKE_FS1
2298                 Multi_join_slider.set_numberItems(0);
2299 #endif
2300                 MJ_LIST_START_SET(-1);
2301                 Multi_join_list_start_item = NULL;
2302
2303                 // return
2304                 return;
2305         }       
2306         
2307         // see if we should be adjusting our currently selected item
2308         if(item_index <= Multi_join_list_selected){
2309                 // the selected item is the head of the list
2310                 if(Multi_join_selected_item == Active_game_head){
2311                         // move the pointer up since this item is about to be destroyed
2312                         Multi_join_selected_item = Multi_join_selected_item->next;
2313                 } else {                        
2314                         // if this is the item being deleted, select the previous one
2315                         if(item == Multi_join_selected_item){
2316                                 // previous item
2317                                 Multi_join_selected_item = Multi_join_selected_item->prev;
2318
2319                                 // decrement the selected index by 1
2320                                 Multi_join_list_selected--;             
2321                         }
2322                         // now we know its a previous item, so our pointer stays the same but our index goes down by one, since there will be
2323                         // 1 less item on the list
2324                         else {
2325                                 // decrement the selected index by 1
2326                                 Multi_join_list_selected--;             
2327                         }
2328                 }
2329         }
2330         
2331         // see if we should be adjusting out current start position
2332         if(item_index <= Multi_join_list_start){
2333                 // the start position is the head of the list
2334                 if(Multi_join_list_start_item == Active_game_head){
2335                         // move the pointer up since this item is about to be destroyed
2336                         Multi_join_list_start_item = Multi_join_list_start_item->next;
2337                 } else {
2338                         // if this is the item being deleted, select the previous one
2339                         if(item == Multi_join_list_start_item){
2340                                 Multi_join_list_start_item = Multi_join_list_start_item->prev;                  
2341                                 
2342                                 // decrement the starting index by 1
2343                                 MJ_LIST_START_DEC();                                                            
2344                         } else {
2345                                 // but decrement the starting index by 1
2346                                 MJ_LIST_START_DEC();                                                            
2347                         }
2348                 }
2349         }
2350
2351         // maybe go back up a bit so that we always have a full page of items   
2352         if(Active_game_count > Mj_max_game_items[gr_screen.res]){
2353                 while((Active_game_count - Multi_join_list_start) < Mj_max_game_items[gr_screen.res]){
2354                         Multi_join_list_start_item = Multi_join_list_start_item->prev;
2355                         MJ_LIST_START_DEC();
2356                 }
2357         }       
2358
2359         // set slider location
2360 #ifndef MAKE_FS1
2361         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);
2362         Multi_join_slider.force_currentItem(Multi_join_list_start);     
2363 #endif
2364 }
2365
2366 void multi_join_send_join_request(int as_observer)
2367 {       
2368         // don't do anything if we have no items selected
2369         if(Multi_join_selected_item == NULL){
2370                 return;
2371         }
2372
2373         // 5/26/98 -- for team v team games, don't allow ingame joining :-(
2374         if ( (Multi_join_selected_item->flags & AG_FLAG_TEAMS) && (Multi_join_selected_item->flags & (AG_FLAG_PAUSE|AG_FLAG_IN_MISSION)) ) {
2375                 popup(0, 1, POPUP_OK, XSTR("Joining ingame is currently not allowed for team vs. team games",772));
2376                 return;
2377         }
2378
2379         memset(&Multi_join_request,0,sizeof(join_request));
2380
2381         // if the netgame is in password mode, put up a request for the password
2382         if(Multi_join_selected_item->flags & AG_FLAG_PASSWD){
2383                 if(!multi_passwd_popup(Multi_join_request.passwd, SDL_arraysize(Multi_join_request.passwd))){
2384                         return;
2385                 }
2386
2387                 nprintf(("Password : %s\n",Multi_join_request.passwd));
2388         }       
2389                 
2390         // fill out the join request struct     
2391         SDL_strlcpy(Multi_join_request.callsign, Player->callsign, SDL_arraysize(Multi_join_request.callsign));
2392         if(strlen(Player->image_filename) > 0){
2393                 SDL_strlcpy(Multi_join_request.image_filename, Player->image_filename, SDL_arraysize(Multi_join_request.image_filename));
2394         }       
2395 #ifndef MAKE_FS1
2396         if(strlen(Player->squad_filename) > 0){
2397                 SDL_strlcpy(Multi_join_request.squad_filename, Player->squad_filename, SDL_arraysize(Multi_join_request.squad_filename));
2398         }
2399 #endif
2400
2401         // tracker id (if any)
2402         Multi_join_request.tracker_id = Multi_tracker_id;
2403
2404         // player's rank (at least, what he wants you to _believe_)
2405         Multi_join_request.player_rank = (ubyte)Player->stats.rank;
2406         
2407         // misc join flags
2408         Multi_join_request.flags = 0;
2409         if(as_observer){
2410                 Multi_join_request.flags |= JOIN_FLAG_AS_OBSERVER;
2411         }       
2412
2413         // if the player has hacked data
2414         if(game_hacked_data()){
2415                 Multi_join_request.flags |= JOIN_FLAG_HAXOR;
2416         }
2417         
2418         // pxo squad info
2419 #ifndef MAKE_FS1
2420         SDL_strlcpy(Multi_join_request.pxo_squad_name, Multi_tracker_squad_name, LOGIN_LEN);
2421 #endif
2422
2423         // version of this server
2424         Multi_join_request.version = MULTI_FS_SERVER_VERSION;
2425
2426         // server compatible version
2427         Multi_join_request.comp_version = MULTI_FS_SERVER_COMPATIBLE_VERSION;
2428
2429         // his local player options
2430         memcpy(&Multi_join_request.player_options,&Player->m_local_options,sizeof(multi_local_options));
2431                         
2432         // set the server address for the netgame
2433         memcpy(&Netgame.server_addr,&Multi_join_selected_item->server_addr,sizeof(net_addr));
2434
2435         // send a join request to the guy
2436         send_join_packet(&Multi_join_selected_item->server_addr,&Multi_join_request);
2437
2438    // now we wait
2439         Multi_join_sent_stamp = timestamp(MULTI_JOIN_SENT_WAIT);
2440
2441         psnet_flush();
2442         multi_common_add_notify(XSTR("Sending join request...",773));
2443 }
2444
2445 void multi_join_create_game()
2446 {
2447         // maybe warn the player about possible crappy server conditions
2448         if(!multi_join_maybe_warn()){
2449                 return;
2450         }
2451
2452         // make sure to flag ourself as being the master
2453         Net_player->flags |= (NETINFO_FLAG_AM_MASTER | NETINFO_FLAG_GAME_HOST);
2454         Net_player->state = NETPLAYER_STATE_HOST_SETUP; 
2455
2456         // if we're in PXO mode, mark it down in our player struct
2457         if(MULTI_IS_TRACKER_GAME){
2458                 Player->flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
2459                 Player->save_flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
2460         } 
2461         // otherwise, if he's already played PXO games, warn him
2462         else {
2463                 /*
2464                 if(Player->flags & PLAYER_FLAGS_HAS_PLAYED_PXO){
2465                         if(!multi_join_warn_pxo()){
2466                                 return;
2467                         }                       
2468                 }
2469                 */
2470         }
2471
2472         gameseq_post_event(GS_EVENT_MULTI_START_GAME);
2473         gamesnd_play_iface(SND_USER_SELECT);                                                            
2474 }
2475
2476 void multi_join_reset_join_stamp()
2477 {
2478         // unset the timestamp here so the user can immediately send another join request
2479         Multi_join_sent_stamp = -1;
2480         multi_common_add_notify("");
2481 }
2482
2483 void multi_join_blit_top_stuff()
2484 {       
2485         // blit the cd icon if he has one
2486         if(Multi_has_cd && (Multi_common_icons[MICON_CD] != -1)){
2487                 // get bitmap width
2488                 int cd_w;
2489                 bm_get_info(Multi_common_icons[MICON_CD], &cd_w, NULL, NULL, NULL, NULL);
2490
2491                 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
2492                 gr_bitmap((gr_screen.max_w / 2) - (cd_w / 2), Mj_cd_coords[gr_screen.res]);
2493         }       
2494 }
2495
2496 #define CW_CODE_CANCEL                          0                               // cancel the action
2497 #define CW_CODE_OK                                      1                               // continue anyway
2498 #define CW_CODE_INFO                                    2                               // gimme some more information
2499
2500 #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)
2501 #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)
2502
2503 #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)
2504 #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)
2505
2506 int multi_join_warn_update_low(int code)
2507 {
2508         switch(code){
2509         case CW_CODE_OK:
2510                 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),LOW_WARN_TEXT);
2511
2512         case CW_CODE_INFO:
2513                 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),LOW_INFO_TEXT);
2514         }
2515
2516         return CW_CODE_CANCEL;
2517 }
2518
2519 int multi_join_warn_update_medium(int code)
2520 {
2521         switch(code){
2522         case CW_CODE_OK:
2523                 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),MED_WARN_TEXT);
2524
2525         case CW_CODE_INFO:
2526                 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),MED_INFO_TEXT);
2527         }
2528
2529         return CW_CODE_CANCEL;
2530 }
2531
2532 int multi_join_maybe_warn()
2533 {
2534         int code;
2535
2536         // if the player is set for low updates
2537         if(Player->m_local_options.obj_update_level == OBJ_UPDATE_LOW){
2538                 code = CW_CODE_OK;
2539                 do {
2540                         code = multi_join_warn_update_low(code);
2541                 } while((code != CW_CODE_CANCEL) && (code != CW_CODE_OK));
2542                 
2543                 return code;
2544         }
2545         
2546         // if the player is set for medium updates
2547         else if(Player->m_local_options.obj_update_level == OBJ_UPDATE_MEDIUM){
2548                 code = CW_CODE_OK;
2549                 do {
2550                         code = multi_join_warn_update_medium(code);
2551                 } while((code != CW_CODE_CANCEL) && (code != CW_CODE_OK));
2552                 
2553                 return code;
2554         } 
2555         
2556         return 1;
2557 }
2558
2559 int multi_join_warn_pxo()
2560 {
2561         // 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;
2562         return 1;
2563 }
2564
2565 void multi_join_blit_protocol()
2566 {
2567         gr_set_color_fast(&Color_bright);
2568
2569         switch(Socket_type){
2570         case NET_TCP:           
2571                 // straight TCP         
2572                 gr_string(5, 2, "TCP");         
2573                 break;
2574
2575         case NET_IPX:
2576                 gr_string(5, 2, "IPX");
2577                 break;
2578         }
2579 }
2580
2581
2582 // -------------------------------------------------------------------------------------------------
2583 //
2584 // MULTIPLAYER START GAME screen
2585 //
2586
2587 //XSTR:OFF
2588 // bitmap defs
2589 #define MULTI_SG_PALETTE                        "InterfacePalette"
2590
2591 static const char *Multi_sg_bitmap_fname[GR_NUM_RESOLUTIONS] = {
2592         "MultiStartGame",                       // GR_640
2593         "2_MultiStartGame"                      // GR_1024
2594 };
2595
2596 static const char *Multi_sg_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
2597         "MultiStartGame-M",                     // GR_640
2598         "2_MultiStartGame-M"                    // GR_1024
2599 };
2600
2601 //XSTR:ON
2602
2603 int Multi_sg_rank_max_display[GR_NUM_RESOLUTIONS] = {
2604         2,              // GR_640
2605         4               // GR_1024
2606 };
2607
2608 // constants for coordinate look ups
2609 #define MSG_X_COORD 0
2610 #define MSG_Y_COORD 1
2611 #define MSG_W_COORD 2
2612 #define MSG_H_COORD 3
2613
2614 // area definitions
2615
2616 // input password field
2617 int Msg_passwd_coords[GR_NUM_RESOLUTIONS][4] = {
2618         { // GR_640
2619 #ifdef MAKE_FS1
2620                 247, 179, 285, 9
2621 #else
2622                 36, 236, 408, 20
2623 #endif
2624         },
2625         { // GR_1024
2626                 58, 377, 652, 32
2627         }
2628 };
2629
2630 // input game title field
2631 int Msg_title_coords[GR_NUM_RESOLUTIONS][4] = {
2632         { // GR_640
2633 #ifdef MAKE_FS1
2634                 178, 66, 354, 9
2635 #else
2636                 29, 49, 415, 23
2637 #endif
2638         },
2639         { // GR_1024
2640                 46, 78, 664, 36
2641         }
2642 };
2643
2644 // rank selected field
2645 int Msg_rank_sel_coords[GR_NUM_RESOLUTIONS][4] = {
2646         { // GR_640
2647 #ifdef MAKE_FS1
2648                 241, 253, 129, 10
2649 #else
2650                 242, 254, 126, 12
2651 #endif
2652         },
2653         { // GR_1024
2654                 242, 254, 126, 12
2655         }
2656 };
2657
2658 // rank list field
2659 int Msg_rank_list_coords[GR_NUM_RESOLUTIONS][4] = {
2660         { // GR_640
2661 #ifdef MAKE_FS1
2662                 241, 285, 129, 51
2663 #else
2664                 37, 297, 131, 16
2665 #endif
2666         },
2667         { // GR_1024
2668                 60, 469, 652, 32
2669         }
2670 };
2671
2672
2673 // button defs
2674 #ifdef MAKE_FS1
2675 #define MULTI_SG_NUM_BUTTONS    12
2676 #define MSG_OPEN_GAME                   0
2677 #define MSG_CLOSED_GAME                 1
2678 #define MSG_PASSWD_GAME                 2
2679 #define MSG_RESTRICTED_GAME             3
2680 #define MSG_RANK_SET_GAME               4
2681 #define MSG_RANK_SCROLL_UP              5
2682 #define MSG_RANK_SCROLL_DOWN    6
2683 #define MSG_RANK_ABOVE                  7
2684 #define MSG_RANK_BELOW                  8
2685 #define MSG_HELP                                9
2686 #define MSG_OPTIONS                             10
2687 #define MSG_ACCEPT                              11
2688 #else
2689 #define MULTI_SG_NUM_BUTTONS    10
2690 #define MSG_OPEN_GAME                   0
2691 //#define MSG_CLOSED_GAME                       1
2692 //#define MSG_RESTRICTED_GAME           2
2693 #define MSG_PASSWD_GAME                 1
2694 #define MSG_RANK_SET_GAME               2
2695 #define MSG_RANK_SCROLL_UP              3
2696 #define MSG_RANK_SCROLL_DOWN    4
2697 #define MSG_RANK_ABOVE                  5
2698 #define MSG_RANK_BELOW                  6
2699 #define MSG_HELP                                        7
2700 #define MSG_OPTIONS                             8
2701 #define MSG_ACCEPT                              9
2702 #endif
2703
2704 UI_WINDOW Multi_sg_window;                                                                                              // the window object for the join screen
2705 UI_BUTTON Multi_sg_rank_button;                                                                         // for selecting the rank marker
2706 UI_INPUTBOX     Multi_sg_game_name;                                                                             // for Netgame.name
2707 UI_INPUTBOX Multi_sg_game_passwd;                                                                       // for Netgame.passwd
2708 int Multi_sg_bitmap;                                                                                                            // the background bitmap
2709
2710 ui_button_info Multi_sg_buttons[GR_NUM_RESOLUTIONS][MULTI_SG_NUM_BUTTONS] = {
2711         { // GR_640
2712 #ifdef MAKE_FS1
2713                 ui_button_info("MSG_00",        75,             111,    -1,     -1,     0),
2714                 ui_button_info("MSG_01",        75,             139,    -1,     -1,     1),
2715                 ui_button_info("MSG_02",        75,             164,    -1,     -1,     2),
2716                 ui_button_info("MSG_03",        75,             199,    -1,     -1,     3),
2717                 ui_button_info("MSG_04",        75,             243,    -1,     -1,     4),
2718                 ui_button_info("MSG_05",        376,    231,    -1,     -1,     5),
2719                 ui_button_info("MSG_06",        376,    258,    -1,     -1,     6),
2720                 ui_button_info("MSG_07",        376,    291,    -1,     -1,     7),
2721                 ui_button_info("MSG_08",        376,    320,    -1,     -1,     8),
2722                 ui_button_info("MSG_09",        469,    427,    -1,     -1,     9),
2723                 ui_button_info("MSG_10",        447,    452,    -1,     -1,     10),
2724                 ui_button_info("MSG_11",        561,    411,    -1,     -1,     11),
2725 #else
2726                 ui_button_info("MSG_00",        1,              184,    34,     191,    2),             // open
2727 //              ui_button_info("MSG_01",        1,              159,    34,     166,    1),             // closed
2728 //              ui_button_info("MSG_02",        1,              184,    34,     191,    2),             // restricted
2729                 ui_button_info("MSG_03",        1,              209,    34,     218,    3),             // password
2730                 ui_button_info("MSG_04",        1,              257,    34,     266,    4),             // rank set
2731                 ui_button_info("MSG_05",        1,              282,    -1,     -1,     5),             // rank scroll up
2732                 ui_button_info("MSG_06",        1,              307,    -1,     -1,     6),             // rank scroll down
2733                 ui_button_info("MSG_07",        177,    282,    210,    290,    7),             // rank above
2734                 ui_button_info("MSG_08",        177,    307,    210,    315,    8),             // rank below
2735                 ui_button_info("MSG_09",        536,    429,    500,    440,    9),             // help
2736                 ui_button_info("MSG_10",        536,    454,    479,    464,    10),            // options
2737                 ui_button_info("MSG_11",        576,    432,    571,    415,    11),            // accept
2738 #endif
2739         },
2740         { // GR_1024
2741                 ui_button_info("2_MSG_00",      2,              295,    51,     307,    2),             // open
2742 //              ui_button_info("2_MSG_01",      2,              254,    51,     267,    1),             // closed
2743 //              ui_button_info("2_MSG_02",      2,              295,    51,     307,    2),             // restricted
2744                 ui_button_info("2_MSG_03",      2,              335,    51,     350,    3),             // password
2745                 ui_button_info("2_MSG_04",      2,              412,    51,     426,    4),             // rank set
2746                 ui_button_info("2_MSG_05",      2,              452,    -1,     -1,     5),             // rank scroll up
2747                 ui_button_info("2_MSG_06",      2,              492,    -1,     -1,     6),             // rank scroll down
2748                 ui_button_info("2_MSG_07",      284,    452,    335,    465,    7),             // rank above
2749                 ui_button_info("2_MSG_08",      284,    492,    335,    505,    8),             // rank below
2750                 ui_button_info("2_MSG_09",      858,    687,    817,    706,    9),             // help
2751                 ui_button_info("2_MSG_10",      858,    728,    797,    743,    10),            // options
2752                 ui_button_info("2_MSG_11",      921,    692,    921,    664,    11),            // accept
2753 #ifdef MAKE_FS1         // filler for extra FS1 buttons
2754                 ui_button_info("none",  -1,     -1,     -1,     -1,     -1),
2755                 ui_button_info("none",  -1, -1, -1, -1, -1),
2756 #endif
2757         },
2758 };
2759
2760 #ifndef MAKE_FS1
2761 #define MULTI_SG_NUM_TEXT                       11
2762
2763 UI_XSTR Multi_sg_text[GR_NUM_RESOLUTIONS][MULTI_SG_NUM_TEXT] = {
2764         { // GR_640
2765                 {"Open",                                        1322,           34,     191,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_sg_buttons[0][MSG_OPEN_GAME].button},
2766 //              {"Closed",                              1323,           34,     166,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_sg_buttons[0][MSG_CLOSED_GAME].button},
2767 //              {"Restricted",                  1324,           34,     191,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_sg_buttons[0][MSG_RESTRICTED_GAME].button},
2768                 {"Password Protected",  1325,   34,     218,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_sg_buttons[0][MSG_PASSWD_GAME].button},
2769                 {"Allow Rank",                  1326,           34,     266,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_sg_buttons[0][MSG_RANK_SET_GAME].button},
2770                 {"Above",                               1327,           210,    290,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_sg_buttons[0][MSG_RANK_ABOVE].button},
2771                 {"Below",                               1328,           210,    315,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_sg_buttons[0][MSG_RANK_BELOW].button},
2772                 {"Help",                                        928,            500,    440,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_sg_buttons[0][MSG_HELP].button},
2773                 {"Options",                             1036,           479,    464,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_sg_buttons[0][MSG_OPTIONS].button},
2774                 {"Accept",                              1035,           571,    415,    UI_XSTR_COLOR_PINK,     -1,     &Multi_sg_buttons[0][MSG_ACCEPT].button},
2775                 {"Start Game",                  1329,           26,     10,     UI_XSTR_COLOR_GREEN,    -1,     NULL},
2776                 {"Title",                               1330,           26,     31,     UI_XSTR_COLOR_GREEN,    -1,     NULL},
2777                 {"Game Type",                   1331,           12,     165,    UI_XSTR_COLOR_GREEN,    -1,     NULL},
2778         },
2779         { // GR_1024
2780                 {"Open",                                        1322,           51,     307,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_sg_buttons[1][MSG_OPEN_GAME].button},
2781 //              {"Closed",                              1323,           51,     267,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_sg_buttons[1][MSG_CLOSED_GAME].button},
2782 //              {"Restricted",                  1324,           51,     307,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_sg_buttons[1][MSG_RESTRICTED_GAME].button},
2783                 {"Password Protected",  1325,   51,     350,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_sg_buttons[1][MSG_PASSWD_GAME].button},
2784                 {"Allow Rank",                  1326,           51,     426,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_sg_buttons[1][MSG_RANK_SET_GAME].button},
2785                 {"Above",                               1327,           335,    465,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_sg_buttons[1][MSG_RANK_ABOVE].button},
2786                 {"Below",                               1328,           335,    505,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_sg_buttons[1][MSG_RANK_BELOW].button},
2787                 {"Help",                                        928,            817,    706,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_sg_buttons[1][MSG_HELP].button},
2788                 {"Options",                             1036,           797,    743,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_sg_buttons[1][MSG_OPTIONS].button},
2789                 {"Accept",                              1035,           921,    664,    UI_XSTR_COLOR_PINK,     -1,     &Multi_sg_buttons[1][MSG_ACCEPT].button},
2790                 {"Start Game",                  1329,           42,     22,     UI_XSTR_COLOR_GREEN,    -1,     NULL},
2791                 {"Title",                               1330,           42,     50,     UI_XSTR_COLOR_GREEN,    -1,     NULL},
2792                 {"Game Type",                   1331,           20,     264,    UI_XSTR_COLOR_GREEN,    -1,     NULL},
2793         }
2794 };
2795 #endif
2796
2797 // starting index for displaying ranks
2798 int Multi_sg_rank_start;
2799 int Multi_sg_rank_select;
2800
2801 // netgame pointer to indirect through
2802 netgame_info *Multi_sg_netgame;
2803
2804 // hold temporary values in this structure when on a standalone server
2805 netgame_info Multi_sg_netgame_temp;
2806
2807 // forward declarations
2808 void multi_sg_check_buttons();
2809 void multi_sg_button_pressed(int n);
2810 void multi_sg_init_gamenet();
2811 void multi_sg_draw_radio_buttons();
2812 void multi_sg_rank_scroll_up();
2813 void multi_sg_rank_scroll_down();
2814 void multi_sg_rank_display_stuff();
2815 void multi_sg_rank_process_select();
2816 void multi_sg_rank_build_name(char *in, char *out, const int max_outlen);
2817 void multi_sg_check_passwd();
2818 void multi_sg_check_name();
2819 void multi_sg_release_passwd();
2820 int multi_sg_rank_select_valid(int rank);
2821 void multi_sg_select_rank_default();
2822
2823 // function which takes a rank name and returns the index.  Useful for commandline options
2824 // for above and below rank.  We return the index of the rank in the Ranks[] array.  If
2825 // the rank isn't found, we return -1
2826 int multi_start_game_rank_from_name( char *rank ) {
2827         int i;
2828
2829 #ifdef MAKE_FS1
2830         for ( i = 0; i <= MAX_FREESPACE1_RANK; i++ ) {
2831 #else
2832         for ( i = 0; i <= MAX_FREESPACE2_RANK; i++ ) {
2833 #endif
2834                 if ( !SDL_strcasecmp(Ranks[i].name, rank) ) {
2835                         return i;
2836                 }
2837         }
2838
2839         return -1;
2840 }
2841
2842 void multi_start_game_init()
2843 {
2844         int idx;
2845
2846         // initialize the gamenet
2847         multi_sg_init_gamenet();
2848         
2849         // create the interface window
2850         Multi_sg_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
2851         Multi_sg_window.set_mask_bmap(Multi_sg_bitmap_mask_fname[gr_screen.res]);
2852
2853         // load the background bitmap
2854         Multi_sg_bitmap = bm_load(Multi_sg_bitmap_fname[gr_screen.res]);
2855         if(Multi_sg_bitmap < 0){
2856                 // we failed to load the bitmap - this is very bad
2857                 Int3();
2858         }
2859         
2860         // initialize the common notification messaging
2861         multi_common_notify_init();
2862
2863         // initialize the common text area
2864         multi_common_set_text("");
2865
2866         // use the common interface palette
2867         multi_common_set_palette();
2868         
2869         // create the interface buttons
2870         for(idx=0; idx<MULTI_SG_NUM_BUTTONS; idx++){
2871                 // create the object
2872                 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);
2873
2874                 // set the sound to play when highlighted
2875                 Multi_sg_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
2876
2877                 // set the ani for the button
2878                 Multi_sg_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sg_buttons[gr_screen.res][idx].filename);
2879
2880                 // set the hotspot
2881                 Multi_sg_buttons[gr_screen.res][idx].button.link_hotspot(Multi_sg_buttons[gr_screen.res][idx].hotspot);
2882         }       
2883
2884 #ifndef MAKE_FS1
2885         // add all xstrs
2886         for(idx=0; idx<MULTI_SG_NUM_TEXT; idx++){
2887                 Multi_sg_window.add_XSTR(&Multi_sg_text[gr_screen.res][idx]);
2888         }
2889 #endif
2890
2891         // load the help overlay
2892         help_overlay_load(MULTI_START_OVERLAY);
2893         help_overlay_set_state(MULTI_START_OVERLAY,0);
2894
2895         // intiialize the rank selection items  
2896         multi_sg_select_rank_default(); 
2897         Multi_sg_rank_start = Multi_sg_rank_select;
2898
2899         // create the rank select button
2900         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);       
2901         Multi_sg_rank_button.hide();            
2902
2903         // create the netgame name input box
2904         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);
2905
2906         // create the netgame password input box, and disable it by default
2907         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);
2908         Multi_sg_game_passwd.hide();
2909         Multi_sg_game_passwd.disable();
2910
2911         // set the netgame text to this gadget and make it have focus
2912         Multi_sg_game_name.set_text(Multi_sg_netgame->name);
2913         Multi_sg_game_name.set_focus(); 
2914
2915         // if starting a netgame, set the name of the game and any other options that are appropriate
2916         if ( Cmdline_start_netgame ) {
2917                 if ( Cmdline_game_name != NULL ) {
2918                         SDL_strlcpy( Multi_sg_netgame->name, Cmdline_game_name, SDL_arraysize(Multi_sg_netgame->name) );
2919                         Multi_sg_game_name.set_text(Multi_sg_netgame->name);
2920                 }
2921
2922                 // deal with the different game types -- only one should even be active, so we will just go down
2923                 // the line.  Last one wins.
2924                 if ( Cmdline_closed_game ) {
2925                         Multi_sg_netgame->mode = NG_MODE_CLOSED;
2926                 } else if ( Cmdline_restricted_game ) {
2927                         Multi_sg_netgame->mode = NG_MODE_RESTRICTED;
2928                 } else if ( Cmdline_game_password != NULL ) {
2929                         Multi_sg_netgame->mode = NG_MODE_PASSWORD;
2930                         SDL_strlcpy(Multi_sg_netgame->passwd, Cmdline_game_password, SDL_arraysize(Multi_sg_netgame->passwd));
2931                         Multi_sg_game_passwd.set_text(Multi_sg_netgame->passwd);
2932                 }
2933
2934                 // deal with rank above and rank below
2935                 if ( (Cmdline_rank_above != NULL) || (Cmdline_rank_below != NULL) ) {
2936                         int rank;
2937                         char *rank_str;
2938
2939                         if ( Cmdline_rank_above != NULL ) {
2940                                 rank_str = Cmdline_rank_above;
2941                         } else {
2942                                 rank_str = Cmdline_rank_below;
2943                         }
2944
2945                         // try and get the rank index from the name -- if found, then set the rank base
2946                         // and the game type.  apparently we only support either above or below, not both
2947                         // together, so I make a random choice
2948                         rank = multi_start_game_rank_from_name( rank_str );
2949                         if ( rank != -1 ) {
2950                                 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
2951
2952                                 // now an arbitrary decision
2953                                 if ( Cmdline_rank_above != NULL ) {
2954                                         Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
2955                                 } else {
2956                                         Multi_sg_netgame->mode = NG_MODE_RANK_BELOW;
2957                                 }
2958                         }
2959                 }
2960
2961                 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
2962         }
2963 }
2964
2965 void multi_start_game_do()
2966 {
2967         // return here since we will be moving to the next stage anyway -- I don't want to see the backgrounds of
2968         // all the screens for < 1 second for every screen we automatically move to.
2969         if ( Cmdline_start_netgame ) {
2970                 return;
2971         }
2972
2973         int k = Multi_sg_window.process();
2974
2975         // process any keypresses
2976         switch(k){
2977         case SDLK_ESCAPE :
2978                 if(help_overlay_active(MULTI_START_OVERLAY)){
2979                         help_overlay_set_state(MULTI_START_OVERLAY,0);
2980                 } else {
2981                         gamesnd_play_iface(SND_USER_SELECT);
2982                         multi_quit_game(PROMPT_NONE);
2983                 }
2984                 break;
2985         
2986         // same as ACCEPT
2987         case SDLK_LCTRL + SDLK_RETURN :
2988         case SDLK_RCTRL + SDLK_RETURN :
2989                 gamesnd_play_iface(SND_COMMIT_PRESSED);
2990                 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
2991                 break;
2992         }       
2993
2994         if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
2995                 help_overlay_set_state(MULTI_START_OVERLAY, 0);
2996         }
2997
2998         // check to see if the user has selected a different rank
2999         multi_sg_rank_process_select();
3000
3001         // check any button presses
3002         multi_sg_check_buttons();
3003
3004         // check to see if any of the input boxes have changed, and update the appropriate Netgame fields if necessary
3005         multi_sg_check_passwd();
3006         multi_sg_check_name();
3007
3008         // draw the background, etc
3009         gr_reset_clip();
3010         GR_MAYBE_CLEAR_RES(Multi_sg_bitmap);
3011         if(Multi_sg_bitmap != -1){
3012                 gr_set_bitmap(Multi_sg_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
3013                 gr_bitmap(0,0);
3014         }
3015         Multi_sg_window.draw();
3016         
3017         // display rank stuff
3018         multi_sg_rank_display_stuff();
3019
3020         // display any pending notification messages
3021         multi_common_notify_do();
3022
3023         // draw all radio button
3024         multi_sg_draw_radio_buttons();
3025
3026         // draw the help overlay
3027         help_overlay_maybe_blit(MULTI_START_OVERLAY);
3028         
3029         // flip the buffer
3030         gr_flip();
3031 }
3032
3033 void multi_start_game_close()
3034 {
3035         // if i'm the host on a standalone server, send him my start game options (passwd, mode, etc)
3036         if((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
3037                 multi_options_update_start_game(Multi_sg_netgame);
3038         }
3039         
3040         // unload any bitmaps
3041         if(!bm_unload(Multi_sg_bitmap)){
3042                 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_sg_bitmap_fname[gr_screen.res]));
3043         }
3044
3045         // unload the help overlay
3046         help_overlay_unload(MULTI_START_OVERLAY);
3047         
3048         // destroy the UI_WINDOW
3049         Multi_sg_window.destroy();      
3050 }
3051
3052 void multi_sg_check_buttons()
3053 {
3054         int idx;
3055         for(idx=0;idx<MULTI_SG_NUM_BUTTONS;idx++){
3056                 // we only really need to check for one button pressed at a time, so we can break after 
3057                 // finding one.
3058                 if(Multi_sg_buttons[gr_screen.res][idx].button.pressed()){
3059                         multi_sg_button_pressed(idx);
3060                         break;
3061                 }
3062         }
3063 }
3064
3065 void multi_sg_button_pressed(int n)
3066 {
3067         switch(n){              
3068         // go to the options screen
3069         case MSG_OPTIONS:
3070                 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
3071                 break;  
3072
3073         // help overlay 
3074         case MSG_HELP:
3075                 if(!help_overlay_active(MULTI_START_OVERLAY)){
3076                         help_overlay_set_state(MULTI_START_OVERLAY,1);
3077                 } else {
3078                         help_overlay_set_state(MULTI_START_OVERLAY,0);
3079                 }
3080                 break;
3081
3082         // the open button was pressed
3083         case MSG_OPEN_GAME:             
3084                 // if the closed option is selected
3085                 if(Multi_sg_netgame->mode != NG_MODE_OPEN){
3086                         Multi_sg_netgame->mode = NG_MODE_OPEN;
3087
3088                         gamesnd_play_iface(SND_USER_SELECT);
3089                         
3090                         // release the password control if necessary
3091                         multi_sg_release_passwd();
3092                 }
3093                 // if its already selected
3094                 else {
3095                         gamesnd_play_iface(SND_GENERAL_FAIL);
3096                 }
3097                 break;
3098
3099 #ifdef MAKE_FS1
3100         // the open button was pressed
3101         case MSG_CLOSED_GAME:           
3102                 // if the closed option is selected
3103                 if(Multi_sg_netgame->mode != NG_MODE_CLOSED){
3104                         Multi_sg_netgame->mode = NG_MODE_CLOSED;
3105
3106                         gamesnd_play_iface(SND_USER_SELECT);
3107                         
3108                         // release the password control if necessary
3109                         multi_sg_release_passwd();
3110                 }
3111                 // if its already selected
3112                 else {
3113                         gamesnd_play_iface(SND_GENERAL_FAIL);
3114                 }
3115                 break;
3116 #endif
3117
3118         // toggle password protection
3119         case MSG_PASSWD_GAME:           
3120                 // if we selected it
3121                 if(Multi_sg_netgame->mode != NG_MODE_PASSWORD){
3122                         gamesnd_play_iface(SND_USER_SELECT);            
3123
3124                         Multi_sg_game_passwd.enable();                  
3125                         Multi_sg_game_passwd.unhide();
3126                         Multi_sg_game_passwd.set_focus();
3127
3128                         Multi_sg_netgame->mode = NG_MODE_PASSWORD;                      
3129
3130                         // copy in the current network password
3131                         Multi_sg_game_passwd.set_text(Multi_sg_netgame->passwd);
3132                 } else {
3133                         gamesnd_play_iface(SND_GENERAL_FAIL);
3134                 }                       
3135                 break;
3136
3137 #ifdef MAKE_FS1
3138         // toggle "restricted" on or off
3139         case MSG_RESTRICTED_GAME:
3140                 if(Multi_sg_netgame->mode != NG_MODE_RESTRICTED){
3141                         gamesnd_play_iface(SND_USER_SELECT);
3142                         Multi_sg_netgame->mode = NG_MODE_RESTRICTED;
3143
3144                         // release the password control if necessary
3145                         multi_sg_release_passwd();
3146                 } else {
3147                         gamesnd_play_iface(SND_GENERAL_FAIL);
3148                 }
3149                 break;
3150 #endif
3151
3152         // turn off all rank requirements
3153         case MSG_RANK_SET_GAME:         
3154                 // if either is set, then turn then both off
3155                 if((Multi_sg_netgame->mode != NG_MODE_RANK_BELOW) && (Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE)){
3156                         gamesnd_play_iface(SND_USER_SELECT);
3157
3158                         // set it to the default case if we're turning it off
3159                         multi_sg_select_rank_default();
3160                         Multi_sg_rank_start = Multi_sg_rank_select;                     
3161
3162                         Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
3163
3164                         // release the password control if necessary
3165                         multi_sg_release_passwd();
3166                 } else {
3167                         gamesnd_play_iface(SND_GENERAL_FAIL);
3168                 }               
3169                 break;
3170
3171         // rank above was pressed
3172         case MSG_RANK_ABOVE :
3173                 if((Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE) || (Multi_sg_netgame->mode == NG_MODE_RANK_BELOW)){
3174                         Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
3175
3176                         // select the first item
3177                         multi_sg_select_rank_default();
3178                         Multi_sg_rank_start = Multi_sg_rank_select;
3179
3180                         // play a sound
3181                         gamesnd_play_iface(SND_USER_SELECT);                    
3182                 } else {
3183                         gamesnd_play_iface(SND_GENERAL_FAIL);
3184                 }
3185                 break;
3186
3187         // rank below was pressed
3188         case MSG_RANK_BELOW :
3189                 if((Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE) || (Multi_sg_netgame->mode == NG_MODE_RANK_BELOW)){
3190                         Multi_sg_netgame->mode = NG_MODE_RANK_BELOW;
3191                         
3192                         // select the first item
3193                         multi_sg_select_rank_default();
3194                         Multi_sg_rank_start = Multi_sg_rank_select;                     
3195
3196                         // play a sound
3197                         gamesnd_play_iface(SND_USER_SELECT);                    
3198                 } else {
3199                         gamesnd_play_iface(SND_GENERAL_FAIL);
3200                 }       
3201                 break;
3202
3203         // scroll the rank list up
3204         case MSG_RANK_SCROLL_UP:
3205                 multi_sg_rank_scroll_up();
3206                 break;
3207
3208         // scroll the rank list down
3209         case MSG_RANK_SCROLL_DOWN:
3210                 multi_sg_rank_scroll_down();
3211                 break;
3212
3213         // move to the create game screen
3214         case MSG_ACCEPT:
3215                 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
3216                 gamesnd_play_iface(SND_COMMIT_PRESSED);
3217                 break;
3218
3219         default :
3220                 gamesnd_play_iface(SND_GENERAL_FAIL);
3221                 multi_common_add_notify(XSTR("Not implemented yet!",760));              
3222                 break;
3223         }
3224 }
3225
3226 // NOTE : this is where all Netgame initialization should take place on the host
3227 void multi_sg_init_gamenet()
3228 {
3229         char buf[128],out_name[128];            
3230         net_addr save;
3231         net_player *server_save;        
3232
3233         // back this data up in case we are already connected to a standalone
3234         memcpy(&save,&Netgame.server_addr,sizeof(net_addr));
3235         server_save = Netgame.server;
3236
3237         // remove campaign flags
3238         Game_mode &= ~(GM_CAMPAIGN_MODE);
3239
3240         // clear out the Netgame structure and start filling in the values
3241         memset( &Netgame, 0, sizeof(Netgame) ); 
3242         memset( &Multi_sg_netgame_temp, 0, sizeof(netgame_info) );
3243         
3244         // if we're on the standalone, we're not the server, so we don't care about setting the netgame state
3245         if(Net_player->state != NETPLAYER_STATE_STD_HOST_SETUP){                
3246                 Netgame.game_state = NETGAME_STATE_HOST_SETUP;
3247                 Multi_sg_netgame = &Netgame;    
3248
3249                 // NETLOG
3250                 ml_string(NOX("Starting netgame as Host/Server"));              
3251         } else {
3252                 Multi_sg_netgame = &Multi_sg_netgame_temp;
3253
3254                 // NETLOG
3255                 in_addr temp_addr;
3256                 memcpy(&temp_addr.s_addr, &Netgame.server_addr, 4);
3257                 char *server_addr = inet_ntoa(temp_addr);                               
3258                 ml_printf(NOX("Starting netgame as Host on Standalone server : %s"), (server_addr == NULL) ? NOX("Unknown") : server_addr);
3259         }
3260         
3261         Net_player->tracker_player_id = Multi_tracker_id;
3262
3263         Multi_sg_netgame->security = (rand() % 32766) + 1;                      // get some random security number      
3264         Multi_sg_netgame->mode = NG_MODE_OPEN;
3265         Multi_sg_netgame->rank_base = RANK_ENSIGN;
3266         if(Multi_sg_netgame->security < 16){
3267                 Multi_sg_netgame->security += 16;
3268         }
3269
3270         // set the version_info field
3271         Multi_sg_netgame->version_info = NG_VERSION_ID;
3272         
3273         if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
3274                 Netgame.host = Net_player;
3275         }
3276         
3277         // set the default netgame flags
3278         Multi_sg_netgame->flags = 0;
3279
3280         // intialize endgame stuff
3281         multi_endgame_init();
3282
3283         // load in my netgame options
3284         multi_options_netgame_load(&Netgame.options);           
3285
3286         // load my local netplayer options
3287         multi_options_local_load(&Net_player->p_info.options, Net_player);      
3288         
3289         // setup the default game name, taking care of string length and player callsigns
3290         memset(out_name,0,128);
3291         memset(buf,0,128);
3292         pilot_format_callsign_personal(Player->callsign, out_name, SDL_arraysize(out_name));
3293         SDL_snprintf(buf, SDL_arraysize(buf), XSTR("%s game",782), out_name);  // [[ %s will be a pilot's name ]]
3294         if ( strlen(buf) > MAX_GAMENAME_LEN ){
3295                 SDL_strlcpy(buf, XSTR("Temporary name",783), SDL_arraysize(buf));
3296         }
3297         SDL_strlcpy(Multi_sg_netgame->name, buf, SDL_arraysize(Multi_sg_netgame->name));
3298
3299         // set the default qos and duration
3300         multi_voice_maybe_update_vars(Netgame.options.voice_qos,Netgame.options.voice_record_time);
3301
3302         // make sure to set the server correctly (me or the standalone)
3303         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){         
3304                 memcpy(&Netgame.server_addr, &Psnet_my_addr,sizeof(net_addr));
3305                 Netgame.server = Net_player;
3306                 Net_player->player_id = multi_get_new_id();
3307
3308                 // setup debug flags
3309                 Netgame.debug_flags = 0;
3310                 /*
3311                 if(!Cmdline_server_firing){
3312                         Netgame.debug_flags |= NETD_FLAG_CLIENT_FIRING;
3313                 }
3314                 if(!Cmdline_client_dodamage){
3315                         Netgame.debug_flags |= NETD_FLAG_CLIENT_NODAMAGE;
3316                 }
3317                 */
3318         } else {
3319                 memcpy(&Netgame.server_addr,&save,sizeof(net_addr));
3320                 Netgame.server = server_save;
3321         }
3322
3323         // if I have a cd or not
3324         if(Multi_has_cd){
3325                 Net_player->flags |= NETINFO_FLAG_HAS_CD;
3326         }       
3327
3328         // if I have hacked data
3329         if(game_hacked_data()){
3330                 Net_player->flags |= NETINFO_FLAG_HAXOR;
3331         }
3332
3333         // assign my player struct and other data       
3334         Net_player->flags |= (NETINFO_FLAG_CONNECTED | NETINFO_FLAG_DO_NETWORKING);
3335         Net_player->s_info.voice_token_timestamp = -1;  
3336
3337         // if we're supposed to flush our cache directory, do so now
3338         if(Net_player->p_info.options.flags & MLO_FLAG_FLUSH_CACHE){
3339                 multi_flush_multidata_cache();
3340
3341                 // NETLOG
3342                 ml_string(NOX("Flushing multi-data cache"));
3343         }
3344                         
3345         game_flush();
3346 }
3347
3348 void multi_sg_draw_radio_buttons()
3349 {
3350         // draw the appropriate radio button
3351         switch(Multi_sg_netgame->mode){
3352         case NG_MODE_OPEN:
3353                 Multi_sg_buttons[gr_screen.res][MSG_OPEN_GAME].button.draw_forced(2);
3354                 break;
3355
3356 #ifdef MAKE_FS1
3357         case NG_MODE_CLOSED:
3358                 Multi_sg_buttons[gr_screen.res][MSG_CLOSED_GAME].button.draw_forced(2);
3359                 break;
3360 #endif
3361
3362         case NG_MODE_PASSWORD:
3363                 Multi_sg_buttons[gr_screen.res][MSG_PASSWD_GAME].button.draw_forced(2);
3364                 break;
3365
3366 #ifdef MAKE_FS1
3367         case NG_MODE_RESTRICTED:
3368                 Multi_sg_buttons[gr_screen.res][MSG_RESTRICTED_GAME].button.draw_forced(2);
3369                 break;
3370 #endif
3371
3372         case NG_MODE_RANK_ABOVE:
3373                 Multi_sg_buttons[gr_screen.res][MSG_RANK_SET_GAME].button.draw_forced(2);
3374                 Multi_sg_buttons[gr_screen.res][MSG_RANK_ABOVE].button.draw_forced(2);
3375                 break;
3376         case NG_MODE_RANK_BELOW:
3377                 Multi_sg_buttons[gr_screen.res][MSG_RANK_SET_GAME].button.draw_forced(2);
3378                 Multi_sg_buttons[gr_screen.res][MSG_RANK_BELOW].button.draw_forced(2);
3379                 break;
3380         }
3381 }
3382
3383 void multi_sg_rank_scroll_up()
3384 {       
3385         // if he doesn't have either of the rank flags set, then ignore this
3386         if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3387                 return;
3388         }
3389
3390         if(Multi_sg_rank_start > 0){
3391                 Multi_sg_rank_start--;
3392                 gamesnd_play_iface(SND_SCROLL);
3393         } else {
3394                 gamesnd_play_iface(SND_GENERAL_FAIL);
3395         }
3396 }
3397
3398 void multi_sg_rank_scroll_down()
3399 {
3400         // if he doesn't have either of the rank flags set, then ignore this
3401         if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3402                 return;
3403         }
3404         
3405         if((NUM_RANKS - Multi_sg_rank_start) > Multi_sg_rank_max_display[gr_screen.res]){
3406                 Multi_sg_rank_start++;
3407                 gamesnd_play_iface(SND_SCROLL);
3408         } else {
3409                 gamesnd_play_iface(SND_GENERAL_FAIL);
3410         }       
3411 }
3412
3413 void multi_sg_rank_display_stuff()
3414 {
3415         int y,idx,count;
3416         char rank_name[40];
3417
3418         // if he doesn't have either of the rank flags set, then ignore this
3419         if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3420                 return;
3421         }
3422                 
3423         // display the list of ranks
3424         y = Msg_rank_list_coords[gr_screen.res][MSG_Y_COORD];
3425         idx = Multi_sg_rank_start;
3426         count = 0;
3427         while((count < NUM_RANKS) && (count < Multi_sg_rank_max_display[gr_screen.res]) && (idx < NUM_RANKS)){  
3428                 // if its the selected item, then color it differently
3429                 if(idx == Multi_sg_rank_select){
3430                         gr_set_color_fast(&Color_text_selected);
3431                 } else {
3432                         gr_set_color_fast(&Color_text_normal);
3433                 }
3434
3435                 // print the text
3436                 multi_sg_rank_build_name(Ranks[idx].name, rank_name, sizeof(rank_name));
3437                 gr_string(Msg_rank_list_coords[gr_screen.res][MSG_X_COORD],y,rank_name);
3438
3439                 // increment stuff
3440                 y+=10;
3441                 idx++;
3442                 count++;
3443         }
3444
3445         // display the selected rank
3446 #ifdef MAKE_FS1
3447         gr_set_color_fast(&Color_bright);
3448         multi_sg_rank_build_name(Ranks[Multi_sg_netgame->rank_base].name, rank_name, SDL_arraysize(rank_name));
3449         gr_string(Msg_rank_sel_coords[gr_screen.res][MSG_X_COORD],Msg_rank_sel_coords[gr_screen.res][MSG_Y_COORD],rank_name);
3450 #endif
3451 }
3452
3453 void multi_sg_rank_process_select()
3454 {
3455         char string[255];
3456         
3457         // if he doesn't have either of the rank flags set, then ignore this
3458         if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3459                 return;
3460         }
3461         
3462         // see if he's clicked on an item on the rank list
3463         if(Multi_sg_rank_button.pressed()){              
3464                 int y,item;             
3465                 Multi_sg_rank_button.get_mouse_pos(NULL,&y);
3466                 item = y / 10;
3467                 
3468                 if(item + Multi_sg_rank_start < NUM_RANKS){             
3469                         // evaluate whether this rank is valid for the guy to pick              
3470                         if(multi_sg_rank_select_valid(item + Multi_sg_rank_start)){
3471                                 gamesnd_play_iface(SND_USER_SELECT);
3472
3473                                 Multi_sg_rank_select = item + Multi_sg_rank_start;                                              
3474
3475                                 // set the Netgame rank
3476                                 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
3477                         } else {
3478                                 gamesnd_play_iface(SND_GENERAL_FAIL);
3479
3480                                 memset(string,0,255);
3481                                 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);
3482                                 multi_common_add_notify(string);
3483                         }
3484                 }               
3485         }
3486 }
3487
3488 void multi_sg_rank_build_name(char *in, char *out, const int max_outlen)
3489 {
3490         char use[100];
3491         char *first;
3492
3493         SDL_strlcpy(use, in, SDL_arraysize(use));
3494         first = strtok(use," ");
3495
3496         // just copy the string
3497         if(first == NULL){
3498                 SDL_strlcpy(out, in, max_outlen);
3499         }
3500         
3501         // if the first part of the string is lieutenant, then abbreivate it and tack on the rest of the string 
3502         if (SDL_strcasecmp(first,XSTR("lieutenant",785)) == 0) {
3503                 first = strtok(NULL, NOX("\n"));
3504
3505                 // if he's not just a plain lieutenant
3506                 if(first != NULL){
3507                         SDL_snprintf(out, max_outlen, "%s%s", XSTR("Lt. ",786), first); // [[ lieutenant ]]
3508                 }
3509                 // if he _is_ just a plain lieutenant
3510                 else {
3511                         SDL_strlcpy(out, in, max_outlen);
3512                 }
3513         } else {
3514                 SDL_strlcpy(out, in, max_outlen);
3515         }
3516 }
3517
3518 void multi_sg_check_passwd()
3519 {
3520         // check to see if the password input box has been pressed
3521         if(Multi_sg_game_passwd.changed()){
3522                 Multi_sg_game_passwd.get_text(Multi_sg_netgame->passwd);
3523         }
3524 }
3525
3526 void multi_sg_check_name()
3527 {
3528         // check to see if the game name input box has been pressed
3529         if(Multi_sg_game_name.changed()){
3530                 Multi_sg_game_name.get_text(Multi_sg_netgame->name);
3531         }
3532 }
3533
3534 void multi_sg_release_passwd()
3535 {
3536         // hide and disable the password input box
3537         Multi_sg_game_passwd.hide();
3538         Multi_sg_game_passwd.disable();
3539
3540         // set the focus back to the name input box
3541         Multi_sg_game_name.set_focus();
3542 }
3543
3544 int multi_sg_rank_select_valid(int rank)
3545 {
3546         // rank above mode
3547         if(Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE){
3548                 if(Net_player->player->stats.rank >= rank){
3549                         return 1;
3550                 }
3551         }
3552         // rank below mode
3553         else {
3554                 if(Net_player->player->stats.rank <= rank){
3555                         return 1;
3556                 }
3557         }
3558         
3559         return 0;
3560 }
3561
3562 void multi_sg_select_rank_default()
3563 {
3564         // pick our rank for now
3565         Multi_sg_rank_select = Net_player->player->stats.rank;
3566
3567         // set the Netgame rank
3568         Multi_sg_netgame->rank_base = Multi_sg_rank_select;
3569 }
3570                 
3571 // -------------------------------------------------------------------------------------------------
3572 //
3573 // MULTIPLAYER CREATE GAME screen
3574 //
3575
3576 //XSTR:OFF
3577 // bitmaps defs
3578 const char *Multi_create_bitmap_fname[GR_NUM_RESOLUTIONS] = {
3579         "MultiCreate",                  // GR_640
3580         "2_MultiCreate"         // GR_1024
3581 };
3582
3583 const char *Multi_create_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
3584         "MultiCreate-M",                // GR_640
3585         "2_MultiCreate-M"               // GR_1024
3586 };
3587
3588 const char *Multi_create_loading_fname[GR_NUM_RESOLUTIONS] = {
3589         "PleaseWait",                   // GR_640
3590         "2_PleaseWait"                  // GR_1024
3591 };
3592 //XSTR:ON
3593
3594 #define MULTI_CREATE_NUM_BUTTONS        23
3595
3596 // button defs
3597 #define MC_SHOW_ALL                                     0
3598 #define MC_SHOW_COOP                                    1
3599 #define MC_SHOW_TEAM                                    2
3600 #define MC_SHOW_DOGFIGHT                        3
3601 #define MC_PXO_REFRESH                          4
3602 #define MC_PILOT_INFO                           5
3603 #define MC_SCROLL_LIST_UP                       6 
3604 #define MC_SCROLL_LIST_DOWN             7
3605 #define MC_SCROLL_PLAYERS_UP            8
3606 #define MC_SCROLL_PLAYERS_DOWN  9
3607 #define MC_MISSION_FILTER                       10
3608 #define MC_CAMPAIGN_FILTER                      11
3609 #define MC_CANCEL                                               12
3610 #define MC_TEAM0                                                13
3611 #define MC_TEAM1                                                14
3612 #define MC_KICK                                         15
3613 #define MC_CLOSE                                                16
3614 #define MC_SCROLL_INFO_UP                       17
3615 #define MC_SCROLL_INFO_DOWN             18
3616 #define MC_HOST_OPTIONS                         19
3617 #define MC_HELP                                         20
3618 #define MC_OPTIONS                                      21
3619 #define MC_ACCEPT                                               22
3620
3621
3622 UI_WINDOW Multi_create_window;                                                                          // the window object for the create screen
3623 UI_BUTTON Multi_create_player_select_button;                                            // for selecting players
3624 UI_BUTTON Multi_create_list_select_button;                                              // for selecting missions/campaigns
3625 int Multi_create_bitmap;                                                                                                // the background bitmap
3626 UI_SLIDER2 Multi_create_slider;                                                                         // for create list
3627
3628 // constants for coordinate look ups
3629 #define MC_X_COORD 0
3630 #define MC_Y_COORD 1
3631 #define MC_W_COORD 2
3632 #define MC_H_COORD 3
3633
3634 ui_button_info Multi_create_buttons[GR_NUM_RESOLUTIONS][MULTI_CREATE_NUM_BUTTONS] = {   
3635         { // GR_640
3636 #ifdef MAKE_FS1
3637                 ui_button_info("MC_18", 34,             131,    -1,     -1,     18),    // all
3638                 ui_button_info("MC_19", 72,             131,    -1,     -1,     19),    // coop
3639                 ui_button_info("MC_20", 120,    131,    -1,     -1,     20),    // team
3640 //              ui_button_info("MC_21", 166,    131,    -1,     -1,     21),    // dogfight
3641                 ui_button_info("none",  -1,             -1,             -1,     -1,     -1),    // dogfight (not used)
3642                 ui_button_info("none",  -1,             -1,             -1,     -1,     -1),    // pxo?
3643                 ui_button_info("MC_26", 540,    114,    -1,     -1,     26),    // pilot info
3644                 ui_button_info("MC_03", 0,              187,    -1,     -1,     2),     // scroll list up
3645                 ui_button_info("MC_02", 0,              227,    -1,     -1,     3),     // scroll list down
3646                 ui_button_info("MC_04", 611,    182,    -1,     -1,     4),     // scroll players up
3647                 ui_button_info("MC_05", 611,    221,    -1,     -1,     5),     // scroll players down
3648                 ui_button_info("MC_06", 18,             322,    -1,     -1,     6),     // mission filter
3649                 ui_button_info("MC_07", 18,             344,    -1,     -1,     7),     // campaign filter
3650                 ui_button_info("MC_10", 317,    339,    -1,     -1,     10),    // cancel
3651                 ui_button_info("MC_14", 464,    350,    -1,     -1,     14),    // team 1
3652                 ui_button_info("MC_15", 498,    350,    -1,     -1,     15),    // team 2
3653                 ui_button_info("MC_16", 527,    346,    -1,     -1,     16),    // kick
3654                 ui_button_info("MC_17", 572,    346,    -1,     -1,     17),    // close
3655                 ui_button_info("MC_08", 0,              398,    -1,     -1,     8),     // scroll mission info up
3656                 ui_button_info("MC_09", 0,              435,    -1,     -1,     9),     // scroll mission info down
3657                 ui_button_info("MC_27", 447,    402,    -1,     -1,     27),    // host options
3658                 ui_button_info("MC_11", 510,    428,    -1,     -1,     11),    // help
3659                 ui_button_info("MC_12", 510,    453,    -1,     -1,     12),    // options
3660                 ui_button_info("Mc_13", 562,    412,    -1,     -1,     13),    // commit
3661 #else
3662                 ui_button_info("MC_00", 32,     129,    36,     158,    0),             // show all missions
3663                 ui_button_info("MC_01", 76,     129,    71,     158,    1),             // show coop missions
3664                 ui_button_info("MC_02", 121,    129,    119,    158,    2),             // show team missions
3665                 ui_button_info("MC_03", 164,    129,    166,    158,    3),             // show dogfight missions
3666                 ui_button_info("MC_04", 399,    129,    229,    130,    4),             // pxo mission refresh
3667                 ui_button_info("MC_05", 567,    123,    467,    132,    5),             // pilot info
3668                 ui_button_info("MC_06", 1,              161,    -1,     -1,     6),             // scroll mission info up
3669                 ui_button_info("MC_08", 1,              304,    -1,     -1,     8),             // scroll mission info down
3670                 ui_button_info("MC_09", 613,    160,    -1,     -1,     9),             // scroll players up
3671                 ui_button_info("MC_10", 613,    202,    -1,     -1,     10),            // scroll players down
3672                 ui_button_info("MC_11", 22,     346,    27,     376,    11),            // mission filter
3673                 ui_button_info("MC_12", 104,    346,    110,    376,    12),            // campaign filter
3674                 ui_button_info("MC_13", 392,    341,    328,    364,    13),            // cancel
3675                 ui_button_info("MC_14", 472,    352,    482,    381,    14),            // team 0       
3676                 ui_button_info("MC_15", 506,    352,    514,    381,    15),            // team 1
3677                 ui_button_info("MC_16", 539,    346,    539,    381,    16),            // kick
3678                 ui_button_info("MC_17", 589,    346,    582,    381,    17),            // close
3679                 ui_button_info("MC_18", 1,              406,    -1,     -1,     18),            // scroll list up
3680                 ui_button_info("MC_19", 1,              447,    -1,     -1,     19),            // scroll list down
3681                 ui_button_info("MC_20", 499,    434,    436,    423,    20),            // host options
3682                 ui_button_info("MC_21", 534,    426,    -1,     -1,     21),            // help
3683                 ui_button_info("MC_22", 534,    452,    -1,     -1,     22),            // options
3684                 ui_button_info("MC_23", 571,    426,    572,    413,    23),            // commit
3685 #endif
3686         },      
3687         { // GR_1024
3688                 ui_button_info("2_MC_00", 51,           207,    61,     253,    0),             // show all missions
3689                 ui_button_info("2_MC_01", 122,  207,    124,    253,    1),             // show coop missions
3690                 ui_button_info("2_MC_02", 193,  207,    194,    253,    2),             // show team missions
3691                 ui_button_info("2_MC_03", 263,  207,    261,    253,    3),             // show dogfight missions
3692                 ui_button_info("2_MC_04", 639,  207,    479,    218,    4),             // pxo mission refresh
3693                 ui_button_info("2_MC_05", 907,  197,    748,    216,    5),             // pilot info
3694                 ui_button_info("2_MC_06", 1,            258,    -1,     -1,     6),             // scroll mission info up
3695                 ui_button_info("2_MC_08", 1,            487,    -1,     -1,     8),             // scroll mission info down
3696                 ui_button_info("2_MC_09", 981,  256,    -1,     -1,     9),             // scroll players up
3697                 ui_button_info("2_MC_10", 981,  323,    -1,     -1,     10),            // scroll players down
3698                 ui_button_info("2_MC_11",  35,  554,    46,     601,    11),            // mission filter
3699                 ui_button_info("2_MC_12", 166,  554,    174,    601,    12),            // campaign filter
3700                 ui_button_info("2_MC_13", 628,  545,    559,    582,    13),            // cancel
3701                 ui_button_info("2_MC_14", 756,  564,    772,    610,    14),            // team 0       
3702                 ui_button_info("2_MC_15", 810,  564,    826,    610,    15),            // team 1
3703                 ui_button_info("2_MC_16", 862,  554,    872,    610,    16),            // kick
3704                 ui_button_info("2_MC_17", 943,  554,    949,    610,    17),            // close
3705                 ui_button_info("2_MC_18", 1,            649,    -1,     -1,     18),            // scroll list up
3706                 ui_button_info("2_MC_19", 1,            716,    -1,     -1,     19),            // scroll list down
3707                 ui_button_info("2_MC_20", 798,  695,    726,    667,    20),            // host options
3708                 ui_button_info("2_MC_21", 854,  681,    -1,     -1,     21),            // help
3709                 ui_button_info("2_MC_22", 854,  724,    -1,     -1,     22),            // options
3710                 ui_button_info("2_MC_23", 914,  681,    932,    667,    23),            // commit
3711         },      
3712 };
3713
3714 #ifdef MAKE_FS1
3715 #define MULTI_CREATE_NUM_TEXT                           0
3716 #else
3717 #define MULTI_CREATE_NUM_TEXT                           15
3718 #endif
3719 UI_XSTR Multi_create_text[GR_NUM_RESOLUTIONS][MULTI_CREATE_NUM_BUTTONS] = {
3720         { // GR_640
3721                 // not needed for FS1
3722 #ifndef MAKE_FS1
3723                 {"All",                                 1256,           36,     158,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[0][MC_SHOW_ALL].button},
3724                 {"Coop",                                        1257,           71,     158,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[0][MC_SHOW_COOP].button},
3725                 {"Team",                                        1258,           119,    158,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[0][MC_SHOW_TEAM].button},
3726                 {"Dogfight",                    1259,           166,    158,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[0][MC_SHOW_DOGFIGHT].button},
3727                 {"Refresh Missions",    1260,           229,    130,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[0][MC_PXO_REFRESH].button},
3728                 {"Pilot Info",                  1261,           467,    132,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[0][MC_PILOT_INFO].button},        
3729                 {"Missions",                    1262,           27,     376,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[0][MC_MISSION_FILTER].button},
3730                 {"Campaigns",                   1263,           110,    376,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[0][MC_CAMPAIGN_FILTER].button},
3731                 {"Cancel",                              387,            328,    364,    UI_XSTR_COLOR_PINK,     -1,     &Multi_create_buttons[0][MC_CANCEL].button},
3732                 {"1",                                           1264,           482,    381,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[0][MC_TEAM0].button},
3733                 {"2",                                           1265,           514,    381,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[0][MC_TEAM1].button},
3734                 {"Kick",                                        1266,           539,    381,    UI_XSTR_COLOR_PINK,     -1,     &Multi_create_buttons[0][MC_KICK].button},
3735                 {"Close",                               1508,           582,    381,    UI_XSTR_COLOR_PINK,     -1,     &Multi_create_buttons[0][MC_CLOSE].button},     
3736                 {"Host Options",                1267,           436,    423,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[0][MC_HOST_OPTIONS].button},              
3737                 {"Commit",                              1062,           572,    413,    UI_XSTR_COLOR_PINK,     -1,     &Multi_create_buttons[0][MC_ACCEPT].button}
3738 #endif
3739         },              
3740         { // GR_1024            
3741                 // not needed for FS1
3742 #ifndef MAKE_FS1
3743                 {"All",                                 1256,           61,     253,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[1][MC_SHOW_ALL].button},
3744                 {"Coop",                                        1257,           124,    253,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[1][MC_SHOW_COOP].button},
3745                 {"Team",                                        1258,           194,    253,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[1][MC_SHOW_TEAM].button},
3746                 {"Dogfight",                    1259,           261,    253,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[1][MC_SHOW_DOGFIGHT].button},
3747                 {"Refresh Missions",    1260,           501,    218,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[1][MC_PXO_REFRESH].button},
3748                 {"Pilot Info",                  1261,           814,    216,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[1][MC_PILOT_INFO].button},        
3749                 {"Missions",                    1262,           46,     601,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[1][MC_MISSION_FILTER].button},
3750                 {"Campaigns",                   1263,           174,    601,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[1][MC_CAMPAIGN_FILTER].button},
3751                 {"Cancel",                              387,            559,    582,    UI_XSTR_COLOR_PINK,     -1,     &Multi_create_buttons[1][MC_CANCEL].button},
3752                 {"1",                                           1264,           772,    610,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[1][MC_TEAM0].button},
3753                 {"2",                                           1265,           826,    610,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[1][MC_TEAM1].button},
3754                 {"Kick",                                        1266,           872,    610,    UI_XSTR_COLOR_PINK,     -1,     &Multi_create_buttons[1][MC_KICK].button},
3755                 {"Close",                               1508,           949,    610,    UI_XSTR_COLOR_PINK,     -1,     &Multi_create_buttons[1][MC_CLOSE].button},     
3756                 {"Host Options",                1267,           755,    683,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[1][MC_HOST_OPTIONS].button},              
3757                 {"Commit",                              1062,           932,    667,    UI_XSTR_COLOR_PINK,     -1,     &Multi_create_buttons[1][MC_ACCEPT].button}
3758 #endif
3759         },
3760 };
3761
3762 // squad war checkbox
3763 UI_CHECKBOX     Multi_create_sw_checkbox;
3764 const char *Multi_create_sw_checkbox_fname[GR_NUM_RESOLUTIONS] = {
3765         "MC_SW_00",
3766         "MC_SW_00",
3767 };
3768 int Multi_create_sw_checkbox_coords[GR_NUM_RESOLUTIONS][2] = {
3769         { // GR_640
3770                 6, 75
3771         },
3772         { // GR_1024
3773                 18, 135
3774         }
3775 };
3776 int Multi_create_sw_checkbox_text[GR_NUM_RESOLUTIONS][2] = {
3777         { // GR_640
3778                 6, 95
3779         },
3780         { // GR_640
3781                 18, 155
3782         },
3783 };
3784
3785 // game information text areas
3786 int Mc_list_coords[GR_NUM_RESOLUTIONS][4] = {
3787         { // GR_640
3788 #ifdef MAKE_FS1
3789                 105, 170, 315, 146
3790 #else
3791                 105, 173, 311, 152
3792 #endif
3793         },
3794         { // GR_1024
3795                 62, 275, 600, 262
3796         }
3797 };
3798
3799 int Mc_players_coords[GR_NUM_RESOLUTIONS][4] = {
3800         { // GR_640
3801 #ifdef MAKE_FS1
3802                 465, 163, 138, 172
3803 #else
3804                 463, 164, 144, 180
3805 #endif
3806         },
3807         { // GR_1024
3808                 741, 262, 144, 180
3809         }
3810 };
3811
3812 int Mc_info_coords[GR_NUM_RESOLUTIONS][4] = {
3813         { // GR_640
3814 #ifdef MAKE_FS1
3815                 49, 407, 372, 56
3816 #else
3817                 47, 405, 363, 59
3818 #endif
3819         },
3820         { // GR_1024
3821                 75, 648, 363, 59
3822         }
3823 };
3824
3825 // mission icon stuff
3826 int Mc_icon_type_coords[GR_NUM_RESOLUTIONS][2] = {
3827         { // GR_640
3828                 38, -2          // y is an offset
3829         },
3830         { // GR_1024
3831                 61, -2          // y is an offset
3832         }
3833 };
3834
3835 int Mc_icon_volition_coords[GR_NUM_RESOLUTIONS][2] = {
3836         { // GR_640
3837                 61, -1          // y is an offset
3838         },
3839         { // GR_1024
3840                 98, 1           // y is an offset
3841         }
3842 };
3843
3844 int Mc_icon_silent_coords[GR_NUM_RESOLUTIONS][2] = {
3845         { // GR_640
3846                 72, 0           // y is an offset
3847         },
3848         { // GR_1024
3849                 115, 0          // y is an offset
3850         }
3851 };
3852
3853 int Mc_icon_valid_coords[GR_NUM_RESOLUTIONS][2] = {
3854         { // GR_640
3855                 91, 0           // y is an offset
3856         },
3857         { // GR_1024
3858                 146, 0          // y is an offset
3859         }
3860 };
3861
3862 // mission/campaign list column areas
3863 int Mc_column1_w[GR_NUM_RESOLUTIONS] = {
3864         194,            // GR_640
3865         310             // GR_1024
3866 };
3867
3868 int Mc_column2_w[GR_NUM_RESOLUTIONS] = {
3869         38,             // GR_640
3870         61                      // GR_1024
3871 };
3872
3873 int Mc_column3_w[GR_NUM_RESOLUTIONS] = {
3874         77,             // GR_640
3875         123             // GR_1024
3876 };
3877
3878 int Mc_mission_name_x[GR_NUM_RESOLUTIONS] = {
3879         105,            // GR_640
3880         168             // GR_1024
3881 };
3882
3883 int Mc_mission_count_x[GR_NUM_RESOLUTIONS] = {
3884         314,            // GR_640
3885         502             // GR_1024
3886 };
3887
3888 int Mc_mission_fname_x[GR_NUM_RESOLUTIONS] = {
3889         337,            // GR_640
3890         539             // GR_1024
3891 };
3892
3893 int Mc_create_game_text[GR_NUM_RESOLUTIONS][2] = {
3894         {13, 116},      // GR_640
3895         {21, 186}       // GR_1024
3896 };
3897
3898 int Mc_players_text[GR_NUM_RESOLUTIONS][2] = {
3899         {467, 150},     // GR_640
3900         {747, 240}      // GR_1024
3901 };
3902
3903 int Mc_team_text[GR_NUM_RESOLUTIONS][2] = {
3904         {484, 342},     // GR_640
3905         {774, 547}      // GR_1024
3906 };
3907
3908 int Mc_slider_coords[GR_NUM_RESOLUTIONS][4] = {
3909         {
3910                 3, 197, 13, 105 // GR_640
3911         },
3912         {
3913                 5, 316, 20, 168 // GR_1024
3914         }
3915 };
3916
3917 const char *Mc_slider_bitmap[GR_NUM_RESOLUTIONS] = {
3918         "slider",
3919         "2_slider"
3920 };
3921
3922 // player list control thingie defs
3923 #define MULTI_CREATE_PLIST_MAX_DISPLAY          20
3924 int Multi_create_plist_select_flag;                                                                     // flag indicating if we have a play selected
3925 short Multi_create_plist_select_id;                                                     // the net address of the currently selected player (for lookup)
3926
3927 // master tracker details
3928 int Multi_create_frame_count;                                                                                   // framecount
3929 int Multi_create_mt_tried_login;                                                                                // attempted to login this server on the MT
3930
3931 // mission filter settings                                                                              
3932 int Multi_create_filter;                                                                                                // what mode we're in
3933
3934 // game/campaign list control defs
3935 int Multi_create_list_max_display[GR_NUM_RESOLUTIONS] = {
3936         15,             // GR_640
3937         26                      // GR_1024
3938 };
3939
3940
3941 int Multi_create_list_count;                                                                                    // number of items in listbox
3942 int Multi_create_list_mode;                                                                                     // 0 == mission mode, 1 == campaign mode
3943 int Multi_create_list_start;                                                                                    // where to start displaying from
3944 int Multi_create_list_select;                                                                                   // which item is currently highlighted
3945 int Multi_create_files_loaded;
3946
3947 char Multi_create_files_array[MULTI_CREATE_MAX_LIST_ITEMS][MAX_FILENAME_LEN];
3948
3949 int Multi_create_mission_count;                                                                                 // how many we have
3950 int Multi_create_campaign_count;
3951 multi_create_info Multi_create_mission_list[MULTI_CREATE_MAX_LIST_ITEMS];
3952 multi_create_info Multi_create_campaign_list[MULTI_CREATE_MAX_LIST_ITEMS];
3953
3954 // use a pointer for the file list.  Will point to either the missions or the campaigns
3955 multi_create_info *Multi_create_file_list;
3956
3957 // LOCAL function definitions
3958 void multi_create_check_buttons();
3959 void multi_create_button_pressed(int n);
3960 void multi_create_init_as_server();
3961 void multi_create_init_as_client();
3962 void multi_create_do_netstuff();
3963 void multi_create_plist_scroll_up();
3964 void multi_create_plist_scroll_down();
3965 void multi_create_plist_process();
3966 void multi_create_plist_blit_normal();
3967 void multi_create_plist_blit_team();
3968 void multi_create_list_scroll_up();
3969 void multi_create_list_scroll_down();
3970 void multi_create_list_do();
3971 void multi_create_list_select_item(int n);
3972 void multi_create_list_blit_icons(int list_index, int y_start);
3973 void multi_create_accept_hit();
3974 void multi_create_draw_filter_buttons();
3975 void multi_create_set_selected_team(int team);
3976 short multi_create_get_mouse_id();
3977 int multi_create_ok_to_commit();
3978 int multi_create_verify_cds();
3979 void multi_create_refresh_pxo();
3980 void multi_create_sw_clicked();
3981
3982 // since we can selectively filter out mission/campaign types we always need to map a selected index (which is relative 
3983 // to the displayed list), to an absolute index (which is relative to the total file list - some of which may filtered out)
3984 void multi_create_select_to_filename(int select_index, char *filename, const int max_filelen);
3985 int multi_create_select_to_index(int select_index);
3986
3987 int Multi_create_should_show_popup = 0;
3988
3989
3990 // sorting function to sort mission lists.. Basic sorting on mission name
3991 int multi_create_sort_func(const void *a, const void *b)
3992 {
3993         multi_create_info *m1, *m2;
3994
3995         m1 = (multi_create_info *)a;
3996         m2 = (multi_create_info *)b;
3997
3998         return ( strcmp(m1->name, m2->name) );
3999 }
4000
4001 void multi_create_setup_list_data(int mode)
4002 {       
4003         int idx,should_sort,switched_modes;
4004         
4005         // set the current mode
4006         should_sort = 0;
4007         switched_modes = 0;
4008         if((Multi_create_list_mode != mode) && (mode != -1)){
4009                 Multi_create_list_mode = mode;  
4010                 switched_modes = 1;
4011
4012                 // set up the list pointers
4013                 if ( Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ) {
4014                         Multi_create_file_list = Multi_create_mission_list;                     
4015                 } else if ( Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ) {
4016                         Multi_create_file_list = Multi_create_campaign_list;                    
4017                 } else {
4018                         Int3();
4019                 }
4020         }
4021
4022         // get the mission count based upon the filter selected
4023         if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){               
4024                 switch(Multi_create_filter){
4025                 case MISSION_TYPE_MULTI:
4026                         Multi_create_list_count = Multi_create_mission_count;
4027                         break;
4028                 default : 
4029                         Multi_create_list_count = 0;
4030                         // find all missions which match 
4031                         for(idx=0;idx<Multi_create_mission_count;idx++){
4032                                 if(Multi_create_mission_list[idx].flags & Multi_create_filter){
4033                                         Multi_create_list_count++;
4034                                 }
4035                         }
4036
4037                         // if we switched modes and we have more than 0 items, sort them
4038                         if(switched_modes && (Multi_create_list_count > 0)){
4039                                 should_sort = 1;
4040                         }
4041                         break;
4042                 }
4043         } else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
4044                 switch(Multi_create_filter){
4045                 case MISSION_TYPE_MULTI:
4046                         Multi_create_list_count = Multi_create_campaign_count;
4047                         break;
4048                 default :
4049                         Multi_create_list_count = 0;
4050                         // find all missions which match 
4051                         for(idx=0;idx<Multi_create_campaign_count;idx++){
4052                                 if(Multi_create_campaign_list[idx].flags & Multi_create_filter){
4053                                         Multi_create_list_count++;
4054                                 }
4055                         }
4056
4057                         // if we switched modes and we have more than 0 items, sort them
4058                         if(switched_modes && (Multi_create_list_count > 0)){
4059                                 should_sort = 1;
4060                         }
4061                         break;
4062                 }
4063         }
4064         
4065         // reset the list start and selected indices
4066         Multi_create_list_start = 0;
4067         Multi_create_list_select = -1;
4068         multi_create_list_select_item(Multi_create_list_start); 
4069
4070         // sort the list of missions if necessary
4071         if( should_sort ) {             
4072                 qsort(Multi_create_file_list, Multi_create_list_count, sizeof(multi_create_info), multi_create_sort_func);              
4073         }
4074
4075 #ifndef MAKE_FS1
4076         // reset the slider
4077         Multi_create_slider.set_numberItems(Multi_create_list_count > Multi_create_list_max_display[gr_screen.res] ? Multi_create_list_count-Multi_create_list_max_display[gr_screen.res] : 0);
4078 #endif
4079 }
4080
4081 void multi_create_game_init()
4082 {
4083         int idx;
4084         ui_button_info *b;
4085         
4086         // now make sure to initialze various netgame stuff based upon whether we're on a standalone or not
4087         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4088                 multi_create_init_as_server();
4089         } else {
4090                 multi_create_init_as_client();
4091         }
4092
4093         // initialize the player list data              
4094         Multi_create_plist_select_flag = 0;
4095         Multi_create_plist_select_id = -1;      
4096
4097         // create the interface window
4098         Multi_create_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
4099         Multi_create_window.set_mask_bmap(Multi_create_bitmap_mask_fname[gr_screen.res]);
4100
4101         // load the background bitmap
4102         Multi_create_bitmap = bm_load(Multi_create_bitmap_fname[gr_screen.res]);
4103         if(Multi_create_bitmap < 0){
4104                 // we failed to load the bitmap - this is very bad
4105                 Int3();
4106         }
4107
4108         // close any previous existing instances of the chatbox and create a new one
4109         chatbox_close();
4110         chatbox_create();
4111
4112         // load the help overlay 
4113         help_overlay_load(MULTI_CREATE_OVERLAY);
4114         help_overlay_set_state(MULTI_CREATE_OVERLAY, 0);
4115
4116         // initialize the common notification messaging
4117         multi_common_notify_init();             
4118
4119         // use the common interface palette
4120         multi_common_set_palette();
4121
4122         // create the interface buttons
4123         for(idx=0; idx<MULTI_CREATE_NUM_BUTTONS; idx++){
4124                 b = &Multi_create_buttons[gr_screen.res][idx];
4125         
4126                 // create the object            
4127                 b->button.create(&Multi_create_window, "", b->x, b->y, 1, 1, ((idx == MC_SCROLL_LIST_UP) || (idx == MC_SCROLL_LIST_DOWN)), 1);
4128
4129                 // set the sound to play when highlighted
4130                 b->button.set_highlight_action(common_play_highlight_sound);
4131
4132                 // set the ani for the button
4133                 b->button.set_bmaps(b->filename);
4134
4135                 // set the hotspot
4136                 b->button.link_hotspot(b->hotspot);             
4137
4138                 // some special case stuff for the pxo refresh button
4139                 if(idx == MC_PXO_REFRESH){                      
4140                         // if not a PXO game, or if I'm not a server disable and hide the button
4141                         if(!MULTI_IS_TRACKER_GAME || !MULTIPLAYER_MASTER){
4142                                 b->button.hide();
4143                                 b->button.disable();
4144                         }                       
4145                 }
4146         }       
4147
4148         // create xstrs
4149         for(idx=0; idx<MULTI_CREATE_NUM_TEXT; idx++){
4150                 Multi_create_window.add_XSTR(&Multi_create_text[gr_screen.res][idx]);
4151         }
4152
4153         // if this is a PXO game, enable the squadwar checkbox  
4154         Multi_create_sw_checkbox.create(&Multi_create_window, "", Multi_create_sw_checkbox_coords[gr_screen.res][0], Multi_create_sw_checkbox_coords[gr_screen.res][1], 0);
4155         Multi_create_sw_checkbox.set_bmaps(Multi_create_sw_checkbox_fname[gr_screen.res], 6, 0);
4156         if(!MULTI_IS_TRACKER_GAME){
4157                 Multi_create_sw_checkbox.hide();
4158                 Multi_create_sw_checkbox.disable();
4159         }
4160         
4161 #ifdef FS2_DEMO
4162         // disable squad war button in demo
4163         Multi_create_sw_checkbox.hide();
4164         Multi_create_sw_checkbox.disable();
4165 #endif
4166
4167         // initialize the mission type filtering mode
4168         Multi_create_filter = MISSION_TYPE_MULTI;
4169
4170         // initialize the list mode, and load in a list
4171         memset(Multi_create_mission_list, 0, sizeof(multi_create_info) * MULTI_CREATE_MAX_LIST_ITEMS);
4172         memset(Multi_create_campaign_list, 0, sizeof(multi_create_info) * MULTI_CREATE_MAX_LIST_ITEMS);
4173         for(idx=0; idx<MULTI_CREATE_MAX_LIST_ITEMS; idx++){
4174                 Multi_create_mission_list[idx].valid_status = MVALID_STATUS_UNKNOWN;
4175                 Multi_create_campaign_list[idx].valid_status = MVALID_STATUS_UNKNOWN;
4176         }
4177         Multi_create_list_mode = MULTI_CREATE_SHOW_MISSIONS;
4178         Multi_create_list_start = -1;
4179         Multi_create_list_select = -1;
4180         Multi_create_list_count = 0;
4181
4182 #ifndef MAKE_FS1
4183         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);
4184 #endif
4185
4186         // create the player list select button
4187         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);
4188         Multi_create_player_select_button.hide();               
4189         
4190         // create the mission/campaign list select button
4191         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);
4192         Multi_create_list_select_button.hide(); 
4193
4194         // set hotkeys for a couple of things.
4195         Multi_create_buttons[gr_screen.res][MC_ACCEPT].button.set_hotkey(KEY_CTRLED+SDLK_RETURN);
4196
4197         // init some master tracker stuff
4198         Multi_create_frame_count = 0;
4199         Multi_create_mt_tried_login = 0;
4200
4201         // remove campaign flags
4202         Game_mode &= ~(GM_CAMPAIGN_MODE);
4203         
4204         // send any pilots as appropriate
4205         multi_data_send_my_junk();
4206         Multi_create_file_list = Multi_create_mission_list;
4207
4208         Multi_create_campaign_count = 0;
4209         Multi_create_mission_count = 0;
4210         Multi_create_files_loaded = 0;  
4211 }
4212
4213 void multi_create_game_do()
4214 {
4215         int player_index;
4216 #ifndef MAKE_FS1
4217         const char *loading_str = XSTR("Loading", 1336);
4218         int str_w, str_h;
4219 #endif
4220
4221         // set this if we want to show the pilot info popup
4222         Multi_create_should_show_popup = 0;
4223
4224         // first thing is to load the files
4225         if ( !Multi_create_files_loaded ) {
4226                 // if I am a client, send a list request to the server for the missions
4227                 if ( MULTIPLAYER_CLIENT ) {
4228                         send_mission_list_request( MISSION_LIST_REQUEST );
4229                 } else {
4230                         int loading_bitmap;
4231
4232                         loading_bitmap = bm_load(Multi_create_loading_fname[gr_screen.res]);
4233
4234                         // draw the background, etc
4235                         gr_reset_clip();
4236                         GR_MAYBE_CLEAR_RES(Multi_create_bitmap);
4237                         if(Multi_create_bitmap != -1){
4238                                 gr_set_bitmap(Multi_create_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4239                                 gr_bitmap(0, 0);
4240                         }
4241                         chatbox_render();
4242                         if ( loading_bitmap > -1 ){
4243                                 gr_set_bitmap(loading_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4244                         }
4245                         gr_bitmap( Please_wait_coords[gr_screen.res][MC_X_COORD], Please_wait_coords[gr_screen.res][MC_Y_COORD] );
4246
4247                         // draw "Loading" on it
4248 #ifndef MAKE_FS1
4249                         gr_set_color_fast(&Color_normal);
4250                         gr_set_font(FONT2);
4251                         gr_get_string_size(&str_w, &str_h, loading_str);
4252                         gr_string((gr_screen.max_w - str_w) / 2, (gr_screen.max_h - str_h) / 2, loading_str);
4253                         gr_set_font(FONT1);
4254 #endif
4255
4256                         gr_flip();
4257
4258                         multi_create_list_load_missions();
4259                         multi_create_list_load_campaigns();
4260
4261                         // if this is a tracker game, validate missions
4262                         if(MULTI_IS_TRACKER_GAME){
4263                                 multi_update_valid_missions();
4264                         }
4265
4266                         // update the file list
4267                         multi_create_setup_list_data(MULTI_CREATE_SHOW_MISSIONS);                                               
4268                 }
4269
4270                 // don't bother setting netgame state if ont the server
4271                 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4272                         Netgame.game_state = NETGAME_STATE_FORMING;
4273                         send_netgame_update_packet();
4274                 }       
4275
4276                 // if we're on the standalone we have to tell him that we're now in the host setup screen       
4277                 Net_player->state = NETPLAYER_STATE_HOST_SETUP; 
4278                 send_netplayer_update_packet();
4279
4280                 Multi_create_files_loaded = 1;
4281         }
4282
4283         int k = chatbox_process();
4284         k = Multi_create_window.process(k,0);
4285
4286         switch(k){      
4287         // same as the cancel button
4288         case SDLK_ESCAPE:
4289                 if(help_overlay_active(MULTI_CREATE_OVERLAY)){
4290                         help_overlay_set_state(MULTI_CREATE_OVERLAY,0);
4291                 } else {                
4292                         gamesnd_play_iface(SND_USER_SELECT);            
4293                         multi_quit_game(PROMPT_HOST);           
4294                 }
4295                 break;  
4296         }       
4297
4298         if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
4299                 help_overlay_set_state(MULTI_CREATE_OVERLAY, 0);
4300         }
4301
4302         // process any button clicks
4303         multi_create_check_buttons();
4304
4305         // do any network related stuff
4306         multi_create_do_netstuff(); 
4307
4308         // draw the background, etc
4309         gr_reset_clip();
4310         GR_MAYBE_CLEAR_RES(Multi_create_bitmap);
4311         if(Multi_create_bitmap != -1){
4312                 gr_set_bitmap(Multi_create_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4313                 gr_bitmap(0,0);
4314         }
4315
4316         // if we're not in team vs. team mode, don't draw the team buttons
4317         if(!(Netgame.type_flags & NG_TYPE_TEAM)){
4318                 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.hide();
4319                 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.hide();
4320                 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.disable();
4321                 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.disable();
4322         } else {
4323                 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.enable();
4324                 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.enable();
4325                 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.unhide();
4326                 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.unhide();                          
4327         }               
4328
4329         // draw the window itself
4330         Multi_create_window.draw();
4331
4332         gr_set_color_fast(&Color_normal);
4333
4334 #ifndef MAKE_FS1
4335         // draw Create Game text
4336         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));
4337
4338         // draw players text
4339         gr_string(Mc_players_text[gr_screen.res][MC_X_COORD], Mc_players_text[gr_screen.res][MC_Y_COORD], XSTR("Players", 1269));
4340
4341         // draw players text
4342         gr_string(Mc_team_text[gr_screen.res][MC_X_COORD], Mc_team_text[gr_screen.res][MC_Y_COORD], XSTR("Team", 1258));
4343 #endif
4344
4345         // process and display the player list  
4346         // NOTE : this must be done before the buttons are checked to insure that a player hasn't dropped 
4347         multi_create_plist_process();
4348         if(Netgame.type_flags & NG_TYPE_TEAM){
4349                 multi_create_plist_blit_team();
4350         } else {
4351                 multi_create_plist_blit_normal();
4352         }
4353
4354         // process and display the game/campaign list
4355         multi_create_list_do();
4356         
4357         // draw the correct mission filter button
4358         multi_create_draw_filter_buttons();
4359
4360         // display any text in the info area
4361         multi_common_render_text();
4362
4363         // display any pending notification messages
4364         multi_common_notify_do();       
4365         
4366         // force the correct mission/campaign button to light up
4367         if( Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ){
4368                 Multi_create_buttons[gr_screen.res][MC_MISSION_FILTER].button.draw_forced(2);
4369         } else {
4370                 Multi_create_buttons[gr_screen.res][MC_CAMPAIGN_FILTER].button.draw_forced(2);
4371         }
4372
4373         // force draw the closed button if it is toggled on
4374         if(Netgame.flags & NG_FLAG_TEMP_CLOSED){
4375                 Multi_create_buttons[gr_screen.res][MC_CLOSE].button.draw_forced(2);
4376         }
4377
4378         // process and show the chatbox thingie 
4379         chatbox_render();
4380
4381         // draw tooltips
4382         Multi_create_window.draw_tooltip();
4383
4384         // display the voice status indicator
4385         multi_common_voice_display_status();
4386
4387         // blit the help overlay if necessary
4388         help_overlay_maybe_blit(MULTI_CREATE_OVERLAY);
4389
4390         // test code
4391         if(MULTI_IS_TRACKER_GAME){
4392                 if(Netgame.type_flags & NG_TYPE_SW){
4393                         gr_set_color_fast(&Color_bright);
4394                 } else {
4395                         gr_set_color_fast(&Color_normal);
4396                 }
4397                 gr_string(Multi_create_sw_checkbox_text[gr_screen.res][0], Multi_create_sw_checkbox_text[gr_screen.res][1], "SquadWar");
4398         }
4399
4400         // flip the buffer
4401         gr_flip();              
4402                 
4403         // if we're supposed to show the pilot info popup, do it now
4404         if(Multi_create_should_show_popup){             
4405                 // get the player index and address of the player item the mouse is currently over
4406                 if(Multi_create_plist_select_flag){             
4407                         player_index = find_player_id(Multi_create_plist_select_id);
4408                         if(player_index != -1){                 
4409                                 multi_pinfo_popup(&Net_players[player_index]);
4410                         }
4411                 }
4412         }
4413
4414         // increment the frame count
4415         Multi_create_frame_count++;     
4416 }
4417
4418 void multi_create_game_close()
4419 {
4420         // unload any bitmaps
4421         if(!bm_unload(Multi_create_bitmap)){
4422                 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_create_bitmap_fname[gr_screen.res]));
4423         }               
4424
4425         // unload the help overlay
4426         help_overlay_unload(MULTI_CREATE_OVERLAY);
4427         
4428         // destroy the chatbox
4429         // chatbox_close();
4430         
4431         // destroy the UI_WINDOW
4432         Multi_create_window.destroy();
4433 }
4434
4435 void multi_create_check_buttons()
4436 {
4437         int idx;
4438         for(idx=0;idx<MULTI_CREATE_NUM_BUTTONS;idx++){
4439                 // we only really need to check for one button pressed at a time, so we can break after 
4440                 // finding one.
4441                 if(Multi_create_buttons[gr_screen.res][idx].button.pressed()){
4442                         multi_create_button_pressed(idx);
4443                         break;
4444                 }
4445         }
4446
4447         // if the squad war checkbox was clicked
4448         if(Multi_create_sw_checkbox.changed()){
4449                 multi_create_sw_clicked();
4450         }
4451 }
4452
4453 void multi_create_button_pressed(int n)
4454 {
4455         int idx;
4456         
4457         switch(n){
4458         case MC_CANCEL :
4459                 gamesnd_play_iface(SND_USER_SELECT);            
4460                 multi_quit_game(PROMPT_HOST);           
4461                 break;
4462         case MC_ACCEPT :        
4463                 // if valid commit conditions have not been met
4464                 if(!multi_create_ok_to_commit()){
4465                         break;
4466                 }
4467
4468                 // commit
4469                 multi_create_accept_hit();              
4470                 break;
4471
4472         // help button
4473         case MC_HELP :
4474                 if(!help_overlay_active(MULTI_CREATE_OVERLAY)){
4475                         help_overlay_set_state(MULTI_CREATE_OVERLAY,1);
4476                 } else {
4477                         help_overlay_set_state(MULTI_CREATE_OVERLAY,0);
4478                 }
4479                 break;
4480
4481         // scroll the info text box up
4482         case MC_SCROLL_INFO_UP:
4483                 multi_common_scroll_text_up();
4484                 break;
4485
4486         // scroll the info text box down
4487         case MC_SCROLL_INFO_DOWN:
4488                 multi_common_scroll_text_down();
4489                 break;
4490
4491         // scroll the player list up
4492         case MC_SCROLL_PLAYERS_UP:
4493                 multi_create_plist_scroll_up();
4494                 break;
4495
4496         // scroll the player list down
4497         case MC_SCROLL_PLAYERS_DOWN:
4498                 multi_create_plist_scroll_down();
4499                 break;
4500
4501         // scroll the game/campaign list up
4502         case MC_SCROLL_LIST_UP:
4503                 multi_create_list_scroll_up();
4504 #ifndef MAKE_FS1
4505                 Multi_create_slider.forceUp();  // move slider up
4506 #endif
4507                 break;
4508
4509         // scroll the game/campaign list down
4510         case MC_SCROLL_LIST_DOWN:
4511                 multi_create_list_scroll_down();
4512 #ifndef MAKE_FS1
4513                 Multi_create_slider.forceDown();        // move slider down
4514 #endif
4515                 break;
4516
4517         // go to the options screen
4518         case MC_OPTIONS:                
4519                 gamesnd_play_iface(SND_USER_SELECT);
4520                 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
4521                 break;  
4522
4523         // show all missions
4524         case MC_SHOW_ALL:
4525                 if(Multi_create_filter != MISSION_TYPE_MULTI){
4526                         gamesnd_play_iface(SND_USER_SELECT);
4527                         Multi_create_filter = MISSION_TYPE_MULTI;
4528                         multi_create_setup_list_data(Multi_create_list_mode);                                           // update the file list
4529                 } else {
4530                         gamesnd_play_iface(SND_GENERAL_FAIL);
4531                 }
4532                 break;
4533
4534         // show cooperative missions
4535         case MC_SHOW_COOP:
4536                 if(Multi_create_filter != MISSION_TYPE_MULTI_COOP){
4537                         gamesnd_play_iface(SND_USER_SELECT);
4538                         Multi_create_filter = MISSION_TYPE_MULTI_COOP;                  
4539                         multi_create_setup_list_data(Multi_create_list_mode);                                           // update the file list
4540                 } else {
4541                         gamesnd_play_iface(SND_GENERAL_FAIL);
4542                 }
4543                 break;
4544
4545         // show team vs. team missions
4546         case MC_SHOW_TEAM:
4547                 if(Multi_create_filter != MISSION_TYPE_MULTI_TEAMS){
4548                         gamesnd_play_iface(SND_USER_SELECT);
4549                         Multi_create_filter = MISSION_TYPE_MULTI_TEAMS; 
4550                         multi_create_setup_list_data(Multi_create_list_mode);                                           // update the file list
4551                 } else {
4552                         gamesnd_play_iface(SND_GENERAL_FAIL);
4553                 }
4554                 break;  
4555
4556         // show dogfight missions
4557 #ifndef MAKE_FS1
4558         case MC_SHOW_DOGFIGHT:
4559                 if (Multi_create_filter != MISSION_TYPE_MULTI_DOGFIGHT){
4560                         gamesnd_play_iface(SND_USER_SELECT);
4561                         Multi_create_filter = MISSION_TYPE_MULTI_DOGFIGHT;
4562                         multi_create_setup_list_data(Multi_create_list_mode);                                           // update the file list
4563                 } else {
4564                         gamesnd_play_iface(SND_GENERAL_FAIL);
4565                 }
4566                 break;
4567 #endif
4568
4569         // toggle temporary netgame closed on/off
4570         case MC_CLOSE:
4571                 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4572                         Netgame.flags ^= NG_FLAG_TEMP_CLOSED;
4573                 } else {
4574                         Netgame.options.flags |= MLO_FLAG_TEMP_CLOSED;
4575                         multi_options_update_netgame();
4576                 }
4577                 gamesnd_play_iface(SND_USER_SELECT);
4578                 break;
4579
4580         // kick the currently selected player (if possible)
4581         case MC_KICK:
4582                 // lookup the player at the specified index             
4583                 if(Multi_create_plist_select_flag){              
4584                         idx = find_player_id(Multi_create_plist_select_id);
4585                         // kick him - but don't ban him
4586                         if(idx != -1){                  
4587                                 multi_kick_player(idx,0);                               
4588                         }
4589                 }
4590                 break;
4591                         
4592         // switch to individual mission mode and load in a list
4593         case MC_MISSION_FILTER:
4594                 if(Multi_create_list_mode != MULTI_CREATE_SHOW_MISSIONS){
4595                         Netgame.campaign_mode = MP_SINGLE;
4596
4597                         gamesnd_play_iface(SND_USER_SELECT);                                                                                            
4598                         
4599                         // update the file list
4600                         multi_create_setup_list_data(MULTI_CREATE_SHOW_MISSIONS);                                               
4601                 } else {
4602                         gamesnd_play_iface(SND_GENERAL_FAIL);
4603                 }
4604                 break;
4605
4606         // switch to campaign mode and load in a list
4607         case MC_CAMPAIGN_FILTER:                
4608                 // switch off squad war
4609                 Multi_create_sw_checkbox.set_state(0);
4610                 Netgame.type_flags = NG_TYPE_COOP;
4611
4612                 if(Multi_create_list_mode != MULTI_CREATE_SHOW_CAMPAIGNS){
4613                         Netgame.campaign_mode = MP_CAMPAIGN;
4614
4615                         gamesnd_play_iface(SND_USER_SELECT);                    
4616                         
4617                         // update the file list
4618                         multi_create_setup_list_data(MULTI_CREATE_SHOW_CAMPAIGNS);                                              
4619                 } else {
4620                         gamesnd_play_iface(SND_GENERAL_FAIL);
4621                 }
4622                 break;
4623
4624         // attempt to set the selected player's team
4625         case MC_TEAM0:
4626                 multi_create_set_selected_team(0);
4627                 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4628                         multi_team_send_update();
4629                 }
4630                 break;
4631
4632         // attempt to set the selected player's team
4633         case MC_TEAM1:
4634                 multi_create_set_selected_team(1);
4635                 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4636                         multi_team_send_update();
4637                 }
4638                 break;  
4639
4640         // popup the pilot info dialog for the currently selected pilot (will occur at the end of the frame)
4641         case MC_PILOT_INFO:
4642                 Multi_create_should_show_popup = 1;
4643                 break;
4644
4645         // go to the host options screen
4646         case MC_HOST_OPTIONS:
4647                 gamesnd_play_iface(SND_USER_SELECT);
4648                 gameseq_post_event(GS_EVENT_MULTI_HOST_OPTIONS);
4649                 break;
4650
4651         // refresh PXO file list
4652         case MC_PXO_REFRESH:            
4653                 if(!MULTI_IS_TRACKER_GAME){
4654                         break;
4655                 }
4656                 multi_create_refresh_pxo();             
4657                 break;
4658
4659         default :
4660                 gamesnd_play_iface(SND_GENERAL_FAIL);
4661                 multi_common_add_notify(XSTR("Not implemented yet!",760));              
4662                 break;
4663         }
4664 }
4665
4666 // do stuff like pinging servers, sending out requests, etc
4667 void multi_create_do_netstuff()
4668 {
4669 }
4670
4671 // if not on a standalone
4672 void multi_create_init_as_server()
4673 {
4674         // set me up as the host and master
4675         Net_player->flags |= (NETINFO_FLAG_AM_MASTER | NETINFO_FLAG_GAME_HOST);         
4676 }
4677
4678 // if on a standalone
4679 void multi_create_init_as_client()
4680 {
4681         Net_player->flags |= NETINFO_FLAG_GAME_HOST;    
4682 }
4683
4684 // scroll up through the player list
4685 void multi_create_plist_scroll_up()
4686 {       
4687         gamesnd_play_iface(SND_GENERAL_FAIL);
4688 }
4689
4690 // scroll down through the player list
4691 void multi_create_plist_scroll_down()
4692 {       
4693         gamesnd_play_iface(SND_GENERAL_FAIL);
4694 }
4695
4696 void multi_create_plist_process()
4697 {
4698         int test_count,idx,player_index;
4699         
4700         // first determine if there are 0 players in the game. This should never happen since the host is _always_ in the game
4701         test_count = 0;
4702         for(idx=0;idx<MAX_PLAYERS;idx++){
4703                 // count anyone except the standalone server (if applicable)
4704                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
4705                         test_count++;
4706                 }
4707         }
4708         if(test_count <= 0){
4709                 return;
4710         }
4711         
4712         // if we had a selected item but that player has left, select myself instead
4713         if(Multi_create_plist_select_flag){
4714                 player_index = find_player_id(Multi_create_plist_select_id);
4715                 if(player_index == -1){
4716                         Multi_create_plist_select_id = Net_player->player_id;
4717                 }
4718         } else {
4719                 Multi_create_plist_select_flag = 1;
4720                 Multi_create_plist_select_id = Net_player->player_id;           
4721         }       
4722                 
4723         // if the player has clicked somewhere in the player list area
4724         if(Multi_create_player_select_button.pressed()){                                
4725                 short player_id;
4726
4727                 // get the player index and address of the player item the mouse is currently over
4728                 player_id = multi_create_get_mouse_id();
4729                 player_index = find_player_id(player_id);
4730                 if(player_index != -1){
4731                         Multi_create_plist_select_flag = 1;
4732                         Multi_create_plist_select_id = player_id;                       
4733                 } 
4734         }               
4735 }
4736
4737 void multi_create_plist_blit_normal()
4738 {
4739         int idx;                
4740         char str[CALLSIGN_LEN+5];
4741         int y_start = Mc_players_coords[gr_screen.res][MC_Y_COORD];     
4742         int total_offset;
4743
4744         // display all the players      
4745         for(idx=0;idx<MAX_PLAYERS;idx++){               
4746                 // count anyone except the standalone server (if applicable)
4747                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
4748                         // total x offset
4749                         total_offset = 0;
4750
4751                         // highlight him if he's the host                       
4752                         if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4753                                 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4754                                         gr_set_color_fast(&Color_text_active_hi);
4755                                 } else {
4756                                         gr_set_color_fast(&Color_bright);
4757                                 }
4758                         } else {
4759                                 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4760                                         gr_set_color_fast(&Color_text_active);
4761                                 } else {
4762                                         gr_set_color_fast(&Color_text_normal);
4763                                 }
4764                         }
4765                         
4766                         // optionally draw his CD status
4767                         if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4768                                 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4769                                 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4770
4771                                 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4772                         }                       
4773                         
4774                         // make sure the string will fit, then display it
4775                         SDL_strlcpy(str, Net_players[idx].player->callsign, SDL_arraysize(str));
4776                         if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4777                                 SDL_strlcat(str, XSTR("(O)",787), SDL_arraysize(str));  // [[ Observer ]]
4778                         }
4779                         gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4780                         gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4781
4782                         y_start += 10;                  
4783                 }
4784         }               
4785 }
4786
4787 void multi_create_plist_blit_team()
4788 {
4789         int idx;                
4790         char str[CALLSIGN_LEN+1];
4791         int y_start = Mc_players_coords[gr_screen.res][MC_Y_COORD];     
4792         int total_offset;
4793
4794         // display all the red players first
4795         for(idx=0;idx<MAX_PLAYERS;idx++){
4796                 // count anyone except the standalone server (if applicable)
4797                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
4798                         // reset total offset
4799                         total_offset = 0;
4800
4801                         // highlight him if he's the host                       
4802                         if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4803                                 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4804                                         gr_set_color_fast(&Color_text_active_hi);
4805
4806                                         // be sure to blit the correct team button 
4807                                         Multi_create_buttons[gr_screen.res][MC_TEAM0].button.draw_forced(2);
4808                                 } else {
4809                                         gr_set_color_fast(&Color_bright);
4810                                 }
4811                         } else {
4812                                 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4813                                         gr_set_color_fast(&Color_text_active);
4814
4815                                         // be sure to blit the correct team button 
4816                                         Multi_create_buttons[gr_screen.res][MC_TEAM0].button.draw_forced(2);
4817                                 } else {
4818                                         gr_set_color_fast(&Color_text_normal);
4819                                 }
4820                         }
4821
4822                         // optionally draw his CD status
4823                         if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4824                                 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4825                                 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4826
4827                                 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4828                         }                       
4829
4830                         // blit the red team indicator                  
4831                         if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
4832                                 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
4833                                         gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4834                                         gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4835
4836                                         total_offset += Multi_common_icon_dims[MICON_TEAM0_SELECT][0] + 1;                      
4837                                 }
4838                         } else {
4839                                 if(Multi_common_icons[MICON_TEAM0] != -1){
4840                                         gr_set_bitmap(Multi_common_icons[MICON_TEAM0], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4841                                         gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4842
4843                                         total_offset += Multi_common_icon_dims[MICON_TEAM0][0] + 1;                     
4844                                 }                               
4845                         }                                               
4846
4847                         // make sure the string will fit
4848                         SDL_strlcpy(str, Net_players[idx].player->callsign, SDL_arraysize(str));
4849                         if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4850                                 SDL_strlcat(str, XSTR("(O)",787), SDL_arraysize(str));
4851                         }
4852                         gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4853
4854                         // display him in the correct half of the list depending on his team
4855                         gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4856                         y_start += 10;
4857                 }
4858         }       
4859         
4860         // display all the green players next
4861         for(idx=0;idx<MAX_PLAYERS;idx++){
4862                 // count anyone except the standalone server (if applicable)
4863                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
4864                         // reset total offset
4865                         total_offset = 0;
4866
4867                         // highlight him if he's the host                       
4868                         if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4869                                 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4870                                         gr_set_color_fast(&Color_text_active_hi);
4871
4872                                         // be sure to blit the correct team button 
4873                                         Multi_create_buttons[gr_screen.res][MC_TEAM1].button.draw_forced(2);
4874                                 } else {
4875                                         gr_set_color_fast(&Color_bright);
4876                                 }
4877                         } else {
4878                                 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4879                                         gr_set_color_fast(&Color_text_active);
4880
4881                                         // be sure to blit the correct team button 
4882                                         Multi_create_buttons[gr_screen.res][MC_TEAM1].button.draw_forced(2);
4883                                 } else {
4884                                         gr_set_color_fast(&Color_text_normal);
4885                                 }
4886                         }
4887
4888                         // optionally draw his CD status
4889                         if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4890                                 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4891                                 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4892
4893                                 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4894                         }                       
4895
4896                         // blit the red team indicator                  
4897                         if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
4898                                 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
4899                                         gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4900                                         gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4901
4902                                         total_offset += Multi_common_icon_dims[MICON_TEAM1_SELECT][0] + 1;
4903                                 }                               
4904                         } else {
4905                                 if(Multi_common_icons[MICON_TEAM1] != -1){
4906                                         gr_set_bitmap(Multi_common_icons[MICON_TEAM1], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4907                                         gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4908
4909                                         total_offset += Multi_common_icon_dims[MICON_TEAM1][0] + 1;
4910                                 }
4911                         }
4912
4913                         // make sure the string will fit
4914                         SDL_strlcpy(str, Net_players[idx].player->callsign, SDL_arraysize(str));
4915                         if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4916                                 SDL_strlcat(str, XSTR("(O)",787), SDL_arraysize(str));
4917                         }
4918                         gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4919
4920                         // display him in the correct half of the list depending on his team
4921                         gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4922                         y_start += 10;
4923                 }
4924         }                       
4925 }
4926
4927 void multi_create_list_scroll_up()
4928 {
4929         if(Multi_create_list_start > 0){
4930                 Multi_create_list_start--;              
4931
4932                 gamesnd_play_iface(SND_SCROLL);
4933         } else {
4934                 gamesnd_play_iface(SND_GENERAL_FAIL);
4935         }
4936 }
4937
4938 void multi_create_list_scroll_down()
4939 {
4940         if((Multi_create_list_count - Multi_create_list_start) > Multi_create_list_max_display[gr_screen.res]){
4941                 Multi_create_list_start++;              
4942
4943                 gamesnd_play_iface(SND_SCROLL);
4944         } else {
4945                 gamesnd_play_iface(SND_GENERAL_FAIL);
4946         }
4947 }
4948
4949 // gets a list of multiplayer misisons
4950 void multi_create_list_load_missions()
4951 {
4952         char *fname, mission_name[NAME_LENGTH+1];
4953         char wild_card[6];
4954         int file_count,idx;
4955
4956         SDL_snprintf(wild_card, SDL_arraysize(wild_card), "*%s", FS_MISSION_FILE_EXT);
4957         file_count = cf_get_file_list_preallocated(MULTI_CREATE_MAX_LIST_ITEMS, Multi_create_files_array, NULL, CF_TYPE_MISSIONS, wild_card);
4958         Multi_create_mission_count = 0;
4959
4960         // maybe create a standalone dialog
4961         if(Game_mode & GM_STANDALONE_SERVER){
4962                 std_create_gen_dialog("Loading missions");
4963                 std_gen_set_text("Mission:", 1);
4964         }
4965
4966         for(idx = 0; idx < file_count; idx++){
4967                 int flags,max_players;
4968                 char *filename;
4969                 uint m_respawn;         
4970
4971                 fname = Multi_create_files_array[idx];
4972                 
4973                 // tack on any necessary file extension
4974                 filename = cf_add_ext( fname, FS_MISSION_FILE_EXT );
4975
4976                 // for multiplayer beta builds, only accept builtin missions
4977 #if defined(MULTIPLAYER_BETA_BUILD) || defined(FS2_DEMO)
4978                 if(game_find_builtin_mission(filename) == NULL){
4979                         continue;
4980                 }
4981 #elif defined(PD_BUILD)
4982                 if((game_find_builtin_mission(filename) == NULL) && !strstr(filename, "peterdrake")){
4983                         continue;
4984                 }
4985 #endif
4986
4987                 if(Game_mode & GM_STANDALONE_SERVER){                   
4988                         std_gen_set_text(filename, 2);
4989                 }
4990
4991                 flags = mission_parse_is_multi(filename, mission_name);         
4992
4993                 // if the mission is a multiplayer mission, then add it to the mission list
4994                 if ( flags ) {
4995                         max_players = mission_parse_get_multi_mission_info( filename );                         
4996                         m_respawn = The_mission.num_respawns;
4997
4998                         if ( Multi_create_mission_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
4999                                 multi_create_info *mcip;
5000
5001                                 mcip = &Multi_create_mission_list[Multi_create_mission_count];                          
5002                                 SDL_strlcpy(mcip->filename, filename, SDL_arraysize(mcip->filename));
5003                                 SDL_strlcpy(mcip->name, mission_name, SDL_arraysize(mcip->name));
5004                                 mcip->flags = flags;
5005                                 mcip->respawn = m_respawn;
5006                                 mcip->max_players = (ubyte)max_players;
5007
5008                                 // get any additional information for possibly builtin missions
5009                                 fs_builtin_mission *fb = game_find_builtin_mission(filename);
5010                                 if(fb != NULL){                                 
5011                                 }
5012
5013                                 Multi_create_mission_count++;
5014                         }
5015                 }
5016         }
5017
5018 #ifndef MAKE_FS1
5019         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);
5020 #endif
5021
5022         // maybe create a standalone dialog
5023         if(Game_mode & GM_STANDALONE_SERVER){
5024                 std_destroy_gen_dialog();               
5025         }
5026 }
5027
5028 void multi_create_list_load_campaigns()
5029 {       
5030         char *fname;
5031         int idx, file_count;
5032         int campaign_type,max_players;
5033         char title[255];
5034         char wild_card[6];
5035
5036         // maybe create a standalone dialog
5037         if(Game_mode & GM_STANDALONE_SERVER){
5038                 std_create_gen_dialog("Loading campaigns");
5039                 std_gen_set_text("Campaign:", 1);
5040         }
5041
5042         Multi_create_campaign_count = 0;
5043         SDL_snprintf(wild_card, SDL_arraysize(wild_card), "*%s", FS_CAMPAIGN_FILE_EXT);
5044         file_count = cf_get_file_list_preallocated(MULTI_CREATE_MAX_LIST_ITEMS, Multi_create_files_array, NULL, CF_TYPE_MISSIONS, wild_card);
5045         for(idx = 0; idx < file_count; idx++){
5046                 int flags;
5047                 char *filename, name[NAME_LENGTH];
5048
5049                 fname = Multi_create_files_array[idx];
5050                 
5051                 // tack on any necessary file extension
5052                 filename = cf_add_ext( fname, FS_CAMPAIGN_FILE_EXT );
5053
5054                 // for multiplayer beta builds, only accept builtin missions
5055 #if defined(MULTIPLAYER_BETA_BUILD) || defined(FS2_DEMO)
5056                 if(game_find_builtin_mission(filename) == NULL){
5057                         continue;
5058                 }
5059 #elif defined(PD_BUILD)
5060                 if((game_find_builtin_mission(filename) == NULL) && !strstr(filename, "peterdrake")){
5061                         continue;
5062                 }
5063 #endif
5064
5065                 if(Game_mode & GM_STANDALONE_SERVER){                   
5066                         std_gen_set_text(filename, 2);
5067                 }
5068
5069                 // if the campaign is a multiplayer campaign, then add the data to the campaign list items
5070                 flags = mission_campaign_parse_is_multi( filename, name, SDL_arraysize(name) );
5071                 if( flags != CAMPAIGN_TYPE_SINGLE && mission_campaign_get_info(filename,title,&campaign_type,&max_players)) {
5072                         if ( Multi_create_campaign_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
5073                                 multi_create_info *mcip;
5074
5075                                 mcip = &Multi_create_campaign_list[Multi_create_campaign_count];
5076                                 SDL_strlcpy(mcip->filename, filename, SDL_arraysize(mcip->filename));
5077                                 SDL_strlcpy(mcip->name, name, SDL_arraysize(mcip->name));
5078                                 
5079                                 // setup various flags
5080                                 if ( flags == CAMPAIGN_TYPE_MULTI_COOP ){
5081                                         mcip->flags = MISSION_TYPE_MULTI_COOP | MISSION_TYPE_MULTI;
5082                                 } else if ( flags == CAMPAIGN_TYPE_MULTI_TEAMS ) {
5083                                         mcip->flags = MISSION_TYPE_MULTI_TEAMS | MISSION_TYPE_MULTI;
5084                                 } else {
5085                                         Int3();                 // bogus campaign multi type -- find allender
5086                                 }                                                       
5087
5088                                 // 0 respawns for campaign files (should be contained within the mission files themselves)
5089                                 mcip->respawn = 0;
5090
5091                                 // 0 max players for campaign files
5092                                 mcip->max_players = (unsigned char)max_players;
5093
5094                                 // get any additional information for possibly builtin missions
5095                                 fs_builtin_mission *fb = game_find_builtin_mission(filename);
5096                                 if(fb != NULL){                                 
5097                                 }
5098
5099                                 Multi_create_campaign_count++;
5100                         }
5101                 }
5102         }       
5103         
5104         // maybe create a standalone dialog
5105         if(Game_mode & GM_STANDALONE_SERVER){
5106                 std_destroy_gen_dialog();               
5107         }
5108 }
5109
5110 void multi_create_list_do()
5111 {
5112         int idx;
5113         int start_index,stop_index;
5114         char selected_name[255];
5115
5116         // bail early if there aren't any selectable items
5117         if(Multi_create_list_count == 0){
5118                 return;
5119         }
5120         
5121         // first check to see if the user has clicked on an item
5122         if(Multi_create_list_select_button.pressed()){           
5123                 int y,item;                             
5124                 Multi_create_list_select_button.get_mouse_pos(NULL,&y);
5125                 item = (y / 10);
5126
5127                 // make sure we are selectedin valid indices
5128                 if((item < Multi_create_list_max_display[gr_screen.res]) && (item >= 0)){                                       
5129                         item += Multi_create_list_start;                
5130
5131                         if(item < Multi_create_list_count){             
5132                                 multi_create_list_select_item(item);
5133                                 gamesnd_play_iface(SND_IFACE_MOUSE_CLICK);
5134                         }               
5135                 }
5136         }       
5137
5138         // bail early if we don't have a start position
5139         if(Multi_create_list_start == -1){
5140                 return;
5141         }
5142
5143         // display the list of individual campaigns/missions
5144         int count = 0;
5145         int y_start = Mc_list_coords[gr_screen.res][MC_Y_COORD];                
5146
5147         start_index = multi_create_select_to_index(Multi_create_list_start);
5148         stop_index = Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ? Multi_create_mission_count : Multi_create_campaign_count;
5149         for(idx=start_index; idx<stop_index; idx++){
5150                 // see if we should drop out
5151                 if(count == Multi_create_list_max_display[gr_screen.res]){
5152                         break;
5153                 }
5154
5155                 // see if we should filter out this mission
5156                 if ( !(Multi_create_file_list[idx].flags & Multi_create_filter) ){
5157                         continue;
5158                 }
5159                 
5160                 // highlight the selected item
5161                 multi_create_select_to_filename(Multi_create_list_select, selected_name, SDL_arraysize(selected_name));
5162                 if(!strcmp(selected_name,Multi_create_file_list[idx].filename)){                
5163                         gr_set_color_fast(&Color_text_selected);
5164                 } else {
5165                         gr_set_color_fast(&Color_text_normal);
5166                 }               
5167
5168                 // draw the type icon           
5169                 multi_create_list_blit_icons(idx, y_start);             
5170                 
5171                 // force fit the mission name string
5172                 SDL_strlcpy(selected_name, Multi_create_file_list[idx].name, SDL_arraysize(selected_name));
5173                 gr_force_fit_string(selected_name,255,Mc_column1_w[gr_screen.res]);
5174                 gr_string(Mc_mission_name_x[gr_screen.res],y_start,selected_name);
5175
5176                 // draw the max players if in mission mode              
5177                 SDL_snprintf(selected_name,SDL_arraysize(selected_name),"%d",(int)Multi_create_file_list[idx].max_players);
5178                 gr_string(Mc_mission_count_x[gr_screen.res],y_start,selected_name);             
5179
5180                 // force fit the mission filename string
5181                 SDL_strlcpy(selected_name, Multi_create_file_list[idx].filename, SDL_arraysize(selected_name));
5182                 gr_force_fit_string(selected_name,255,Mc_column3_w[gr_screen.res]);
5183                 gr_string(Mc_mission_fname_x[gr_screen.res],y_start,selected_name);
5184
5185                 y_start+=10;
5186                 count++;
5187         }
5188 }
5189
5190 // takes care of stuff like changing indices around and setting up the netgame structure
5191 void multi_create_list_select_item(int n)
5192 {
5193         int abs_index,campaign_type,max_players;
5194         char title[NAME_LENGTH+1];
5195         netgame_info ng_temp;
5196         netgame_info *ng;
5197
5198         char *campaign_desc;
5199
5200         // if not on the standalone server
5201         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5202                 ng = &Netgame;  
5203         }
5204         // on the standalone
5205         else {
5206                 memset(&ng_temp,0,sizeof(netgame_info));
5207                 ng = &ng_temp;
5208         }
5209         
5210         if ( n != Multi_create_list_select ) {
5211                 // check to see if this is a valid index, and bail if it is not
5212                 abs_index = multi_create_select_to_index(n);
5213                 if(abs_index == -1){
5214                         return;
5215                 }
5216
5217                 Multi_create_list_select = n;
5218                                 
5219                 // set the mission name
5220                 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5221                         multi_create_select_to_filename(n, ng->mission_name, SDL_arraysize(ng->mission_name));
5222                 } else {
5223                         multi_create_select_to_filename(n, ng->campaign_name, SDL_arraysize(ng->campaign_name));
5224                 }
5225
5226                 // make sure the netgame type is properly set
5227                 int old_type = Netgame.type_flags;
5228                 abs_index = multi_create_select_to_index(n);
5229                 if(abs_index != -1){
5230                         if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_TEAMS){
5231                                 // if we're in squad war mode, leave it as squad war
5232                                 if(old_type & NG_TYPE_SW){
5233                                         ng->type_flags = NG_TYPE_SW;
5234                                 } else {
5235                                         ng->type_flags = NG_TYPE_TVT;
5236                                 }
5237                         } else if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_COOP){
5238                                 ng->type_flags = NG_TYPE_COOP;
5239                         } else if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_DOGFIGHT){
5240                                 ng->type_flags = NG_TYPE_DOGFIGHT;
5241                         }
5242                 }
5243
5244                 // if we're no longer in a TvT game, just uncheck the squadwar checkbox
5245                 if(!(ng->type_flags & NG_TYPE_TEAM)){
5246                         Multi_create_sw_checkbox.set_state(0);
5247                 }
5248
5249                 // if we switched from something else to team vs. team mode, do some special processing
5250                 if((ng->type_flags & NG_TYPE_TEAM) && (ng->type_flags != old_type) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
5251                         multi_team_reset();
5252                 }
5253
5254                 switch(Multi_create_list_mode){
5255                 case MULTI_CREATE_SHOW_MISSIONS:                
5256                         // don't forget to update the info box window thingie
5257                         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){                 
5258                                 ship_init();            // mwa -- 10/15/97.  Call this function to reset number of ships in mission
5259                                 ng->max_players = mission_parse_get_multi_mission_info( ng->mission_name );                             
5260                                 
5261                                 SDL_assert(ng->max_players > 0);
5262                                 SDL_strlcpy(ng->title, The_mission.name, SDL_arraysize(ng->title));
5263
5264                                 // set the information area text
5265                                 multi_common_set_text(The_mission.mission_desc);
5266                         }
5267                         // if we're on the standalone, send a request for the description
5268                         else {
5269                                 send_netgame_descript_packet(&Netgame.server_addr,0);
5270                                 multi_common_set_text("");
5271                         }
5272
5273                         // set the respawns as appropriate
5274                         if(Netgame.options.respawn <= Multi_create_file_list[abs_index].respawn){
5275                                 ng->respawn = Netgame.options.respawn;
5276                                 nprintf(("Network","Using netgame options for respawn count (%d %d)\n",Netgame.options.respawn,Multi_create_file_list[abs_index].respawn));
5277                         } else {
5278                                 ng->respawn = Multi_create_file_list[abs_index].respawn;
5279                                 nprintf(("Network","Using mission settings for respawn count (%d %d)\n",Netgame.options.respawn,Multi_create_file_list[abs_index].respawn));
5280                         }
5281                         break;
5282                 case MULTI_CREATE_SHOW_CAMPAIGNS:
5283                         // if not on the standalone server
5284                         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5285                                 // get the campaign info                                
5286                                 memset(title,0,NAME_LENGTH+1);
5287                                 if(!mission_campaign_get_info(ng->campaign_name,title,&campaign_type,&max_players, &campaign_desc)) {
5288                                         memset(ng->campaign_name,0,NAME_LENGTH+1);
5289                                         ng->max_players = 0;
5290                                 }
5291                                 // if we successfully got the # of players
5292                                 else {
5293                                         memset(ng->title,0,NAME_LENGTH+1);
5294                                         SDL_strlcpy(ng->title, title, SDL_arraysize(ng->title));
5295                                         ng->max_players = max_players;                                  
5296                                 }
5297
5298                                 nprintf(("Network","MC MAX PLAYERS : %d\n",ng->max_players));
5299
5300                                 // set the information area text
5301                                 // multi_common_set_text(ng->title);
5302                                 multi_common_set_text(campaign_desc);
5303                         }
5304                         // if on the standalone server, send a request for the description
5305                         else {
5306                                 // no descriptions currently kept for campaigns
5307                         }
5308
5309                         // netgame respawns are always 0 for campaigns (until the first mission is loaded)
5310                         ng->respawn = 0;
5311                         break;
5312                 }
5313
5314                 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5315                         // update players 
5316                         send_netgame_update_packet();                   
5317
5318                         // update all machines about stuff like respawns, etc.
5319                         multi_options_update_netgame();
5320                 } else {
5321                         multi_options_update_mission(ng, Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ? 1 : 0);
5322                 }
5323         }
5324 }
5325
5326 void multi_create_list_blit_icons(int list_index, int y_start)
5327 {
5328         multi_create_info *mcip;
5329         fs_builtin_mission *fb; 
5330         int max_index;
5331
5332         // get a pointer to the list item
5333         max_index = Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ? Multi_create_mission_count - 1 : Multi_create_campaign_count - 1;
5334         if((list_index < 0) || (list_index > max_index)){
5335                 return;
5336         }       
5337         mcip = &Multi_create_file_list[list_index];
5338
5339         // blit the multiplayer type icons
5340         if(mcip->flags & MISSION_TYPE_MULTI_COOP){
5341                 if(Multi_common_icons[MICON_COOP] >= 0){
5342                         gr_set_bitmap(Multi_common_icons[MICON_COOP], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5343                         gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5344                 }
5345         } else if(mcip->flags & MISSION_TYPE_MULTI_TEAMS){
5346                 if(Multi_common_icons[MICON_TVT] >= 0){
5347                         gr_set_bitmap(Multi_common_icons[MICON_TVT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5348                         gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5349                 }
5350         } else if(mcip->flags & MISSION_TYPE_MULTI_DOGFIGHT){
5351                 if(Multi_common_icons[MICON_DOGFIGHT] >= 0){
5352                         gr_set_bitmap(Multi_common_icons[MICON_DOGFIGHT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5353                         gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5354                 }
5355         } 
5356
5357         // if its a valid mission, blit the valid mission icon
5358         if(MULTI_IS_TRACKER_GAME && (mcip->valid_status == MVALID_STATUS_VALID)){
5359                 if(Multi_common_icons[MICON_VALID] >= 0){
5360                         gr_set_bitmap(Multi_common_icons[MICON_VALID], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5361                         gr_bitmap(Mc_icon_valid_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_valid_coords[gr_screen.res][MC_Y_COORD]);
5362                 }
5363         }
5364
5365         // now see if its a builtin mission
5366         fb = game_find_builtin_mission(mcip->filename); 
5367         // if the mission is from volition, blit the volition icon
5368         if((fb != NULL) && (fb->flags & FSB_FROM_VOLITION)){
5369                 if(Multi_common_icons[MICON_VOLITION] >= 0){
5370                         gr_set_bitmap(Multi_common_icons[MICON_VOLITION], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5371                         gr_bitmap(Mc_icon_volition_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_volition_coords[gr_screen.res][MC_Y_COORD]);
5372                 }
5373         }       
5374
5375 #ifdef MAKE_FS1
5376         // check for mdisk mission
5377         fb = game_find_builtin_mission(mcip->filename);
5378         if((fb != NULL) && (fb->flags & FSB_FROM_MDISK)){
5379                 if(Multi_common_icons[MICON_MDISK] >= 0){
5380                         gr_set_bitmap(Multi_common_icons[MICON_MDISK], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5381                         gr_bitmap(Mc_icon_silent_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_silent_coords[gr_screen.res][MC_Y_COORD]);
5382                 }
5383         }
5384 #endif
5385 }
5386
5387 void multi_create_accept_hit()
5388 {
5389         char selected_name[255];
5390         int start_campaign = 0; 
5391
5392         // make sure all players have finished joining
5393         if(!multi_netplayer_state_check(NETPLAYER_STATE_JOINED,1)){
5394                 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("Please wait until all clients have finished joining",788));
5395                 gamesnd_play_iface(SND_GENERAL_FAIL);
5396                 return;
5397         } else {
5398                 gamesnd_play_iface(SND_COMMIT_PRESSED);
5399         }       
5400         
5401         // do single mission stuff
5402         switch(Multi_create_list_mode){
5403         case MULTI_CREATE_SHOW_MISSIONS:        
5404                 if(Multi_create_list_select != -1){
5405                         // set the netgame mode
5406                         Netgame.campaign_mode = MP_SINGLE;
5407
5408                         // setup various filenames and mission names
5409                         multi_create_select_to_filename(Multi_create_list_select, selected_name, SDL_arraysize(selected_name));
5410                         SDL_strlcpy( Game_current_mission_filename, selected_name, MAX_FILENAME_LEN );
5411                         SDL_strlcpy( Netgame.mission_name, selected_name, MAX_FILENAME_LEN );
5412
5413                         // NETLOG
5414                         ml_printf(NOX("Starting single mission %s, with %d players"), Game_current_mission_filename, multi_num_players());
5415                 } else {
5416                         multi_common_add_notify(XSTR("No mission selected!",789));
5417                         return ;
5418                 }
5419                 break;
5420
5421         case MULTI_CREATE_SHOW_CAMPAIGNS:
5422                 // do campaign related stuff    
5423                 if(Multi_create_list_select != -1){
5424                         // set the netgame mode
5425                         Netgame.campaign_mode = MP_CAMPAIGN;
5426
5427                         // start a campaign instead of a single mission
5428                         multi_create_select_to_filename(Multi_create_list_select, selected_name, SDL_arraysize(selected_name));
5429                         multi_campaign_start(selected_name);                    
5430                         start_campaign = 1;
5431
5432                         // NETLOG
5433                         ml_printf(NOX("Starting campaign %s, with %d players"), selected_name, multi_num_players());
5434                 } else {
5435                         multi_common_add_notify(XSTR("No campaign selected!",790));
5436                         return ;
5437                 }
5438                 break;
5439         }
5440
5441         // if this is a team vs team situation, lock the players send a final team update
5442         if((Netgame.type_flags & NG_TYPE_TEAM) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
5443                 multi_team_host_lock_all();
5444                 multi_team_send_update();
5445         }
5446
5447         // if not on the standalone, move to the mission sync state which will take care of everything
5448         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5449                 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
5450                 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);                                
5451         } 
5452         // otherwise tell the standalone to do so
5453         else {
5454                 // when the standalone receives this, he'll do the mission syncing himself
5455                 send_mission_sync_packet(MULTI_SYNC_PRE_BRIEFING,start_campaign);
5456         }                               
5457 }
5458
5459 void multi_create_draw_filter_buttons()
5460 {
5461         // highlight the correct filter button
5462         if ( Multi_create_filter == MISSION_TYPE_MULTI ){
5463                 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL].button.draw_forced(2);
5464         } else if ( Multi_create_filter == MISSION_TYPE_MULTI_COOP ) {
5465                 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 1].button.draw_forced(2);
5466         } else if ( Multi_create_filter == MISSION_TYPE_MULTI_TEAMS ) {
5467                 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 2].button.draw_forced(2);
5468         } else if ( Multi_create_filter == MISSION_TYPE_MULTI_DOGFIGHT ){
5469                 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 3].button.draw_forced(2);
5470         } else {
5471                 Int3();
5472         }
5473 }
5474
5475 void multi_create_set_selected_team(int team)
5476 {       
5477         int player_index;
5478         
5479         // if we don't currently have a player selected, don't do anything
5480         if(!Multi_create_plist_select_flag){
5481                 gamesnd_play_iface(SND_GENERAL_FAIL);
5482                 return;
5483         }
5484         gamesnd_play_iface(SND_USER_SELECT);
5485
5486         // otherwise attempt to set the team for this guy       
5487         player_index = find_player_id(Multi_create_plist_select_id);
5488         if(player_index != -1){ 
5489                 multi_team_set_team(&Net_players[player_index],team);           
5490         }
5491 }
5492
5493 void multi_create_handle_join(net_player *pl)
5494 {
5495         // for now just play a bloop sound
5496         gamesnd_play_iface(SND_ICON_DROP_ON_WING);
5497 }
5498
5499 // fill in net address of player the mouse is over, return player index (or -1 if none)
5500 short multi_create_get_mouse_id()
5501 {
5502         // determine where he clicked (y pixel value)
5503         int y,nth,idx;          
5504         Multi_create_player_select_button.get_mouse_pos(NULL,&y);
5505
5506         // select things a little differently if we're in team vs. team or non-team vs. team mode                       
5507         nth = (y / 10);                 
5508         if(Netgame.type_flags & NG_TYPE_TEAM){
5509                 int player_index = -1;
5510
5511                 // look through all of team red first
5512                 for(idx=0;idx<MAX_PLAYERS;idx++){
5513                         if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
5514                                 nth--;
5515
5516                                 // if this is the _nth_ guy 
5517                                 if(nth < 0){
5518                                         player_index = idx;                                             
5519                                         break;
5520                                 }
5521                         }
5522                 }
5523                         
5524                 // if we still haven't found him yet, look through the green team
5525                 if(player_index == -1){
5526                         for(idx=0;idx<MAX_PLAYERS;idx++){                                       
5527                                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){                            
5528                                         nth--;
5529                                         // if this is the _nth_ guy 
5530                                         if(nth < 0){
5531                                                 player_index = idx;                                             
5532                                                 break;
5533                                         }
5534                                 }
5535                         }
5536                 }
5537
5538                 if(player_index != -1){
5539                         return Net_players[player_index].player_id;
5540                 }
5541         } else {
5542                 // select the nth active player if possible, disregarding the standalone server
5543                 for(idx=0;idx<MAX_PLAYERS;idx++){
5544                         if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
5545                                 nth--;
5546
5547                                 // if this is the _nth_ guy 
5548                                 if(nth < 0){
5549                                         return Net_players[idx].player_id;                                      
5550                                 }                               
5551                         }
5552                 }
5553         }                               
5554         
5555         return -1;
5556 }
5557
5558 void multi_create_select_to_filename(int select_index, char *filename, const int max_filelen)
5559 {
5560         int idx;
5561
5562         // look through the mission list
5563         if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5564                 for(idx=0;idx<Multi_create_mission_count;idx++){
5565                         if(Multi_create_file_list[idx].flags & Multi_create_filter){
5566                                 select_index--;
5567                         }
5568
5569                         // if we found the item
5570                         if(select_index < 0){
5571                                 SDL_strlcpy(filename, Multi_create_file_list[idx].filename, max_filelen);
5572                                 return;
5573                         }
5574                 }
5575         }
5576         // look through the campaign list
5577         else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
5578                 for(idx=0;idx<Multi_create_campaign_count;idx++){
5579                         select_index--;
5580
5581                         // if we found the item
5582                         if(select_index < 0){
5583                                 SDL_strlcpy(filename, Multi_create_file_list[idx].filename, max_filelen);
5584                                 return;
5585                         }               
5586                 }
5587         }
5588
5589         SDL_strlcpy(filename, "", max_filelen);
5590 }
5591
5592 int multi_create_select_to_index(int select_index)
5593 {
5594         int idx;
5595         int lookup_index = 0;
5596
5597         // look through the mission list
5598         if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5599                 for(idx=0;idx<Multi_create_mission_count;idx++){
5600                         if(Multi_create_file_list[idx].flags & Multi_create_filter){
5601                                 lookup_index++;
5602                         } 
5603
5604                         // if we found the item
5605                         if(select_index < lookup_index){                                
5606                                 return idx;
5607                         }
5608                 }
5609         }
5610         // look through the campaign list
5611         else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
5612                 for(idx=0;idx<Multi_create_campaign_count;idx++){
5613                         select_index--;
5614
5615                         // if we found the item
5616                         if(select_index < 0){                           
5617                                 return idx;
5618                         }               
5619                 }
5620         }
5621
5622         return -1;
5623 }
5624
5625 int multi_create_ok_to_commit()
5626 {
5627         int player_count, observer_count, idx;
5628         int notify_of_hacked_ships_tbl = 0;
5629         int notify_of_hacked_weapons_tbl = 0;
5630         char err_string[255];
5631         int abs_index;
5632         int found_hack;
5633
5634         // make sure we have a valid mission selected
5635         if(Multi_create_list_select < 0){
5636                 return 0;
5637         }       
5638
5639         // if this is not a valid mission, let the player know
5640         abs_index = multi_create_select_to_index(Multi_create_list_select);             
5641         if(abs_index < 0){
5642                 return 0;
5643         }
5644
5645         // if we're playing with a hacked ships.tbl (on PXO)
5646         notify_of_hacked_ships_tbl = 0;
5647         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5648                 if(!Game_ships_tbl_valid){
5649                         notify_of_hacked_ships_tbl = 1;
5650                 }
5651         } else {
5652                 if(Netgame.flags & NG_FLAG_HACKED_SHIPS_TBL){
5653                         notify_of_hacked_ships_tbl = 1;
5654                 }
5655         }
5656         if(!MULTI_IS_TRACKER_GAME){
5657                 notify_of_hacked_ships_tbl = 0;
5658         }
5659         if(notify_of_hacked_ships_tbl){
5660                 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){
5661                         return 0;
5662                 }
5663         }
5664
5665         // if we're playing with a hacked weapons.tbl (on PXO)
5666         notify_of_hacked_weapons_tbl = 0;
5667         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5668                 if(!Game_weapons_tbl_valid){
5669                         notify_of_hacked_weapons_tbl = 1;
5670                 }
5671         } else {
5672                 if(Netgame.flags & NG_FLAG_HACKED_WEAPONS_TBL){
5673                         notify_of_hacked_weapons_tbl = 1;
5674                 }
5675         }
5676         if(!MULTI_IS_TRACKER_GAME){
5677                 notify_of_hacked_weapons_tbl = 0;
5678         }
5679         if(notify_of_hacked_weapons_tbl){
5680                 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){
5681                         return 0;
5682                 }
5683         }
5684
5685         // if any of the players have hacked data
5686         found_hack = 0;
5687         for(idx=0; idx<MAX_PLAYERS; idx++){
5688                 // look for hacked players
5689                 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_HAXOR)){
5690                         // we found a hack
5691                         found_hack = 1;
5692
5693                         // message everyone - haha
5694                         if(Net_players[idx].player != NULL){
5695                                 SDL_snprintf(err_string, SDL_arraysize(err_string), "%s %s", Net_players[idx].player->callsign, XSTR("has hacked tables/data", 1271));
5696                         } else {
5697                                 SDL_snprintf(err_string, SDL_arraysize(err_string), "somebody %s", XSTR("has hacked tables/data", 1271));
5698                         }
5699                         send_game_chat_packet(Net_player, err_string, MULTI_MSG_ALL, NULL, NULL, 1);
5700                 }
5701         }
5702         // if we found a hacked set of data
5703         if(found_hack){
5704                 // if we're on PXO
5705                 if(MULTI_IS_TRACKER_GAME){
5706                         // don't allow squad war matches to continue
5707                         if(Netgame.type_flags & NG_TYPE_SW){
5708 #ifdef RELEASE_REAL
5709                                 // if this is squad war, don't allow it to continue                     
5710                                 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));
5711
5712                                 return 0;
5713 #endif
5714                         }
5715                         // otherwise, warn the players that stats will not saved
5716                         else {
5717                                 // if this is squad war, don't allow it to continue                     
5718                                 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){
5719                                         return 0;
5720                                 }
5721                         }
5722                 }
5723                 // non-pxo, just give a notice
5724                 else {
5725                         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){
5726                                 return 0;
5727                         }
5728                 }
5729         }
5730
5731         // check to see that we don't have too many observers
5732         observer_count = multi_num_observers();
5733         if(observer_count > Netgame.options.max_observers){
5734                 // print up the error string
5735                 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);
5736
5737                 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, err_string);
5738                 return 0;
5739         }
5740
5741         // check to see that we have a valid # of players for the the # of ships in the game            
5742         player_count = multi_num_players();
5743         if(player_count > Netgame.max_players){
5744                 // print up the error string
5745                 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);
5746
5747                 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, err_string);
5748                 return 0;
5749         }
5750         
5751         // check to see if teams are assigned properly in a team vs. team situation
5752         if(Netgame.type_flags & NG_TYPE_TEAM){
5753                 if(!multi_team_ok_to_commit()){
5754                         gamesnd_play_iface(SND_GENERAL_FAIL);
5755                         popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("Teams and/or team captains are not assigned properly", 793));                   
5756                         return 0;
5757                 }
5758         }
5759
5760         // verify cd's  
5761         if(!multi_create_verify_cds()){
5762                 gamesnd_play_iface(SND_GENERAL_FAIL);
5763
5764 #ifdef MULTIPLAYER_BETA_BUILD
5765                 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, "You need 1 CD for every player!");                   
5766 #else 
5767         #ifdef DVD_MESSAGE_HACK
5768                         popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("You need 1 DVD for every 4 players!", 794));                    
5769         #else
5770                         popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("You need 1 CD for every 4 players!", 794));                     
5771         #endif
5772 #endif
5773                 return 0;
5774         }       
5775         
5776         // if we're playing on the tracker
5777         if(MULTI_IS_TRACKER_GAME){
5778 #ifdef PXO_CHECK_VALID_MISSIONS         
5779                 if((Multi_create_file_list == Multi_create_mission_list) && (Multi_create_file_list[abs_index].valid_status != MVALID_STATUS_VALID)){
5780                         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){
5781                                 return 0;
5782                         }
5783                 }               
5784 #endif
5785
5786                 // non-squad war
5787                 if(!(Netgame.type_flags & NG_TYPE_SW)){
5788                         // if he is playing by himself, tell him stats will not be accepted
5789                         if(multi_num_players() == 1){
5790                                 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){
5791                                         return 0;
5792                                 }
5793                         }
5794                 }
5795                 // squad war
5796                 else {                  
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                                 multi_maybe_set_mission_loop();
9312                         } else {
9313                                 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));
9314                 
9315                                 // evaluate the result
9316                                 switch(res){
9317                                 // undo the accept
9318                                 case -1:
9319                                 case 0:
9320                                         Multi_debrief_accept_hit = 0;
9321                                         return;
9322
9323                                 // set the accept code to be "not accepting"
9324                                 case 2 :
9325                                         multi_debrief_stats_toss();
9326                                         multi_maybe_set_mission_loop();
9327                                         break;
9328                                 
9329                                 // accept the stats and continue
9330                                 case 1 :
9331                                         multi_debrief_stats_accept();
9332                                         multi_maybe_set_mission_loop();
9333                                         break;
9334                                 }
9335                         }
9336                 }
9337
9338                 // set my netplayer state to be "debrief_accept", and be done with it
9339                 Net_player->state = NETPLAYER_STATE_DEBRIEF_ACCEPT;
9340                 send_netplayer_update_packet();
9341         }
9342 }
9343
9344 // handle all cases for when the escape key is hit in a multiplayer debriefing
9345 void multi_debrief_esc_hit()
9346 {
9347         int res;
9348
9349         // if the server has left
9350         if(Multi_debrief_server_left){
9351                 multi_quit_game(PROMPT_ALL);
9352                 return;
9353         }
9354         
9355         // display a popup
9356         if(Net_player->flags & NETINFO_FLAG_GAME_HOST){         
9357                 // if the stats have already been accepted
9358                 if((Multi_debrief_stats_accept_code != -1) || (MULTI_IS_TRACKER_GAME)){
9359                         multi_quit_game(PROMPT_HOST);
9360                 } else {
9361                         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));
9362                 
9363                         // evaluate the result
9364                         switch(res){                    
9365                         // undo the accept
9366                         case -1:
9367                         case 0:                         
9368                                 break;
9369
9370                         // set the accept code to be "not accepting"
9371                         case 2 :
9372                                 multi_debrief_stats_toss();
9373                                 multi_quit_game(PROMPT_NONE);
9374                                 break;
9375                                 
9376                         // accept the stats and continue
9377                         case 1 :
9378                                 multi_debrief_stats_accept();
9379                                 multi_quit_game(PROMPT_NONE);
9380                                 break;                                          
9381                         }               
9382                 }
9383         } else {                
9384                 // if the stats haven't been accepted yet, or this is a tracker game
9385                 if((Multi_debrief_stats_accept_code == -1) && !(MULTI_IS_TRACKER_GAME)){
9386                         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));
9387
9388                         // evaluate the result
9389                         if(res == 1){
9390                                 multi_quit_game(PROMPT_NONE);
9391                         }
9392                 }
9393                 // otherwise go through the normal endgame channels
9394                 else {
9395                         multi_quit_game(PROMPT_ALL);
9396                 }               
9397         }
9398 }
9399
9400 void multi_debrief_replay_hit()
9401 {
9402         // only the host should ever get here
9403         SDL_assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
9404
9405         // if the button was already pressed, do nothing
9406         if(Multi_debrief_accept_hit){
9407                 return;
9408         }
9409         
9410         // same as hittin the except button except no stats are kept
9411         Multi_debrief_accept_hit = 1;
9412
9413         // mark myself as being in the replay state so we know what to do next
9414         Net_player->state = NETPLAYER_STATE_DEBRIEF_REPLAY;
9415         send_netplayer_update_packet();
9416 }
9417
9418 // call this when the server has left and we would otherwise be saying "contact lost with server
9419 void multi_debrief_server_left()
9420 {
9421         // the server left
9422         Multi_debrief_server_left = 1;
9423
9424         // undo any "accept" hit so that clients can hit accept again to leave
9425         Multi_debrief_accept_hit = 0;
9426 }
9427
9428 void multi_debrief_stats_accept()
9429 {
9430         // don't do anything if we've already accepted
9431         if(Multi_debrief_stats_accept_code != -1){
9432                 return;
9433         }
9434         
9435         Multi_debrief_stats_accept_code = 1;
9436
9437         // if we're the host, and we're on a standalone, tell the standalone to begin the stats storing process
9438         if((Net_player->flags & NETINFO_FLAG_GAME_HOST) || (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
9439                 // send a packet to the players telling them to store their stats
9440                 send_store_stats_packet(1);
9441         } 
9442
9443         // add a chat line saying "stats have been accepted"
9444         multi_display_chat_msg(XSTR("<stats have been accepted>",850),0,0);
9445
9446         // NETLOG
9447         ml_string(NOX("Stats stored"));
9448 }
9449
9450 void multi_debrief_stats_toss()
9451 {
9452         // don't do anything if we've already accepted
9453         if(Multi_debrief_stats_accept_code != -1){
9454                 return;
9455         }
9456         
9457         Multi_debrief_stats_accept_code = 0;
9458
9459         // if we're the host, and we're on a standalone, tell everyone to "toss" stats
9460         if((Net_player->flags & NETINFO_FLAG_GAME_HOST) || (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
9461                 // send a packet to the players telling them to store their stats
9462                 send_store_stats_packet(0);
9463         } 
9464
9465         // add a chat line saying "stats have been accepted"
9466         multi_display_chat_msg(XSTR("<stats have been tossed>",851),0,0);
9467
9468         // NETLOG
9469         ml_string(NOX("Stats tossed"));
9470 }
9471
9472 int multi_debrief_stats_accept_code()
9473 {
9474         return Multi_debrief_stats_accept_code;
9475 }
9476
9477 void multi_debrief_server_process()
9478 {       
9479         int idx;
9480         int player_status,other_status;
9481
9482         Multi_debrief_server_framecount++;
9483
9484         // if we're > 10 seconds into the debrief and not everyone is here, try warping everyone out again
9485         if((Multi_debrief_time >= Multi_debrief_resend_time) && !multi_netplayer_state_check3(NETPLAYER_STATE_DEBRIEF, NETPLAYER_STATE_DEBRIEF_ACCEPT, NETPLAYER_STATE_DEBRIEF_REPLAY, 1)){
9486                 // find all players who are not in the debrief state and hit them with the endgame packet
9487                 for(idx=0; idx<MAX_PLAYERS; idx++){
9488                         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)) ){
9489                                 send_endgame_packet(&Net_players[idx]);
9490                         }
9491                 }
9492
9493                 // next check time
9494                 Multi_debrief_resend_time += 7.0f;
9495         }
9496
9497         // evaluate the status of all players in the game (0 == not ready, 1 == ready to continue, 2 == ready to replay)
9498         other_status = 1;
9499
9500         // check all players
9501         for(idx=0;idx<MAX_PLAYERS;idx++){
9502                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_HOST(Net_players[idx])){                                  
9503                         if(Net_players[idx].state != NETPLAYER_STATE_DEBRIEF_ACCEPT){
9504                                 other_status = 0;
9505                                 break;
9506                         }
9507                 }
9508         }
9509
9510         // if we haven't already reported TvT results
9511         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)){
9512                 multi_team_report();
9513                 Multi_debrief_reported_tvt = 1;
9514         }       
9515
9516         // if all other players are good to go, check the host
9517         if(other_status){
9518                 // if he is ready to continue
9519                 if(Netgame.host->state == NETPLAYER_STATE_DEBRIEF_ACCEPT){                                      
9520                         player_status = 1;                      
9521                 } 
9522                 // if he wants to replay the mission
9523                 else if(Netgame.host->state == NETPLAYER_STATE_DEBRIEF_REPLAY){
9524                         player_status = 2;                      
9525                 } 
9526                 // if he is not ready
9527                 else {
9528                         player_status = 0;
9529                 }
9530         }
9531         // if all players are _not_ good to go
9532         else {
9533                 player_status = 0;
9534         }
9535                 
9536         // if we're in the debriefing state in a campaign mode, process accordingly
9537         if(Netgame.campaign_mode == MP_CAMPAIGN){
9538                 multi_campaign_do_debrief(player_status);
9539         }
9540         // otherwise process as normal (looking for all players to be ready to go to the next mission
9541         else {
9542                 if(player_status == 1){
9543                         multi_flush_mission_stuff();
9544
9545                         // set the netgame state to be forming and continue
9546                         Netgame.game_state = NETGAME_STATE_FORMING;
9547                         send_netgame_update_packet();
9548
9549                         // move to the proper state
9550                         if(Game_mode & GM_STANDALONE_SERVER){
9551                                 gameseq_post_event(GS_EVENT_STANDALONE_MAIN);
9552                         } else {
9553                                 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
9554                         }
9555
9556                         multi_reset_timestamps();
9557                 } else if(player_status == 2){
9558                         multi_flush_mission_stuff();
9559
9560                         // tell everyone to move into the pre-briefing sync state
9561                         Netgame.game_state = NETGAME_STATE_MISSION_SYNC;
9562                         send_netgame_update_packet();
9563
9564                         // move back to the mission sync screen for the same mission again
9565                         Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
9566                         gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
9567
9568                         multi_reset_timestamps();
9569                 }
9570         }       
9571 }
9572
9573
9574 // -------------------------------------------------------------------------------------------------------------
9575 // 
9576 // MULTIPLAYER PASSWORD POPUP
9577 //
9578
9579 //XSTR:OFF
9580 // bitmaps defs
9581 static const char *Multi_pwd_bitmap_fname[GR_NUM_RESOLUTIONS] = {
9582         "Password",                     // GR_640
9583         "2_Password"            // GR_1024
9584 };
9585
9586 static const char *Multi_pwd_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
9587         "Password-M",           // GR_640
9588         "2_Password-M"          // GR_1024
9589 };
9590
9591 //XSTR:ON
9592
9593 // constants for coordinate lookup
9594 #define MPWD_X_COORD 0
9595 #define MPWD_Y_COORD 1
9596 #define MPWD_W_COORD 2
9597 #define MPWD_H_COORD 3
9598
9599 // button defs
9600 #define MULTI_PWD_NUM_BUTTONS                   2
9601 #define MPWD_CANCEL                     0
9602 #define MPWD_COMMIT                     1
9603
9604 // password area defs
9605 int Mpwd_coords[GR_NUM_RESOLUTIONS][4] = {
9606         { // GR_640
9607 #ifdef MAKE_FS1
9608                 147, 114, 343, 13
9609 #else
9610                 134, 112, 367, 15
9611 #endif
9612         },
9613         { // GR_1024
9614                 215, 190, 587, 24
9615         }
9616 };
9617
9618 UI_WINDOW Multi_pwd_window;                                                                                             // the window object for the join screen
9619 UI_INPUTBOX     Multi_pwd_passwd;                                                                                               // for Netgame.passwd
9620 int Multi_pwd_bitmap;                                                                                                           // the background bitmap
9621 int Multi_passwd_background = -1;
9622 int Multi_passwd_done = -1;
9623 int Multi_passwd_running = 0;
9624
9625 // password buttons
9626 ui_button_info Multi_pwd_buttons[GR_NUM_RESOLUTIONS][MULTI_PWD_NUM_BUTTONS] = {
9627         { // GR_640
9628 #ifdef MAKE_FS1
9629                 ui_button_info("PWB_00",        402,    134,    -1,     -1,     0),
9630                 ui_button_info("PWB_01",        450,    134,    -1,     -1,     1),
9631 #else
9632                 ui_button_info("PWB_00",        411,    151,    405,    141,    0),
9633                 ui_button_info("PWB_01",        460,    151,    465,    141,    1),
9634 #endif
9635         }, 
9636         { // GR_1024
9637                 ui_button_info("2_PWB_00",      659,    242,    649,    225,    0),
9638                 ui_button_info("2_PWB_01",      737,    242,    736,    225,    1),
9639         }, 
9640 };
9641
9642 // text
9643 #ifndef MAKE_FS1
9644 #define MULTI_PWD_NUM_TEXT                              3
9645
9646 UI_XSTR Multi_pwd_text[GR_NUM_RESOLUTIONS][MULTI_PWD_NUM_TEXT] = {
9647         { // GR_640
9648                 { "Cancel",                     387,    400,    141,    UI_XSTR_COLOR_GREEN, -1,        &Multi_pwd_buttons[0][MPWD_CANCEL].button},
9649                 { "Commit",                     1062,   455,    141,    UI_XSTR_COLOR_GREEN, -1,        &Multi_pwd_buttons[0][MPWD_COMMIT].button},
9650                 { "Enter Password",     1332,   149,    92,     UI_XSTR_COLOR_GREEN, -1,        NULL},
9651         },
9652         { // GR_1024
9653                 { "Cancel",                     387,    649,    225,    UI_XSTR_COLOR_GREEN, -1,        &Multi_pwd_buttons[1][MPWD_CANCEL].button},
9654                 { "Commit",                     1062,   736,    225,    UI_XSTR_COLOR_GREEN, -1,        &Multi_pwd_buttons[1][MPWD_COMMIT].button},
9655                 { "Enter Password",     1332,   239,    148,    UI_XSTR_COLOR_GREEN, -1,        NULL},
9656         }
9657 };
9658 #endif
9659
9660 // initialize all graphics, etc
9661 void multi_passwd_init()
9662 {
9663         int idx;
9664
9665         // store the background as it currently is
9666         Multi_passwd_background = gr_save_screen();     
9667         
9668         // create the interface window
9669         Multi_pwd_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
9670         Multi_pwd_window.set_mask_bmap(Multi_pwd_bitmap_mask_fname[gr_screen.res]);
9671
9672         // load the background bitmap
9673         Multi_pwd_bitmap = bm_load(Multi_pwd_bitmap_fname[gr_screen.res]);
9674         if(Multi_pwd_bitmap < 0){
9675                 // we failed to load the bitmap - this is very bad
9676                 Int3();
9677         }
9678                         
9679         // create the interface buttons
9680         for(idx=0; idx<MULTI_PWD_NUM_BUTTONS; idx++){
9681                 // create the object
9682                 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);
9683
9684                 // set the sound to play when highlighted
9685                 Multi_pwd_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
9686
9687                 // set the ani for the button
9688                 Multi_pwd_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pwd_buttons[gr_screen.res][idx].filename);
9689
9690                 // set the hotspot
9691                 Multi_pwd_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pwd_buttons[gr_screen.res][idx].hotspot);
9692         }       
9693
9694 #ifndef MAKE_FS1
9695         // add all xstrs
9696         for(idx=0; idx<MULTI_PWD_NUM_TEXT; idx++){
9697                 Multi_pwd_window.add_XSTR(&Multi_pwd_text[gr_screen.res][idx]);
9698         }
9699 #endif
9700         
9701         // create the password input box
9702         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);
9703         Multi_pwd_passwd.set_focus();
9704         
9705         // link the enter key to ACCEPT
9706         Multi_pwd_buttons[gr_screen.res][MPWD_COMMIT].button.set_hotkey(SDLK_RETURN);
9707
9708         Multi_passwd_done = -1;
9709         Multi_passwd_running = 1;
9710 }
9711
9712 // close down all graphics, etc
9713 void multi_passwd_close()
9714 {
9715         // unload any bitmaps
9716         bm_release(Multi_pwd_bitmap);           
9717                 
9718         // destroy the UI_WINDOW
9719         Multi_pwd_window.destroy();
9720
9721         // free up the saved background screen
9722         if(Multi_passwd_background >= 0){
9723                 gr_free_screen(Multi_passwd_background);        
9724                 Multi_passwd_background = -1;
9725         }
9726
9727         Multi_passwd_running = 0;
9728 }
9729
9730 // process any button pressed
9731 void multi_passwd_process_buttons()
9732 {
9733         // if the accept button was pressed
9734         if(Multi_pwd_buttons[gr_screen.res][MPWD_COMMIT].button.pressed()){
9735                 gamesnd_play_iface(SND_USER_SELECT);
9736                 Multi_passwd_done = 1;
9737         }
9738
9739         // if the cancel button was pressed
9740         if(Multi_pwd_buttons[gr_screen.res][MPWD_CANCEL].button.pressed()){
9741                 gamesnd_play_iface(SND_USER_SELECT);
9742                 Multi_passwd_done = 0;
9743         }
9744 }
9745
9746 // run the passwd popup
9747 void multi_passwd_do(char *passwd, const int max_passlen)
9748 {
9749         int k;
9750
9751         while(Multi_passwd_done == -1){ 
9752                 // set frametime and run background stuff
9753                 game_set_frametime(-1);
9754                 game_do_state_common(gameseq_get_state());
9755
9756                 k = Multi_pwd_window.process();
9757
9758                 // process any keypresses
9759                 switch(k){
9760                 case SDLK_ESCAPE :
9761                         // set this to indicate the user has cancelled for one reason or another
9762                         Multi_passwd_done = 0;
9763                         break;          
9764                 }       
9765
9766                 // if the input box text has changed
9767                 if(Multi_pwd_passwd.changed()){
9768                         SDL_strlcpy(passwd, "", max_passlen);
9769                         Multi_pwd_passwd.get_text(passwd);
9770                 }
9771
9772                 // process any button pressed
9773                 multi_passwd_process_buttons();
9774         
9775                 // draw the background, etc
9776                 gr_reset_clip();
9777                 gr_clear();
9778                 if(Multi_passwd_background >= 0){
9779                         gr_restore_screen(Multi_passwd_background);             
9780                 }
9781                 gr_set_bitmap(Multi_pwd_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
9782                 gr_bitmap(0,0);
9783                 Multi_pwd_window.draw();
9784                         
9785                 // flip the buffer
9786                 gr_flip();
9787         }
9788 }
9789
9790 // bring up the password string popup, fill in passwd (return 1 if accept was pressed, 0 if cancel was pressed)
9791 int multi_passwd_popup(char *passwd, const int max_plen)
9792 {
9793         // if the popup is already running for some reason, don't do anything
9794         if(Multi_passwd_running){
9795                 return 0;
9796         }
9797
9798         // initialize all graphics
9799         multi_passwd_init();
9800
9801         // run the popup
9802         multi_passwd_do(passwd, max_plen);
9803
9804         // shut everything down
9805         multi_passwd_close();
9806
9807         return Multi_passwd_done;
9808 }