]> icculus.org git repositories - taylor/freespace2.git/blob - src/network/multiui.cpp
merge in updated graphics code
[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                 strcpy(Multi_common_all_text,str);
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                 strcat(Multi_common_all_text,str);
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                 strncpy(Multi_common_text[i], p_str[i], n_chars[i]);
665                 Multi_common_text[i][n_chars[i]] = 0;
666                 drop_leading_white_space(Multi_common_text[i]);         
667         }
668
669         Multi_common_top_text_line = 0;
670         Multi_common_num_text_lines = n_lines;  
671 }
672
673 void multi_common_render_text()
674 {
675         int i, fh, line_count;
676
677         fh = gr_get_font_height();
678         
679         line_count = 0;
680         gr_set_color_fast(&Color_text_normal);
681         for ( i = Multi_common_top_text_line; i < Multi_common_num_text_lines; i++ ) {
682                 if ( line_count >= Multi_common_text_max_display[gr_screen.res] ){
683                         break;  
684                 }
685                 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]);              
686                 line_count++;
687         }
688
689         if ( (Multi_common_num_text_lines - Multi_common_top_text_line) > Multi_common_text_max_display[gr_screen.res] ) {
690                 gr_set_color_fast(&Color_bright_red);
691                 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));
692         }
693 }
694
695 // common notification messaging stuff
696 #define MULTI_COMMON_NOTIFY_TIME                3500
697 int Multi_common_join_y[GR_NUM_RESOLUTIONS] = {
698         375,            // GR_640
699         605             // GR_1024
700 };
701 int Multi_common_create_y[GR_NUM_RESOLUTIONS] = {
702         380,            // GR_640
703         610             // GR_1024
704 };
705
706 int Multi_common_jw_y[GR_NUM_RESOLUTIONS] = {
707         380,            // GR_640
708         610             // GR_1024
709 };
710
711 int Multi_common_msg_y[GR_NUM_RESOLUTIONS] = {
712         380,            // GR_640
713         610             // GR_1024
714 };
715
716 char Multi_common_notify_text[200];
717 int Multi_common_notify_stamp;
718
719 void multi_common_notify_init()
720 {
721         strcpy(Multi_common_notify_text,"");
722         Multi_common_notify_stamp = -1;
723 }
724
725 // add a notification string, drawing appropriately depending on the state/screen we're in
726 void multi_common_add_notify(const char *str)
727 {
728         if(str){
729                 strcpy(Multi_common_notify_text,str);
730                 Multi_common_notify_stamp = timestamp(MULTI_COMMON_NOTIFY_TIME);
731         }
732 }
733
734 // process/display notification messages
735 void multi_common_notify_do()
736 {
737         if(Multi_common_notify_stamp != -1){
738                 if(timestamp_elapsed(Multi_common_notify_stamp)){
739                         Multi_common_notify_stamp = -1;
740                 } else {
741                         int w,h,y;
742                         gr_get_string_size(&w,&h,Multi_common_notify_text);
743                         gr_set_color_fast(&Color_white);
744                         
745                         // determine where it should be placed based upon which screen we're on
746                         y = -1;
747                         switch(gameseq_get_state()){
748                         case GS_STATE_MULTI_JOIN_GAME :
749                                 y = Multi_common_join_y[gr_screen.res];
750                                 break;
751                         case GS_STATE_MULTI_HOST_SETUP :
752                                 y = Multi_common_create_y[gr_screen.res];
753                                 break;
754                         case GS_STATE_MULTI_CLIENT_SETUP :
755                                 y = Multi_common_jw_y[gr_screen.res];
756                                 break;
757                         case GS_STATE_MULTI_START_GAME :
758                                 y = Multi_common_msg_y[gr_screen.res];
759                                 break;                  
760                         }
761                         if(y != -1){
762                                 gr_string((gr_screen.max_w - w)/2, y, Multi_common_notify_text);
763                         }
764                 }
765         }
766 }
767
768 // common icon stuff
769 int Multi_common_icons[MULTI_NUM_COMMON_ICONS];
770 //XSTR:OFF
771 const char *Multi_common_icon_names[MULTI_NUM_COMMON_ICONS] = {
772         "DotRed",                               // voice denied
773         "DotGreen",                             // voice recording
774         "OvalGreen",                    // team 0
775         "OvalGreen01",                  // team 0 select
776         "OvalRed",                              // team 1
777         "OvalRed01",                    // team 1 select
778         "mp_coop",                              // coop mission
779         "mp_teams",                             // TvT mission
780         "mp_furball",                   // furball mission
781         "icon-volition",                // volition mission     
782         "icon-valid",                   // mission is valid
783 #ifndef MAKE_FS1
784         "cd"                                            // cd icon
785 #else
786         "cd",                                   // cd icon
787         "icon-silent"                   // SilentThreat
788 #endif
789 };
790 //XSTR:ON
791 // width and height of the icons
792 int Multi_common_icon_dims[MULTI_NUM_COMMON_ICONS][2] = {
793         {11, 11},                               // voice denied
794         {11, 11},                               // voice recording
795         {11, 11},                               // team 0
796         {11, 11},                               // team 0 select
797         {11, 11},                               // team 1
798         {11, 11},                               // team 1 select
799         {18, 11},                               // mp coop
800         {18, 11},                               // mp TvT
801         {18, 11},                               // mp furball
802         {9, 9},                                 // volition mission     
803         {8, 8},                                 // mission is valid
804 #ifndef MAKE_FS1
805         {8, 8}                                  // cd icon
806 #else
807         {8, 8},                                 // cd icon
808         {16, 7}                                 // silent threat
809 #endif
810 };
811
812 void multi_load_common_icons()
813 {
814         int idx;
815
816         // load all icons
817         for(idx=0; idx<MULTI_NUM_COMMON_ICONS; idx++){
818                 Multi_common_icons[idx] = -1;
819                 Multi_common_icons[idx] = bm_load(Multi_common_icon_names[idx]);
820         }
821 }
822
823 void multi_unload_common_icons()
824 {
825         int idx;
826
827         // unload all icons
828         for(idx=0; idx<MULTI_NUM_COMMON_ICONS; idx++){
829                 if(Multi_common_icons[idx] != -1){
830                         bm_unload(Multi_common_icons[idx]);
831                         Multi_common_icons[idx] = -1;
832                 }
833         }
834 }
835
836 // display any relevant voice status icons
837 void multi_common_voice_display_status()
838 {       
839         switch(multi_voice_status()){
840         // i have been denied the voice token
841         case MULTI_VOICE_STATUS_DENIED:
842                 if(Multi_common_icons[MICON_VOICE_DENIED] != -1){
843                         gr_set_bitmap(Multi_common_icons[MICON_VOICE_DENIED], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
844                         gr_bitmap(0,0);
845                 }
846                 break;
847
848         // i am currently recording
849         case MULTI_VOICE_STATUS_RECORDING:
850                 if(Multi_common_icons[MICON_VOICE_RECORDING] != -1){
851                         gr_set_bitmap(Multi_common_icons[MICON_VOICE_RECORDING], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
852                         gr_bitmap(0,0);
853                 }
854                 break;
855
856         // i am currently playing back sound
857         case MULTI_VOICE_STATUS_PLAYING:
858                 break;
859
860         // the system is currently idle
861         case MULTI_VOICE_STATUS_IDLE:
862                 break;
863         }
864 }
865
866 //XSTR:OFF
867 // palette initialization stuff
868 #define MULTI_COMMON_PALETTE_FNAME                      "InterfacePalette"
869 //XSTR:ON
870
871 int Multi_common_interface_palette = -1;
872
873 void multi_common_load_palette();
874 void multi_common_set_palette();
875 void multi_common_unload_palette();
876
877 // load in the palette if it doesn't already exist
878 void multi_common_load_palette()
879 {
880         if(Multi_common_interface_palette != -1){
881                 return;
882         }
883
884         Multi_common_interface_palette = bm_load(MULTI_COMMON_PALETTE_FNAME);
885         if(Multi_common_interface_palette == -1){
886                 nprintf(("Network","Error loading multiplayer common palette!\n"));
887         }
888 }
889
890 // set the common palette to be the active one
891 void multi_common_set_palette()
892 {
893         // if the palette is not loaded yet, do so now
894         if(Multi_common_interface_palette == -1){
895                 multi_common_load_palette();
896         }
897 }
898
899 // unload the bitmap palette
900 void multi_common_unload_palette()
901 {
902         if(Multi_common_interface_palette != -1){
903                 bm_unload(Multi_common_interface_palette);
904                 Multi_common_interface_palette = -1;
905         }
906 }
907
908 void multi_common_verify_cd()
909 {
910         Multi_has_cd = 1;
911 }
912
913
914 // -------------------------------------------------------------------------------------------------------------
915 // 
916 // MULTIPLAYER JOIN SCREEN 
917 //
918
919 #define MULTI_JOIN_NUM_BUTTONS  11
920
921 //XSTR:OFF
922 // bitmaps defs
923 #define MULTI_JOIN_PALETTE                              "InterfacePalette"
924
925 static const char *Multi_join_bitmap_fname[GR_NUM_RESOLUTIONS] = {
926         "MultiJoin",            // GR_640
927         "2_MultiJoin"                   // GR_1024
928 };
929
930 static const char *Multi_join_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
931         "MultiJoin-M",          // GR_640
932         "2_MultiJoin-M"         // GR_1024
933 };
934 //XSTR:ON
935
936 // slider
937 #ifndef MAKE_FS1
938 const char *Mj_slider_name[GR_NUM_RESOLUTIONS] = {
939         "slider",
940         "2_slider"
941 };
942 int Mj_slider_coords[GR_NUM_RESOLUTIONS][4] = {
943         { // GR_640
944                 2, 91, 15, 202
945         },
946         { // GR_1024
947                 8, 147, 30, 322
948         }
949 };
950 #endif
951
952 // button defs
953 #define MJ_SCROLL_UP                            0
954 #define MJ_SCROLL_DOWN                  1
955 #define MJ_REFRESH                              2
956 #define MJ_SCROLL_INFO_UP               3
957 #define MJ_SCROLL_INFO_DOWN     4
958 #define MJ_JOIN_OBSERVER                5
959 #define MJ_START_GAME                   6
960 #define MJ_CANCEL                                       7
961 #define MJ_HELP                                 8
962 #define MJ_OPTIONS                              9
963 #define MJ_ACCEPT                                       10
964
965 // uses MULTI_JOIN_REFRESH_TIME as its timestamp
966 int Multi_join_glr_stamp;
967
968 #define MULTI_JOIN_PING_TIME     15000        // how often we ping all the known servers
969 int Multi_join_ping_stamp;
970 UI_WINDOW Multi_join_window;                                                                                    // the window object for the join screen
971 UI_BUTTON Multi_join_select_button;                                                                     // for selecting list items
972 #ifndef MAKE_FS1
973 UI_SLIDER2 Multi_join_slider;                                                                                   // handy dandy slider
974 #endif
975 int Multi_join_bitmap;                                                                                                  // the background bitmap
976
977 ui_button_info Multi_join_buttons[GR_NUM_RESOLUTIONS][MULTI_JOIN_NUM_BUTTONS] = {
978         { // GR_640
979 #ifdef MAKE_FS1
980                 ui_button_info("MJ_00",         0,              85,             -1,     -1,     0),
981                 ui_button_info("MJ_01",         0,              125,    -1,     -1,     1),
982                 ui_button_info("MJ_03",         20,             324,    -1,     -1,     3),
983                 ui_button_info("MJ_04",         0,              399,    -1,     -1,     4),
984                 ui_button_info("MJ_05",         0,              436,    -1,     -1,     5),
985                 ui_button_info("MJ_15",         450,    323,    -1,     -1,     15),
986                 ui_button_info("MJ_10",         519,    323,    -1,     -1,     10),
987                 ui_button_info("MJ_06",         574,    323,    -1,     -1,     6),
988                 ui_button_info("MJ_08",         470,    427,    -1,     -1,     8),
989                 ui_button_info("MJ_09",         448,    454,    -1,     -1,     9),
990                 ui_button_info("MJ_07",         563,    411,    -1,     -1,     7),
991 #else
992                 ui_button_info( "MJ_00",        1,              57,     -1,     -1,     0 ),                                            // scroll up
993                 ui_button_info( "MJ_02",        1,              297,    -1,     -1,     2 ),                                            // scroll down
994                 ui_button_info( "MJ_03",        10,     338,    65,     364,    3 ),                                            // refresh
995                 ui_button_info( "MJ_04",        1,              405,    -1,     -1,     4 ),                                            // scroll info up
996                 ui_button_info( "MJ_05",        1,              446,    -1,     -1,     5 ),                                            // scroll info down
997                 ui_button_info( "MJ_06",        489,    339,    -1,     -1,     6 ),                                            // join as observer
998                 ui_button_info( "MJ_07",        538,    339,    -1,     -1,     7 ),                                            // create game
999                 ui_button_info( "MJ_08",        583,    339,    588,    376,    8 ),                                            // cancel
1000                 ui_button_info( "MJ_09",        534,    426,    -1,     -1,     9 ),                                            // help
1001                 ui_button_info( "MJ_10",        534,    454,    -1,     -1,     10 ),                                           // options
1002                 ui_button_info( "MJ_11",        571,    426,    589,    416,    11 ),                                           // join
1003 #endif
1004         },
1005         { // GR_1024
1006                 ui_button_info( "2_MJ_00",      2,              92,     -1,     -1,     0 ),                                            // scroll up
1007                 ui_button_info( "2_MJ_02",      2,              475,    -1,     -1,     2 ),                                            // scroll down
1008                 ui_button_info( "2_MJ_03",      16,     541,    104,    582,    3 ),                                            // refresh
1009                 ui_button_info( "2_MJ_04",      2,              648,    -1,     -1,     4 ),                                            // scroll info up
1010                 ui_button_info( "2_MJ_05",      2,              713,    -1,     -1,     5 ),                                            // scroll info down
1011                 ui_button_info( "2_MJ_06",      783,    542,    -1,     -1,     6 ),                                            // join as observer
1012                 ui_button_info( "2_MJ_07",      861,    542,    -1,     -1,     7 ),                                            // create game
1013                 ui_button_info( "2_MJ_08",      933,    542,    588,    376,    8 ),                                            // cancel
1014                 ui_button_info( "2_MJ_09",      854,    681,    -1,     -1,     9 ),                                            // help
1015                 ui_button_info( "2_MJ_10",      854,    727,    -1,     -1,     10 ),                                           // options
1016                 ui_button_info( "2_MJ_11",      914,    681,    937,    668,    11 ),                                           // join
1017         }
1018 };
1019
1020 #ifndef MAKE_FS1
1021 #define MULTI_JOIN_NUM_TEXT                     13
1022
1023 UI_XSTR Multi_join_text[GR_NUM_RESOLUTIONS][MULTI_JOIN_NUM_TEXT] = {
1024         { // GR_640             
1025                 {"Refresh",                                                     1299, 65,       364,    UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_REFRESH].button},
1026                 {"Join as",                                                     1300,   476,    376,    UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_JOIN_OBSERVER].button},
1027                 {"Observer",                                            1301,   467,    385,    UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_JOIN_OBSERVER].button},
1028                 {"Create",                                                      1408,   535,    376,    UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_START_GAME].button}, 
1029                 {"Game",                                                                1302,   541,    385,    UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_START_GAME].button},
1030                 {"Cancel",                                                      387,    588,    376,    UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[0][MJ_CANCEL].button},      
1031                 {"Help",                                                                928,    479,    436,    UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_HELP].button},
1032                 {"Options",                                                     1036,   479,    460,    UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_OPTIONS].button},
1033                 {"Join",                                                                1303,   589,    416,    UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[0][MJ_ACCEPT].button},
1034                 {"Status",                                                      1304,   37,     37,     UI_XSTR_COLOR_GREEN,    -1, NULL},
1035                 {"Server",                                                      1305,   116,    37,     UI_XSTR_COLOR_GREEN, -1, NULL},
1036                 {"Players",                                                     1306,   471,    37,     UI_XSTR_COLOR_GREEN,    -1, NULL},
1037                 {"Ping",                                                                1307,   555,    37,     UI_XSTR_COLOR_GREEN, -1, NULL}
1038         },
1039         { // GR_1024            
1040                 {"Refresh",                                                     1299, 104,      582,    UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_REFRESH].button},
1041                 {"Join as",                                                     1300,   783,    602,    UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_JOIN_OBSERVER].button},
1042                 {"Observer",                                            1301,   774,    611,    UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_JOIN_OBSERVER].button},              
1043                 {"Create",                                                      1408,   868,    602,    UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_START_GAME].button},
1044                 {"Game",                                                                1302,   872,    611,    UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_START_GAME].button},
1045                 {"Cancel",                                                      387,    941,    602,    UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[1][MJ_CANCEL].button},
1046                 {"Help",                                                                928,    782,    699,    UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_HELP].button},
1047                 {"Options",                                                     1036,   782,    736,    UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_OPTIONS].button},
1048                 {"Join",                                                                1303,   937,    668,    UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[1][MJ_ACCEPT].button},
1049                 {"Status",                                                      1304,   60,     60,     UI_XSTR_COLOR_GREEN,    -1, NULL},
1050                 {"Server",                                                      1305,   186,    60,     UI_XSTR_COLOR_GREEN, -1, NULL},
1051                 {"Players",                                                     1306,   753,    60,     UI_XSTR_COLOR_GREEN,    -1, NULL},
1052                 {"Ping",                                                                1307,   888,    60,     UI_XSTR_COLOR_GREEN, -1, NULL}
1053         }
1054 };
1055 #endif
1056
1057 // constants for coordinate look ups
1058 #define MJ_X_COORD 0
1059 #define MJ_Y_COORD 1
1060 #define MJ_W_COORD 2
1061 #define MJ_H_COORD 3
1062
1063 #define MULTI_JOIN_SENT_WAIT            10000                                   // wait this long since a join was sent to allow another
1064 int Multi_join_sent_stamp;
1065
1066 // game information text areas
1067 int Mj_max_game_items[GR_NUM_RESOLUTIONS] = {
1068         28,                     // GR_640
1069         46                              // GR_1024
1070 };
1071
1072 int Mj_list_y[GR_NUM_RESOLUTIONS] = {
1073         53,                     // GR_640
1074         77                              // GR_1024
1075 };
1076
1077 int Mj_status_coords[GR_NUM_RESOLUTIONS][4] = {
1078         { // GR_640
1079                 34, 53, 55, 287
1080         },
1081         { // GR_1024
1082                 53, 77, 57, 459
1083         }
1084 };
1085
1086 int Mj_game_icon_coords[GR_NUM_RESOLUTIONS][3] = {
1087         { // GR_640
1088                 98, 53, 28
1089         },
1090         { // GR_1024
1091                 124, 77, 28
1092         }
1093 };      
1094
1095 int Mj_speed_coords[GR_NUM_RESOLUTIONS][4] = {
1096         { // GR_640
1097                 122, 53, 56, 261
1098         },
1099         { // GR_1024
1100                 152, 77, 56, 261
1101         }
1102 };      
1103
1104 int Mj_game_name_coords[GR_NUM_RESOLUTIONS][4] = {
1105         { // GR_640
1106                 186, 53, 280, 261
1107         },
1108         { // GR_1024
1109                 206, 77, 311, 261
1110         }
1111 };      
1112
1113 int Mj_players_coords[GR_NUM_RESOLUTIONS][4] = {
1114         { // GR_640
1115                 473, 53, 50, 261
1116         },
1117         { // GR_1024
1118                 748, 77, 67, 459
1119         }
1120 };      
1121
1122 int Mj_ping_coords[GR_NUM_RESOLUTIONS][4] = {
1123         { // GR_640
1124                 551, 53, 47, 261
1125         },
1126         { // GR_1024
1127                 880, 77, 48, 459
1128         }
1129 };      
1130
1131 // game speed labels
1132 #define MJ_NUM_SPEED_LABELS             5
1133 const char *Multi_join_speed_labels[MJ_NUM_SPEED_LABELS] = {
1134         "< 56k",
1135         "56k",
1136         "isdn",
1137         "cable",
1138         "t1/adsl+"
1139 };
1140 color *Multi_join_speed_colors[MJ_NUM_SPEED_LABELS] = {
1141         &Color_bright_red,
1142         &Color_bright_red,
1143         &Color_bright_green,
1144         &Color_bright_green,
1145         &Color_bright_green
1146 };
1147
1148 int Mj_cd_coords[GR_NUM_RESOLUTIONS] = {
1149         20, 30
1150 };
1151
1152 //XSTR:OFF
1153 // extents of the entire boundable game info region
1154 // NOTE : these numbers are completely empirical
1155 #define MJ_PING_GREEN                           160
1156 #define MJ_PING_YELLOW                          300
1157
1158 int Mj_list_area_coords[GR_NUM_RESOLUTIONS][4] = {
1159         { // GR_640
1160                 23, 53, 565, 279 
1161         },
1162         { // GR_1024
1163                 53, 76, 887, 461 
1164         }
1165 };      
1166
1167 // PXO channel filter
1168 #define MJ_PXO_FILTER_Y                         0
1169
1170 // special chars to indicate various status modes for servers
1171 #define MJ_CHAR_STANDALONE                      "*"
1172 #define MJ_CHAR_CAMPAIGN                        "c"
1173 //XSTR:ON
1174
1175 // various interface indices
1176 int Multi_join_list_start;                                                      // where to start displaying from
1177 active_game *Multi_join_list_start_item;                // a pointer to the corresponding active_game
1178 int Multi_join_list_selected;                                           // which item we have selected
1179 active_game *Multi_join_selected_item;                  // a pointer to the corresponding active_game
1180
1181 // use this macro to modify the list start
1182 #define MJ_LIST_START_INC()                     do { Multi_join_list_start++; } while(0);
1183 #define MJ_LIST_START_DEC()                     do { Multi_join_list_start--; } while(0);
1184 #define MJ_LIST_START_SET(vl)                   do { Multi_join_list_start = vl; } while(0);
1185
1186 // if we should be sending a join request at the end of the frame
1187 int Multi_join_should_send = -1;
1188
1189 // master tracker details
1190 int Multi_join_frame_count;                                             // keep a count of frames displayed
1191 int Multi_join_mt_tried_verify;                                 // already tried verifying the pilot with the tracker
1192
1193 // data stuff for auto joining a game
1194 #define MULTI_AUTOJOIN_JOIN_STAMP               2000
1195 #define MULTI_AUTOJOIN_QUERY_STAMP              2000
1196
1197 int Multi_did_autojoin;
1198 net_addr Multi_autojoin_addr;
1199 int Multi_autojoin_join_stamp;
1200 int Multi_autojoin_query_stamp;
1201
1202 // our join request
1203 join_request Multi_join_request;
1204
1205 // LOCAL function definitions
1206 void multi_join_check_buttons();
1207 void multi_join_button_pressed(int n);
1208 void multi_join_display_games();
1209 void multi_join_blit_game_status(active_game *game, int y);
1210 void multi_join_load_tcp_addrs();
1211 void multi_join_do_netstuff();
1212 void multi_join_ping_all();
1213 void multi_join_process_select();
1214 void multi_join_list_scroll_up();
1215 void multi_join_list_scroll_down();
1216 void multi_join_list_page_up();
1217 void multi_join_list_page_down();
1218 active_game *multi_join_get_game(int n);
1219 void multi_join_cull_timeouts();
1220 void multi_join_handle_item_cull(active_game *item, int item_index);
1221 void multi_join_send_join_request(int as_observer);
1222 void multi_join_create_game();
1223 void multi_join_blit_top_stuff();
1224 int multi_join_maybe_warn();
1225 int multi_join_warn_pxo();
1226 void multi_join_blit_protocol();
1227
1228 DCF(mj_make, "")
1229 {
1230         active_game ag, *newitem;;
1231         int idx;
1232
1233         dc_get_arg(ARG_INT);
1234         for(idx=0; idx<Dc_arg_int; idx++){
1235                 // stuff some fake info
1236                 memset(&ag, 0, sizeof(active_game));
1237                 sprintf(ag.name, "Game %d", idx);
1238                 ag.version = MULTI_FS_SERVER_VERSION;
1239                 ag.comp_version = MULTI_FS_SERVER_VERSION;
1240                 ag.server_addr.addr[0] = (char)idx;
1241                 ag.flags = (AG_FLAG_COOP | AG_FLAG_FORMING | AG_FLAG_STANDALONE);               
1242
1243                 // add the game
1244                 newitem = multi_update_active_games(&ag);
1245
1246                 // timestamp it so we get random timeouts
1247                 if(newitem != NULL){
1248                         // newitem->heard_from_timer = timestamp((int)frand_range(500.0f, 10000.0f));
1249                 }
1250         }       
1251 }
1252
1253 void multi_join_notify_new_game()
1254 {       
1255 #ifndef MAKE_FS1
1256         // reset the # of items 
1257         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);
1258         Multi_join_slider.force_currentItem(Multi_join_list_start);
1259 #endif
1260 }
1261
1262 int multi_join_autojoin_do()
1263 {
1264         // if we have an active game on the list, then return a positive value so that we
1265         // can join the game
1266         if ( Active_game_head && (Active_game_count > 0) ) {
1267                 Multi_join_selected_item = Active_game_head;
1268                 return 1;
1269         }
1270
1271         // send out a server_query again
1272         if ( timestamp_elapsed(Multi_autojoin_query_stamp) ) {
1273                 send_server_query(&Multi_autojoin_addr);
1274                 Multi_autojoin_query_stamp = timestamp(MULTI_AUTOJOIN_QUERY_STAMP);
1275         }
1276
1277         return -1;
1278 }
1279
1280 void multi_join_game_init()
1281 {
1282         int idx;
1283
1284         // do the multiplayer init stuff - multi_level_init() now does all net_player zeroing.
1285         // setup various multiplayer things
1286         SDL_assert( Game_mode & GM_MULTIPLAYER );
1287         SDL_assert( Net_player != NULL );
1288
1289         switch (Multi_options_g.protocol) {     
1290         case NET_IPX:
1291                 ADDRESS_LENGTH = IPX_ADDRESS_LENGTH;
1292                 PORT_LENGTH = IPX_PORT_LENGTH;
1293                 break;
1294
1295         case NET_TCP:
1296                 ADDRESS_LENGTH = IP_ADDRESS_LENGTH;             
1297                 PORT_LENGTH = IP_PORT_LENGTH;                   
1298                 break;
1299
1300         default :
1301                 Int3();
1302         } // end switch
1303         
1304         HEADER_LENGTH = 1;
1305
1306         memset( &Netgame, 0, sizeof(Netgame) );
1307
1308         multi_level_init();             
1309         Net_player->flags |= NETINFO_FLAG_DO_NETWORKING;        
1310         Net_player->player = Player;
1311         memcpy(&Net_player->p_info.addr,&Psnet_my_addr,sizeof(net_addr));
1312
1313         // check for the existence of a CD
1314         multi_common_verify_cd();
1315
1316         // load my local netplayer options
1317         multi_options_local_load(&Net_player->p_info.options, Net_player);      
1318
1319         game_flush();
1320
1321         // set the palette
1322 #ifdef MAKE_FS1
1323         common_set_interface_palette(MULTI_JOIN_PALETTE);
1324 #endif
1325
1326         // destroy any chatbox contents which previously existed (from another game)
1327         chatbox_clear();
1328
1329         // create the interface window
1330         Multi_join_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
1331         Multi_join_window.set_mask_bmap(Multi_join_bitmap_mask_fname[gr_screen.res]);
1332
1333         // load the background bitmap
1334         Multi_join_bitmap = bm_load(Multi_join_bitmap_fname[gr_screen.res]);
1335         if(Multi_join_bitmap < 0){
1336                 // we failed to load the bitmap - this is very bad
1337                 Int3();
1338         }
1339
1340         // intialize the endgame system
1341         multi_endgame_init();   
1342
1343         // initialize the common notification messaging
1344         multi_common_notify_init();
1345
1346         // initialize the common text area
1347         multi_common_set_text("");      
1348
1349         // load and use the common interface palette
1350         multi_common_load_palette();
1351         multi_common_set_palette();
1352
1353         // load the help overlay
1354         help_overlay_load(MULTI_JOIN_OVERLAY);
1355         help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1356         
1357         // do TCP and VMT specific initialization
1358         if(Multi_options_g.protocol == NET_TCP){                
1359                 // if this is a TCP (non tracker) game, we'll load up our default address list right now                
1360                 multi_join_load_tcp_addrs();            
1361         }       
1362
1363         // initialize any and all timestamps    
1364         Multi_join_glr_stamp = -1;
1365         Multi_join_ping_stamp = -1;
1366         Multi_join_sent_stamp = -1;
1367
1368         // reset frame count
1369         Multi_join_frame_count = 0;
1370
1371         // haven't tried to verify on the tracker yet.
1372         Multi_join_mt_tried_verify = 0;
1373
1374         // clear our all game lists to save hassles
1375         multi_join_clear_game_list();
1376
1377         // create the interface buttons
1378         for(idx=0; idx<MULTI_JOIN_NUM_BUTTONS; idx++){
1379                 // create the object
1380                 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);
1381
1382                 // set the sound to play when highlighted
1383                 Multi_join_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
1384
1385                 // set the ani for the button
1386                 Multi_join_buttons[gr_screen.res][idx].button.set_bmaps(Multi_join_buttons[gr_screen.res][idx].filename);
1387
1388                 // set the hotspot
1389                 Multi_join_buttons[gr_screen.res][idx].button.link_hotspot(Multi_join_buttons[gr_screen.res][idx].hotspot);
1390         }               
1391
1392 #ifndef MAKE_FS1
1393         // create all xstrs
1394         for(idx=0; idx<MULTI_JOIN_NUM_TEXT; idx++){
1395                 Multi_join_window.add_XSTR(&Multi_join_text[gr_screen.res][idx]);
1396         }
1397 #endif
1398
1399         Multi_join_should_send = -1;
1400
1401         // close any previously open chatbox
1402         chatbox_close();
1403
1404         // create the list item select button
1405         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);
1406         Multi_join_select_button.hide();
1407
1408 #ifndef MAKE_FS1
1409         // slider
1410         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);
1411 #endif
1412
1413         // if starting a network game, then go to the create game screen
1414         if ( Cmdline_start_netgame ) {
1415                 multi_join_create_game();               
1416         } else if ( Cmdline_connect_addr != NULL ) {
1417                 char *p;
1418                 short port_num;
1419                 int ip_addr;
1420
1421                 // joining a game.  Send a join request to the given IP address, and wait for the return.
1422                 memset( &Multi_autojoin_addr, 0, sizeof(net_addr) );
1423                 Multi_autojoin_addr.type = NET_TCP;
1424
1425                 // create the address, looking out for port number at the end
1426                 port_num = DEFAULT_GAME_PORT;
1427                 p = strrchr(Cmdline_connect_addr, ':');
1428                 if ( p ) {
1429                         *p = 0;
1430                         p++;
1431                         port_num = (short)atoi(p);
1432                 }
1433                 ip_addr = inet_addr(Cmdline_connect_addr);
1434                 memcpy(Multi_autojoin_addr.addr, &ip_addr, 4);
1435                 Multi_autojoin_addr.port = port_num;
1436
1437                 send_server_query(&Multi_autojoin_addr);
1438                 Multi_autojoin_query_stamp = timestamp(MULTI_AUTOJOIN_QUERY_STAMP);
1439                 Multi_did_autojoin = 0;
1440         }
1441 }
1442
1443 void multi_join_clear_game_list()
1444 {
1445         // misc data    
1446         Multi_join_list_selected = -1;  
1447         Multi_join_selected_item = NULL;        
1448         MJ_LIST_START_SET(-1);
1449         Multi_join_list_start_item = NULL;      
1450
1451         // free up the active game list
1452         multi_free_active_games();
1453
1454         // initialize the active game list
1455         Active_game_head = NULL;
1456         Active_game_count = 0;
1457 }
1458
1459 void multi_join_game_do_frame()
1460 {
1461         // check the status of our reliable socket.  If not valid, popup error and return to main menu
1462         // I put this code here to avoid nasty gameseq issues with states.  Also, we will have nice
1463         // background for the popup
1464         if ( !psnet_rel_check() ) {
1465                 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));
1466                 gameseq_post_event(GS_EVENT_MAIN_MENU);
1467                 return;
1468         }       
1469
1470         // return here since we will be moving to the next stage anyway -- I don't want to see the backgrounds of
1471         // all the screens for < 1 second for every screen we automatically move to.
1472         if ( Cmdline_start_netgame ) {
1473                 return;
1474         }
1475
1476         // when joining a network game, wait for the server query to come back, and then join the game
1477         if ( Cmdline_connect_addr != NULL ) {
1478                 int rval;
1479
1480                 if ( !Multi_did_autojoin ) {
1481                         rval = popup_till_condition(multi_join_autojoin_do, XSTR("&Cancel", 779), XSTR("Joining netgame", 1500) );
1482                         if ( rval == 0 ) {
1483                                 // cancel was hit.  Send the user back to the main hall
1484                                 gameseq_post_event(GS_EVENT_MAIN_MENU);
1485                                 Cmdline_connect_addr = NULL;            // reset this value.
1486                         }
1487
1488                         // when we get here, we have the data -- join the game.
1489                         multi_join_send_join_request(0);
1490                         Multi_autojoin_join_stamp = timestamp(MULTI_AUTOJOIN_JOIN_STAMP);
1491                         Multi_did_autojoin = 1;
1492                 }
1493
1494                 if ( timestamp_elapsed(Multi_autojoin_join_stamp) ) {
1495                         multi_join_send_join_request(0);
1496                         Multi_autojoin_join_stamp = timestamp(MULTI_AUTOJOIN_JOIN_STAMP);
1497                 }
1498                 return;
1499
1500         }       
1501
1502         // reset the should send var
1503         Multi_join_should_send = -1;
1504
1505         int k = Multi_join_window.process();
1506
1507         // process any keypresses
1508         switch(k){
1509         case SDLK_ESCAPE :
1510                 if(help_overlay_active(MULTI_JOIN_OVERLAY)){
1511                         help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1512                 } else {                
1513                         gameseq_post_event(GS_EVENT_MAIN_MENU);                 
1514                         gamesnd_play_iface(SND_USER_SELECT);
1515                 }
1516                 break;
1517
1518         // page up the game list
1519         case SDLK_PAGEUP:
1520                 multi_join_list_page_up();      
1521 #ifndef MAKE_FS1
1522                 Multi_join_slider.force_currentItem(Multi_join_list_start);
1523 #endif
1524                 break;
1525
1526         case SDLK_t:
1527                 multi_pinfo_popup(Net_player);
1528                 break;
1529
1530         // page down the game list
1531         case SDLK_PAGEDOWN:
1532                 multi_join_list_page_down();
1533 #ifndef MAKE_FS1
1534                 Multi_join_slider.force_currentItem(Multi_join_list_start);
1535 #endif
1536                 break;
1537
1538         // send out a ping-all
1539         case SDLK_p :
1540                 multi_join_ping_all();          
1541                 Multi_join_ping_stamp = timestamp(MULTI_JOIN_PING_TIME);
1542                 break;  
1543
1544         // shortcut to start a game     
1545         case SDLK_s :
1546                 multi_join_create_game();               
1547                 break;
1548
1549         // scroll the game list up
1550         case SDLK_UP:
1551                 multi_join_list_scroll_up();
1552 #ifndef MAKE_FS1
1553                 Multi_join_slider.force_currentItem(Multi_join_list_start);
1554 #endif
1555                 break;
1556
1557         // scroll the game list down
1558         case SDLK_DOWN:
1559                 multi_join_list_scroll_down();
1560 #ifndef MAKE_FS1
1561                 Multi_join_slider.force_currentItem(Multi_join_list_start);
1562 #endif
1563                 break;
1564         }       
1565
1566         if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
1567                 help_overlay_set_state(MULTI_JOIN_OVERLAY, 0);
1568         }
1569
1570         // do any network related stuff
1571         multi_join_do_netstuff(); 
1572
1573         // process any button clicks
1574         multi_join_check_buttons();
1575
1576         // process any list selection stuff
1577         multi_join_process_select();
1578
1579         // draw the background, etc
1580         gr_reset_clip();
1581         GR_MAYBE_CLEAR_RES(Multi_join_bitmap);
1582         if(Multi_join_bitmap != -1){            
1583                 gr_set_bitmap(Multi_join_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1584                 gr_bitmap(0,0);
1585         }
1586         Multi_join_window.draw();
1587
1588         // display the active games
1589         multi_join_display_games();
1590
1591         // display any text in the info area
1592         multi_common_render_text();
1593
1594         // display any pending notification messages
1595         multi_common_notify_do();
1596
1597         // blit the CD icon and any PXO filter stuff
1598         multi_join_blit_top_stuff();
1599
1600         // draw the help overlay
1601         help_overlay_maybe_blit(MULTI_JOIN_OVERLAY);
1602         
1603         // flip the buffer
1604         gr_flip();
1605
1606         // if we are supposed to be sending a join request
1607         if(Multi_join_should_send != -1){               
1608                 multi_join_send_join_request(Multi_join_should_send);
1609         }
1610         Multi_join_should_send = -1;
1611
1612         // increment the frame count
1613         Multi_join_frame_count++;
1614 }
1615
1616 void multi_join_game_close()
1617 {
1618         // unload any bitmaps
1619         if(!bm_unload(Multi_join_bitmap)){
1620                 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_join_bitmap_fname[gr_screen.res]));
1621         }
1622
1623         // unload the help overlay
1624         help_overlay_unload(MULTI_JOIN_OVERLAY);        
1625
1626         // free up the active game list
1627         multi_free_active_games();
1628         
1629         // destroy the UI_WINDOW
1630         Multi_join_window.destroy();
1631
1632 #ifdef MAKE_FS1
1633         common_free_interface_palette();
1634 #endif
1635 }
1636
1637 void multi_join_check_buttons()
1638 {
1639         int idx;
1640         for(idx=0;idx<MULTI_JOIN_NUM_BUTTONS;idx++){
1641                 // we only really need to check for one button pressed at a time, so we can break after 
1642                 // finding one.
1643                 if(Multi_join_buttons[gr_screen.res][idx].button.pressed()){
1644                         multi_join_button_pressed(idx);
1645                         break;
1646                 }
1647         }
1648 }
1649
1650 void multi_join_button_pressed(int n)
1651 {
1652         switch(n){
1653         case MJ_CANCEL :
1654                 // if we're player PXO, go back there   
1655                 gameseq_post_event(GS_EVENT_MAIN_MENU);         
1656                 gamesnd_play_iface(SND_USER_SELECT);            
1657                 break;
1658         case MJ_ACCEPT :
1659                 if(Active_game_count <= 0){
1660                         multi_common_add_notify(XSTR("No games found!",757));
1661                         gamesnd_play_iface(SND_GENERAL_FAIL);
1662                 } else if(Multi_join_list_selected == -1){
1663                         multi_common_add_notify(XSTR("No game selected!",758));
1664                         gamesnd_play_iface(SND_GENERAL_FAIL);
1665                 } else if((Multi_join_sent_stamp != -1) && !timestamp_elapsed(Multi_join_sent_stamp)){
1666                         multi_common_add_notify(XSTR("Still waiting on previous join request!",759));
1667                         gamesnd_play_iface(SND_GENERAL_FAIL);
1668                 } else {                        
1669                         // otherwise, if he's already played PXO games, warn him        
1670                         /*
1671                         if(Player->flags & PLAYER_FLAGS_HAS_PLAYED_PXO){
1672                                 if(!multi_join_warn_pxo()){
1673                                         break;
1674                                 }                       
1675                         }
1676                         */
1677
1678                         // send the join request here
1679                         SDL_assert(Multi_join_selected_item != NULL);
1680
1681                         // send a join request packet
1682                         Multi_join_should_send = 0;                     
1683                         
1684                         gamesnd_play_iface(SND_COMMIT_PRESSED);
1685                 }
1686                 break;
1687
1688         // help overlay
1689         case MJ_HELP:
1690                 if(!help_overlay_active(MULTI_JOIN_OVERLAY)){
1691                         help_overlay_set_state(MULTI_JOIN_OVERLAY,1);
1692                 } else {
1693                         help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1694                 }
1695                 break;
1696
1697         // scroll the game list up
1698         case MJ_SCROLL_UP:
1699                 multi_join_list_scroll_up();
1700 #ifndef MAKE_FS1
1701                 Multi_join_slider.force_currentItem(Multi_join_list_start);
1702 #endif
1703                 break;
1704
1705         // scroll the game list down
1706         case MJ_SCROLL_DOWN:
1707                 multi_join_list_scroll_down();
1708 #ifndef MAKE_FS1
1709                 Multi_join_slider.force_currentItem(Multi_join_list_start);
1710 #endif
1711                 break;
1712
1713         // scroll the info text box up
1714         case MJ_SCROLL_INFO_UP:
1715                 multi_common_scroll_text_up();
1716                 break;
1717
1718         // scroll the info text box down
1719         case MJ_SCROLL_INFO_DOWN:
1720                 multi_common_scroll_text_down();
1721                 break;
1722
1723         // go to the options screen
1724         case MJ_OPTIONS:
1725                 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
1726                 break;
1727
1728         // go to the start game screen  
1729         case MJ_START_GAME:
1730                 multi_join_create_game();               
1731                 break;
1732
1733         // refresh the game/server list
1734         case MJ_REFRESH:                
1735                 gamesnd_play_iface(SND_USER_SELECT);
1736                 broadcast_game_query();         
1737                 break;
1738
1739         // join a game as an observer
1740         case MJ_JOIN_OBSERVER:
1741                 if(Active_game_count <= 0){
1742                         multi_common_add_notify(XSTR("No games found!",757));
1743                         gamesnd_play_iface(SND_GENERAL_FAIL);
1744                 } else if(Multi_join_list_selected == -1){
1745                         multi_common_add_notify(XSTR("No game selected!",758));
1746                         gamesnd_play_iface(SND_GENERAL_FAIL);
1747                 } else if((Multi_join_sent_stamp != -1) && !timestamp_elapsed(Multi_join_sent_stamp)){
1748                         multi_common_add_notify(XSTR("Still waiting on previous join request!",759));
1749                         gamesnd_play_iface(SND_GENERAL_FAIL);
1750                 } else {                        
1751                         // send the join request here
1752                         SDL_assert(Multi_join_selected_item != NULL);
1753
1754                         Multi_join_should_send = 1;             
1755
1756                         gamesnd_play_iface(SND_COMMIT_PRESSED);
1757                 }
1758                 break;
1759
1760         default :
1761                 multi_common_add_notify(XSTR("Not implemented yet!",760));
1762                 gamesnd_play_iface(SND_GENERAL_FAIL);
1763                 break;
1764         }
1765 }
1766
1767 // display all relevant info for active games
1768 void multi_join_display_games()
1769 {
1770         active_game *moveup = Multi_join_list_start_item;       
1771         char str[200];          
1772         int w,h;
1773         int con_type;
1774         int y_start = Mj_list_y[gr_screen.res];
1775         int count = 0;
1776         
1777         if(moveup != NULL){
1778                 do {                    
1779                         // blit the game status (including text and type icon)
1780                         multi_join_blit_game_status(moveup,y_start);                    
1781                         
1782                         // get the connection type
1783                         con_type = (moveup->flags & AG_FLAG_CONNECTION_SPEED_MASK) >> AG_FLAG_CONNECTION_BIT;
1784                         if((con_type > 4) || (con_type < 0)){
1785                                 con_type = 0;
1786                         }
1787
1788                         // display the connection speed
1789                         str[0] = '\0';
1790                         strcpy(str, Multi_join_speed_labels[con_type]);
1791                         gr_set_color_fast(Multi_join_speed_colors[con_type]);
1792                         gr_string(Mj_speed_coords[gr_screen.res][MJ_X_COORD], y_start, str);
1793
1794                         // we'll want to have different colors for highlighted items, etc.
1795                         if(moveup == Multi_join_selected_item){
1796                                 gr_set_color_fast(&Color_text_selected);
1797                         } else {
1798                                 gr_set_color_fast(&Color_text_normal);
1799                         }
1800
1801                         // display the game name, adding appropriate status chars
1802                         str[0] = '\0';
1803                         if(moveup->flags & AG_FLAG_STANDALONE){
1804                                 strcat(str,MJ_CHAR_STANDALONE);
1805                         }
1806                         if(moveup->flags & AG_FLAG_CAMPAIGN){
1807                                 strcat(str,MJ_CHAR_CAMPAIGN);
1808                         }
1809
1810                         // tack on the actual server name                       
1811                         strcat(str," ");
1812                         strcat(str,moveup->name);
1813                         if(strlen(moveup->mission_name) > 0){
1814                                 strcat(str, " / ");
1815                                 strcat(str,moveup->mission_name);
1816                         } 
1817
1818                         // make sure the string fits in the display area and draw it
1819                         gr_force_fit_string(str,200,Mj_game_name_coords[gr_screen.res][MJ_W_COORD]);                    
1820                         gr_string(Mj_game_name_coords[gr_screen.res][MJ_X_COORD],y_start,str);
1821
1822                         // display the ping time
1823                         if(moveup->ping.ping_avg > 0){
1824                                 if(moveup->ping.ping_avg > 1000){
1825                                         gr_set_color_fast(&Color_bright_red);
1826                                         strcpy(str,XSTR("> 1 sec",761));
1827                                 } else {
1828                                         // set the appropriate ping time color indicator
1829                                         if(moveup->ping.ping_avg > MJ_PING_YELLOW){
1830                                                 gr_set_color_fast(&Color_bright_red);
1831                                         } else if(moveup->ping.ping_avg > MJ_PING_YELLOW){
1832                                                 gr_set_color_fast(&Color_bright_yellow);
1833                                         } else {
1834                                                 gr_set_color_fast(&Color_bright_green);
1835                                         }
1836
1837                                         sprintf(str,"%d",moveup->ping.ping_avg);
1838                                         strcat(str,XSTR(" ms",762));  // [[ Milliseconds ]]
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                         sprintf(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                 strcpy(status_text,XSTR("Forming",764));
1912                 break;
1913         case AG_FLAG_BRIEFING:
1914                 gr_set_color_fast(&Color_bright_red);
1915                 strcpy(status_text,XSTR("Briefing",765));
1916                 break;
1917         case AG_FLAG_DEBRIEF:
1918                 gr_set_color_fast(&Color_bright_red);
1919                 strcpy(status_text,XSTR("Debrief",766));
1920                 break;
1921         case AG_FLAG_PAUSE:
1922                 gr_set_color_fast(&Color_bright_red);
1923                 strcpy(status_text,XSTR("Paused",767));
1924                 break;
1925         case AG_FLAG_IN_MISSION:
1926                 gr_set_color_fast(&Color_bright_red);
1927                 strcpy(status_text,XSTR("Playing",768));
1928                 break;
1929         default:
1930                 gr_set_color_fast(&Color_bright);
1931                 strcpy(status_text,XSTR("Unknown",769));
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);
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)){
2384                         return;
2385                 }
2386
2387                 nprintf(("Password : %s\n",Multi_join_request.passwd));
2388         }       
2389                 
2390         // fill out the join request struct     
2391         strcpy(Multi_join_request.callsign,Player->callsign);
2392         if(strlen(Player->image_filename) > 0){
2393                 strcpy(Multi_join_request.image_filename, Player->image_filename);
2394         }       
2395 #ifndef MAKE_FS1
2396         if(strlen(Player->squad_filename) > 0){
2397                 strcpy(Multi_join_request.squad_filename, Player->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         strncpy(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);
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                         strcpy( Multi_sg_netgame->name, Cmdline_game_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                         strcpy(Multi_sg_netgame->passwd, Cmdline_game_password);
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);
3293         sprintf(buf, XSTR("%s game",782), out_name);  // [[ %s will be a pilot's name ]]
3294         if ( strlen(buf) > MAX_GAMENAME_LEN ){
3295                 strcpy(buf, XSTR("Temporary name",783));
3296         }
3297         strcpy(Multi_sg_netgame->name, buf);
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);
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);
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                                 sprintf(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)
3489 {
3490         char use[100];
3491         char *first;
3492
3493         strcpy(use,in);
3494         first = strtok(use," ");
3495
3496         // just copy the string
3497         if(first == NULL){
3498                 strcpy(out,in);
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                         strcpy(out,XSTR("Lt. ",786));  // [[ lieutenant ]]
3508                         strcat(out,first);
3509                 }
3510                 // if he _is_ just a plain lieutenant
3511                 else {
3512                         strcpy(out,in);
3513                 }
3514         } else {
3515                 strcpy(out,in);
3516         }
3517 }
3518
3519 void multi_sg_check_passwd()
3520 {
3521         // check to see if the password input box has been pressed
3522         if(Multi_sg_game_passwd.changed()){
3523                 Multi_sg_game_passwd.get_text(Multi_sg_netgame->passwd);
3524         }
3525 }
3526
3527 void multi_sg_check_name()
3528 {
3529         // check to see if the game name input box has been pressed
3530         if(Multi_sg_game_name.changed()){
3531                 Multi_sg_game_name.get_text(Multi_sg_netgame->name);
3532         }
3533 }
3534
3535 void multi_sg_release_passwd()
3536 {
3537         // hide and disable the password input box
3538         Multi_sg_game_passwd.hide();
3539         Multi_sg_game_passwd.disable();
3540
3541         // set the focus back to the name input box
3542         Multi_sg_game_name.set_focus();
3543 }
3544
3545 int multi_sg_rank_select_valid(int rank)
3546 {
3547         // rank above mode
3548         if(Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE){
3549                 if(Net_player->player->stats.rank >= rank){
3550                         return 1;
3551                 }
3552         }
3553         // rank below mode
3554         else {
3555                 if(Net_player->player->stats.rank <= rank){
3556                         return 1;
3557                 }
3558         }
3559         
3560         return 0;
3561 }
3562
3563 void multi_sg_select_rank_default()
3564 {
3565         // pick our rank for now
3566         Multi_sg_rank_select = Net_player->player->stats.rank;
3567
3568         // set the Netgame rank
3569         Multi_sg_netgame->rank_base = Multi_sg_rank_select;
3570 }
3571                 
3572 // -------------------------------------------------------------------------------------------------
3573 //
3574 // MULTIPLAYER CREATE GAME screen
3575 //
3576
3577 //XSTR:OFF
3578 // bitmaps defs
3579 const char *Multi_create_bitmap_fname[GR_NUM_RESOLUTIONS] = {
3580         "MultiCreate",                  // GR_640
3581         "2_MultiCreate"         // GR_1024
3582 };
3583
3584 const char *Multi_create_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
3585         "MultiCreate-M",                // GR_640
3586         "2_MultiCreate-M"               // GR_1024
3587 };
3588
3589 const char *Multi_create_loading_fname[GR_NUM_RESOLUTIONS] = {
3590         "PleaseWait",                   // GR_640
3591         "2_PleaseWait"                  // GR_1024
3592 };
3593 //XSTR:ON
3594
3595 #define MULTI_CREATE_NUM_BUTTONS        23
3596
3597 // button defs
3598 #define MC_SHOW_ALL                                     0
3599 #define MC_SHOW_COOP                                    1
3600 #define MC_SHOW_TEAM                                    2
3601 #define MC_SHOW_DOGFIGHT                        3
3602 #define MC_PXO_REFRESH                          4
3603 #define MC_PILOT_INFO                           5
3604 #define MC_SCROLL_LIST_UP                       6 
3605 #define MC_SCROLL_LIST_DOWN             7
3606 #define MC_SCROLL_PLAYERS_UP            8
3607 #define MC_SCROLL_PLAYERS_DOWN  9
3608 #define MC_MISSION_FILTER                       10
3609 #define MC_CAMPAIGN_FILTER                      11
3610 #define MC_CANCEL                                               12
3611 #define MC_TEAM0                                                13
3612 #define MC_TEAM1                                                14
3613 #define MC_KICK                                         15
3614 #define MC_CLOSE                                                16
3615 #define MC_SCROLL_INFO_UP                       17
3616 #define MC_SCROLL_INFO_DOWN             18
3617 #define MC_HOST_OPTIONS                         19
3618 #define MC_HELP                                         20
3619 #define MC_OPTIONS                                      21
3620 #define MC_ACCEPT                                               22
3621
3622
3623 UI_WINDOW Multi_create_window;                                                                          // the window object for the create screen
3624 UI_BUTTON Multi_create_player_select_button;                                            // for selecting players
3625 UI_BUTTON Multi_create_list_select_button;                                              // for selecting missions/campaigns
3626 int Multi_create_bitmap;                                                                                                // the background bitmap
3627 UI_SLIDER2 Multi_create_slider;                                                                         // for create list
3628
3629 // constants for coordinate look ups
3630 #define MC_X_COORD 0
3631 #define MC_Y_COORD 1
3632 #define MC_W_COORD 2
3633 #define MC_H_COORD 3
3634
3635 ui_button_info Multi_create_buttons[GR_NUM_RESOLUTIONS][MULTI_CREATE_NUM_BUTTONS] = {   
3636         { // GR_640
3637 #ifdef MAKE_FS1
3638                 ui_button_info("MC_18", 34,             131,    -1,     -1,     18),    // all
3639                 ui_button_info("MC_19", 72,             131,    -1,     -1,     19),    // coop
3640                 ui_button_info("MC_20", 120,    131,    -1,     -1,     20),    // team
3641 //              ui_button_info("MC_21", 166,    131,    -1,     -1,     21),    // dogfight
3642                 ui_button_info("none",  -1,             -1,             -1,     -1,     -1),    // dogfight (not used)
3643                 ui_button_info("none",  -1,             -1,             -1,     -1,     -1),    // pxo?
3644                 ui_button_info("MC_26", 540,    114,    -1,     -1,     26),    // pilot info
3645                 ui_button_info("MC_03", 0,              187,    -1,     -1,     2),     // scroll list up
3646                 ui_button_info("MC_02", 0,              227,    -1,     -1,     3),     // scroll list down
3647                 ui_button_info("MC_04", 611,    182,    -1,     -1,     4),     // scroll players up
3648                 ui_button_info("MC_05", 611,    221,    -1,     -1,     5),     // scroll players down
3649                 ui_button_info("MC_06", 18,             322,    -1,     -1,     6),     // mission filter
3650                 ui_button_info("MC_07", 18,             344,    -1,     -1,     7),     // campaign filter
3651                 ui_button_info("MC_10", 317,    339,    -1,     -1,     10),    // cancel
3652                 ui_button_info("MC_14", 464,    350,    -1,     -1,     14),    // team 1
3653                 ui_button_info("MC_15", 498,    350,    -1,     -1,     15),    // team 2
3654                 ui_button_info("MC_16", 527,    346,    -1,     -1,     16),    // kick
3655                 ui_button_info("MC_17", 572,    346,    -1,     -1,     17),    // close
3656                 ui_button_info("MC_08", 0,              398,    -1,     -1,     8),     // scroll mission info up
3657                 ui_button_info("MC_09", 0,              435,    -1,     -1,     9),     // scroll mission info down
3658                 ui_button_info("MC_27", 447,    402,    -1,     -1,     27),    // host options
3659                 ui_button_info("MC_11", 510,    428,    -1,     -1,     11),    // help
3660                 ui_button_info("MC_12", 510,    453,    -1,     -1,     12),    // options
3661                 ui_button_info("Mc_13", 562,    412,    -1,     -1,     13),    // commit
3662 #else
3663                 ui_button_info("MC_00", 32,     129,    36,     158,    0),             // show all missions
3664                 ui_button_info("MC_01", 76,     129,    71,     158,    1),             // show coop missions
3665                 ui_button_info("MC_02", 121,    129,    119,    158,    2),             // show team missions
3666                 ui_button_info("MC_03", 164,    129,    166,    158,    3),             // show dogfight missions
3667                 ui_button_info("MC_04", 399,    129,    229,    130,    4),             // pxo mission refresh
3668                 ui_button_info("MC_05", 567,    123,    467,    132,    5),             // pilot info
3669                 ui_button_info("MC_06", 1,              161,    -1,     -1,     6),             // scroll mission info up
3670                 ui_button_info("MC_08", 1,              304,    -1,     -1,     8),             // scroll mission info down
3671                 ui_button_info("MC_09", 613,    160,    -1,     -1,     9),             // scroll players up
3672                 ui_button_info("MC_10", 613,    202,    -1,     -1,     10),            // scroll players down
3673                 ui_button_info("MC_11", 22,     346,    27,     376,    11),            // mission filter
3674                 ui_button_info("MC_12", 104,    346,    110,    376,    12),            // campaign filter
3675                 ui_button_info("MC_13", 392,    341,    328,    364,    13),            // cancel
3676                 ui_button_info("MC_14", 472,    352,    482,    381,    14),            // team 0       
3677                 ui_button_info("MC_15", 506,    352,    514,    381,    15),            // team 1
3678                 ui_button_info("MC_16", 539,    346,    539,    381,    16),            // kick
3679                 ui_button_info("MC_17", 589,    346,    582,    381,    17),            // close
3680                 ui_button_info("MC_18", 1,              406,    -1,     -1,     18),            // scroll list up
3681                 ui_button_info("MC_19", 1,              447,    -1,     -1,     19),            // scroll list down
3682                 ui_button_info("MC_20", 499,    434,    436,    423,    20),            // host options
3683                 ui_button_info("MC_21", 534,    426,    -1,     -1,     21),            // help
3684                 ui_button_info("MC_22", 534,    452,    -1,     -1,     22),            // options
3685                 ui_button_info("MC_23", 571,    426,    572,    413,    23),            // commit
3686 #endif
3687         },      
3688         { // GR_1024
3689                 ui_button_info("2_MC_00", 51,           207,    61,     253,    0),             // show all missions
3690                 ui_button_info("2_MC_01", 122,  207,    124,    253,    1),             // show coop missions
3691                 ui_button_info("2_MC_02", 193,  207,    194,    253,    2),             // show team missions
3692                 ui_button_info("2_MC_03", 263,  207,    261,    253,    3),             // show dogfight missions
3693                 ui_button_info("2_MC_04", 639,  207,    479,    218,    4),             // pxo mission refresh
3694                 ui_button_info("2_MC_05", 907,  197,    748,    216,    5),             // pilot info
3695                 ui_button_info("2_MC_06", 1,            258,    -1,     -1,     6),             // scroll mission info up
3696                 ui_button_info("2_MC_08", 1,            487,    -1,     -1,     8),             // scroll mission info down
3697                 ui_button_info("2_MC_09", 981,  256,    -1,     -1,     9),             // scroll players up
3698                 ui_button_info("2_MC_10", 981,  323,    -1,     -1,     10),            // scroll players down
3699                 ui_button_info("2_MC_11",  35,  554,    46,     601,    11),            // mission filter
3700                 ui_button_info("2_MC_12", 166,  554,    174,    601,    12),            // campaign filter
3701                 ui_button_info("2_MC_13", 628,  545,    559,    582,    13),            // cancel
3702                 ui_button_info("2_MC_14", 756,  564,    772,    610,    14),            // team 0       
3703                 ui_button_info("2_MC_15", 810,  564,    826,    610,    15),            // team 1
3704                 ui_button_info("2_MC_16", 862,  554,    872,    610,    16),            // kick
3705                 ui_button_info("2_MC_17", 943,  554,    949,    610,    17),            // close
3706                 ui_button_info("2_MC_18", 1,            649,    -1,     -1,     18),            // scroll list up
3707                 ui_button_info("2_MC_19", 1,            716,    -1,     -1,     19),            // scroll list down
3708                 ui_button_info("2_MC_20", 798,  695,    726,    667,    20),            // host options
3709                 ui_button_info("2_MC_21", 854,  681,    -1,     -1,     21),            // help
3710                 ui_button_info("2_MC_22", 854,  724,    -1,     -1,     22),            // options
3711                 ui_button_info("2_MC_23", 914,  681,    932,    667,    23),            // commit
3712         },      
3713 };
3714
3715 #ifdef MAKE_FS1
3716 #define MULTI_CREATE_NUM_TEXT                           0
3717 #else
3718 #define MULTI_CREATE_NUM_TEXT                           15
3719 #endif
3720 UI_XSTR Multi_create_text[GR_NUM_RESOLUTIONS][MULTI_CREATE_NUM_BUTTONS] = {
3721         { // GR_640
3722                 // not needed for FS1
3723 #ifndef MAKE_FS1
3724                 {"All",                                 1256,           36,     158,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[0][MC_SHOW_ALL].button},
3725                 {"Coop",                                        1257,           71,     158,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[0][MC_SHOW_COOP].button},
3726                 {"Team",                                        1258,           119,    158,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[0][MC_SHOW_TEAM].button},
3727                 {"Dogfight",                    1259,           166,    158,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[0][MC_SHOW_DOGFIGHT].button},
3728                 {"Refresh Missions",    1260,           229,    130,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[0][MC_PXO_REFRESH].button},
3729                 {"Pilot Info",                  1261,           467,    132,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[0][MC_PILOT_INFO].button},        
3730                 {"Missions",                    1262,           27,     376,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[0][MC_MISSION_FILTER].button},
3731                 {"Campaigns",                   1263,           110,    376,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[0][MC_CAMPAIGN_FILTER].button},
3732                 {"Cancel",                              387,            328,    364,    UI_XSTR_COLOR_PINK,     -1,     &Multi_create_buttons[0][MC_CANCEL].button},
3733                 {"1",                                           1264,           482,    381,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[0][MC_TEAM0].button},
3734                 {"2",                                           1265,           514,    381,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[0][MC_TEAM1].button},
3735                 {"Kick",                                        1266,           539,    381,    UI_XSTR_COLOR_PINK,     -1,     &Multi_create_buttons[0][MC_KICK].button},
3736                 {"Close",                               1508,           582,    381,    UI_XSTR_COLOR_PINK,     -1,     &Multi_create_buttons[0][MC_CLOSE].button},     
3737                 {"Host Options",                1267,           436,    423,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[0][MC_HOST_OPTIONS].button},              
3738                 {"Commit",                              1062,           572,    413,    UI_XSTR_COLOR_PINK,     -1,     &Multi_create_buttons[0][MC_ACCEPT].button}
3739 #endif
3740         },              
3741         { // GR_1024            
3742                 // not needed for FS1
3743 #ifndef MAKE_FS1
3744                 {"All",                                 1256,           61,     253,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[1][MC_SHOW_ALL].button},
3745                 {"Coop",                                        1257,           124,    253,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[1][MC_SHOW_COOP].button},
3746                 {"Team",                                        1258,           194,    253,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[1][MC_SHOW_TEAM].button},
3747                 {"Dogfight",                    1259,           261,    253,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[1][MC_SHOW_DOGFIGHT].button},
3748                 {"Refresh Missions",    1260,           501,    218,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[1][MC_PXO_REFRESH].button},
3749                 {"Pilot Info",                  1261,           814,    216,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[1][MC_PILOT_INFO].button},        
3750                 {"Missions",                    1262,           46,     601,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[1][MC_MISSION_FILTER].button},
3751                 {"Campaigns",                   1263,           174,    601,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[1][MC_CAMPAIGN_FILTER].button},
3752                 {"Cancel",                              387,            559,    582,    UI_XSTR_COLOR_PINK,     -1,     &Multi_create_buttons[1][MC_CANCEL].button},
3753                 {"1",                                           1264,           772,    610,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[1][MC_TEAM0].button},
3754                 {"2",                                           1265,           826,    610,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[1][MC_TEAM1].button},
3755                 {"Kick",                                        1266,           872,    610,    UI_XSTR_COLOR_PINK,     -1,     &Multi_create_buttons[1][MC_KICK].button},
3756                 {"Close",                               1508,           949,    610,    UI_XSTR_COLOR_PINK,     -1,     &Multi_create_buttons[1][MC_CLOSE].button},     
3757                 {"Host Options",                1267,           755,    683,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_create_buttons[1][MC_HOST_OPTIONS].button},              
3758                 {"Commit",                              1062,           932,    667,    UI_XSTR_COLOR_PINK,     -1,     &Multi_create_buttons[1][MC_ACCEPT].button}
3759 #endif
3760         },
3761 };
3762
3763 // squad war checkbox
3764 UI_CHECKBOX     Multi_create_sw_checkbox;
3765 const char *Multi_create_sw_checkbox_fname[GR_NUM_RESOLUTIONS] = {
3766         "MC_SW_00",
3767         "MC_SW_00",
3768 };
3769 int Multi_create_sw_checkbox_coords[GR_NUM_RESOLUTIONS][2] = {
3770         { // GR_640
3771                 6, 75
3772         },
3773         { // GR_1024
3774                 18, 135
3775         }
3776 };
3777 int Multi_create_sw_checkbox_text[GR_NUM_RESOLUTIONS][2] = {
3778         { // GR_640
3779                 6, 95
3780         },
3781         { // GR_640
3782                 18, 155
3783         },
3784 };
3785
3786 // game information text areas
3787 int Mc_list_coords[GR_NUM_RESOLUTIONS][4] = {
3788         { // GR_640
3789 #ifdef MAKE_FS1
3790                 105, 170, 315, 146
3791 #else
3792                 105, 173, 311, 152
3793 #endif
3794         },
3795         { // GR_1024
3796                 62, 275, 600, 262
3797         }
3798 };
3799
3800 int Mc_players_coords[GR_NUM_RESOLUTIONS][4] = {
3801         { // GR_640
3802 #ifdef MAKE_FS1
3803                 465, 163, 138, 172
3804 #else
3805                 463, 164, 144, 180
3806 #endif
3807         },
3808         { // GR_1024
3809                 741, 262, 144, 180
3810         }
3811 };
3812
3813 int Mc_info_coords[GR_NUM_RESOLUTIONS][4] = {
3814         { // GR_640
3815 #ifdef MAKE_FS1
3816                 49, 407, 372, 56
3817 #else
3818                 47, 405, 363, 59
3819 #endif
3820         },
3821         { // GR_1024
3822                 75, 648, 363, 59
3823         }
3824 };
3825
3826 // mission icon stuff
3827 int Mc_icon_type_coords[GR_NUM_RESOLUTIONS][2] = {
3828         { // GR_640
3829                 38, -2          // y is an offset
3830         },
3831         { // GR_1024
3832                 61, -2          // y is an offset
3833         }
3834 };
3835
3836 int Mc_icon_volition_coords[GR_NUM_RESOLUTIONS][2] = {
3837         { // GR_640
3838                 61, -1          // y is an offset
3839         },
3840         { // GR_1024
3841                 98, 1           // y is an offset
3842         }
3843 };
3844
3845 int Mc_icon_silent_coords[GR_NUM_RESOLUTIONS][2] = {
3846         { // GR_640
3847                 72, 0           // y is an offset
3848         },
3849         { // GR_1024
3850                 115, 0          // y is an offset
3851         }
3852 };
3853
3854 int Mc_icon_valid_coords[GR_NUM_RESOLUTIONS][2] = {
3855         { // GR_640
3856                 91, 0           // y is an offset
3857         },
3858         { // GR_1024
3859                 146, 0          // y is an offset
3860         }
3861 };
3862
3863 // mission/campaign list column areas
3864 int Mc_column1_w[GR_NUM_RESOLUTIONS] = {
3865         194,            // GR_640
3866         310             // GR_1024
3867 };
3868
3869 int Mc_column2_w[GR_NUM_RESOLUTIONS] = {
3870         38,             // GR_640
3871         61                      // GR_1024
3872 };
3873
3874 int Mc_column3_w[GR_NUM_RESOLUTIONS] = {
3875         77,             // GR_640
3876         123             // GR_1024
3877 };
3878
3879 int Mc_mission_name_x[GR_NUM_RESOLUTIONS] = {
3880         105,            // GR_640
3881         168             // GR_1024
3882 };
3883
3884 int Mc_mission_count_x[GR_NUM_RESOLUTIONS] = {
3885         314,            // GR_640
3886         502             // GR_1024
3887 };
3888
3889 int Mc_mission_fname_x[GR_NUM_RESOLUTIONS] = {
3890         337,            // GR_640
3891         539             // GR_1024
3892 };
3893
3894 int Mc_create_game_text[GR_NUM_RESOLUTIONS][2] = {
3895         {13, 116},      // GR_640
3896         {21, 186}       // GR_1024
3897 };
3898
3899 int Mc_players_text[GR_NUM_RESOLUTIONS][2] = {
3900         {467, 150},     // GR_640
3901         {747, 240}      // GR_1024
3902 };
3903
3904 int Mc_team_text[GR_NUM_RESOLUTIONS][2] = {
3905         {484, 342},     // GR_640
3906         {774, 547}      // GR_1024
3907 };
3908
3909 int Mc_slider_coords[GR_NUM_RESOLUTIONS][4] = {
3910         {
3911                 3, 197, 13, 105 // GR_640
3912         },
3913         {
3914                 5, 316, 20, 168 // GR_1024
3915         }
3916 };
3917
3918 const char *Mc_slider_bitmap[GR_NUM_RESOLUTIONS] = {
3919         "slider",
3920         "2_slider"
3921 };
3922
3923 // player list control thingie defs
3924 #define MULTI_CREATE_PLIST_MAX_DISPLAY          20
3925 int Multi_create_plist_select_flag;                                                                     // flag indicating if we have a play selected
3926 short Multi_create_plist_select_id;                                                     // the net address of the currently selected player (for lookup)
3927
3928 // master tracker details
3929 int Multi_create_frame_count;                                                                                   // framecount
3930 int Multi_create_mt_tried_login;                                                                                // attempted to login this server on the MT
3931
3932 // mission filter settings                                                                              
3933 int Multi_create_filter;                                                                                                // what mode we're in
3934
3935 // game/campaign list control defs
3936 int Multi_create_list_max_display[GR_NUM_RESOLUTIONS] = {
3937         15,             // GR_640
3938         26                      // GR_1024
3939 };
3940
3941
3942 int Multi_create_list_count;                                                                                    // number of items in listbox
3943 int Multi_create_list_mode;                                                                                     // 0 == mission mode, 1 == campaign mode
3944 int Multi_create_list_start;                                                                                    // where to start displaying from
3945 int Multi_create_list_select;                                                                                   // which item is currently highlighted
3946 int Multi_create_files_loaded;
3947
3948 char Multi_create_files_array[MULTI_CREATE_MAX_LIST_ITEMS][MAX_FILENAME_LEN];
3949
3950 int Multi_create_mission_count;                                                                                 // how many we have
3951 int Multi_create_campaign_count;
3952 multi_create_info Multi_create_mission_list[MULTI_CREATE_MAX_LIST_ITEMS];
3953 multi_create_info Multi_create_campaign_list[MULTI_CREATE_MAX_LIST_ITEMS];
3954
3955 // use a pointer for the file list.  Will point to either the missions or the campaigns
3956 multi_create_info *Multi_create_file_list;
3957
3958 // LOCAL function definitions
3959 void multi_create_check_buttons();
3960 void multi_create_button_pressed(int n);
3961 void multi_create_init_as_server();
3962 void multi_create_init_as_client();
3963 void multi_create_do_netstuff();
3964 void multi_create_plist_scroll_up();
3965 void multi_create_plist_scroll_down();
3966 void multi_create_plist_process();
3967 void multi_create_plist_blit_normal();
3968 void multi_create_plist_blit_team();
3969 void multi_create_list_scroll_up();
3970 void multi_create_list_scroll_down();
3971 void multi_create_list_do();
3972 void multi_create_list_select_item(int n);
3973 void multi_create_list_blit_icons(int list_index, int y_start);
3974 void multi_create_accept_hit();
3975 void multi_create_draw_filter_buttons();
3976 void multi_create_set_selected_team(int team);
3977 short multi_create_get_mouse_id();
3978 int multi_create_ok_to_commit();
3979 int multi_create_verify_cds();
3980 void multi_create_refresh_pxo();
3981 void multi_create_sw_clicked();
3982
3983 // since we can selectively filter out mission/campaign types we always need to map a selected index (which is relative 
3984 // to the displayed list), to an absolute index (which is relative to the total file list - some of which may filtered out)
3985 void multi_create_select_to_filename(int select_index,char *filename);
3986 int multi_create_select_to_index(int select_index);
3987
3988 int Multi_create_should_show_popup = 0;
3989
3990
3991 // sorting function to sort mission lists.. Basic sorting on mission name
3992 int multi_create_sort_func(const void *a, const void *b)
3993 {
3994         multi_create_info *m1, *m2;
3995
3996         m1 = (multi_create_info *)a;
3997         m2 = (multi_create_info *)b;
3998
3999         return ( strcmp(m1->name, m2->name) );
4000 }
4001
4002 void multi_create_setup_list_data(int mode)
4003 {       
4004         int idx,should_sort,switched_modes;
4005         
4006         // set the current mode
4007         should_sort = 0;
4008         switched_modes = 0;
4009         if((Multi_create_list_mode != mode) && (mode != -1)){
4010                 Multi_create_list_mode = mode;  
4011                 switched_modes = 1;
4012
4013                 // set up the list pointers
4014                 if ( Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ) {
4015                         Multi_create_file_list = Multi_create_mission_list;                     
4016                 } else if ( Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ) {
4017                         Multi_create_file_list = Multi_create_campaign_list;                    
4018                 } else {
4019                         Int3();
4020                 }
4021         }
4022
4023         // get the mission count based upon the filter selected
4024         if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){               
4025                 switch(Multi_create_filter){
4026                 case MISSION_TYPE_MULTI:
4027                         Multi_create_list_count = Multi_create_mission_count;
4028                         break;
4029                 default : 
4030                         Multi_create_list_count = 0;
4031                         // find all missions which match 
4032                         for(idx=0;idx<Multi_create_mission_count;idx++){
4033                                 if(Multi_create_mission_list[idx].flags & Multi_create_filter){
4034                                         Multi_create_list_count++;
4035                                 }
4036                         }
4037
4038                         // if we switched modes and we have more than 0 items, sort them
4039                         if(switched_modes && (Multi_create_list_count > 0)){
4040                                 should_sort = 1;
4041                         }
4042                         break;
4043                 }
4044         } else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
4045                 switch(Multi_create_filter){
4046                 case MISSION_TYPE_MULTI:
4047                         Multi_create_list_count = Multi_create_campaign_count;
4048                         break;
4049                 default :
4050                         Multi_create_list_count = 0;
4051                         // find all missions which match 
4052                         for(idx=0;idx<Multi_create_campaign_count;idx++){
4053                                 if(Multi_create_campaign_list[idx].flags & Multi_create_filter){
4054                                         Multi_create_list_count++;
4055                                 }
4056                         }
4057
4058                         // if we switched modes and we have more than 0 items, sort them
4059                         if(switched_modes && (Multi_create_list_count > 0)){
4060                                 should_sort = 1;
4061                         }
4062                         break;
4063                 }
4064         }
4065         
4066         // reset the list start and selected indices
4067         Multi_create_list_start = 0;
4068         Multi_create_list_select = -1;
4069         multi_create_list_select_item(Multi_create_list_start); 
4070
4071         // sort the list of missions if necessary
4072         if( should_sort ) {             
4073                 qsort(Multi_create_file_list, Multi_create_list_count, sizeof(multi_create_info), multi_create_sort_func);              
4074         }
4075
4076 #ifndef MAKE_FS1
4077         // reset the slider
4078         Multi_create_slider.set_numberItems(Multi_create_list_count > Multi_create_list_max_display[gr_screen.res] ? Multi_create_list_count-Multi_create_list_max_display[gr_screen.res] : 0);
4079 #endif
4080 }
4081
4082 void multi_create_game_init()
4083 {
4084         int idx;
4085         ui_button_info *b;
4086         
4087         // now make sure to initialze various netgame stuff based upon whether we're on a standalone or not
4088         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4089                 multi_create_init_as_server();
4090         } else {
4091                 multi_create_init_as_client();
4092         }
4093
4094         // initialize the player list data              
4095         Multi_create_plist_select_flag = 0;
4096         Multi_create_plist_select_id = -1;      
4097
4098         // create the interface window
4099         Multi_create_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
4100         Multi_create_window.set_mask_bmap(Multi_create_bitmap_mask_fname[gr_screen.res]);
4101
4102         // load the background bitmap
4103         Multi_create_bitmap = bm_load(Multi_create_bitmap_fname[gr_screen.res]);
4104         if(Multi_create_bitmap < 0){
4105                 // we failed to load the bitmap - this is very bad
4106                 Int3();
4107         }
4108
4109         // close any previous existing instances of the chatbox and create a new one
4110         chatbox_close();
4111         chatbox_create();
4112
4113         // load the help overlay 
4114         help_overlay_load(MULTI_CREATE_OVERLAY);
4115         help_overlay_set_state(MULTI_CREATE_OVERLAY, 0);
4116
4117         // initialize the common notification messaging
4118         multi_common_notify_init();             
4119
4120         // use the common interface palette
4121         multi_common_set_palette();
4122
4123         // create the interface buttons
4124         for(idx=0; idx<MULTI_CREATE_NUM_BUTTONS; idx++){
4125                 b = &Multi_create_buttons[gr_screen.res][idx];
4126         
4127                 // create the object            
4128                 b->button.create(&Multi_create_window, "", b->x, b->y, 1, 1, ((idx == MC_SCROLL_LIST_UP) || (idx == MC_SCROLL_LIST_DOWN)), 1);
4129
4130                 // set the sound to play when highlighted
4131                 b->button.set_highlight_action(common_play_highlight_sound);
4132
4133                 // set the ani for the button
4134                 b->button.set_bmaps(b->filename);
4135
4136                 // set the hotspot
4137                 b->button.link_hotspot(b->hotspot);             
4138
4139                 // some special case stuff for the pxo refresh button
4140                 if(idx == MC_PXO_REFRESH){                      
4141                         // if not a PXO game, or if I'm not a server disable and hide the button
4142                         if(!MULTI_IS_TRACKER_GAME || !MULTIPLAYER_MASTER){
4143                                 b->button.hide();
4144                                 b->button.disable();
4145                         }                       
4146                 }
4147         }       
4148
4149         // create xstrs
4150         for(idx=0; idx<MULTI_CREATE_NUM_TEXT; idx++){
4151                 Multi_create_window.add_XSTR(&Multi_create_text[gr_screen.res][idx]);
4152         }
4153
4154         // if this is a PXO game, enable the squadwar checkbox  
4155         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);
4156         Multi_create_sw_checkbox.set_bmaps(Multi_create_sw_checkbox_fname[gr_screen.res], 6, 0);
4157         if(!MULTI_IS_TRACKER_GAME){
4158                 Multi_create_sw_checkbox.hide();
4159                 Multi_create_sw_checkbox.disable();
4160         }
4161         
4162 #ifdef FS2_DEMO
4163         // disable squad war button in demo
4164         Multi_create_sw_checkbox.hide();
4165         Multi_create_sw_checkbox.disable();
4166 #endif
4167
4168         // initialize the mission type filtering mode
4169         Multi_create_filter = MISSION_TYPE_MULTI;
4170
4171         // initialize the list mode, and load in a list
4172         memset(Multi_create_mission_list, 0, sizeof(multi_create_info) * MULTI_CREATE_MAX_LIST_ITEMS);
4173         memset(Multi_create_campaign_list, 0, sizeof(multi_create_info) * MULTI_CREATE_MAX_LIST_ITEMS);
4174         for(idx=0; idx<MULTI_CREATE_MAX_LIST_ITEMS; idx++){
4175                 Multi_create_mission_list[idx].valid_status = MVALID_STATUS_UNKNOWN;
4176                 Multi_create_campaign_list[idx].valid_status = MVALID_STATUS_UNKNOWN;
4177         }
4178         Multi_create_list_mode = MULTI_CREATE_SHOW_MISSIONS;
4179         Multi_create_list_start = -1;
4180         Multi_create_list_select = -1;
4181         Multi_create_list_count = 0;
4182
4183 #ifndef MAKE_FS1
4184         Multi_create_slider.create(&Multi_create_window, Mc_slider_coords[gr_screen.res][MC_X_COORD], Mc_slider_coords[gr_screen.res][MC_Y_COORD], Mc_slider_coords[gr_screen.res][MC_W_COORD],Mc_slider_coords[gr_screen.res][MC_H_COORD], MULTI_CREATE_MAX_LIST_ITEMS, Mc_slider_bitmap[gr_screen.res], &multi_create_list_scroll_up, &multi_create_list_scroll_down, NULL);
4185 #endif
4186
4187         // create the player list select button
4188         Multi_create_player_select_button.create(&Multi_create_window, "", Mc_players_coords[gr_screen.res][MC_X_COORD], Mc_players_coords[gr_screen.res][MC_Y_COORD], Mc_players_coords[gr_screen.res][MC_W_COORD], Mc_players_coords[gr_screen.res][MC_H_COORD], 0, 1);
4189         Multi_create_player_select_button.hide();               
4190         
4191         // create the mission/campaign list select button
4192         Multi_create_list_select_button.create(&Multi_create_window, "", Mc_list_coords[gr_screen.res][MC_X_COORD], Mc_list_coords[gr_screen.res][MC_Y_COORD], Mc_list_coords[gr_screen.res][MC_W_COORD], Mc_list_coords[gr_screen.res][MC_H_COORD], 0, 1);
4193         Multi_create_list_select_button.hide(); 
4194
4195         // set hotkeys for a couple of things.
4196         Multi_create_buttons[gr_screen.res][MC_ACCEPT].button.set_hotkey(KEY_CTRLED+SDLK_RETURN);
4197
4198         // init some master tracker stuff
4199         Multi_create_frame_count = 0;
4200         Multi_create_mt_tried_login = 0;
4201
4202         // remove campaign flags
4203         Game_mode &= ~(GM_CAMPAIGN_MODE);
4204         
4205         // send any pilots as appropriate
4206         multi_data_send_my_junk();
4207         Multi_create_file_list = Multi_create_mission_list;
4208
4209         Multi_create_campaign_count = 0;
4210         Multi_create_mission_count = 0;
4211         Multi_create_files_loaded = 0;  
4212 }
4213
4214 void multi_create_game_do()
4215 {
4216         int player_index;
4217 #ifndef MAKE_FS1
4218         const char *loading_str = XSTR("Loading", 1336);
4219         int str_w, str_h;
4220 #endif
4221
4222         // set this if we want to show the pilot info popup
4223         Multi_create_should_show_popup = 0;
4224
4225         // first thing is to load the files
4226         if ( !Multi_create_files_loaded ) {
4227                 // if I am a client, send a list request to the server for the missions
4228                 if ( MULTIPLAYER_CLIENT ) {
4229                         send_mission_list_request( MISSION_LIST_REQUEST );
4230                 } else {
4231                         int loading_bitmap;
4232
4233                         loading_bitmap = bm_load(Multi_create_loading_fname[gr_screen.res]);
4234
4235                         // draw the background, etc
4236                         gr_reset_clip();
4237                         GR_MAYBE_CLEAR_RES(Multi_create_bitmap);
4238                         if(Multi_create_bitmap != -1){
4239                                 gr_set_bitmap(Multi_create_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4240                                 gr_bitmap(0, 0);
4241                         }
4242                         chatbox_render();
4243                         if ( loading_bitmap > -1 ){
4244                                 gr_set_bitmap(loading_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4245                         }
4246                         gr_bitmap( Please_wait_coords[gr_screen.res][MC_X_COORD], Please_wait_coords[gr_screen.res][MC_Y_COORD] );
4247
4248                         // draw "Loading" on it
4249 #ifndef MAKE_FS1
4250                         gr_set_color_fast(&Color_normal);
4251                         gr_set_font(FONT2);
4252                         gr_get_string_size(&str_w, &str_h, loading_str);
4253                         gr_string((gr_screen.max_w - str_w) / 2, (gr_screen.max_h - str_h) / 2, loading_str);
4254                         gr_set_font(FONT1);
4255 #endif
4256
4257                         gr_flip();
4258
4259                         multi_create_list_load_missions();
4260                         multi_create_list_load_campaigns();
4261
4262                         // if this is a tracker game, validate missions
4263                         if(MULTI_IS_TRACKER_GAME){
4264                                 multi_update_valid_missions();
4265                         }
4266
4267                         // update the file list
4268                         multi_create_setup_list_data(MULTI_CREATE_SHOW_MISSIONS);                                               
4269                 }
4270
4271                 // don't bother setting netgame state if ont the server
4272                 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4273                         Netgame.game_state = NETGAME_STATE_FORMING;
4274                         send_netgame_update_packet();
4275                 }       
4276
4277                 // if we're on the standalone we have to tell him that we're now in the host setup screen       
4278                 Net_player->state = NETPLAYER_STATE_HOST_SETUP; 
4279                 send_netplayer_update_packet();
4280
4281                 Multi_create_files_loaded = 1;
4282         }
4283
4284         int k = chatbox_process();
4285         k = Multi_create_window.process(k,0);
4286
4287         switch(k){      
4288         // same as the cancel button
4289         case SDLK_ESCAPE:
4290                 if(help_overlay_active(MULTI_CREATE_OVERLAY)){
4291                         help_overlay_set_state(MULTI_CREATE_OVERLAY,0);
4292                 } else {                
4293                         gamesnd_play_iface(SND_USER_SELECT);            
4294                         multi_quit_game(PROMPT_HOST);           
4295                 }
4296                 break;  
4297         }       
4298
4299         if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
4300                 help_overlay_set_state(MULTI_CREATE_OVERLAY, 0);
4301         }
4302
4303         // process any button clicks
4304         multi_create_check_buttons();
4305
4306         // do any network related stuff
4307         multi_create_do_netstuff(); 
4308
4309         // draw the background, etc
4310         gr_reset_clip();
4311         GR_MAYBE_CLEAR_RES(Multi_create_bitmap);
4312         if(Multi_create_bitmap != -1){
4313                 gr_set_bitmap(Multi_create_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4314                 gr_bitmap(0,0);
4315         }
4316
4317         // if we're not in team vs. team mode, don't draw the team buttons
4318         if(!(Netgame.type_flags & NG_TYPE_TEAM)){
4319                 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.hide();
4320                 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.hide();
4321                 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.disable();
4322                 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.disable();
4323         } else {
4324                 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.enable();
4325                 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.enable();
4326                 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.unhide();
4327                 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.unhide();                          
4328         }               
4329
4330         // draw the window itself
4331         Multi_create_window.draw();
4332
4333         gr_set_color_fast(&Color_normal);
4334
4335 #ifndef MAKE_FS1
4336         // draw Create Game text
4337         gr_string(Mc_create_game_text[gr_screen.res][MC_X_COORD], Mc_create_game_text[gr_screen.res][MC_Y_COORD], XSTR("Create Game", 1268));
4338
4339         // draw players text
4340         gr_string(Mc_players_text[gr_screen.res][MC_X_COORD], Mc_players_text[gr_screen.res][MC_Y_COORD], XSTR("Players", 1269));
4341
4342         // draw players text
4343         gr_string(Mc_team_text[gr_screen.res][MC_X_COORD], Mc_team_text[gr_screen.res][MC_Y_COORD], XSTR("Team", 1258));
4344 #endif
4345
4346         // process and display the player list  
4347         // NOTE : this must be done before the buttons are checked to insure that a player hasn't dropped 
4348         multi_create_plist_process();
4349         if(Netgame.type_flags & NG_TYPE_TEAM){
4350                 multi_create_plist_blit_team();
4351         } else {
4352                 multi_create_plist_blit_normal();
4353         }
4354
4355         // process and display the game/campaign list
4356         multi_create_list_do();
4357         
4358         // draw the correct mission filter button
4359         multi_create_draw_filter_buttons();
4360
4361         // display any text in the info area
4362         multi_common_render_text();
4363
4364         // display any pending notification messages
4365         multi_common_notify_do();       
4366         
4367         // force the correct mission/campaign button to light up
4368         if( Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ){
4369                 Multi_create_buttons[gr_screen.res][MC_MISSION_FILTER].button.draw_forced(2);
4370         } else {
4371                 Multi_create_buttons[gr_screen.res][MC_CAMPAIGN_FILTER].button.draw_forced(2);
4372         }
4373
4374         // force draw the closed button if it is toggled on
4375         if(Netgame.flags & NG_FLAG_TEMP_CLOSED){
4376                 Multi_create_buttons[gr_screen.res][MC_CLOSE].button.draw_forced(2);
4377         }
4378
4379         // process and show the chatbox thingie 
4380         chatbox_render();
4381
4382         // draw tooltips
4383         Multi_create_window.draw_tooltip();
4384
4385         // display the voice status indicator
4386         multi_common_voice_display_status();
4387
4388         // blit the help overlay if necessary
4389         help_overlay_maybe_blit(MULTI_CREATE_OVERLAY);
4390
4391         // test code
4392         if(MULTI_IS_TRACKER_GAME){
4393                 if(Netgame.type_flags & NG_TYPE_SW){
4394                         gr_set_color_fast(&Color_bright);
4395                 } else {
4396                         gr_set_color_fast(&Color_normal);
4397                 }
4398                 gr_string(Multi_create_sw_checkbox_text[gr_screen.res][0], Multi_create_sw_checkbox_text[gr_screen.res][1], "SquadWar");
4399         }
4400
4401         // flip the buffer
4402         gr_flip();              
4403                 
4404         // if we're supposed to show the pilot info popup, do it now
4405         if(Multi_create_should_show_popup){             
4406                 // get the player index and address of the player item the mouse is currently over
4407                 if(Multi_create_plist_select_flag){             
4408                         player_index = find_player_id(Multi_create_plist_select_id);
4409                         if(player_index != -1){                 
4410                                 multi_pinfo_popup(&Net_players[player_index]);
4411                         }
4412                 }
4413         }
4414
4415         // increment the frame count
4416         Multi_create_frame_count++;     
4417 }
4418
4419 void multi_create_game_close()
4420 {
4421         // unload any bitmaps
4422         if(!bm_unload(Multi_create_bitmap)){
4423                 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_create_bitmap_fname[gr_screen.res]));
4424         }               
4425
4426         // unload the help overlay
4427         help_overlay_unload(MULTI_CREATE_OVERLAY);
4428         
4429         // destroy the chatbox
4430         // chatbox_close();
4431         
4432         // destroy the UI_WINDOW
4433         Multi_create_window.destroy();
4434 }
4435
4436 void multi_create_check_buttons()
4437 {
4438         int idx;
4439         for(idx=0;idx<MULTI_CREATE_NUM_BUTTONS;idx++){
4440                 // we only really need to check for one button pressed at a time, so we can break after 
4441                 // finding one.
4442                 if(Multi_create_buttons[gr_screen.res][idx].button.pressed()){
4443                         multi_create_button_pressed(idx);
4444                         break;
4445                 }
4446         }
4447
4448         // if the squad war checkbox was clicked
4449         if(Multi_create_sw_checkbox.changed()){
4450                 multi_create_sw_clicked();
4451         }
4452 }
4453
4454 void multi_create_button_pressed(int n)
4455 {
4456         int idx;
4457         
4458         switch(n){
4459         case MC_CANCEL :
4460                 gamesnd_play_iface(SND_USER_SELECT);            
4461                 multi_quit_game(PROMPT_HOST);           
4462                 break;
4463         case MC_ACCEPT :        
4464                 // if valid commit conditions have not been met
4465                 if(!multi_create_ok_to_commit()){
4466                         break;
4467                 }
4468
4469                 // commit
4470                 multi_create_accept_hit();              
4471                 break;
4472
4473         // help button
4474         case MC_HELP :
4475                 if(!help_overlay_active(MULTI_CREATE_OVERLAY)){
4476                         help_overlay_set_state(MULTI_CREATE_OVERLAY,1);
4477                 } else {
4478                         help_overlay_set_state(MULTI_CREATE_OVERLAY,0);
4479                 }
4480                 break;
4481
4482         // scroll the info text box up
4483         case MC_SCROLL_INFO_UP:
4484                 multi_common_scroll_text_up();
4485                 break;
4486
4487         // scroll the info text box down
4488         case MC_SCROLL_INFO_DOWN:
4489                 multi_common_scroll_text_down();
4490                 break;
4491
4492         // scroll the player list up
4493         case MC_SCROLL_PLAYERS_UP:
4494                 multi_create_plist_scroll_up();
4495                 break;
4496
4497         // scroll the player list down
4498         case MC_SCROLL_PLAYERS_DOWN:
4499                 multi_create_plist_scroll_down();
4500                 break;
4501
4502         // scroll the game/campaign list up
4503         case MC_SCROLL_LIST_UP:
4504                 multi_create_list_scroll_up();
4505 #ifndef MAKE_FS1
4506                 Multi_create_slider.forceUp();  // move slider up
4507 #endif
4508                 break;
4509
4510         // scroll the game/campaign list down
4511         case MC_SCROLL_LIST_DOWN:
4512                 multi_create_list_scroll_down();
4513 #ifndef MAKE_FS1
4514                 Multi_create_slider.forceDown();        // move slider down
4515 #endif
4516                 break;
4517
4518         // go to the options screen
4519         case MC_OPTIONS:                
4520                 gamesnd_play_iface(SND_USER_SELECT);
4521                 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
4522                 break;  
4523
4524         // show all missions
4525         case MC_SHOW_ALL:
4526                 if(Multi_create_filter != MISSION_TYPE_MULTI){
4527                         gamesnd_play_iface(SND_USER_SELECT);
4528                         Multi_create_filter = MISSION_TYPE_MULTI;
4529                         multi_create_setup_list_data(Multi_create_list_mode);                                           // update the file list
4530                 } else {
4531                         gamesnd_play_iface(SND_GENERAL_FAIL);
4532                 }
4533                 break;
4534
4535         // show cooperative missions
4536         case MC_SHOW_COOP:
4537                 if(Multi_create_filter != MISSION_TYPE_MULTI_COOP){
4538                         gamesnd_play_iface(SND_USER_SELECT);
4539                         Multi_create_filter = MISSION_TYPE_MULTI_COOP;                  
4540                         multi_create_setup_list_data(Multi_create_list_mode);                                           // update the file list
4541                 } else {
4542                         gamesnd_play_iface(SND_GENERAL_FAIL);
4543                 }
4544                 break;
4545
4546         // show team vs. team missions
4547         case MC_SHOW_TEAM:
4548                 if(Multi_create_filter != MISSION_TYPE_MULTI_TEAMS){
4549                         gamesnd_play_iface(SND_USER_SELECT);
4550                         Multi_create_filter = MISSION_TYPE_MULTI_TEAMS; 
4551                         multi_create_setup_list_data(Multi_create_list_mode);                                           // update the file list
4552                 } else {
4553                         gamesnd_play_iface(SND_GENERAL_FAIL);
4554                 }
4555                 break;  
4556
4557         // show dogfight missions
4558 #ifndef MAKE_FS1
4559         case MC_SHOW_DOGFIGHT:
4560                 if (Multi_create_filter != MISSION_TYPE_MULTI_DOGFIGHT){
4561                         gamesnd_play_iface(SND_USER_SELECT);
4562                         Multi_create_filter = MISSION_TYPE_MULTI_DOGFIGHT;
4563                         multi_create_setup_list_data(Multi_create_list_mode);                                           // update the file list
4564                 } else {
4565                         gamesnd_play_iface(SND_GENERAL_FAIL);
4566                 }
4567                 break;
4568 #endif
4569
4570         // toggle temporary netgame closed on/off
4571         case MC_CLOSE:
4572                 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4573                         Netgame.flags ^= NG_FLAG_TEMP_CLOSED;
4574                 } else {
4575                         Netgame.options.flags |= MLO_FLAG_TEMP_CLOSED;
4576                         multi_options_update_netgame();
4577                 }
4578                 gamesnd_play_iface(SND_USER_SELECT);
4579                 break;
4580
4581         // kick the currently selected player (if possible)
4582         case MC_KICK:
4583                 // lookup the player at the specified index             
4584                 if(Multi_create_plist_select_flag){              
4585                         idx = find_player_id(Multi_create_plist_select_id);
4586                         // kick him - but don't ban him
4587                         if(idx != -1){                  
4588                                 multi_kick_player(idx,0);                               
4589                         }
4590                 }
4591                 break;
4592                         
4593         // switch to individual mission mode and load in a list
4594         case MC_MISSION_FILTER:
4595                 if(Multi_create_list_mode != MULTI_CREATE_SHOW_MISSIONS){
4596                         Netgame.campaign_mode = MP_SINGLE;
4597
4598                         gamesnd_play_iface(SND_USER_SELECT);                                                                                            
4599                         
4600                         // update the file list
4601                         multi_create_setup_list_data(MULTI_CREATE_SHOW_MISSIONS);                                               
4602                 } else {
4603                         gamesnd_play_iface(SND_GENERAL_FAIL);
4604                 }
4605                 break;
4606
4607         // switch to campaign mode and load in a list
4608         case MC_CAMPAIGN_FILTER:                
4609                 // switch off squad war
4610                 Multi_create_sw_checkbox.set_state(0);
4611                 Netgame.type_flags = NG_TYPE_COOP;
4612
4613                 if(Multi_create_list_mode != MULTI_CREATE_SHOW_CAMPAIGNS){
4614                         Netgame.campaign_mode = MP_CAMPAIGN;
4615
4616                         gamesnd_play_iface(SND_USER_SELECT);                    
4617                         
4618                         // update the file list
4619                         multi_create_setup_list_data(MULTI_CREATE_SHOW_CAMPAIGNS);                                              
4620                 } else {
4621                         gamesnd_play_iface(SND_GENERAL_FAIL);
4622                 }
4623                 break;
4624
4625         // attempt to set the selected player's team
4626         case MC_TEAM0:
4627                 multi_create_set_selected_team(0);
4628                 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4629                         multi_team_send_update();
4630                 }
4631                 break;
4632
4633         // attempt to set the selected player's team
4634         case MC_TEAM1:
4635                 multi_create_set_selected_team(1);
4636                 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4637                         multi_team_send_update();
4638                 }
4639                 break;  
4640
4641         // popup the pilot info dialog for the currently selected pilot (will occur at the end of the frame)
4642         case MC_PILOT_INFO:
4643                 Multi_create_should_show_popup = 1;
4644                 break;
4645
4646         // go to the host options screen
4647         case MC_HOST_OPTIONS:
4648                 gamesnd_play_iface(SND_USER_SELECT);
4649                 gameseq_post_event(GS_EVENT_MULTI_HOST_OPTIONS);
4650                 break;
4651
4652         // refresh PXO file list
4653         case MC_PXO_REFRESH:            
4654                 if(!MULTI_IS_TRACKER_GAME){
4655                         break;
4656                 }
4657                 multi_create_refresh_pxo();             
4658                 break;
4659
4660         default :
4661                 gamesnd_play_iface(SND_GENERAL_FAIL);
4662                 multi_common_add_notify(XSTR("Not implemented yet!",760));              
4663                 break;
4664         }
4665 }
4666
4667 // do stuff like pinging servers, sending out requests, etc
4668 void multi_create_do_netstuff()
4669 {
4670 }
4671
4672 // if not on a standalone
4673 void multi_create_init_as_server()
4674 {
4675         // set me up as the host and master
4676         Net_player->flags |= (NETINFO_FLAG_AM_MASTER | NETINFO_FLAG_GAME_HOST);         
4677 }
4678
4679 // if on a standalone
4680 void multi_create_init_as_client()
4681 {
4682         Net_player->flags |= NETINFO_FLAG_GAME_HOST;    
4683 }
4684
4685 // scroll up through the player list
4686 void multi_create_plist_scroll_up()
4687 {       
4688         gamesnd_play_iface(SND_GENERAL_FAIL);
4689 }
4690
4691 // scroll down through the player list
4692 void multi_create_plist_scroll_down()
4693 {       
4694         gamesnd_play_iface(SND_GENERAL_FAIL);
4695 }
4696
4697 void multi_create_plist_process()
4698 {
4699         int test_count,idx,player_index;
4700         
4701         // first determine if there are 0 players in the game. This should never happen since the host is _always_ in the game
4702         test_count = 0;
4703         for(idx=0;idx<MAX_PLAYERS;idx++){
4704                 // count anyone except the standalone server (if applicable)
4705                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
4706                         test_count++;
4707                 }
4708         }
4709         if(test_count <= 0){
4710                 return;
4711         }
4712         
4713         // if we had a selected item but that player has left, select myself instead
4714         if(Multi_create_plist_select_flag){
4715                 player_index = find_player_id(Multi_create_plist_select_id);
4716                 if(player_index == -1){
4717                         Multi_create_plist_select_id = Net_player->player_id;
4718                 }
4719         } else {
4720                 Multi_create_plist_select_flag = 1;
4721                 Multi_create_plist_select_id = Net_player->player_id;           
4722         }       
4723                 
4724         // if the player has clicked somewhere in the player list area
4725         if(Multi_create_player_select_button.pressed()){                                
4726                 short player_id;
4727
4728                 // get the player index and address of the player item the mouse is currently over
4729                 player_id = multi_create_get_mouse_id();
4730                 player_index = find_player_id(player_id);
4731                 if(player_index != -1){
4732                         Multi_create_plist_select_flag = 1;
4733                         Multi_create_plist_select_id = player_id;                       
4734                 } 
4735         }               
4736 }
4737
4738 void multi_create_plist_blit_normal()
4739 {
4740         int idx;                
4741         char str[CALLSIGN_LEN+5];
4742         int y_start = Mc_players_coords[gr_screen.res][MC_Y_COORD];     
4743         int total_offset;
4744
4745         // display all the players      
4746         for(idx=0;idx<MAX_PLAYERS;idx++){               
4747                 // count anyone except the standalone server (if applicable)
4748                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
4749                         // total x offset
4750                         total_offset = 0;
4751
4752                         // highlight him if he's the host                       
4753                         if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4754                                 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4755                                         gr_set_color_fast(&Color_text_active_hi);
4756                                 } else {
4757                                         gr_set_color_fast(&Color_bright);
4758                                 }
4759                         } else {
4760                                 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4761                                         gr_set_color_fast(&Color_text_active);
4762                                 } else {
4763                                         gr_set_color_fast(&Color_text_normal);
4764                                 }
4765                         }
4766                         
4767                         // optionally draw his CD status
4768                         if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4769                                 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4770                                 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4771
4772                                 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4773                         }                       
4774                         
4775                         // make sure the string will fit, then display it
4776                         strcpy(str,Net_players[idx].player->callsign);
4777                         if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4778                                 strcat(str,XSTR("(O)",787));  // [[ Observer ]]
4779                         }
4780                         gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4781                         gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4782
4783                         y_start += 10;                  
4784                 }
4785         }               
4786 }
4787
4788 void multi_create_plist_blit_team()
4789 {
4790         int idx;                
4791         char str[CALLSIGN_LEN+1];
4792         int y_start = Mc_players_coords[gr_screen.res][MC_Y_COORD];     
4793         int total_offset;
4794
4795         // display all the red players first
4796         for(idx=0;idx<MAX_PLAYERS;idx++){
4797                 // count anyone except the standalone server (if applicable)
4798                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
4799                         // reset total offset
4800                         total_offset = 0;
4801
4802                         // highlight him if he's the host                       
4803                         if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4804                                 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4805                                         gr_set_color_fast(&Color_text_active_hi);
4806
4807                                         // be sure to blit the correct team button 
4808                                         Multi_create_buttons[gr_screen.res][MC_TEAM0].button.draw_forced(2);
4809                                 } else {
4810                                         gr_set_color_fast(&Color_bright);
4811                                 }
4812                         } else {
4813                                 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4814                                         gr_set_color_fast(&Color_text_active);
4815
4816                                         // be sure to blit the correct team button 
4817                                         Multi_create_buttons[gr_screen.res][MC_TEAM0].button.draw_forced(2);
4818                                 } else {
4819                                         gr_set_color_fast(&Color_text_normal);
4820                                 }
4821                         }
4822
4823                         // optionally draw his CD status
4824                         if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4825                                 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4826                                 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4827
4828                                 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4829                         }                       
4830
4831                         // blit the red team indicator                  
4832                         if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
4833                                 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
4834                                         gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4835                                         gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4836
4837                                         total_offset += Multi_common_icon_dims[MICON_TEAM0_SELECT][0] + 1;                      
4838                                 }
4839                         } else {
4840                                 if(Multi_common_icons[MICON_TEAM0] != -1){
4841                                         gr_set_bitmap(Multi_common_icons[MICON_TEAM0], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4842                                         gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4843
4844                                         total_offset += Multi_common_icon_dims[MICON_TEAM0][0] + 1;                     
4845                                 }                               
4846                         }                                               
4847
4848                         // make sure the string will fit
4849                         strcpy(str,Net_players[idx].player->callsign);
4850                         if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4851                                 strcat(str,XSTR("(O)",787));
4852                         }
4853                         gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4854
4855                         // display him in the correct half of the list depending on his team
4856                         gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4857                         y_start += 10;
4858                 }
4859         }       
4860         
4861         // display all the green players next
4862         for(idx=0;idx<MAX_PLAYERS;idx++){
4863                 // count anyone except the standalone server (if applicable)
4864                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
4865                         // reset total offset
4866                         total_offset = 0;
4867
4868                         // highlight him if he's the host                       
4869                         if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4870                                 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4871                                         gr_set_color_fast(&Color_text_active_hi);
4872
4873                                         // be sure to blit the correct team button 
4874                                         Multi_create_buttons[gr_screen.res][MC_TEAM1].button.draw_forced(2);
4875                                 } else {
4876                                         gr_set_color_fast(&Color_bright);
4877                                 }
4878                         } else {
4879                                 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4880                                         gr_set_color_fast(&Color_text_active);
4881
4882                                         // be sure to blit the correct team button 
4883                                         Multi_create_buttons[gr_screen.res][MC_TEAM1].button.draw_forced(2);
4884                                 } else {
4885                                         gr_set_color_fast(&Color_text_normal);
4886                                 }
4887                         }
4888
4889                         // optionally draw his CD status
4890                         if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4891                                 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4892                                 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4893
4894                                 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4895                         }                       
4896
4897                         // blit the red team indicator                  
4898                         if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
4899                                 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
4900                                         gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4901                                         gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4902
4903                                         total_offset += Multi_common_icon_dims[MICON_TEAM1_SELECT][0] + 1;
4904                                 }                               
4905                         } else {
4906                                 if(Multi_common_icons[MICON_TEAM1] != -1){
4907                                         gr_set_bitmap(Multi_common_icons[MICON_TEAM1], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4908                                         gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4909
4910                                         total_offset += Multi_common_icon_dims[MICON_TEAM1][0] + 1;
4911                                 }
4912                         }
4913
4914                         // make sure the string will fit
4915                         strcpy(str,Net_players[idx].player->callsign);
4916                         if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4917                                 strcat(str,XSTR("(O)",787));
4918                         }
4919                         gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4920
4921                         // display him in the correct half of the list depending on his team
4922                         gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4923                         y_start += 10;
4924                 }
4925         }                       
4926 }
4927
4928 void multi_create_list_scroll_up()
4929 {
4930         if(Multi_create_list_start > 0){
4931                 Multi_create_list_start--;              
4932
4933                 gamesnd_play_iface(SND_SCROLL);
4934         } else {
4935                 gamesnd_play_iface(SND_GENERAL_FAIL);
4936         }
4937 }
4938
4939 void multi_create_list_scroll_down()
4940 {
4941         if((Multi_create_list_count - Multi_create_list_start) > Multi_create_list_max_display[gr_screen.res]){
4942                 Multi_create_list_start++;              
4943
4944                 gamesnd_play_iface(SND_SCROLL);
4945         } else {
4946                 gamesnd_play_iface(SND_GENERAL_FAIL);
4947         }
4948 }
4949
4950 // gets a list of multiplayer misisons
4951 void multi_create_list_load_missions()
4952 {
4953         char *fname, mission_name[NAME_LENGTH+1];
4954         char wild_card[256];
4955         int file_count,idx;
4956
4957         memset(wild_card, 0, 256);
4958         strcpy(wild_card, NOX("*"));
4959         strcat(wild_card, FS_MISSION_FILE_EXT);
4960         file_count = cf_get_file_list_preallocated(MULTI_CREATE_MAX_LIST_ITEMS, Multi_create_files_array, NULL, CF_TYPE_MISSIONS, wild_card);
4961         Multi_create_mission_count = 0;
4962
4963         // maybe create a standalone dialog
4964         if(Game_mode & GM_STANDALONE_SERVER){
4965                 std_create_gen_dialog("Loading missions");
4966                 std_gen_set_text("Mission:", 1);
4967         }
4968
4969         for(idx = 0; idx < file_count; idx++){
4970                 int flags,max_players;
4971                 char *filename;
4972                 uint m_respawn;         
4973
4974                 fname = Multi_create_files_array[idx];
4975                 
4976                 // tack on any necessary file extension
4977                 filename = cf_add_ext( fname, FS_MISSION_FILE_EXT );
4978
4979                 // for multiplayer beta builds, only accept builtin missions
4980 #if defined(MULTIPLAYER_BETA_BUILD) || defined(FS2_DEMO)
4981                 if(game_find_builtin_mission(filename) == NULL){
4982                         continue;
4983                 }
4984 #elif defined(PD_BUILD)
4985                 if((game_find_builtin_mission(filename) == NULL) && !strstr(filename, "peterdrake")){
4986                         continue;
4987                 }
4988 #endif
4989
4990                 if(Game_mode & GM_STANDALONE_SERVER){                   
4991                         std_gen_set_text(filename, 2);
4992                 }
4993
4994                 flags = mission_parse_is_multi(filename, mission_name);         
4995
4996                 // if the mission is a multiplayer mission, then add it to the mission list
4997                 if ( flags ) {
4998                         max_players = mission_parse_get_multi_mission_info( filename );                         
4999                         m_respawn = The_mission.num_respawns;
5000
5001                         if ( Multi_create_mission_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
5002                                 multi_create_info *mcip;
5003
5004                                 mcip = &Multi_create_mission_list[Multi_create_mission_count];                          
5005                                 strcpy(mcip->filename, filename );
5006                                 strcpy(mcip->name, mission_name );
5007                                 mcip->flags = flags;
5008                                 mcip->respawn = m_respawn;
5009                                 mcip->max_players = (ubyte)max_players;
5010
5011                                 // get any additional information for possibly builtin missions
5012                                 fs_builtin_mission *fb = game_find_builtin_mission(filename);
5013                                 if(fb != NULL){                                 
5014                                 }
5015
5016                                 Multi_create_mission_count++;
5017                         }
5018                 }
5019         }
5020
5021 #ifndef MAKE_FS1
5022         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);
5023 #endif
5024
5025         // maybe create a standalone dialog
5026         if(Game_mode & GM_STANDALONE_SERVER){
5027                 std_destroy_gen_dialog();               
5028         }
5029 }
5030
5031 void multi_create_list_load_campaigns()
5032 {       
5033         char *fname;
5034         int idx, file_count;
5035         int campaign_type,max_players;
5036         char title[255];
5037         char wild_card[256];
5038
5039         // maybe create a standalone dialog
5040         if(Game_mode & GM_STANDALONE_SERVER){
5041                 std_create_gen_dialog("Loading campaigns");
5042                 std_gen_set_text("Campaign:", 1);
5043         }
5044
5045         Multi_create_campaign_count = 0;
5046         memset(wild_card, 0, 256);
5047         strcpy(wild_card, NOX("*"));
5048         strcat(wild_card, FS_CAMPAIGN_FILE_EXT);
5049         file_count = cf_get_file_list_preallocated(MULTI_CREATE_MAX_LIST_ITEMS, Multi_create_files_array, NULL, CF_TYPE_MISSIONS, wild_card);
5050         for(idx = 0; idx < file_count; idx++){
5051                 int flags;
5052                 char *filename, name[NAME_LENGTH];
5053
5054                 fname = Multi_create_files_array[idx];
5055                 
5056                 // tack on any necessary file extension
5057                 filename = cf_add_ext( fname, FS_CAMPAIGN_FILE_EXT );
5058
5059                 // for multiplayer beta builds, only accept builtin missions
5060 #if defined(MULTIPLAYER_BETA_BUILD) || defined(FS2_DEMO)
5061                 if(game_find_builtin_mission(filename) == NULL){
5062                         continue;
5063                 }
5064 #elif defined(PD_BUILD)
5065                 if((game_find_builtin_mission(filename) == NULL) && !strstr(filename, "peterdrake")){
5066                         continue;
5067                 }
5068 #endif
5069
5070                 if(Game_mode & GM_STANDALONE_SERVER){                   
5071                         std_gen_set_text(filename, 2);
5072                 }
5073
5074                 // if the campaign is a multiplayer campaign, then add the data to the campaign list items
5075                 flags = mission_campaign_parse_is_multi( filename, name );
5076                 if( flags != CAMPAIGN_TYPE_SINGLE && mission_campaign_get_info(filename,title,&campaign_type,&max_players)) {
5077                         if ( Multi_create_campaign_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
5078                                 multi_create_info *mcip;
5079
5080                                 mcip = &Multi_create_campaign_list[Multi_create_campaign_count];
5081                                 strcpy(mcip->filename, filename );
5082                                 strcpy(mcip->name, name );
5083                                 
5084                                 // setup various flags
5085                                 if ( flags == CAMPAIGN_TYPE_MULTI_COOP ){
5086                                         mcip->flags = MISSION_TYPE_MULTI_COOP | MISSION_TYPE_MULTI;
5087                                 } else if ( flags == CAMPAIGN_TYPE_MULTI_TEAMS ) {
5088                                         mcip->flags = MISSION_TYPE_MULTI_TEAMS | MISSION_TYPE_MULTI;
5089                                 } else {
5090                                         Int3();                 // bogus campaign multi type -- find allender
5091                                 }                                                       
5092
5093                                 // 0 respawns for campaign files (should be contained within the mission files themselves)
5094                                 mcip->respawn = 0;
5095
5096                                 // 0 max players for campaign files
5097                                 mcip->max_players = (unsigned char)max_players;
5098
5099                                 // get any additional information for possibly builtin missions
5100                                 fs_builtin_mission *fb = game_find_builtin_mission(filename);
5101                                 if(fb != NULL){                                 
5102                                 }
5103
5104                                 Multi_create_campaign_count++;
5105                         }
5106                 }
5107         }       
5108         
5109         // maybe create a standalone dialog
5110         if(Game_mode & GM_STANDALONE_SERVER){
5111                 std_destroy_gen_dialog();               
5112         }
5113 }
5114
5115 void multi_create_list_do()
5116 {
5117         int idx;
5118         int start_index,stop_index;
5119         char selected_name[255];
5120
5121         // bail early if there aren't any selectable items
5122         if(Multi_create_list_count == 0){
5123                 return;
5124         }
5125         
5126         // first check to see if the user has clicked on an item
5127         if(Multi_create_list_select_button.pressed()){           
5128                 int y,item;                             
5129                 Multi_create_list_select_button.get_mouse_pos(NULL,&y);
5130                 item = (y / 10);
5131
5132                 // make sure we are selectedin valid indices
5133                 if((item < Multi_create_list_max_display[gr_screen.res]) && (item >= 0)){                                       
5134                         item += Multi_create_list_start;                
5135
5136                         if(item < Multi_create_list_count){             
5137                                 multi_create_list_select_item(item);
5138                                 gamesnd_play_iface(SND_IFACE_MOUSE_CLICK);
5139                         }               
5140                 }
5141         }       
5142
5143         // bail early if we don't have a start position
5144         if(Multi_create_list_start == -1){
5145                 return;
5146         }
5147
5148         // display the list of individual campaigns/missions
5149         int count = 0;
5150         int y_start = Mc_list_coords[gr_screen.res][MC_Y_COORD];                
5151
5152         start_index = multi_create_select_to_index(Multi_create_list_start);
5153         stop_index = Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ? Multi_create_mission_count : Multi_create_campaign_count;
5154         for(idx=start_index; idx<stop_index; idx++){
5155                 // see if we should drop out
5156                 if(count == Multi_create_list_max_display[gr_screen.res]){
5157                         break;
5158                 }
5159
5160                 // see if we should filter out this mission
5161                 if ( !(Multi_create_file_list[idx].flags & Multi_create_filter) ){
5162                         continue;
5163                 }
5164                 
5165                 // highlight the selected item
5166                 multi_create_select_to_filename(Multi_create_list_select,selected_name);
5167                 if(!strcmp(selected_name,Multi_create_file_list[idx].filename)){                
5168                         gr_set_color_fast(&Color_text_selected);
5169                 } else {
5170                         gr_set_color_fast(&Color_text_normal);
5171                 }               
5172
5173                 // draw the type icon           
5174                 multi_create_list_blit_icons(idx, y_start);             
5175                 
5176                 // force fit the mission name string
5177                 strcpy(selected_name,Multi_create_file_list[idx].name);
5178                 gr_force_fit_string(selected_name,255,Mc_column1_w[gr_screen.res]);
5179                 gr_string(Mc_mission_name_x[gr_screen.res],y_start,selected_name);
5180
5181                 // draw the max players if in mission mode              
5182                 sprintf(selected_name,"%d",(int)Multi_create_file_list[idx].max_players);
5183                 gr_string(Mc_mission_count_x[gr_screen.res],y_start,selected_name);             
5184
5185                 // force fit the mission filename string
5186                 strcpy(selected_name,Multi_create_file_list[idx].filename);
5187                 gr_force_fit_string(selected_name,255,Mc_column3_w[gr_screen.res]);
5188                 gr_string(Mc_mission_fname_x[gr_screen.res],y_start,selected_name);
5189
5190                 y_start+=10;
5191                 count++;
5192         }
5193 }
5194
5195 // takes care of stuff like changing indices around and setting up the netgame structure
5196 void multi_create_list_select_item(int n)
5197 {
5198         int abs_index,campaign_type,max_players;
5199         char title[NAME_LENGTH+1];
5200         netgame_info ng_temp;
5201         netgame_info *ng;
5202
5203         char *campaign_desc;
5204
5205         // if not on the standalone server
5206         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5207                 ng = &Netgame;  
5208         }
5209         // on the standalone
5210         else {
5211                 memset(&ng_temp,0,sizeof(netgame_info));
5212                 ng = &ng_temp;
5213         }
5214         
5215         if ( n != Multi_create_list_select ) {
5216                 // check to see if this is a valid index, and bail if it is not
5217                 abs_index = multi_create_select_to_index(n);
5218                 if(abs_index == -1){
5219                         return;
5220                 }
5221
5222                 Multi_create_list_select = n;
5223                                 
5224                 // set the mission name
5225                 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5226                         multi_create_select_to_filename(n,ng->mission_name);            
5227                 } else {
5228                         multi_create_select_to_filename(n,ng->campaign_name);
5229                 }
5230
5231                 // make sure the netgame type is properly set
5232                 int old_type = Netgame.type_flags;
5233                 abs_index = multi_create_select_to_index(n);
5234                 if(abs_index != -1){
5235                         if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_TEAMS){
5236                                 // if we're in squad war mode, leave it as squad war
5237                                 if(old_type & NG_TYPE_SW){
5238                                         ng->type_flags = NG_TYPE_SW;
5239                                 } else {
5240                                         ng->type_flags = NG_TYPE_TVT;
5241                                 }
5242                         } else if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_COOP){
5243                                 ng->type_flags = NG_TYPE_COOP;
5244                         } else if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_DOGFIGHT){
5245                                 ng->type_flags = NG_TYPE_DOGFIGHT;
5246                         }
5247                 }
5248
5249                 // if we're no longer in a TvT game, just uncheck the squadwar checkbox
5250                 if(!(ng->type_flags & NG_TYPE_TEAM)){
5251                         Multi_create_sw_checkbox.set_state(0);
5252                 }
5253
5254                 // if we switched from something else to team vs. team mode, do some special processing
5255                 if((ng->type_flags & NG_TYPE_TEAM) && (ng->type_flags != old_type) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
5256                         multi_team_reset();
5257                 }
5258
5259                 switch(Multi_create_list_mode){
5260                 case MULTI_CREATE_SHOW_MISSIONS:                
5261                         // don't forget to update the info box window thingie
5262                         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){                 
5263                                 ship_init();            // mwa -- 10/15/97.  Call this function to reset number of ships in mission
5264                                 ng->max_players = mission_parse_get_multi_mission_info( ng->mission_name );                             
5265                                 
5266                                 SDL_assert(ng->max_players > 0);
5267                                 strcpy(ng->title,The_mission.name);                                                             
5268
5269                                 // set the information area text
5270                                 multi_common_set_text(The_mission.mission_desc);
5271                         }
5272                         // if we're on the standalone, send a request for the description
5273                         else {
5274                                 send_netgame_descript_packet(&Netgame.server_addr,0);
5275                                 multi_common_set_text("");
5276                         }
5277
5278                         // set the respawns as appropriate
5279                         if(Netgame.options.respawn <= Multi_create_file_list[abs_index].respawn){
5280                                 ng->respawn = Netgame.options.respawn;
5281                                 nprintf(("Network","Using netgame options for respawn count (%d %d)\n",Netgame.options.respawn,Multi_create_file_list[abs_index].respawn));
5282                         } else {
5283                                 ng->respawn = Multi_create_file_list[abs_index].respawn;
5284                                 nprintf(("Network","Using mission settings for respawn count (%d %d)\n",Netgame.options.respawn,Multi_create_file_list[abs_index].respawn));
5285                         }
5286                         break;
5287                 case MULTI_CREATE_SHOW_CAMPAIGNS:
5288                         // if not on the standalone server
5289                         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5290                                 // get the campaign info                                
5291                                 memset(title,0,NAME_LENGTH+1);
5292                                 if(!mission_campaign_get_info(ng->campaign_name,title,&campaign_type,&max_players, &campaign_desc)) {
5293                                         memset(ng->campaign_name,0,NAME_LENGTH+1);
5294                                         ng->max_players = 0;
5295                                 }
5296                                 // if we successfully got the # of players
5297                                 else {
5298                                         memset(ng->title,0,NAME_LENGTH+1);
5299                                         strcpy(ng->title,title);
5300                                         ng->max_players = max_players;                                  
5301                                 }
5302
5303                                 nprintf(("Network","MC MAX PLAYERS : %d\n",ng->max_players));
5304
5305                                 // set the information area text
5306                                 // multi_common_set_text(ng->title);
5307                                 multi_common_set_text(campaign_desc);
5308                         }
5309                         // if on the standalone server, send a request for the description
5310                         else {
5311                                 // no descriptions currently kept for campaigns
5312                         }
5313
5314                         // netgame respawns are always 0 for campaigns (until the first mission is loaded)
5315                         ng->respawn = 0;
5316                         break;
5317                 }
5318
5319                 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5320                         // update players 
5321                         send_netgame_update_packet();                   
5322
5323                         // update all machines about stuff like respawns, etc.
5324                         multi_options_update_netgame();
5325                 } else {
5326                         multi_options_update_mission(ng, Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ? 1 : 0);
5327                 }
5328         }
5329 }
5330
5331 void multi_create_list_blit_icons(int list_index, int y_start)
5332 {
5333         multi_create_info *mcip;
5334         fs_builtin_mission *fb; 
5335         int max_index;
5336
5337         // get a pointer to the list item
5338         max_index = Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ? Multi_create_mission_count - 1 : Multi_create_campaign_count - 1;
5339         if((list_index < 0) || (list_index > max_index)){
5340                 return;
5341         }       
5342         mcip = &Multi_create_file_list[list_index];
5343
5344         // blit the multiplayer type icons
5345         if(mcip->flags & MISSION_TYPE_MULTI_COOP){
5346                 if(Multi_common_icons[MICON_COOP] >= 0){
5347                         gr_set_bitmap(Multi_common_icons[MICON_COOP], 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_TEAMS){
5351                 if(Multi_common_icons[MICON_TVT] >= 0){
5352                         gr_set_bitmap(Multi_common_icons[MICON_TVT], 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         } else if(mcip->flags & MISSION_TYPE_MULTI_DOGFIGHT){
5356                 if(Multi_common_icons[MICON_DOGFIGHT] >= 0){
5357                         gr_set_bitmap(Multi_common_icons[MICON_DOGFIGHT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5358                         gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5359                 }
5360         } 
5361
5362         // if its a valid mission, blit the valid mission icon
5363         if(MULTI_IS_TRACKER_GAME && (mcip->valid_status == MVALID_STATUS_VALID)){
5364                 if(Multi_common_icons[MICON_VALID] >= 0){
5365                         gr_set_bitmap(Multi_common_icons[MICON_VALID], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5366                         gr_bitmap(Mc_icon_valid_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_valid_coords[gr_screen.res][MC_Y_COORD]);
5367                 }
5368         }
5369
5370         // now see if its a builtin mission
5371         fb = game_find_builtin_mission(mcip->filename); 
5372         // if the mission is from volition, blit the volition icon
5373         if((fb != NULL) && (fb->flags & FSB_FROM_VOLITION)){
5374                 if(Multi_common_icons[MICON_VOLITION] >= 0){
5375                         gr_set_bitmap(Multi_common_icons[MICON_VOLITION], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5376                         gr_bitmap(Mc_icon_volition_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_volition_coords[gr_screen.res][MC_Y_COORD]);
5377                 }
5378         }       
5379
5380 #ifdef MAKE_FS1
5381         // check for mdisk mission
5382         fb = game_find_builtin_mission(mcip->filename);
5383         if((fb != NULL) && (fb->flags & FSB_FROM_MDISK)){
5384                 if(Multi_common_icons[MICON_MDISK] >= 0){
5385                         gr_set_bitmap(Multi_common_icons[MICON_MDISK], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5386                         gr_bitmap(Mc_icon_silent_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_silent_coords[gr_screen.res][MC_Y_COORD]);
5387                 }
5388         }
5389 #endif
5390 }
5391
5392 void multi_create_accept_hit()
5393 {
5394         char selected_name[255];
5395         int start_campaign = 0; 
5396
5397         // make sure all players have finished joining
5398         if(!multi_netplayer_state_check(NETPLAYER_STATE_JOINED,1)){
5399                 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("Please wait until all clients have finished joining",788));
5400                 gamesnd_play_iface(SND_GENERAL_FAIL);
5401                 return;
5402         } else {
5403                 gamesnd_play_iface(SND_COMMIT_PRESSED);
5404         }       
5405         
5406         // do single mission stuff
5407         switch(Multi_create_list_mode){
5408         case MULTI_CREATE_SHOW_MISSIONS:        
5409                 if(Multi_create_list_select != -1){
5410                         // set the netgame mode
5411                         Netgame.campaign_mode = MP_SINGLE;
5412
5413                         // setup various filenames and mission names
5414                         multi_create_select_to_filename(Multi_create_list_select,selected_name);
5415                         strncpy( Game_current_mission_filename, selected_name, MAX_FILENAME_LEN );
5416                         strncpy(Netgame.mission_name,selected_name,MAX_FILENAME_LEN);                   
5417
5418                         // NETLOG
5419                         ml_printf(NOX("Starting single mission %s, with %d players"), Game_current_mission_filename, multi_num_players());
5420                 } else {
5421                         multi_common_add_notify(XSTR("No mission selected!",789));
5422                         return ;
5423                 }
5424                 break;
5425
5426         case MULTI_CREATE_SHOW_CAMPAIGNS:
5427                 // do campaign related stuff    
5428                 if(Multi_create_list_select != -1){
5429                         // set the netgame mode
5430                         Netgame.campaign_mode = MP_CAMPAIGN;
5431
5432                         // start a campaign instead of a single mission
5433                         multi_create_select_to_filename(Multi_create_list_select,selected_name);
5434                         multi_campaign_start(selected_name);                    
5435                         start_campaign = 1;
5436
5437                         // NETLOG
5438                         ml_printf(NOX("Starting campaign %s, with %d players"), selected_name, multi_num_players());
5439                 } else {
5440                         multi_common_add_notify(XSTR("No campaign selected!",790));
5441                         return ;
5442                 }
5443                 break;
5444         }
5445
5446         // if this is a team vs team situation, lock the players send a final team update
5447         if((Netgame.type_flags & NG_TYPE_TEAM) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
5448                 multi_team_host_lock_all();
5449                 multi_team_send_update();
5450         }
5451
5452         // if not on the standalone, move to the mission sync state which will take care of everything
5453         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5454                 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
5455                 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);                                
5456         } 
5457         // otherwise tell the standalone to do so
5458         else {
5459                 // when the standalone receives this, he'll do the mission syncing himself
5460                 send_mission_sync_packet(MULTI_SYNC_PRE_BRIEFING,start_campaign);
5461         }                               
5462 }
5463
5464 void multi_create_draw_filter_buttons()
5465 {
5466         // highlight the correct filter button
5467         if ( Multi_create_filter == MISSION_TYPE_MULTI ){
5468                 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL].button.draw_forced(2);
5469         } else if ( Multi_create_filter == MISSION_TYPE_MULTI_COOP ) {
5470                 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 1].button.draw_forced(2);
5471         } else if ( Multi_create_filter == MISSION_TYPE_MULTI_TEAMS ) {
5472                 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 2].button.draw_forced(2);
5473         } else if ( Multi_create_filter == MISSION_TYPE_MULTI_DOGFIGHT ){
5474                 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 3].button.draw_forced(2);
5475         } else {
5476                 Int3();
5477         }
5478 }
5479
5480 void multi_create_set_selected_team(int team)
5481 {       
5482         int player_index;
5483         
5484         // if we don't currently have a player selected, don't do anything
5485         if(!Multi_create_plist_select_flag){
5486                 gamesnd_play_iface(SND_GENERAL_FAIL);
5487                 return;
5488         }
5489         gamesnd_play_iface(SND_USER_SELECT);
5490
5491         // otherwise attempt to set the team for this guy       
5492         player_index = find_player_id(Multi_create_plist_select_id);
5493         if(player_index != -1){ 
5494                 multi_team_set_team(&Net_players[player_index],team);           
5495         }
5496 }
5497
5498 void multi_create_handle_join(net_player *pl)
5499 {
5500         // for now just play a bloop sound
5501         gamesnd_play_iface(SND_ICON_DROP_ON_WING);
5502 }
5503
5504 // fill in net address of player the mouse is over, return player index (or -1 if none)
5505 short multi_create_get_mouse_id()
5506 {
5507         // determine where he clicked (y pixel value)
5508         int y,nth,idx;          
5509         Multi_create_player_select_button.get_mouse_pos(NULL,&y);
5510
5511         // select things a little differently if we're in team vs. team or non-team vs. team mode                       
5512         nth = (y / 10);                 
5513         if(Netgame.type_flags & NG_TYPE_TEAM){
5514                 int player_index = -1;
5515
5516                 // look through all of team red first
5517                 for(idx=0;idx<MAX_PLAYERS;idx++){
5518                         if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
5519                                 nth--;
5520
5521                                 // if this is the _nth_ guy 
5522                                 if(nth < 0){
5523                                         player_index = idx;                                             
5524                                         break;
5525                                 }
5526                         }
5527                 }
5528                         
5529                 // if we still haven't found him yet, look through the green team
5530                 if(player_index == -1){
5531                         for(idx=0;idx<MAX_PLAYERS;idx++){                                       
5532                                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){                            
5533                                         nth--;
5534                                         // if this is the _nth_ guy 
5535                                         if(nth < 0){
5536                                                 player_index = idx;                                             
5537                                                 break;
5538                                         }
5539                                 }
5540                         }
5541                 }
5542
5543                 if(player_index != -1){
5544                         return Net_players[player_index].player_id;
5545                 }
5546         } else {
5547                 // select the nth active player if possible, disregarding the standalone server
5548                 for(idx=0;idx<MAX_PLAYERS;idx++){
5549                         if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
5550                                 nth--;
5551
5552                                 // if this is the _nth_ guy 
5553                                 if(nth < 0){
5554                                         return Net_players[idx].player_id;                                      
5555                                 }                               
5556                         }
5557                 }
5558         }                               
5559         
5560         return -1;
5561 }
5562
5563 void multi_create_select_to_filename(int select_index,char *filename)
5564 {
5565         int idx;
5566
5567         // look through the mission list
5568         if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5569                 for(idx=0;idx<Multi_create_mission_count;idx++){
5570                         if(Multi_create_file_list[idx].flags & Multi_create_filter){
5571                                 select_index--;
5572                         }
5573
5574                         // if we found the item
5575                         if(select_index < 0){
5576                                 strcpy(filename,Multi_create_file_list[idx].filename);
5577                                 return;
5578                         }
5579                 }
5580         }
5581         // look through the campaign list
5582         else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
5583                 for(idx=0;idx<Multi_create_campaign_count;idx++){
5584                         select_index--;
5585
5586                         // if we found the item
5587                         if(select_index < 0){
5588                                 strcpy(filename,Multi_create_file_list[idx].filename);
5589                                 return;
5590                         }               
5591                 }
5592         }
5593
5594         strcpy(filename,"");
5595 }
5596
5597 int multi_create_select_to_index(int select_index)
5598 {
5599         int idx;
5600         int lookup_index = 0;
5601
5602         // look through the mission list
5603         if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5604                 for(idx=0;idx<Multi_create_mission_count;idx++){
5605                         if(Multi_create_file_list[idx].flags & Multi_create_filter){
5606                                 lookup_index++;
5607                         } 
5608
5609                         // if we found the item
5610                         if(select_index < lookup_index){                                
5611                                 return idx;
5612                         }
5613                 }
5614         }
5615         // look through the campaign list
5616         else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
5617                 for(idx=0;idx<Multi_create_campaign_count;idx++){
5618                         select_index--;
5619
5620                         // if we found the item
5621                         if(select_index < 0){                           
5622                                 return idx;
5623                         }               
5624                 }
5625         }
5626
5627         return -1;
5628 }
5629
5630 int multi_create_ok_to_commit()
5631 {
5632         int player_count, observer_count, idx;
5633         int notify_of_hacked_ships_tbl = 0;
5634         int notify_of_hacked_weapons_tbl = 0;
5635         char err_string[255];
5636         int abs_index;
5637         int found_hack;
5638
5639         // make sure we have a valid mission selected
5640         if(Multi_create_list_select < 0){
5641                 return 0;
5642         }       
5643
5644         // if this is not a valid mission, let the player know
5645         abs_index = multi_create_select_to_index(Multi_create_list_select);             
5646         if(abs_index < 0){
5647                 return 0;
5648         }
5649
5650         // if we're playing with a hacked ships.tbl (on PXO)
5651         notify_of_hacked_ships_tbl = 0;
5652         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5653                 if(!Game_ships_tbl_valid){
5654                         notify_of_hacked_ships_tbl = 1;
5655                 }
5656         } else {
5657                 if(Netgame.flags & NG_FLAG_HACKED_SHIPS_TBL){
5658                         notify_of_hacked_ships_tbl = 1;
5659                 }
5660         }
5661         if(!MULTI_IS_TRACKER_GAME){
5662                 notify_of_hacked_ships_tbl = 0;
5663         }
5664         if(notify_of_hacked_ships_tbl){
5665                 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){
5666                         return 0;
5667                 }
5668         }
5669
5670         // if we're playing with a hacked weapons.tbl (on PXO)
5671         notify_of_hacked_weapons_tbl = 0;
5672         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5673                 if(!Game_weapons_tbl_valid){
5674                         notify_of_hacked_weapons_tbl = 1;
5675                 }
5676         } else {
5677                 if(Netgame.flags & NG_FLAG_HACKED_WEAPONS_TBL){
5678                         notify_of_hacked_weapons_tbl = 1;
5679                 }
5680         }
5681         if(!MULTI_IS_TRACKER_GAME){
5682                 notify_of_hacked_weapons_tbl = 0;
5683         }
5684         if(notify_of_hacked_weapons_tbl){
5685                 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){
5686                         return 0;
5687                 }
5688         }
5689
5690         // if any of the players have hacked data
5691         found_hack = 0;
5692         for(idx=0; idx<MAX_PLAYERS; idx++){
5693                 // look for hacked players
5694                 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_HAXOR)){
5695                         // we found a hack
5696                         found_hack = 1;
5697
5698                         // message everyone - haha
5699                         if(Net_players[idx].player != NULL){
5700                                 sprintf(err_string, "%s %s", Net_players[idx].player->callsign, XSTR("has hacked tables/data", 1271)); 
5701                         } else {
5702                                 sprintf(err_string, "somebody %s", XSTR("has hacked tables/data", 1271)); 
5703                         }
5704                         send_game_chat_packet(Net_player, err_string, MULTI_MSG_ALL, NULL, NULL, 1);
5705                 }
5706         }
5707         // if we found a hacked set of data
5708         if(found_hack){
5709                 // if we're on PXO
5710                 if(MULTI_IS_TRACKER_GAME){
5711                         // don't allow squad war matches to continue
5712                         if(Netgame.type_flags & NG_TYPE_SW){
5713 #ifdef RELEASE_REAL
5714                                 // if this is squad war, don't allow it to continue                     
5715                                 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));
5716
5717                                 return 0;
5718 #endif
5719                         }
5720                         // otherwise, warn the players that stats will not saved
5721                         else {
5722                                 // if this is squad war, don't allow it to continue                     
5723                                 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){
5724                                         return 0;
5725                                 }
5726                         }
5727                 }
5728                 // non-pxo, just give a notice
5729                 else {
5730                         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){
5731                                 return 0;
5732                         }
5733                 }
5734         }
5735
5736         // check to see that we don't have too many observers
5737         observer_count = multi_num_observers();
5738         if(observer_count > Netgame.options.max_observers){
5739                 // print up the error string
5740                 sprintf(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);
5741
5742                 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, err_string);
5743                 return 0;
5744         }
5745
5746         // check to see that we have a valid # of players for the the # of ships in the game            
5747         player_count = multi_num_players();
5748         if(player_count > Netgame.max_players){
5749                 // print up the error string
5750                 sprintf(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);
5751
5752                 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, err_string);
5753                 return 0;
5754         }
5755         
5756         // check to see if teams are assigned properly in a team vs. team situation
5757         if(Netgame.type_flags & NG_TYPE_TEAM){
5758                 if(!multi_team_ok_to_commit()){
5759                         gamesnd_play_iface(SND_GENERAL_FAIL);
5760                         popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("Teams and/or team captains are not assigned properly", 793));                   
5761                         return 0;
5762                 }
5763         }
5764
5765         // verify cd's  
5766         if(!multi_create_verify_cds()){
5767                 gamesnd_play_iface(SND_GENERAL_FAIL);
5768
5769 #ifdef MULTIPLAYER_BETA_BUILD
5770                 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, "You need 1 CD for every player!");                   
5771 #else 
5772         #ifdef DVD_MESSAGE_HACK
5773                         popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("You need 1 DVD for every 4 players!", 794));                    
5774         #else
5775                         popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("You need 1 CD for every 4 players!", 794));                     
5776         #endif
5777 #endif
5778                 return 0;
5779         }       
5780         
5781         // if we're playing on the tracker
5782         if(MULTI_IS_TRACKER_GAME){
5783 #ifdef PXO_CHECK_VALID_MISSIONS         
5784                 if((Multi_create_file_list == Multi_create_mission_list) && (Multi_create_file_list[abs_index].valid_status != MVALID_STATUS_VALID)){
5785                         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){
5786                                 return 0;
5787                         }
5788                 }               
5789 #endif
5790
5791                 // non-squad war
5792                 if(!(Netgame.type_flags & NG_TYPE_SW)){
5793                         // if he is playing by himself, tell him stats will not be accepted
5794                         if(multi_num_players() == 1){
5795                                 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){
5796                                         return 0;
5797                                 }
5798                         }
5799                 }
5800                 // squad war
5801                 else {                  
5802                 }
5803         }       
5804                 
5805         return 1;
5806 }
5807
5808 int multi_create_verify_cds()
5809 {
5810         int player_count = multi_num_players();
5811         int multi_cd_count;
5812         int idx;
5813
5814         // count how many cds we have
5815         multi_cd_count = 0;
5816         for(idx=0;idx<MAX_PLAYERS;idx++){
5817                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_HAS_CD)){
5818                         multi_cd_count++;
5819                 }
5820         }
5821
5822         // for the beta, everyone must have a CD
5823 #ifdef MULTIPLAYER_BETA_BUILD
5824         if(multi_cd_count < player_count){
5825                 return 0;
5826         }
5827 #else
5828         // determine if we have enough
5829         float ratio = (float)player_count / (float)multi_cd_count;
5830         // greater than a 4 to 1 ratio
5831         if(ratio > 4.0f){
5832                 return 0;
5833         } 
5834 #endif
5835
5836         // we meet the conditions
5837         return 1;
5838 }
5839
5840 // returns an index into Multi_create_mission_list
5841 int multi_create_lookup_mission(char *fname)
5842 {
5843         int idx;
5844
5845         for(idx=0; idx<Multi_create_mission_count; idx++){
5846                 if(!SDL_strcasecmp(fname, Multi_create_mission_list[idx].filename)){
5847                         return idx;
5848                 }
5849         }
5850
5851         // couldn't find the mission
5852         return -1;
5853 }
5854
5855 // returns an index into Multi_create_campaign_list
5856 int multi_create_lookup_campaign(char *fname)
5857 {
5858         int idx;
5859
5860         for(idx=0; idx<Multi_create_campaign_count; idx++){
5861                 if(!SDL_strcasecmp(fname, Multi_create_campaign_list[idx].filename)){
5862                         return idx;
5863                 }
5864         }
5865
5866         // couldn't find the campaign
5867         return -1;
5868 }
5869
5870 void multi_create_refresh_pxo()
5871 {
5872         // delete mvalid.cfg if it exists
5873         cf_delete(MULTI_VALID_MISSION_FILE, CF_TYPE_DATA);
5874
5875         // refresh missions from the tracker
5876         multi_update_valid_missions();
5877 }
5878
5879 void multi_create_sw_clicked()
5880 {
5881         netgame_info ng_temp;
5882         netgame_info *ng;
5883
5884         int file_index = multi_create_select_to_index(Multi_create_list_select);
5885
5886         // either a temporary netgame or the real one
5887         if(MULTIPLAYER_MASTER){
5888                 ng = &Netgame;
5889         } else {
5890                 ng_temp = Netgame;
5891                 ng = &ng_temp;
5892         }
5893
5894         // maybe switch squad war off
5895         if(!Multi_create_sw_checkbox.checked()){
5896                 // if the mission selected is a coop mission, go back to coop mode
5897                 SDL_assert(file_index != -1);
5898                 if(file_index == -1){
5899                         ng->type_flags = NG_TYPE_COOP;                  
5900                 }               
5901                 if(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_TEAMS){
5902                         ng->type_flags = NG_TYPE_TVT;
5903                 } else if(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_DOGFIGHT){
5904                         ng->type_flags = NG_TYPE_DOGFIGHT;
5905                 } else {
5906                         ng->type_flags = NG_TYPE_COOP;
5907                 }
5908         }
5909         // switch squad war on
5910         else {
5911                 SDL_assert(file_index != -1);
5912                 if((file_index == -1) || !(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_TEAMS)){                       
5913                         Multi_create_sw_checkbox.set_state(0);                  
5914                 } else {
5915                         // at this point we know its safe to switch squad war on
5916                         ng->type_flags = NG_TYPE_SW;
5917                 }
5918         }
5919
5920         // update 
5921         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){         
5922                 // update players 
5923                 send_netgame_update_packet();                   
5924
5925                 // update all machines about stuff like respawns, etc.
5926                 multi_options_update_netgame();
5927         }
5928         // on the standalone
5929         else {
5930                 // standalone will take care of polling the usertracker
5931                 multi_options_update_mission(ng, Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ? 1 : 0);
5932         }
5933 }
5934
5935
5936 // -------------------------------------------------------------------------------------------------------------
5937 // 
5938 // MULTIPLAYER HOST OPTIONS SCREEN
5939 //
5940
5941 #define MULTI_HO_NUM_BUTTONS                            12
5942 #define MULTI_HO_NUM_RADIO_BUTTONS              10
5943
5944 //XSTR:OFF
5945 // bitmaps defs
5946 #define MULTI_HO_PALETTE                                "InterfacePalette"
5947
5948 static const char *Multi_ho_bitmap_fname[GR_NUM_RESOLUTIONS] = {
5949         "MultiHost",                    // GR_640
5950         "2_MultiHost"                   // GR_1024
5951 };
5952
5953 static const char *Multi_ho_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
5954         "MultiHost-M",                  // GR_640
5955         "2_MultiHost-M"         // GR_1024
5956 };
5957
5958 //XSTR:ON
5959 UI_WINDOW Multi_ho_window;                                                                                              // the window object for the join screen
5960 UI_INPUTBOX Multi_ho_respawns;                                                                          // the # of respawns allowed in the game
5961 UI_INPUTBOX Multi_ho_time_limit;                                                                                // mission time limit
5962 UI_INPUTBOX Multi_ho_voice_wait;                                                                                // wait time between tokens
5963 UI_INPUTBOX Multi_ho_kill_limit;                                                                                // kill limit in a furball mission
5964 UI_INPUTBOX Multi_ho_obs;                                                                                               // # of observers we'll allow
5965 int Multi_ho_bitmap;                                                                                                            // the background bitmap
5966
5967 // constants for coordinate lookup
5968 #define MULTI_HO_X_COORD                        0
5969 #define MULTI_HO_Y_COORD                        1
5970 #define MULTI_HO_W_COORD                        2
5971 #define MULTI_HO_H_COORD                        3
5972 #define MULTI_HO_TEXT_X_COORD           4
5973 #define MULTI_HO_TEXT_Y_COORD           5
5974
5975 // button defs
5976 #define MULTI_HO_MSG_RANK                               0               // highest ranking players can do messaging
5977 #define MULTI_HO_MSG_LEADER                     1               // wing/team leaders can do messaging
5978 #define MULTI_HO_MSG_ANY                                2               // any player can do messaging
5979 #define MULTI_HO_MSG_HOST                               3               // only the host can do messaging
5980 #define MULTI_HO_END_RANK                               4               // highest rank can and host can end mission
5981 #define MULTI_HO_END_LEADER                     5               // wing/team leaders and host can end the mission
5982 #define MULTI_HO_END_ANY                                6               // any player can end the mission
5983 #define MULTI_HO_END_HOST                               7               // only host can end the mission
5984 #define MULTI_HO_VOICE_ON                               8               // voice toggled on
5985 #define MULTI_HO_VOICE_OFF                              9               // voice toggled off
5986 #define MULTI_HO_HOST_MODIFIES          10              // only the host or team captains can modify ships/weapons in briefing
5987 #define MULTI_HO_ACCEPT                                 11              // accept button
5988
5989 ui_button_info Multi_ho_buttons[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_BUTTONS] = {   
5990         { // GR_640
5991 #ifdef MAKE_FS1
5992                 // who is allowed to message
5993                 ui_button_info("MH_00", 13,             157,    -1,     -1,     0),             // highest rank
5994                 ui_button_info("MH_01", 13,             179,    -1,     -1,     1),             // team/wing leader
5995                 ui_button_info("MH_02", 13,             200,    -1,     -1,     2),             // any
5996                 ui_button_info("MH_03", 13,             223,    -1,     -1,     3),             // host
5997                 
5998                 // who is allowed to end the mission
5999                 ui_button_info("MH_09", 13,             273,    -1,     -1,     9),             // highest rank
6000                 ui_button_info("MH_10", 13,             295,    -1,     -1,     10),            // team/wing leader
6001                 ui_button_info("MH_11", 13,             317,    -1,     -1,     11),            // any
6002                 ui_button_info("MH_12", 13,             339,    -1,     -1,     12),            // host         
6003
6004                 // voice on/off button
6005                 ui_button_info("MH_14", 396,    156,    -1,     -1,     14),    
6006                 ui_button_info("MH_15", 453,    156,    -1,     -1,     15),    
6007
6008                 // host modifies ships
6009                 ui_button_info("MH_19", 215,    410,    -1,     -1,     19),    
6010
6011                 // exit
6012                 ui_button_info("MH_18", 560,    411,    -1,     -1,     18),
6013 #else
6014                 // who is allowed to message
6015                 ui_button_info("MH_00", 3,      160,    46,     166,    0),             // highest rank
6016                 ui_button_info("MH_01", 3,      179,    46,     185,    1),             // team/wing leader
6017                 ui_button_info("MH_02", 3,      196,    46,     203,    2),             // any
6018                 ui_button_info("MH_03", 3,      214,    46,     220,    3),             // host
6019                 
6020                 // who is allowed to end the mission
6021                 ui_button_info("MH_04", 3,      257,    46,     265,    4),             // highest rank
6022                 ui_button_info("MH_05", 3,      276,    46,     283,    5),             // team/wing leader
6023                 ui_button_info("MH_06", 3,      294,    46,     300,    6),             // any
6024                 ui_button_info("MH_07", 3,      311,    46,     317,    7),             // host         
6025
6026                 // voice on/off button
6027                 ui_button_info("MH_09", 542,    158,    545,    185,    9),     
6028                 ui_button_info("MH_10", 598,    158,    604,    185,    10),    
6029
6030                 // host modifies ships
6031                 ui_button_info("MH_13", 542,    377,    437,    363,    13),    
6032
6033                 // exit
6034                 ui_button_info("MH_14", 572,    428,    580,    414,    14),    
6035 #endif
6036         },
6037         { // GR_1024
6038                 // who is allowed to message
6039                 ui_button_info("2_MH_00",       5,      256,    73,     269,    0),             // highest rank
6040                 ui_button_info("2_MH_01",       5,      286,    73,     297,    1),             // team/wing leader
6041                 ui_button_info("2_MH_02",       5,      314,    73,     325,    2),             // any
6042                 ui_button_info("2_MH_03",       5,      341,    73,     352,    3),             // host
6043                 
6044                 // who is allowed to end the mission
6045                 ui_button_info("2_MH_04",       5,      412,    73,     425,    4),             // highest rank
6046                 ui_button_info("2_MH_05",       5,      442,    73,     452,    5),             // team/wing leader
6047                 ui_button_info("2_MH_06",       5,      470,    73,     480,    6),             // any
6048                 ui_button_info("2_MH_07",       5,      497,    73,     508,    7),             // host         
6049
6050                 // voice on/off button
6051                 ui_button_info("2_MH_09",       867,    253,    872,    296,    9),     
6052                 ui_button_info("2_MH_10",       957,    253,    966,    296,    10),    
6053
6054                 // host modifies ships
6055                 ui_button_info("2_MH_13",       867,    603,    784,    581,    13),    
6056
6057                 // exit
6058                 ui_button_info("2_MH_14",       916,    685,    925,    665,    14),    
6059         },
6060 };
6061 UI_XSTR Multi_ho_text[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_BUTTONS] = {
6062         { // GR_640
6063                 // not needed for FS1
6064 #ifndef MAKE_FS1
6065                 {"Highest rank",                                        1280,   46,     166,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_RANK].button},
6066                 {"Team / wing-leader",                  1281, 46,       185,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_LEADER].button},
6067                 {"Any",                                                         1282, 46,       203,    UI_XSTR_COLOR_GREEN,    -1, &Multi_ho_buttons[0][MULTI_HO_MSG_ANY].button},
6068                 {"Host",                                                                1283,   46,     220,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_HOST].button},
6069                 {"Highest rank",                                        1280,   46,     265,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_RANK].button},
6070                 {"Team / wing-leader",                  1281,   46,     283,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_LEADER].button},
6071                 {"Any",                                                         1282, 46,       300,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_ANY].button},
6072                 {"Host",                                                                1283,   46,     317,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_HOST].button},               
6073                 {"On",                                                          1285,   545,    185,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_VOICE_ON].button},
6074                 {"Off",                                                         1286,   604,    185,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_VOICE_OFF].button},
6075                 {"Host modifies ships",                 1287,   437,    363,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_HOST_MODIFIES].button},
6076                 {"Exit",                                                                1417,   572,    418,    UI_XSTR_COLOR_PINK,     -1, &Multi_ho_buttons[0][MULTI_HO_ACCEPT].button},
6077 #endif
6078         },
6079         { // GR_1024
6080                 // not needed for FS1
6081 #ifndef MAKE_FS1
6082                 {"Highest rank",                                        1280,   62,     269,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_RANK].button},
6083                 {"Team / wing-leader",                  1281, 62,       297,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_LEADER].button},
6084                 {"Any",                                                         1282, 62,       325,    UI_XSTR_COLOR_GREEN,    -1, &Multi_ho_buttons[1][MULTI_HO_MSG_ANY].button},
6085                 {"Host",                                                                1283,   62,     352,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_HOST].button},
6086                 {"Highest rank",                                        1280,   62,     425,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_RANK].button},
6087                 {"Team / wing-leader",                  1281,   62,     452,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_LEADER].button},
6088                 {"Any",                                                         1282, 62,       480,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_ANY].button},
6089                 {"Host",                                                                1283,   62,     508,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_HOST].button},               
6090                 {"On",                                                          1285,   877,    294,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_VOICE_ON].button},
6091                 {"Off",                                                         1286,   967,    293,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_VOICE_OFF].button},
6092                 {"Host modifies ships",                 1287,   869,    589,    UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_HOST_MODIFIES].button},
6093                 {"Exit",                                                                1417,   953,    672,    UI_XSTR_COLOR_PINK,     -1, &Multi_ho_buttons[1][MULTI_HO_ACCEPT].button},
6094 #endif
6095         }
6096 };
6097
6098 // radio button controls
6099 #define MULTI_HO_NUM_RADIO_GROUPS               3
6100 #define MULTI_HO_MSG_GROUP                                      0                                                       // group dealing with squadmate messaging
6101 #define MULTI_HO_END_GROUP                                      1                                                       // group dealing with ending the mission
6102 #define MULTI_HO_VOICE_GROUP                            2                                                       // group dealing with voice stuff
6103 int Multi_ho_radio_groups[MULTI_HO_NUM_RADIO_GROUPS] = {                // currently selected button in the radio button group
6104         0,0,0
6105 };
6106 int Multi_ho_radio_info[MULTI_HO_NUM_RADIO_BUTTONS][3] = {      // info related to each of the radio buttons themselves
6107         // { group #, value, button id# }
6108         {0, 0, 0},                              // highest ranking players can do messaging
6109         {0, 1, 1},                              // wing/team leaders can do messaging
6110         {0, 2, 2},                              // any player can do messaging
6111         {0, 3, 3},                              // only host can do messaging   
6112         {1, 0, 4},                              // highest rank and host can end the mission
6113         {1, 1, 5},                              // team/wing leader can end the mission
6114         {1, 2, 6},                              // any player can end the mission
6115         {1, 3, 7},                              // only the host can end the mission
6116         {2, 0, 8},                              // voice toggled on
6117         {2, 1, 9}                               // voice toggled off
6118 };
6119
6120 // slider controls
6121 #define MULTI_HO_NUM_SLIDERS                                    3
6122 #define MULTI_HO_SLIDER_VOICE_QOS                       0                                               // voice quality of sound 
6123 #define MULTI_HO_SLIDER_VOICE_DUR                       1                                               // max duration of voice recording
6124 #define MULTI_HO_SLIDER_SKILL                                   2                                               // skill level
6125 struct ho_sliders {
6126         const char *filename;
6127         int x, y, xt, yt;
6128         int hotspot;
6129         int dot_w;
6130         int dots;
6131         UI_DOT_SLIDER_NEW slider;  // because we have a class inside this struct, we need the constructor below..
6132
6133         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){}
6134 };
6135 ho_sliders Multi_ho_sliders[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_SLIDERS] = {
6136         { // GR_640
6137 #ifdef MAKE_FS1
6138                 ho_sliders("MH_16",     396,    210,    -1,     -1,     16,     19,     10),                    // voice qos
6139                 ho_sliders("MH_17",     396,    263,    -1,     -1,     17,     19,     10),                    // voice duration
6140                 ho_sliders("MH_13",     10,             387,    -1,     -1,     13,     36,     5),                             // skill level
6141 #else
6142                 ho_sliders("MH_11",     428,    214,    437,    199,    11,     19,     10),                    // voice qos
6143                 ho_sliders("MH_12",     428,    261,    437,    246,    12,     19,     10),                    // voice duration
6144                 ho_sliders("MH_08",     237,    454,    230,    411,    8,              36,     5),                     // skill level
6145 #endif
6146         },
6147         { // GR_1024            
6148                 ho_sliders("2_MH_11",   684,    343,    690,    323,    11,     32,     10),                    // voice qos
6149                 ho_sliders("2_MH_12",   685,    418,    837,    468,    12,     32,     10),                    // voice duration
6150                 ho_sliders("2_MH_08",   379,    727,    369,    663,    8,              60,     5),                     // skill level
6151         }
6152 };
6153
6154 int Multi_ho_mission_respawn;
6155
6156 int Multi_ho_host_modifies;
6157
6158 // whether or not any of the inputboxes on this screen had focus last frame
6159 int Multi_ho_lastframe_input = 0;
6160
6161 // game information text areas
6162
6163 // ho titles
6164 #ifndef MAKE_FS1
6165 #define MULTI_HO_NUM_TITLES                                     14
6166
6167 UI_XSTR Multi_ho_titles[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_TITLES] = {
6168         { // GR_640
6169                 { "AI Orders",                          1289,           32,     144, UI_XSTR_COLOR_GREEN, -1, NULL },           
6170                 { "End Mission",                        1290,           32,     242, UI_XSTR_COLOR_GREEN, -1, NULL },
6171                 { "Time Limit",                 1291,           32,     347, UI_XSTR_COLOR_GREEN, -1, NULL },
6172                 { "Min",                                                1292,           74,     362, UI_XSTR_COLOR_GREEN, -1, NULL },
6173                 { "Respawn Limit",              1288,           32,     378, UI_XSTR_COLOR_GREEN, -1, NULL },
6174                 { "Kill Limit",                 1293,           32,     409, UI_XSTR_COLOR_GREEN, -1, NULL },
6175                 { "Observers",                          1294,           32,     441, UI_XSTR_COLOR_GREEN, -1, NULL },
6176                 { "Skill Level",                        1284,           230,    411, UI_XSTR_COLOR_GREEN, -1, NULL },
6177                 { "Voice Transmission", 1295,           437,    144, UI_XSTR_COLOR_GREEN, -1, NULL },
6178                 { "Voice Quality",              1296,           437,    199, UI_XSTR_COLOR_GREEN, -1, NULL },
6179                 { "Message Duration",   1297,           437,    246, UI_XSTR_COLOR_GREEN, -1, NULL },
6180                 { "sec",                                                1522,           523,    292, UI_XSTR_COLOR_GREEN, -1, NULL },           
6181                 { "sec",                                                1523,           523,    332, UI_XSTR_COLOR_GREEN, -1, NULL },           
6182                 { "Voice Wait",                 1298,           437,    313, UI_XSTR_COLOR_GREEN, -1, NULL },
6183         },
6184         { // GR_1024            
6185                 { "AI Orders",                          1289,           48,     238, UI_XSTR_COLOR_GREEN, -1, NULL },           
6186                 { "End Mission",                        1290,           48,     394, UI_XSTR_COLOR_GREEN, -1, NULL },
6187                 { "Time Limit",                 1291,           50,     568, UI_XSTR_COLOR_GREEN, -1, NULL },
6188                 { "Min",                                                1292,           119,    581, UI_XSTR_COLOR_GREEN, -1, NULL },
6189                 { "Respawn Limit",              1288,           50,     618, UI_XSTR_COLOR_GREEN, -1, NULL },
6190                 { "Kill Limit",                 1293,           50,     668, UI_XSTR_COLOR_GREEN, -1, NULL },
6191                 { "Observers",                          1294,           50,     718, UI_XSTR_COLOR_GREEN, -1, NULL },
6192                 { "Skill Level",                        1284,           398,    670, UI_XSTR_COLOR_GREEN, -1, NULL },
6193                 { "Voice Transmission", 1295,           869,    239, UI_XSTR_COLOR_GREEN, -1, NULL },
6194                 { "Voice Quality",              1296,           690,    331, UI_XSTR_COLOR_GREEN, -1, NULL },
6195                 { "Message Duration",   1297,           690,    405, UI_XSTR_COLOR_GREEN, -1, NULL },
6196                 { "sec",                                                1522,           837,    467, UI_XSTR_COLOR_GREEN, -1, NULL },
6197                 { "sec",                                                1523,           837,    534, UI_XSTR_COLOR_GREEN, -1, NULL },
6198                 { "Voice Wait",                 1298,           742,    510, UI_XSTR_COLOR_GREEN, -1, NULL },
6199         }
6200 };
6201 #endif
6202
6203 // mission time limit input box
6204 int Ho_time_coords[GR_NUM_RESOLUTIONS][4] = {
6205         { // GR_640
6206 #ifdef MAKE_FS1
6207                 217, 239, 41, 9
6208 #else
6209                 36, 362, 36, 17
6210 #endif
6211         },
6212         { // GR_1024
6213                 58, 581, 57, 27
6214         }
6215 };
6216
6217 // furball kill limit input box
6218 int Ho_kill_coords[GR_NUM_RESOLUTIONS][4] = {
6219         { // GR_640
6220 #ifdef MAKE_FS1
6221                 217, 364, 70, 9
6222 #else
6223                 36, 425, 45, 17
6224 #endif
6225         },
6226         { // GR_1024
6227                 58, 684, 72, 27
6228         }
6229 };
6230
6231 // voice recording duration text display area
6232 int Ho_vd_coords[GR_NUM_RESOLUTIONS][4] = {
6233         { // GR_640
6234 #ifdef MAKE_FS1
6235                 419, 300, 39, 9
6236 #else
6237                 467, 292, 55, 15
6238 #endif
6239         },
6240         { // GR_1024
6241                 750, 467, 85, 28
6242         }
6243 };
6244
6245 // voice token wait input box
6246 int Ho_vw_coords[GR_NUM_RESOLUTIONS][6] = {
6247         { // GR_640
6248 #ifdef MAKE_FS1
6249                 419, 366, 39, 9
6250 #else
6251                 467, 332, 55, 15
6252 #endif
6253         },
6254         { // GR_1024
6255                 750, 534, 85, 28
6256         }
6257 };
6258
6259 // observer count input box
6260 int Ho_obs_coords[GR_NUM_RESOLUTIONS][4] = {
6261         { // GR_640
6262 #ifdef MAKE_FS1
6263                 217, 176, 70, 9
6264 #else
6265                 36, 457, 45, 17
6266 #endif
6267         },
6268         { // GR_1024
6269                 58, 733, 72, 27
6270         }
6271 };
6272
6273 // skill text description area
6274 int Ho_st_coords[GR_NUM_RESOLUTIONS][4] = {
6275         { // GR_640
6276 #ifdef MAKE_FS1
6277                 40, 426, 167, 9
6278 #else
6279                 249, 435, 172, 10
6280 #endif
6281         },
6282         { // GR_1024
6283                 403, 699, 172, 10
6284         }
6285 };
6286
6287 // respawn input box
6288 int Ho_rsp_coords[GR_NUM_RESOLUTIONS][6] = {
6289         { // GR_640
6290 #ifdef MAKE_FS1
6291                 217, 301, 70, 9,
6292 #else
6293                 36, 394, 45, 17, 
6294 #endif
6295         },
6296         { // GR_1024
6297                 58, 632, 72, 27
6298         }
6299 };
6300
6301 // respawn max text area
6302 int Ho_max_rsp_coords[GR_NUM_RESOLUTIONS][2] = {
6303         { // GR_640
6304 #ifdef MAKE_FS1
6305                 305, 301
6306 #else
6307                 150, 378
6308 #endif
6309         },
6310         { // GR_1024
6311                 190, 618
6312         }
6313 };
6314
6315 // maximum values for various input boxes (to notify user of overruns)
6316 #define MULTI_HO_MAX_TIME_LIMIT                         500
6317 #define MULTI_HO_MAX_TOKEN_WAIT                         5
6318 #define MULTI_HO_MAX_KILL_LIMIT                         9999
6319 #define MULTI_HO_MAX_OBS                                                4
6320
6321 // LOCAL function definitions
6322 void multi_ho_check_buttons();
6323 void multi_ho_button_pressed(int n);
6324 void multi_ho_draw_radio_groups();
6325 void multi_ho_accept_hit();
6326 void multi_ho_get_options();
6327 void multi_ho_apply_options();
6328 void multi_ho_display_record_time();
6329 int multi_ho_check_values();
6330 void multi_ho_check_focus();
6331 void multi_ho_blit_max_respawns();
6332 void multi_ho_display_skill_level();
6333
6334 void multi_host_options_init()
6335 {
6336         int idx;
6337
6338         // create the interface window
6339         Multi_ho_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
6340         Multi_ho_window.set_mask_bmap(Multi_ho_bitmap_mask_fname[gr_screen.res]);
6341
6342         // load the background bitmap
6343         Multi_ho_bitmap = bm_load(Multi_ho_bitmap_fname[gr_screen.res]);
6344         if(Multi_ho_bitmap < 0){
6345                 // we failed to load the bitmap - this is very bad
6346                 Int3();
6347         }               
6348
6349         // initialize the common notification messaging
6350         multi_common_notify_init();     
6351
6352         // use the common interface palette
6353         multi_common_set_palette();     
6354
6355         // create the interface buttons
6356         for(idx=0;idx<MULTI_HO_NUM_BUTTONS;idx++){
6357                 // create the object
6358                 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);
6359
6360                 // set the sound to play when highlighted
6361                 Multi_ho_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
6362
6363                 // set the ani for the button
6364                 Multi_ho_buttons[gr_screen.res][idx].button.set_bmaps(Multi_ho_buttons[gr_screen.res][idx].filename);
6365
6366                 // set the hotspot, ignoring the skill level button             
6367                 Multi_ho_buttons[gr_screen.res][idx].button.link_hotspot(Multi_ho_buttons[gr_screen.res][idx].hotspot);
6368
6369                 // add xstr text
6370 #ifndef MAKE_FS1
6371                 Multi_ho_window.add_XSTR(&Multi_ho_text[gr_screen.res][idx]);
6372 #endif
6373         }               
6374
6375 #ifndef MAKE_FS1
6376         // create misc text
6377         for(idx=0; idx<MULTI_HO_NUM_TITLES; idx++){
6378                 Multi_ho_window.add_XSTR(&Multi_ho_titles[gr_screen.res][idx]);
6379         }
6380 #endif
6381
6382         // create the interface sliders
6383         for(idx=0; idx<MULTI_HO_NUM_SLIDERS; idx++){
6384                 // create the object
6385                 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);
6386         }
6387
6388         // create the respawn count input box
6389         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);          
6390         // if we're in campaign mode, disable it
6391         if(Netgame.campaign_mode == MP_CAMPAIGN){
6392                 Multi_ho_respawns.set_text(XSTR("NA",795));  // [[ Not applicable ]]
6393                 Multi_ho_respawns.disable();
6394         } else {
6395                 Multi_ho_respawns.set_valid_chars("0123456789");
6396         }
6397
6398         // create the time limit input box
6399         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);
6400         Multi_ho_time_limit.set_valid_chars("-0123456789");
6401
6402         // create the voice token wait input box
6403         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);
6404         Multi_ho_voice_wait.set_valid_chars("01243456789");
6405
6406         // create the furball kill limit input box
6407         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);
6408         Multi_ho_kill_limit.set_valid_chars("0123456789");
6409
6410         // create the observer limit input box
6411         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);
6412         Multi_ho_obs.set_valid_chars("01234");  
6413         
6414         // load in the current netgame defaults
6415         multi_ho_get_options();
6416
6417         // whether or not any of the inputboxes on this screen had focus last frame
6418         Multi_ho_lastframe_input = 0;   
6419
6420         // get the # of respawns for the currently selected mission (if any)
6421         if(Multi_create_list_select != -1){
6422                 int abs_index = multi_create_select_to_index(Multi_create_list_select);
6423
6424                 // if he has a valid mission selected
6425                 if(abs_index >= 0){
6426                         Multi_ho_mission_respawn = (int)Multi_create_file_list[abs_index].respawn;
6427                 } else {
6428                         Multi_ho_mission_respawn = -1;
6429                 }
6430         } else {
6431                 Multi_ho_mission_respawn = -1;
6432         }
6433 }
6434
6435 void multi_ho_update_sliders()
6436 {
6437         // game skill slider
6438         if (Game_skill_level != Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos) {
6439                 if ( !(Netgame.type_flags & NG_TYPE_TEAM) ){            
6440                         Game_skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
6441                         gamesnd_play_iface(SND_USER_SELECT);
6442                 } else {        
6443                         Game_skill_level = NUM_SKILL_LEVELS / 2;
6444                 }
6445         }
6446
6447         // get the voice qos options
6448         if (Netgame.options.voice_qos != (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1)) {
6449                 Netgame.options.voice_qos = (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1); 
6450                 gamesnd_play_iface(SND_USER_SELECT);
6451         }
6452
6453         // get the voice duration options
6454         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)) {
6455                 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);
6456                 gamesnd_play_iface(SND_USER_SELECT);
6457         }
6458
6459 }
6460
6461 void multi_host_options_do()
6462 {
6463         int k;  
6464         
6465         // process stuff
6466         k = Multi_ho_window.process();          
6467         chatbox_process(k);     
6468
6469         // process any keypresses
6470         switch(k){
6471         case SDLK_ESCAPE :
6472                 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
6473                 break;
6474         // same as ACCEPT
6475         case KEY_CTRLED + SDLK_RETURN :
6476                 gamesnd_play_iface(SND_COMMIT_PRESSED);
6477                 multi_ho_accept_hit();
6478                 break;
6479         }
6480
6481         // process any button clicks
6482         multi_ho_check_buttons();       
6483
6484         // update the sliders
6485         multi_ho_update_sliders();
6486
6487         // make sure that the chatbox inputbox and any inputbox on this screen are mutually exclusive in terms of focus
6488         multi_ho_check_focus();
6489
6490         // draw the background, etc
6491         gr_reset_clip();
6492         GR_MAYBE_CLEAR_RES(Multi_ho_bitmap);
6493         if(Multi_ho_bitmap != -1){
6494                 gr_set_bitmap(Multi_ho_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
6495                 gr_bitmap(0,0);
6496         }
6497         Multi_ho_window.draw();
6498         
6499         // draw all the radio buttons properly
6500         multi_ho_draw_radio_groups();
6501                         
6502         // display any pending notification messages
6503         multi_common_notify_do();               
6504
6505         // display the voice record time settings
6506         multi_ho_display_record_time(); 
6507
6508         // maybe display the max # of respawns next to the respawn input box
6509         multi_ho_blit_max_respawns();
6510
6511         // blit the proper skill level  
6512         multi_ho_display_skill_level();
6513
6514         // blit the "host modifies button"
6515         if(Multi_ho_host_modifies){
6516                 Multi_ho_buttons[gr_screen.res][MULTI_HO_HOST_MODIFIES].button.draw_forced(2);
6517         }
6518
6519         // process and show the chatbox thingie 
6520         chatbox_render();
6521
6522         // draw tooltips
6523         Multi_ho_window.draw_tooltip(); 
6524
6525         // display the voice status indicator
6526         multi_common_voice_display_status();
6527
6528         // flip the buffer
6529         gr_flip();
6530 }
6531
6532 void multi_host_options_close()
6533 {
6534         // unload any bitmaps
6535         if(!bm_unload(Multi_ho_bitmap)){
6536                 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_ho_bitmap_fname[gr_screen.res]));
6537         }       
6538         
6539         // destroy the UI_WINDOW
6540         Multi_ho_window.destroy();
6541 }
6542
6543 void multi_ho_check_buttons()
6544 {
6545         int idx;
6546         for(idx=0;idx<MULTI_HO_NUM_BUTTONS;idx++){
6547                 // we only really need to check for one button pressed at a time, so we can break after 
6548                 // finding one.
6549                 if(Multi_ho_buttons[gr_screen.res][idx].button.pressed()){
6550                         multi_ho_button_pressed(idx);
6551                         break;
6552                 }
6553         }
6554 }
6555
6556 void multi_ho_button_pressed(int n)
6557 {
6558         int radio_index,idx;
6559         int x_pixel,y_pixel;
6560
6561         // get the pixel position of the click
6562         Multi_ho_buttons[gr_screen.res][n].button.get_mouse_pos(&x_pixel,&y_pixel);
6563                 
6564         switch(n){              
6565         // clicked on the accept button
6566         case MULTI_HO_ACCEPT:
6567                 gamesnd_play_iface(SND_COMMIT_PRESSED);
6568                 multi_ho_accept_hit();
6569                 return; 
6570         
6571         // clicked on the host/captains only modify button
6572         case MULTI_HO_HOST_MODIFIES:
6573                 // toggle it on or off
6574                 Multi_ho_host_modifies = !Multi_ho_host_modifies;
6575                 gamesnd_play_iface(SND_USER_SELECT);
6576                 return;
6577         }
6578
6579         // look through the radio buttons and see which one this corresponds to
6580         radio_index = -1;
6581         for(idx=0;idx<MULTI_HO_NUM_RADIO_BUTTONS;idx++){
6582                 if(Multi_ho_radio_info[idx][2] == n){
6583                         radio_index = idx;
6584                         break;
6585                 }
6586         }
6587         SDL_assert(radio_index != -1);
6588
6589         // check to see if a radio button was pressed
6590         if(radio_index < MULTI_HO_NUM_RADIO_BUTTONS){
6591                 // see if this value is already picked for this radio group
6592                 if(Multi_ho_radio_groups[Multi_ho_radio_info[radio_index][0]] != Multi_ho_radio_info[radio_index][1]){
6593                         gamesnd_play_iface(SND_USER_SELECT);
6594                         Multi_ho_radio_groups[Multi_ho_radio_info[radio_index][0]] = Multi_ho_radio_info[radio_index][1];
6595                 } else {
6596                         gamesnd_play_iface(SND_GENERAL_FAIL);
6597                 }
6598         }
6599 }
6600
6601 void multi_ho_draw_radio_groups()
6602 {
6603         int idx;
6604         
6605         // go through each item and draw it if it is the selected button in its respective group
6606         for(idx=0;idx<MULTI_HO_NUM_RADIO_BUTTONS;idx++){
6607                 /// if this button is the currently selected one in its group
6608                 if(Multi_ho_radio_info[idx][1] == Multi_ho_radio_groups[Multi_ho_radio_info[idx][0]]){
6609                         Multi_ho_buttons[gr_screen.res][Multi_ho_radio_info[idx][2]].button.draw_forced(2);
6610                 }
6611         }
6612 }
6613
6614 void multi_ho_accept_hit()
6615 {
6616         char resp_str[10];
6617
6618         // check the values in the input boxes
6619         if(!multi_ho_check_values()){
6620                 return;
6621         }
6622         
6623         // zero out the netgame flags
6624         Netgame.flags = 0;
6625         
6626         // set default options
6627         Netgame.options.flags = (MSO_FLAG_INGAME_XFER | MSO_FLAG_ACCEPT_PIX);
6628
6629         // set the squadmate messaging flags
6630         switch(Multi_ho_radio_groups[MULTI_HO_MSG_GROUP]){
6631         case 0 : 
6632                 Netgame.options.squad_set = MSO_SQUAD_RANK;
6633                 break;
6634         case 1 :
6635                 Netgame.options.squad_set = MSO_SQUAD_LEADER;           
6636                 break;
6637         case 2 :
6638                 Netgame.options.squad_set = MSO_SQUAD_ANY;              
6639                 break;
6640         case 3 :
6641                 Netgame.options.squad_set = MSO_SQUAD_HOST;
6642                 break;
6643         default : 
6644                 Int3(); 
6645         }
6646
6647         // set the end mission flags
6648         switch(Multi_ho_radio_groups[MULTI_HO_END_GROUP]){
6649         case 0 : 
6650                 Netgame.options.endgame_set = MSO_END_RANK;             
6651                 break;
6652         case 1 :
6653                 Netgame.options.endgame_set = MSO_END_LEADER;           
6654                 break;                  
6655         case 2 : 
6656                 Netgame.options.endgame_set = MSO_END_ANY;              
6657                 break;
6658         case 3 :
6659                 Netgame.options.endgame_set = MSO_END_HOST;             
6660                 break;  
6661         default : 
6662                 Int3(); 
6663         }
6664         
6665         // set the voice toggle
6666         switch(Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP]){
6667         case 0 :
6668                 Netgame.options.flags &= ~(MSO_FLAG_NO_VOICE);
6669                 break;
6670         case 1 :
6671                 Netgame.options.flags |= MSO_FLAG_NO_VOICE;
6672                 break;
6673         default : 
6674                 Int3();
6675         }       
6676
6677         // get the voice qos options
6678         Netgame.options.voice_qos = (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1); 
6679
6680         // get the voice duration options
6681         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);
6682         
6683         // set the skill level.  If in team vs. team mode, preserve the old setting before saving
6684         // the pilot file.  I'll bet that this doesn't work though because the pilot file gets
6685         // written in a bunch of locations....sigh.
6686         if ( !(Netgame.type_flags & NG_TYPE_TEAM) ){            
6687                 Game_skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
6688         } else {        
6689                 Game_skill_level = NUM_SKILL_LEVELS / 2;
6690         }
6691
6692         // set the netgame respawn count
6693         // maybe warn the user that respawns will not be used for a campaign mission
6694         if(Netgame.campaign_mode == MP_SINGLE){
6695                 Multi_ho_respawns.get_text(resp_str);
6696                 uint temp_respawn = (uint)atoi(resp_str);
6697                 // if he currently has no mission selected, let the user set any # of respawns
6698                 if((int)temp_respawn > Multi_ho_mission_respawn){
6699                         if(Multi_ho_mission_respawn == -1){     
6700                                 Netgame.respawn = temp_respawn;         
6701                                 Netgame.options.respawn = temp_respawn;
6702                         }
6703                         // this should have been taken care of by the interface code
6704                         else {
6705                                 Int3();
6706                         }
6707                 } else {        
6708                         Netgame.options.respawn = temp_respawn;
6709                         Netgame.respawn = temp_respawn;
6710                 }
6711         }
6712
6713         // get the mission time limit
6714         Multi_ho_time_limit.get_text(resp_str);
6715         int temp_time = atoi(resp_str);
6716         if(temp_time <= 0){
6717                 Netgame.options.mission_time_limit = fl2f(-1.0f);
6718         } else if(temp_time > MULTI_HO_MAX_TIME_LIMIT){
6719                 Int3();
6720         } else {
6721                 Netgame.options.mission_time_limit = fl2f(60.0f * (float)temp_time);    
6722         }
6723
6724         // get observer count options
6725         Multi_ho_obs.get_text(resp_str);
6726         int temp_obs = atoi(resp_str);
6727         if(temp_obs > MULTI_HO_MAX_OBS){
6728                 Int3();
6729         } 
6730         Netgame.options.max_observers = (ubyte)temp_obs;        
6731
6732         // get the furball kill limit
6733         Multi_ho_kill_limit.get_text(resp_str);
6734         int temp_kills = atoi(resp_str);
6735         if(temp_kills > MULTI_HO_MAX_KILL_LIMIT){
6736                 Int3();
6737         }
6738         Netgame.options.kill_limit = temp_kills;
6739
6740         // get the token wait limit
6741         Multi_ho_voice_wait.get_text(resp_str);
6742         int temp_wait = atoi(resp_str);
6743         if(temp_wait > MULTI_HO_MAX_TOKEN_WAIT){
6744                 Int3();
6745         } 
6746         Netgame.options.voice_token_wait = (temp_wait * 1000);          
6747
6748         // set the netgame option
6749         Netgame.options.skill_level = (ubyte)Game_skill_level;
6750
6751         // get whether we're in host/captains only modify mode
6752         Netgame.options.flags &= ~(MSO_FLAG_SS_LEADERS);
6753         if(Multi_ho_host_modifies){
6754                 Netgame.options.flags |= MSO_FLAG_SS_LEADERS;
6755         }       
6756
6757         // store these values locally
6758         memcpy(&Player->m_local_options,&Net_player->p_info.options,sizeof(multi_local_options));
6759         memcpy(&Player->m_server_options,&Netgame.options,sizeof(multi_server_options));
6760         write_pilot_file(Player);       
6761
6762         // apply any changes in settings (notify everyone of voice qos changes, etc)
6763         multi_ho_apply_options();
6764
6765         // move back to the create game screen
6766         gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
6767 }
6768
6769 void multi_ho_get_options()
6770 {       
6771         char resp_str[10];
6772         
6773         // set the squadmate messaging buttons  
6774         switch(Netgame.options.squad_set){
6775         case MSO_SQUAD_RANK :           
6776                 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 0;
6777                 break;
6778         case MSO_SQUAD_LEADER:                          
6779                 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 1;
6780                 break;
6781         case MSO_SQUAD_ANY:                     
6782                 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 2;
6783                 break;
6784         case MSO_SQUAD_HOST:            
6785                 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 3;
6786                 break;
6787         default : 
6788                 Int3();         
6789         }
6790         
6791         // set the mission end buttons  
6792         switch(Netgame.options.endgame_set){
6793         case MSO_END_RANK:                      
6794                 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 0;
6795                 break;
6796         case MSO_END_LEADER:                    
6797                 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 1;
6798                 break;
6799         case MSO_END_ANY:
6800                 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 2;
6801                 break;
6802         case MSO_END_HOST:
6803                 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 3;
6804                 break;
6805         default : 
6806                 Int3();
6807         }                       
6808
6809         // set the voice toggle buttons
6810         if(Netgame.options.flags & MSO_FLAG_NO_VOICE){
6811                 Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP] = 1;
6812         } else {
6813                 Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP] = 0;
6814         }       
6815
6816         // get the voice qos options
6817         SDL_assert((Netgame.options.voice_qos >= 1) && (Netgame.options.voice_qos <= 10));
6818         Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos = (Netgame.options.voice_qos - 1);
6819
6820         // get the voice duration options
6821         SDL_assert((Netgame.options.voice_record_time > 0) && (Netgame.options.voice_record_time <= MULTI_VOICE_MAX_TIME));
6822         Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos = ((int)((float)Netgame.options.voice_record_time / 500.0f)) - 1; 
6823
6824         // get the current skill level
6825         SDL_assert((Game_skill_level >= 0) && (Game_skill_level < NUM_SKILL_LEVELS));
6826         Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos = Game_skill_level;   
6827
6828         // get the # of observers
6829         memset(resp_str,0,10);
6830         sprintf(resp_str,"%d",Netgame.options.max_observers);
6831         Multi_ho_obs.set_text(resp_str);
6832
6833         // set the respawn count
6834         if(Netgame.campaign_mode == MP_SINGLE){
6835                 memset(resp_str,0,10);
6836                 sprintf(resp_str,"%d",Netgame.respawn);
6837                 Multi_ho_respawns.set_text(resp_str);   
6838         }
6839
6840         // set the mission time limit
6841         memset(resp_str,0,10);
6842         float tl = f2fl(Netgame.options.mission_time_limit);
6843         sprintf(resp_str,"%d",(int)(tl / 60.0f));
6844         Multi_ho_time_limit.set_text(resp_str);
6845
6846         // set the furball kill limit
6847         memset(resp_str,0,10);
6848         sprintf(resp_str,"%d",Netgame.options.kill_limit);
6849         Multi_ho_kill_limit.set_text(resp_str);
6850
6851         // set the token wait time
6852         memset(resp_str,0,10);
6853         sprintf(resp_str,"%d",Netgame.options.voice_token_wait / 1000);
6854         Multi_ho_voice_wait.set_text(resp_str); 
6855
6856         // get whether we're in host/captains only modify mode
6857         if(Netgame.options.flags & MSO_FLAG_SS_LEADERS){
6858                 Multi_ho_host_modifies = 1;
6859         } else {
6860                 Multi_ho_host_modifies = 0;
6861         }
6862 }
6863
6864 void multi_ho_apply_options()
6865 {
6866         // if the voice qos or duration has changed, apply the change
6867         multi_voice_maybe_update_vars(Netgame.options.voice_qos,Netgame.options.voice_record_time);             
6868
6869         // send an options update
6870         multi_options_update_netgame(); 
6871 }
6872
6873 // display the voice record time settings
6874 void multi_ho_display_record_time()
6875 {
6876         char time_str[30];
6877         int full_seconds, half_seconds;
6878
6879         // clear the string
6880         memset(time_str,0,30);
6881
6882         // get the seconds
6883         full_seconds = (((Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 500) / 1000);
6884         
6885         // get the half-seconds
6886         half_seconds = ((((Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 500) % 1000) / 500) * 5;
6887
6888         // format the string
6889         sprintf(time_str,"%d.%d",full_seconds,half_seconds);
6890         gr_set_color_fast(&Color_bright);
6891         gr_string(Ho_vd_coords[gr_screen.res][MULTI_HO_X_COORD],Ho_vd_coords[gr_screen.res][MULTI_HO_Y_COORD],time_str);
6892 }
6893
6894 int multi_ho_check_values()
6895 {
6896         char val_txt[255];
6897
6898         memset(val_txt,0,255);
6899
6900         // check against respawn settings       
6901         if(Multi_ho_mission_respawn != -1){
6902                 Multi_ho_respawns.get_text(val_txt);
6903                 // if the value is invalid, let the user know
6904                 if(atoi(val_txt) > Multi_ho_mission_respawn){
6905                         memset(val_txt,0,255);
6906                         sprintf(val_txt,XSTR("Warning\nRespawn count in greater than mission specified max (%d)",796),Multi_ho_mission_respawn);                        
6907                         popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6908                         return 0;
6909                 }
6910         }
6911
6912         // check against mission time limit max
6913         Multi_ho_time_limit.get_text(val_txt);
6914         // if the value is invalid, force it to be valid
6915         if(atoi(val_txt) > MULTI_HO_MAX_TIME_LIMIT){
6916                 memset(val_txt,0,255);
6917                 sprintf(val_txt,XSTR("Warning\nMission time limit is greater than max allowed (%d)",797),MULTI_HO_MAX_TIME_LIMIT);              
6918                 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6919                 return 0;
6920         }
6921
6922         // check against max observer limit     
6923         Multi_ho_obs.get_text(val_txt);
6924         // if the value is invalid, force it to be valid
6925         if(atoi(val_txt) > MULTI_HO_MAX_OBS){
6926                 memset(val_txt,0,255);
6927                 sprintf(val_txt,XSTR("Warning\nObserver count is greater than max allowed (%d)",798),MULTI_HO_MAX_OBS);         
6928                 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6929                 return 0;
6930         }
6931
6932         // check against furball kill limit     
6933         Multi_ho_kill_limit.get_text(val_txt);
6934         // if the value is invalid, force it to be valid
6935         if(atoi(val_txt) > MULTI_HO_MAX_KILL_LIMIT){
6936                 memset(val_txt,0,255);
6937                 sprintf(val_txt,XSTR("Warning\nMission kill limit is greater than max allowed (%d)",799),MULTI_HO_MAX_KILL_LIMIT);              
6938                 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6939                 return 0;
6940         }
6941
6942         // check against the token wait limit   
6943         Multi_ho_voice_wait.get_text(val_txt);
6944         if(atoi(val_txt) > MULTI_HO_MAX_TOKEN_WAIT){
6945                 memset(val_txt,0,255);
6946                 sprintf(val_txt,XSTR("Warning\nvoice wait time is greater than max allowed (%d)",800),MULTI_HO_MAX_TOKEN_WAIT);         
6947                 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6948                 return 0;
6949         }
6950
6951         // all values are valid
6952         return 1;
6953 }
6954
6955 void multi_ho_check_focus()
6956 {
6957         // if an inputbox has been pressed (hit enter), lose its focus
6958         if (Multi_ho_respawns.pressed() || Multi_ho_time_limit.pressed() || Multi_ho_voice_wait.pressed() || Multi_ho_kill_limit.pressed() || Multi_ho_obs.pressed()) {
6959                 Multi_ho_respawns.clear_focus();
6960                 Multi_ho_time_limit.clear_focus();      
6961                 Multi_ho_voice_wait.clear_focus();                      
6962                 Multi_ho_kill_limit.clear_focus();
6963                 Multi_ho_obs.clear_focus();
6964                 gamesnd_play_iface(SND_COMMIT_PRESSED);
6965                 chatbox_set_focus();
6966                 Multi_ho_lastframe_input = 0;
6967
6968         } else if(!Multi_ho_lastframe_input) {
6969                 // if we didn't have focus last frame
6970                 if(Multi_ho_respawns.has_focus() || Multi_ho_time_limit.has_focus() || Multi_ho_kill_limit.has_focus() || Multi_ho_voice_wait.has_focus() ){
6971                         chatbox_lose_focus();
6972
6973                         Multi_ho_lastframe_input = 1;
6974                 }       
6975         } 
6976         // if we _did_ have focus last frame
6977         else {
6978                 // if we no longer have focus on any of the input boxes, set the focus on the chatbox
6979                 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()){
6980                         chatbox_set_focus();
6981                 }                       
6982                 // if the chatbox now has focus, clear all focus from our inputboxes
6983                 else if (chatbox_has_focus()) {
6984                         Multi_ho_respawns.clear_focus();
6985                         Multi_ho_time_limit.clear_focus();
6986                         Multi_ho_kill_limit.clear_focus();
6987                         Multi_ho_voice_wait.clear_focus();
6988
6989                         Multi_ho_lastframe_input = 0;
6990                 }
6991         }
6992 }
6993
6994 void multi_ho_blit_max_respawns()
6995 {
6996         char string[50];
6997         
6998         // if we're in campaign mode, do nothing
6999         if(Netgame.campaign_mode == MP_CAMPAIGN){
7000                 return;
7001         }
7002         
7003         // otherwise blit the max as specified by the current mission file      
7004         sprintf(string,"(%d)",Multi_ho_mission_respawn);        
7005         gr_set_color_fast(&Color_normal);
7006         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);
7007 }
7008
7009 void multi_ho_display_skill_level()
7010 {
7011         int skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
7012
7013         // sanity
7014         SDL_assert((skill_level >= 0) && (skill_level < NUM_SKILL_LEVELS));
7015         if((skill_level < 0) || (skill_level >= NUM_SKILL_LEVELS)){
7016                 skill_level = 0;
7017         }
7018
7019         gr_set_color_fast(&Color_bright);
7020         gr_string(Ho_st_coords[gr_screen.res][0], Ho_st_coords[gr_screen.res][1], Skill_level_names(skill_level, 1));
7021 }
7022
7023 // -------------------------------------------------------------------------------------------------------------
7024 // 
7025 // MULTIPLAYER JOIN SCREEN 
7026 //
7027
7028 #define MULTI_JW_NUM_BUTTONS    8
7029
7030 //XSTR:OFF
7031 // bitmaps defs
7032 #define MULTI_JW_PALETTE                                "InterfacePalette"
7033
7034 static const char *Multi_jw_bitmap_fname[GR_NUM_RESOLUTIONS] = {
7035         "MultiJoinWait",                // GR_640
7036         "2_MultiJoinWait"               // GR_1024
7037 };
7038
7039 static const char *Multi_jw_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
7040         "MultiJoinWait-M",              // GR_640
7041         "2_MultiJoinWait-M"             // GR_1024
7042 };
7043
7044 //XSTR:ON
7045
7046 // button defs
7047 #define MJW_SCROLL_PLAYERS_UP           0
7048 #define MJW_SCROLL_PLAYERS_DOWN 1
7049 #define MJW_TEAM0                                               2
7050 #define MJW_TEAM1                                               3
7051 #define MJW_PILOT_INFO                          4
7052 #define MJW_SCROLL_INFO_UP                      5
7053 #define MJW_SCROLL_INFO_DOWN            6
7054 #define MJW_CANCEL                                      7
7055
7056 UI_WINDOW Multi_jw_window;                                                                                              // the window object for the join screen
7057 int Multi_jw_bitmap;                                                                                                            // the background bitmap
7058
7059 // constants for coordinate lookup
7060 #define MJW_X_COORD 0
7061 #define MJW_Y_COORD 1
7062 #define MJW_W_COORD 2
7063 #define MJW_H_COORD 3
7064
7065 ui_button_info Multi_jw_buttons[GR_NUM_RESOLUTIONS][MULTI_JW_NUM_BUTTONS] = {
7066         { // GR_640
7067 #ifdef MAKE_FS1
7068                 ui_button_info("MJW_00",        0,              50,             -1,     -1,     0),
7069                 ui_button_info("MJW_01",        0,              87,             -1,     -1,     1),
7070                 ui_button_info("MJW_02",        20,             219,    -1,     -1,     2),
7071                 ui_button_info("MJW_03",        73,             219,    -1,     -1,     3),
7072                 ui_button_info("MJW_09",        131,    213,    -1,     -1,     9),
7073                 ui_button_info("MJW_05",        0,              398,    -1,     -1,     5),
7074                 ui_button_info("MJW_06",        0,              435,    -1,     -1,     6),
7075                 ui_button_info("MJW_04",        559,    411,    -1,     -1,     4),
7076 #else
7077                 ui_button_info("MJW_00",        1,              24,     -1,     -1,     0),
7078                 ui_button_info("MJW_01",        1,              66,     -1,     -1,     1),
7079                 ui_button_info("MJW_02",        30,     244,    20,     272,    2),
7080                 ui_button_info("MJW_03",        84,     244,    73,     272,    3),
7081                 ui_button_info("MJW_04",        139,    242,    134,    272,    4),
7082                 ui_button_info("MJW_05",        1,              406,    -1,     -1,     5),
7083                 ui_button_info("MJW_06",        1,              447,    -1,     -1,     6),
7084                 ui_button_info("MJW_07",        577,    428,    570,    414,    7),
7085 #endif
7086         },
7087         { // GR_1024
7088                 ui_button_info("2_MJW_00",      2,              38,     -1,     -1,     0),
7089                 ui_button_info("2_MJW_01",      2,              106,    -1,     -1,     1),
7090                 ui_button_info("2_MJW_02",      48,     390,    47,     435,    2),
7091                 ui_button_info("2_MJW_03",      134,    390,    133,    435,    3),
7092                 ui_button_info("2_MJW_04",      223,    388,    225,    435,    4),
7093                 ui_button_info("2_MJW_05",      2,              649,    -1,     -1,     5),
7094                 ui_button_info("2_MJW_06",      2,              715,    -1,     -1,     6),
7095                 ui_button_info("2_MJW_07",      923,    685,    931,    667,    7),
7096         }
7097 };
7098
7099 #ifndef MAKE_FS1
7100 #define MULTI_JW_NUM_TEXT                       7
7101
7102 UI_XSTR Multi_jw_text[GR_NUM_RESOLUTIONS][MULTI_JW_NUM_TEXT] = {
7103         { // GR_640
7104                 { "Team 1",                             1308,           20,     272,    UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_TEAM0].button },
7105                 { "Team 2",                             1309,           73,     272,    UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_TEAM1].button },
7106                 { "Pilot",                              1310,           134,    272,    UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_PILOT_INFO].button },
7107                 { "Info",                               1311,           134,    283,    UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_PILOT_INFO].button },
7108                 { "Cancel",                             387,            570,    414,    UI_XSTR_COLOR_PINK, -1, &Multi_jw_buttons[0][MJW_CANCEL].button },
7109                 { "Players",                    1269,           38,     8,              UI_XSTR_COLOR_GREEN, -1, NULL },
7110                 { "Choose Team",                1312,           27,     231,    UI_XSTR_COLOR_GREEN, -1, NULL },
7111         },
7112         { // GR_1024
7113                 { "Team 1",                             1308,           47,     435,    UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_TEAM0].button },
7114                 { "Team 2",                             1309,           133,    435,    UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_TEAM1].button },
7115                 { "Pilot",                              1310,           225,    435,    UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_PILOT_INFO].button },
7116                 { "Info",                               1311,           225,    446,    UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_PILOT_INFO].button },
7117                 { "Cancel",                             387,            931,    667,    UI_XSTR_COLOR_PINK, -1, &Multi_jw_buttons[1][MJW_CANCEL].button },
7118                 { "Players",                    1269,           165,    12,     UI_XSTR_COLOR_GREEN, -1, NULL },
7119                 { "Choose Team",                1312,           45,     373,    UI_XSTR_COLOR_GREEN, -1, NULL },
7120         }
7121 };
7122 #endif
7123
7124 int Mjw_players_coords[GR_NUM_RESOLUTIONS][4] = {
7125         { // GR_640
7126 #ifdef MAKE_FS1
7127                 36, 31, 173, 204
7128 #else
7129                 29, 18, 153, 210
7130 #endif
7131         },
7132         { // GR_1024
7133                 46, 29, 254, 336
7134         }
7135 };
7136
7137 int Mjw_mission_name_coords[GR_NUM_RESOLUTIONS][2] = {
7138         { // GR_640
7139                 5, 380
7140         },
7141         { // GR_1024
7142                 47, 618
7143         }
7144 };
7145
7146 // squad war checkbox
7147 UI_CHECKBOX     Multi_jw_sw_checkbox;
7148 const char *Multi_jw_sw_checkbox_fname[GR_NUM_RESOLUTIONS] = {
7149         "MC_SW_00",
7150         "MC_SW_00",
7151 };
7152 int Multi_jw_sw_checkbox_coords[GR_NUM_RESOLUTIONS][2] = {
7153         { // GR_640
7154                 6, 285
7155         },
7156         { // GR_1024
7157                 18, 450
7158         }
7159 };
7160 int Multi_jw_sw_checkbox_text[GR_NUM_RESOLUTIONS][2] = {
7161         { // GR_640
7162                 6, 305
7163         },
7164         { // GR_640
7165                 18, 470
7166         },
7167 };
7168
7169
7170 // player list control thingie defs
7171 #define MULTI_JW_PLIST_MAX_DISPLAY              19
7172 int Multi_jw_plist_select_flag;                                 // indicates whether we currently have a selected player
7173 short Multi_jw_plist_select_id;                         // id of the current selected player
7174 UI_BUTTON Multi_jw_plist_select_button;         // for selecting a player
7175
7176 int Multi_jw_should_show_popup = 0;
7177
7178 // LOCAL function definitions
7179 void multi_jw_check_buttons();
7180 void multi_jw_button_pressed(int n);
7181 void multi_jw_do_netstuff();
7182 void multi_jw_scroll_players_up();
7183 void multi_jw_scroll_players_down();
7184 void multi_jw_plist_process();
7185 void multi_jw_plist_blit_normal();
7186 void multi_jw_plist_blit_team();
7187 short multi_jw_get_mouse_id();
7188
7189 void multi_game_client_setup_init()
7190 {
7191         int idx;
7192
7193         // create the interface window
7194         Multi_jw_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
7195         Multi_jw_window.set_mask_bmap(Multi_jw_bitmap_mask_fname[gr_screen.res]);
7196
7197         // load the background bitmap
7198         Multi_jw_bitmap = bm_load(Multi_jw_bitmap_fname[gr_screen.res]);
7199         if(Multi_jw_bitmap < 0){
7200                 // we failed to load the bitmap - this is very bad
7201                 Int3();
7202         }
7203
7204         // initialize the player list data      
7205         Multi_jw_plist_select_flag = 0;
7206         Multi_jw_plist_select_id = -1;  
7207         
7208         // kill any old instances of the chatbox and create a new one
7209         chatbox_close();
7210         chatbox_create(CHATBOX_FLAG_BIG | CHATBOX_FLAG_DRAW_BOX | CHATBOX_FLAG_BUTTONS);
7211
7212         // initialize the common notification messaging
7213         multi_common_notify_init();
7214
7215         // initialize the common mission info display area.
7216         multi_common_set_text("");      
7217
7218         // use the common interface palette
7219         multi_common_set_palette();     
7220
7221         // create the interface buttons
7222         for(idx=0; idx<MULTI_JW_NUM_BUTTONS; idx++){
7223                 // create the object
7224                 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);
7225
7226                 // set the sound to play when highlighted
7227                 Multi_jw_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
7228
7229                 // set the ani for the button
7230                 Multi_jw_buttons[gr_screen.res][idx].button.set_bmaps(Multi_jw_buttons[gr_screen.res][idx].filename);
7231
7232                 // set the hotspot
7233                 Multi_jw_buttons[gr_screen.res][idx].button.link_hotspot(Multi_jw_buttons[gr_screen.res][idx].hotspot);
7234         }               
7235
7236         // if this is a PXO game, enable the squadwar checkbox  
7237         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);
7238         Multi_jw_sw_checkbox.set_bmaps(Multi_jw_sw_checkbox_fname[gr_screen.res], 6, 0);
7239         Multi_jw_sw_checkbox.disable();
7240         if(!MULTI_IS_TRACKER_GAME){
7241                 Multi_jw_sw_checkbox.hide();            
7242         }
7243
7244 #ifndef MAKE_FS1
7245         // create all xstrs
7246         for(idx=0; idx<MULTI_JW_NUM_TEXT; idx++){
7247                 Multi_jw_window.add_XSTR(&Multi_jw_text[gr_screen.res][idx]);
7248         }
7249 #endif
7250         
7251         // create the player select list button and hide it
7252         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);
7253         Multi_jw_plist_select_button.hide();
7254
7255         // set hotkeys
7256         Multi_jw_buttons[gr_screen.res][MJW_CANCEL].button.set_hotkey(SDLK_ESCAPE);
7257
7258         // remove campaign flags
7259         Game_mode &= ~(GM_CAMPAIGN_MODE);
7260
7261         // tell the server we have finished joining
7262         Net_player->state = NETPLAYER_STATE_JOINED;
7263         send_netplayer_update_packet(); 
7264
7265         // NETLOG
7266         ml_printf(NOX("Joined netgame %s"), Netgame.name);
7267
7268         // send any appropriate files
7269         multi_data_send_my_junk();      
7270 }
7271
7272 void multi_game_client_setup_do_frame()
7273 {
7274         int player_index;
7275         int k = chatbox_process();
7276         char mission_text[255];
7277         k = Multi_jw_window.process(k,0);       
7278
7279         Multi_jw_should_show_popup = 0;
7280
7281         // process any button clicks
7282         multi_jw_check_buttons();
7283
7284         // do any network related stuff
7285         multi_jw_do_netstuff();                 
7286
7287         // draw the background, etc
7288         gr_reset_clip();
7289         GR_MAYBE_CLEAR_RES(Multi_jw_bitmap);
7290         if(Multi_jw_bitmap != -1){              
7291                 gr_set_bitmap(Multi_jw_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7292                 gr_bitmap(0,0);
7293         }
7294
7295         // if we're not in team vs. team mode, don't draw the team buttons
7296         if(!(Netgame.type_flags & NG_TYPE_TEAM)){
7297                 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.hide();
7298                 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.hide();
7299                 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.disable();
7300                 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.disable();
7301         } else {
7302                 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.enable();
7303                 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.enable();
7304                 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.unhide();
7305                 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.unhide();             
7306         }
7307
7308         if(MULTI_IS_TRACKER_GAME){
7309                 // maybe check the squadwar button
7310                 if(Netgame.type_flags & NG_TYPE_SW){
7311                         Multi_jw_sw_checkbox.set_state(1);
7312                         gr_set_color_fast(&Color_bright);
7313                 } else {
7314                         Multi_jw_sw_checkbox.set_state(0);
7315                         gr_set_color_fast(&Color_normal);
7316                 }
7317                                 
7318                 gr_string(Multi_jw_sw_checkbox_text[gr_screen.res][0], Multi_jw_sw_checkbox_text[gr_screen.res][1], "SquadWar");
7319         }       
7320
7321         // draw the UI window
7322         Multi_jw_window.draw(); 
7323
7324         // process and display the player list  
7325         // NOTE : this must be done before the buttons are checked to insure that a player hasn't dropped 
7326         multi_jw_plist_process();
7327         if(Netgame.type_flags & NG_TYPE_TEAM){
7328                 multi_jw_plist_blit_team();
7329         } else {
7330                 multi_jw_plist_blit_normal();
7331         }
7332                 
7333         // display any text in the info area
7334         multi_common_render_text();
7335
7336         // display any pending notification messages
7337         multi_common_notify_do();
7338
7339         // blit the mission filename if possible
7340         if(Netgame.campaign_mode){
7341                 if(strlen(Netgame.campaign_name) > 0){                  
7342                         strcpy(mission_text,Netgame.campaign_name);
7343                         
7344                         if(strlen(Netgame.title) > 0){
7345                                 strcat(mission_text,", ");
7346                                 strcat(mission_text,Netgame.title);
7347                         }
7348
7349                         gr_set_color_fast(&Color_bright_white);
7350                         gr_string(Mjw_mission_name_coords[gr_screen.res][MJW_X_COORD],Mjw_mission_name_coords[gr_screen.res][MJW_Y_COORD],mission_text);
7351                 }                                                               
7352         } else {
7353                 if(strlen(Netgame.mission_name) > 0){                   
7354                         strcpy(mission_text,Netgame.mission_name);
7355
7356                         if(strlen(Netgame.title) > 0){
7357                                 strcat(mission_text,", ");
7358                                 strcat(mission_text,Netgame.title);
7359                         }                       
7360
7361                         gr_set_color_fast(&Color_bright_white);
7362                         gr_string(Mjw_mission_name_coords[gr_screen.res][MJW_X_COORD],Mjw_mission_name_coords[gr_screen.res][MJW_Y_COORD],mission_text);
7363                 }
7364         }       
7365
7366         // process and show the chatbox thingie 
7367         chatbox_render();
7368
7369         // draw tooltips
7370         Multi_jw_window.draw_tooltip();
7371
7372         // display the voice status indicator
7373         multi_common_voice_display_status();
7374         
7375         // flip the buffer
7376         gr_flip();      
7377
7378         // if we're supposed to be displaying a pilot info popup
7379         if(Multi_jw_should_show_popup){
7380                 player_index = find_player_id(Multi_jw_plist_select_id);
7381                 if(player_index != -1){                 
7382                         multi_pinfo_popup(&Net_players[player_index]);
7383                 }               
7384         }
7385 }
7386
7387 void multi_game_client_setup_close()
7388 {
7389         // unload any bitmaps
7390         if(!bm_unload(Multi_jw_bitmap)){
7391                 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_jw_bitmap_fname[gr_screen.res]));
7392         }               
7393
7394         // destroy the chatbox
7395         // chatbox_close();
7396         
7397         // destroy the UI_WINDOW
7398         Multi_jw_window.destroy();
7399
7400         // play a sound.
7401         if(Netgame.game_state == NETGAME_STATE_MISSION_SYNC){
7402                 gamesnd_play_iface(SND_COMMIT_PRESSED);
7403         }
7404 }
7405
7406
7407 void multi_jw_check_buttons()
7408 {
7409         int idx;
7410         for(idx=0;idx<MULTI_JW_NUM_BUTTONS;idx++){
7411                 // we only really need to check for one button pressed at a time, so we can break after 
7412                 // finding one.
7413                 if(Multi_jw_buttons[gr_screen.res][idx].button.pressed()){
7414                         multi_jw_button_pressed(idx);
7415                         break;
7416                 }
7417         }
7418 }
7419
7420 void multi_jw_button_pressed(int n)
7421 {
7422         switch(n){      
7423         case MJW_CANCEL:
7424                 gamesnd_play_iface(SND_USER_SELECT);            
7425                 multi_quit_game(PROMPT_CLIENT);         
7426                 break;  
7427         case MJW_SCROLL_PLAYERS_UP:
7428                 multi_jw_scroll_players_up();
7429                 break;
7430         case MJW_SCROLL_PLAYERS_DOWN:
7431                 multi_jw_scroll_players_down();
7432                 break;  
7433         case MJW_SCROLL_INFO_UP:
7434                 multi_common_scroll_text_up();
7435                 break;
7436         case MJW_SCROLL_INFO_DOWN:
7437                 multi_common_scroll_text_down();
7438                 break;
7439         
7440         // request to set myself to team 0
7441         case MJW_TEAM0:
7442                 gamesnd_play_iface(SND_USER_SELECT);
7443                 multi_team_set_team(Net_player,0);
7444                 break;
7445
7446         // request to set myself to team 1
7447         case MJW_TEAM1:
7448                 gamesnd_play_iface(SND_USER_SELECT);
7449                 multi_team_set_team(Net_player,1);
7450                 break;
7451
7452         // pilot info popup
7453         case MJW_PILOT_INFO:
7454                 Multi_jw_should_show_popup = 1;
7455                 break;
7456
7457         default :
7458                 multi_common_add_notify(XSTR("Not implemented yet!",760));
7459                 break;
7460         }
7461 }
7462
7463 // do stuff like pinging servers, sending out requests, etc
7464 void multi_jw_do_netstuff()
7465 {
7466 }
7467
7468 void multi_jw_scroll_players_up()
7469 {
7470         gamesnd_play_iface(SND_GENERAL_FAIL);
7471 }
7472
7473 // scroll down through the player list
7474 void multi_jw_scroll_players_down()
7475 {       
7476         gamesnd_play_iface(SND_GENERAL_FAIL);
7477 }
7478
7479 void multi_jw_plist_process()
7480 {
7481         int test_count,player_index,idx;
7482         
7483         // first determine if there are 0 players in the game. This should never happen since the host is _always_ in the game
7484         test_count = 0;
7485         for(idx=0;idx<MAX_PLAYERS;idx++){
7486                 // count anyone except the standalone server (if applicable)
7487                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7488                         test_count++;
7489                 }
7490         }
7491         if(test_count <= 0){
7492                 return;
7493         }
7494         
7495         // if we had a selected item but that player has left, select myself instead
7496         if(Multi_jw_plist_select_flag){
7497                 player_index = find_player_id(Multi_jw_plist_select_id);
7498                 if(player_index == -1){
7499                         Multi_jw_plist_select_id = Net_player->player_id;                                               
7500                 }
7501         } else {
7502                 Multi_jw_plist_select_flag = 1;
7503                 Multi_jw_plist_select_id = Net_player->player_id;               
7504         }
7505                 
7506         // if the player has clicked somewhere in the player list area
7507         if(Multi_jw_plist_select_button.pressed()){
7508                 short player_id;
7509                 int player_index;
7510         
7511                 player_id = multi_jw_get_mouse_id();
7512                 player_index = find_player_id(player_id);
7513                 if(player_index != -1){
7514                         Multi_jw_plist_select_id = player_id;
7515                         Multi_jw_plist_select_flag = 1;
7516                 }
7517         }
7518 }
7519
7520 void multi_jw_plist_blit_normal()
7521 {
7522         int idx;                
7523         char str[CALLSIGN_LEN+1];
7524         int y_start = Mjw_players_coords[gr_screen.res][MJW_Y_COORD];   
7525         int total_offset;
7526
7527         // display all the players      
7528         for(idx=0;idx<MAX_PLAYERS;idx++){               
7529                 // reset total offset
7530                 total_offset = 0;
7531
7532                 // count anyone except the standalone server (if applicable)
7533                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7534                         // highlight him if he's the host                       
7535                         if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7536                                 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7537                                         gr_set_color_fast(&Color_text_active_hi);
7538                                 } else {
7539                                         gr_set_color_fast(&Color_bright);
7540                                 }
7541                         } else {
7542                                 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7543                                         gr_set_color_fast(&Color_text_active);
7544                                 } else {
7545                                         gr_set_color_fast(&Color_text_normal);
7546                                 }
7547                         }
7548
7549                         // optionally draw his CD status
7550                         if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7551                                 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7552                                 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7553
7554                                 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7555                         }                       
7556                         
7557                         // make sure the string will fit, then display it
7558                         strcpy(str,Net_players[idx].player->callsign);
7559                         if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
7560                                 strcat(str,"(0)");
7561                         }
7562                         gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7563                         gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7564
7565                         y_start += 10;                  
7566                 }
7567         }               
7568 }
7569
7570 void multi_jw_plist_blit_team()
7571 {
7572         int idx;                
7573         char str[CALLSIGN_LEN+1];
7574         int y_start = Mjw_players_coords[gr_screen.res][MJW_Y_COORD];   
7575         int total_offset;
7576
7577         // always blit the proper team button based on _my_ team status
7578         if(Net_player->p_info.team == 0){
7579                 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.draw_forced(2);
7580         } else {
7581                 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.draw_forced(2);
7582         }
7583
7584         // display all the red players first
7585         for(idx=0;idx<MAX_PLAYERS;idx++){
7586                 // reset total offset
7587                 total_offset = 0;
7588
7589                 // count anyone except the standalone server (if applicable)
7590                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
7591                         // highlight him if he's the host                       
7592                         if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7593                                 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7594                                         gr_set_color_fast(&Color_text_active_hi);                                       
7595                                 } else {
7596                                         gr_set_color_fast(&Color_bright);
7597                                 }
7598                         } else {
7599                                 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7600                                         gr_set_color_fast(&Color_text_active);                                  
7601                                 } else {
7602                                         gr_set_color_fast(&Color_text_normal);
7603                                 }
7604                         }
7605
7606                         // optionally draw his CD status
7607                         if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7608                                 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7609                                 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7610
7611                                 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7612                         }                       
7613
7614                         // blit the red team indicator
7615                         if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
7616                                 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
7617                                         gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7618                                         gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7619
7620                                         total_offset += Multi_common_icon_dims[MICON_TEAM0_SELECT][0] + 1;
7621                                 }                               
7622                         } else {
7623                                 if(Multi_common_icons[MICON_TEAM0] != -1){
7624                                         gr_set_bitmap(Multi_common_icons[MICON_TEAM0], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7625                                         gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7626
7627                                         total_offset += Multi_common_icon_dims[MICON_TEAM0][0] + 1;
7628                                 }
7629                         }
7630
7631                         // make sure the string will fit
7632                         strcpy(str,Net_players[idx].player->callsign);
7633                         gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7634
7635                         // display him in the correct half of the list depending on his team
7636                         gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7637                         y_start += 10;
7638                 }
7639         }       
7640         
7641         // display all the green players next
7642         for(idx=0;idx<MAX_PLAYERS;idx++){
7643                 // reset total offset
7644                 total_offset = 0;
7645
7646                 // count anyone except the standalone server (if applicable)
7647                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
7648                         // highlight him if he's the host                       
7649                         if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7650                                 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7651                                         gr_set_color_fast(&Color_text_active_hi);                               
7652                                 } else {
7653                                         gr_set_color_fast(&Color_bright);
7654                                 }
7655                         } else {
7656                                 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7657                                         gr_set_color_fast(&Color_text_active);                                  
7658                                 } else {
7659                                         gr_set_color_fast(&Color_text_normal);
7660                                 }
7661                         }
7662
7663                         // optionally draw his CD status
7664                         if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7665                                 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7666                                 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7667
7668                                 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7669                         }                       
7670
7671                         // blit the red team indicator
7672                         if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
7673                                 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
7674                                         gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7675                                         gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7676
7677                                         total_offset += Multi_common_icon_dims[MICON_TEAM1_SELECT][0] + 1;
7678                                 }
7679                         } else {
7680                                 if(Multi_common_icons[MICON_TEAM1] != -1){
7681                                         gr_set_bitmap(Multi_common_icons[MICON_TEAM1], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7682                                         gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7683
7684                                         total_offset += Multi_common_icon_dims[MICON_TEAM1][0] + 1;
7685                                 }
7686                         }
7687
7688                         // make sure the string will fit
7689                         strcpy(str,Net_players[idx].player->callsign);
7690                         if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
7691                                 strcat(str,"(0)");
7692                         }
7693                         gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7694
7695                         // display him in the correct half of the list depending on his team
7696                         gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7697                         y_start += 10;
7698                 }
7699         }                       
7700 }
7701
7702 void multi_jw_handle_join(net_player *pl)
7703 {
7704         // for now just play a bloop sound
7705         gamesnd_play_iface(SND_ICON_DROP_ON_WING);
7706 }
7707
7708 short multi_jw_get_mouse_id()
7709 {
7710         // determine where he clicked (y pixel value)
7711         int y,nth,idx;          
7712         Multi_jw_plist_select_button.get_mouse_pos(NULL,&y);
7713
7714         // select things a little differently if we're in team vs. team or non-team vs. team mode                       
7715         nth = (y / 10);                 
7716         if(Netgame.type_flags & NG_TYPE_TEAM){
7717                 int player_index = -1;
7718
7719                 // look through all of team red first
7720                 for(idx=0;idx<MAX_PLAYERS;idx++){
7721                         if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){                            
7722                                 nth--;
7723
7724                                 // if this is the _nth_ guy 
7725                                 if(nth < 0){
7726                                         player_index = idx;                                             
7727                                         break;
7728                                 }
7729                         }
7730                 }
7731                         
7732                 // if we still haven't found him yet, look through the green team
7733                 if(player_index == -1){
7734                         for(idx=0;idx<MAX_PLAYERS;idx++){
7735                                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){                                    
7736                                         nth--;
7737                                         // if this is the _nth_ guy 
7738                                         if(nth < 0){
7739                                                 player_index = idx;                                             
7740                                                 break;
7741                                         }
7742                                 }
7743                         }
7744                 }
7745                 if(player_index != -1){
7746                         return Net_players[idx].player_id;                      
7747                 }               
7748         } else {
7749                 // select the nth active player if possible, disregarding the standalone server
7750                 for(idx=0;idx<MAX_PLAYERS;idx++){
7751                         if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7752                                 nth--;
7753
7754                                 // if this is the _nth_ guy 
7755                                 if(nth < 0){
7756                                         return Net_players[idx].player_id;                                      
7757                                 }
7758                         }
7759                 }
7760                 return -1;
7761         }                               
7762
7763         return -1;
7764 }
7765
7766
7767 // -------------------------------------------------------------------------------------------------------------
7768 // 
7769 // MULTIPLAYER WAIT/SYNCH SCREEN 
7770 //
7771
7772 #define MULTI_SYNC_HOST_COUNT                           4               // host uses 4 buttons (and sometimes 5)
7773 #define MULTI_SYNC_CLIENT_COUNT                 3               // client only uses 3 buttons
7774
7775 const char *Multi_sync_bitmap_fname[GR_NUM_RESOLUTIONS] = {
7776         "MultiSynch",           // GR_640
7777         "2_MultiSynch"          // GR_1024
7778 };
7779
7780 const char *Multi_sync_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
7781         "MultiSynch-M",         // GR_640
7782         "2_MultiSynch-M"                        // GR_1024
7783 };
7784
7785 //XSTR:ON
7786
7787 // constants for coordinate lookup
7788 #define MS_X_COORD 0
7789 #define MS_Y_COORD 1
7790 #define MS_W_COORD 2
7791 #define MS_H_COORD 3
7792
7793 UI_WINDOW Multi_sync_window;                                                                                            // the window object for the join screen
7794 int Multi_sync_button_count;                                                                                            // the # of buttons to use for this instance of mission sync
7795 int Multi_sync_bitmap;                                                                                                          // the background bitmap
7796
7797 // button defs
7798 #define MULTI_SYNC_NUM_BUTTONS                  5
7799 #define MS_SCROLL_INFO_UP                                       0
7800 #define MS_SCROLL_INFO_DOWN                             1
7801 #define MS_CANCEL                                                               2
7802 #define MS_KICK                                                         3
7803 #define MS_LAUNCH                                                               4
7804 ui_button_info Multi_sync_buttons[GR_NUM_RESOLUTIONS][MULTI_SYNC_NUM_BUTTONS] = {
7805         { // GR_640
7806 #ifdef MAKE_FS1
7807                 ui_button_info("MS_00",         0,              396,    -1,     -1,     0),
7808                 ui_button_info("MS_01",         0,              435,    -1,     -1,     1),
7809                 ui_button_info("MS_02",         496,    411,    -1,     -1,     2),
7810                 ui_button_info("MS_04",         436,    403,    -1,     -1,     4),
7811                 ui_button_info("MS_03",         556,    398,    -1,     -1,     3),
7812 #else
7813                 ui_button_info("MS_00",         1,              404,    -1,     -1,     0),
7814                 ui_button_info("MS_01",         1,              446,    -1,     -1,     1),
7815                 ui_button_info("MS_03",         518,    426,    519,    416,    3),
7816                 ui_button_info("MS_02",         469,    426,    479,    416,    2),             
7817                 ui_button_info("MS_04",         571,    420,    577,    416,    4),
7818 #endif
7819         },
7820         { // GR_1024
7821                 ui_button_info("2_MS_00",               2,              647,    -1,     -1,     0),
7822                 ui_button_info("2_MS_01",               2,              713,    -1,     -1,     1),
7823                 ui_button_info("2_MS_03",               829,    682,    831,    667,    3),
7824                 ui_button_info("2_MS_02",               751,    682,    766,    667,    2),             
7825                 ui_button_info("2_MS_04",               914,    672,    924,    667,    4),
7826         }
7827 };
7828
7829 // text
7830 #ifndef MAKE_FS1
7831 #define MULTI_SYNC_NUM_TEXT                             5
7832 #define MST_KICK                                                                0
7833 #define MST_LAUNCH                                                      2
7834 UI_XSTR Multi_sync_text[GR_NUM_RESOLUTIONS][MULTI_SYNC_NUM_TEXT] = {
7835         { // GR_640
7836                 { "Kick",               1266,           479,    416,    UI_XSTR_COLOR_PINK,     -1, &Multi_sync_buttons[0][MS_KICK].button },
7837                 { "Cancel",             387,            519,    416,    UI_XSTR_COLOR_PINK,     -1, &Multi_sync_buttons[0][MS_CANCEL].button },
7838                 { "Launch",             801,            577,    416,    UI_XSTR_COLOR_PINK,     -1, &Multi_sync_buttons[0][MS_LAUNCH].button },
7839                 { "Players",    1269,           23,     133,    UI_XSTR_COLOR_GREEN,    -1, NULL },
7840                 { "Status",             1304,           228,    133,    UI_XSTR_COLOR_GREEN,    -1, NULL }
7841         },
7842         { // GR_1024
7843                 { "Kick",               1266,           766,    667,    UI_XSTR_COLOR_PINK,     -1, &Multi_sync_buttons[1][MS_KICK].button },
7844                 { "Cancel",             387,            831,    667,    UI_XSTR_COLOR_PINK,     -1, &Multi_sync_buttons[1][MS_CANCEL].button },
7845                 { "Launch",             801,            924,    667,    UI_XSTR_COLOR_PINK,     -1, &Multi_sync_buttons[1][MS_LAUNCH].button },
7846                 { "Players",    1269,           38,     214,    UI_XSTR_COLOR_GREEN,    -1, NULL },
7847                 { "Status",             1304,           366,    214,    UI_XSTR_COLOR_GREEN,    -1, NULL }
7848         }
7849 };
7850 #endif
7851
7852 // player name
7853 int Ms_status_coords[GR_NUM_RESOLUTIONS][4] = {
7854         { // GR_640
7855                 38, 150, 581, 220
7856         },
7857         { // GR_1024
7858                 38, 228, 958, 367
7859         }
7860 };
7861
7862 // player status coords
7863 int Ms_status2_coords[GR_NUM_RESOLUTIONS][4] = {
7864         { // GR_640
7865                 228, 150, 391, 220
7866         },
7867         { // GR_1024
7868                 370, 228, 626, 367
7869         }
7870 };
7871
7872 int Ms_cd_icon_offset[GR_NUM_RESOLUTIONS] = {
7873         10,             // GR_640
7874         10                      // GR_1024
7875 };
7876
7877 int Ms_team_icon_offset[GR_NUM_RESOLUTIONS] = {
7878         38,             // GR_640
7879         38                      // GR_1024
7880 };
7881
7882 // player currently selected, index into Net_players[]
7883 int Multi_sync_player_select = -1;
7884
7885 // player list control thingie defs
7886 #define MULTI_SYNC_PLIST_MAX_DISPLAY    15
7887 int Multi_sync_plist_start;             // where to start displaying from
7888 int Multi_sync_plist_count;             // how many we have
7889
7890 // list select button
7891 UI_BUTTON Multi_sync_plist_button;
7892
7893 int Multi_sync_mode = -1;
7894
7895 #define MULTI_SYNC_COUNTDOWN_TIME                       5                               // in seconds
7896 float Multi_sync_countdown_timer;
7897 int Multi_sync_countdown = -1;
7898
7899 int Multi_launch_button_created;
7900
7901 //XSTR:OFF
7902 // countdown animation timer
7903 const char* Multi_sync_countdown_fname[GR_NUM_RESOLUTIONS] = {
7904         "Count",                // GR_640
7905         "2_Count"               // GR_1024
7906 };
7907
7908 int Multi_sync_countdown_coords[GR_NUM_RESOLUTIONS][2] = {
7909         {
7910                 0, 0            // GR_640
7911         },              
7912         {
7913                 0, 0            // GR_1024
7914         }
7915 };
7916
7917 //XSTR:ON
7918
7919 anim *Multi_sync_countdown_anim = NULL;
7920 anim_instance *Multi_sync_countdown_instance = NULL;
7921
7922
7923 // PREBRIEFING STUFF
7924 // syncing flags used by the server
7925 int Mission_sync_flags = 0;
7926 #define MS_FLAG_SENT_FILESIG                    (1<<0)  // sent filesig requests
7927 #define MS_FLAG_SENT_LOAD                               (1<<1)  // sent load packets
7928 #define MS_FLAG_PUSHED_BRIEFING         (1<<2)  // pushed everyone else into the briefing
7929 #define MS_FLAG_POST_DATA                               (1<<3)  // sent the post data block
7930 #define MS_FLAG_WSS_SLOTS                               (1<<4)  // all players have received wss slot data
7931 #define MS_FLAG_PSETTINGS                               (1<<5)  // send the player settings packet
7932 #define MS_FLAG_MT_STATS_START          (1<<6)  // server has started getting player stats from the tracker
7933 #define MS_FLAG_MT_STATS_DONE                   (1<<7)  // server has finished getting player stats from the tracker (success or fail)
7934 #define MS_FLAG_TS_SLOTS                                (1<<8)  // team/ship slots have been sent
7935 #define MS_FLAG_DATA_DONE                               (1<<9)  // done transferring all necessary data
7936 #define MS_FLAG_CAMP_DONE                               (1<<10) // send campaign pool/goal/event stuff
7937
7938 // POSTBRIEFING STUFF
7939 int Multi_state_timestamp;
7940 int Multi_sync_launch_pressed;
7941
7942 // LOCAL function definitions
7943 void multi_sync_check_buttons();
7944 void multi_sync_button_pressed(int n);
7945 void multi_sync_scroll_info_up();
7946 void multi_sync_scroll_info_down();
7947 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
7948 void multi_sync_display_status(const char *status, int index);                                  // display info on the right hand portion of the status window thingie
7949 void multi_sync_force_start_pre();
7950 void multi_sync_force_start_post();
7951 void multi_sync_launch();
7952 void multi_sync_create_launch_button();
7953 void multi_sync_blit_screen_all();
7954 void multi_sync_handle_plist();
7955
7956 void multi_sync_common_init();
7957 void multi_sync_common_do();
7958 void multi_sync_common_close();
7959
7960 void multi_sync_pre_init();
7961 void multi_sync_pre_do();
7962 void multi_sync_pre_close();
7963
7964 void multi_sync_post_init();
7965 void multi_sync_post_do();
7966 void multi_sync_post_close();
7967
7968 int Sync_test = 1;
7969
7970
7971 // perform the correct init functions
7972 void multi_sync_init()
7973 {       
7974         Multi_sync_countdown = -1;
7975
7976         Sync_test = 1;
7977
7978         // reset all timestamp
7979         multi_reset_timestamps();
7980
7981         extern int Player_multi_died_check;
7982         Player_multi_died_check = -1;
7983
7984         if(!(Game_mode & GM_STANDALONE_SERVER)){
7985                 multi_sync_common_init();
7986         }
7987         
7988         switch(Multi_sync_mode){
7989         case MULTI_SYNC_PRE_BRIEFING:
7990                 multi_sync_pre_init();
7991                 break;
7992         case MULTI_SYNC_POST_BRIEFING:
7993                 multi_sync_post_init();
7994                 break;
7995         case MULTI_SYNC_INGAME:
7996                 multi_ingame_sync_init();
7997                 break;
7998         }
7999 }
8000
8001 // perform the correct do frame functions
8002 void multi_sync_do()
8003 {
8004         if(!(Game_mode & GM_STANDALONE_SERVER)){
8005                 multi_sync_common_do();
8006         }
8007
8008         // if the netgame is ending, don't do any sync processing
8009         if(multi_endgame_ending()){
8010                 return;
8011         }
8012
8013         // process appropriateliy
8014         switch(Multi_sync_mode){
8015         case MULTI_SYNC_PRE_BRIEFING:           
8016                 multi_sync_pre_do();            
8017                 break;
8018         case MULTI_SYNC_POST_BRIEFING:
8019                 multi_sync_post_do();
8020                 break;
8021         case MULTI_SYNC_INGAME:
8022                 multi_ingame_sync_do();
8023
8024                 gr_reset_clip();                
8025                 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8026                 if(Multi_sync_bitmap != -1){
8027                         gr_set_bitmap(Multi_sync_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8028                         gr_bitmap(0,0);
8029                 }
8030                 Multi_sync_window.draw();
8031
8032                 multi_sync_blit_screen_all();
8033
8034                 gr_flip();
8035                 break;
8036         }       
8037 }
8038
8039 // perform the correct close functions
8040 void multi_sync_close()
8041 {
8042         switch(Multi_sync_mode){
8043         case MULTI_SYNC_PRE_BRIEFING:
8044                 multi_sync_pre_close();
8045                 break;
8046         case MULTI_SYNC_POST_BRIEFING:
8047                 multi_sync_post_close();
8048                 break;
8049         case MULTI_SYNC_INGAME:
8050                 multi_ingame_sync_close();
8051                 break;
8052         }
8053         
8054         if(!(Game_mode & GM_STANDALONE_SERVER)){
8055                 multi_sync_common_close();
8056         }
8057 }
8058
8059 const char *multi_sync_tooltip_handler(const char *str)
8060 {
8061         if (!SDL_strcasecmp(str, NOX("@launch"))) {
8062                 if (Multi_launch_button_created){
8063                         return XSTR("Launch",801);
8064                 }
8065         }
8066
8067         return NULL;
8068 }
8069
8070 void multi_sync_common_init()
8071 {
8072         int idx;
8073
8074         // create the interface window
8075         Multi_sync_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
8076         Multi_sync_window.set_mask_bmap(Multi_sync_bitmap_mask_fname[gr_screen.res]);
8077         Multi_sync_window.tooltip_handler = multi_sync_tooltip_handler;
8078
8079         // load the background bitmap
8080         Multi_sync_bitmap = bm_load(Multi_sync_bitmap_fname[gr_screen.res]);
8081         if (Multi_sync_bitmap < 0) {
8082                 // we failed to load the bitmap - this is very bad
8083                 Int3();
8084         }
8085
8086         // initialize the player list data
8087         Multi_sync_plist_start = 0;
8088         Multi_sync_plist_count = 1;                     // we can pretty safely assume that there's one player in the game - me.        
8089
8090         Multi_launch_button_created = 0;        
8091
8092         // create the chatbox thingie   (shouldn't be necesary to do this, but we'll put it in for good measure)
8093         chatbox_create();
8094
8095         // force the chatbox to be small
8096         chatbox_force_small();
8097
8098         // initialize the common notification messaging
8099         multi_common_notify_init();
8100
8101         // initialize the common mission info display area.
8102         multi_common_set_text("");      
8103
8104         // use the common interface palette
8105         multi_common_set_palette();
8106
8107         // don't select any player yet.
8108         Multi_sync_player_select = -1;
8109         
8110         // determine how many of the 5 buttons to create
8111         if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8112                 Multi_sync_button_count = MULTI_SYNC_HOST_COUNT;                
8113         } else {
8114                 Multi_sync_button_count = MULTI_SYNC_CLIENT_COUNT;
8115         }
8116         // create the interface buttons 
8117         for(idx=0; idx<Multi_sync_button_count; idx++){
8118                 // create the object
8119                 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);
8120
8121                 // set the sound to play when highlighted
8122                 Multi_sync_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
8123
8124                 // set the ani for the button
8125                 // this wierdness is necessary because cancel and kick buttons aren't drawn on the background bitmap,
8126                 //   so we have to load in frame 0, too (the file should exist)
8127                 if ((idx == MS_CANCEL) || (idx == MS_KICK) || (idx == MS_LAUNCH)) {
8128                         Multi_sync_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sync_buttons[gr_screen.res][idx].filename, 3, 0);
8129                 } else {
8130                         Multi_sync_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sync_buttons[gr_screen.res][idx].filename);
8131                 }
8132
8133                 // set the hotspot
8134                 Multi_sync_buttons[gr_screen.res][idx].button.link_hotspot(Multi_sync_buttons[gr_screen.res][idx].hotspot);
8135         }               
8136
8137 #ifndef MAKE_FS1
8138         // add xstrs
8139         for(idx=0; idx<MULTI_SYNC_NUM_TEXT; idx++) {
8140                 // don't create the "launch" button text just yet
8141                 if(idx == MST_LAUNCH) {
8142                         continue;
8143                 }
8144                 // multiplayer clients should ignore the kick button
8145                 if(!MULTIPLAYER_MASTER && !MULTIPLAYER_HOST && (idx == MST_KICK)) {
8146                         continue;
8147                 }
8148
8149                 Multi_sync_window.add_XSTR(&Multi_sync_text[gr_screen.res][idx]);
8150         }
8151 #endif
8152
8153         // create the player list select button and hide it
8154         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);
8155         Multi_sync_plist_button.hide();
8156
8157         // set up hotkeys for certain common functions
8158         Multi_sync_buttons[gr_screen.res][MS_CANCEL].button.set_hotkey(SDLK_ESCAPE);
8159 }
8160
8161 void multi_sync_common_do()
8162 {       
8163         int k = chatbox_process();
8164         k = Multi_sync_window.process(k);       
8165
8166         // process the player list
8167         multi_sync_handle_plist();
8168
8169         // process any button clicks
8170         multi_sync_check_buttons();     
8171
8172         // process any keypresses
8173         switch(k){
8174         case SDLK_ESCAPE :
8175                 // Sync_test = 1;
8176                 gamesnd_play_iface(SND_USER_SELECT);
8177                 multi_quit_game(PROMPT_ALL);            
8178                 break;  
8179         }                               
8180 }
8181
8182 void multi_sync_common_close()
8183 {
8184         // unload any bitmaps
8185         if(!bm_unload(Multi_sync_bitmap)){
8186                 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_sync_bitmap_fname[gr_screen.res]));
8187         }       
8188
8189         extern int Player_multi_died_check;
8190         Player_multi_died_check = -1;
8191         
8192         // destroy the UI_WINDOW
8193         Multi_sync_window.destroy();
8194 }
8195
8196 void multi_sync_blit_screen_all()
8197 {
8198         int count,idx;  
8199         int state;
8200         float pct_complete;
8201         char txt[255];
8202         
8203         // display any text in the info area
8204         multi_common_render_text();
8205
8206         // display any pending notification messages
8207         multi_common_notify_do();
8208         
8209         // display any info about visible players
8210         count = 0;      
8211         for(idx=0;idx<MAX_PLAYERS;idx++){
8212                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
8213                         // display his name and status
8214                         multi_sync_display_name(Net_players[idx].player->callsign,count,idx);
8215         
8216                         // get the player state
8217                         state = Net_players[idx].state;
8218
8219                         // if we're ingame joining, show all other players except myself as "playing"
8220                         if((Net_player != NULL) && (&Net_players[idx] != Net_player) && ((Multi_sync_mode == MULTI_SYNC_INGAME) || (Net_player->flags & NETINFO_FLAG_INGAME_JOIN)) ){
8221                                 state = NETPLAYER_STATE_IN_MISSION;
8222                         }
8223
8224                         switch(state){                          
8225                         case NETPLAYER_STATE_MISSION_LOADING:
8226                                 multi_sync_display_status(XSTR("Mission Loading",802),count);
8227                                 break;
8228                         case NETPLAYER_STATE_INGAME_SHIP_SELECT:                                                                        // I don't think its possible to see this state, but...
8229                                 multi_sync_display_status(XSTR("Ingame Ship Select",803),count);
8230                                 break;
8231                         case NETPLAYER_STATE_DEBRIEF:
8232                                 multi_sync_display_status(XSTR("Debriefing",804),count);
8233                                 break;
8234                         case NETPLAYER_STATE_MISSION_SYNC:
8235                                 multi_sync_display_status(XSTR("Mission Sync",805),count);
8236                                 break;
8237                         case NETPLAYER_STATE_JOINING:                                                           
8238                                 multi_sync_display_status(XSTR("Joining",806),count);
8239                                 break;
8240                         case NETPLAYER_STATE_JOINED:                            
8241                                 multi_sync_display_status(XSTR("Joined",807),count);
8242                                 break;
8243                         case NETPLAYER_STATE_SLOT_ACK :                         
8244                                 multi_sync_display_status(XSTR("Slot Ack",808),count);
8245                                 break;                  
8246                         case NETPLAYER_STATE_BRIEFING:                          
8247                                 multi_sync_display_status(XSTR("Briefing",765),count);
8248                                 break;
8249                         case NETPLAYER_STATE_SHIP_SELECT:                               
8250                                 multi_sync_display_status(XSTR("Ship Select",809),count);
8251                                 break;
8252                         case NETPLAYER_STATE_WEAPON_SELECT:                             
8253                                 multi_sync_display_status(XSTR("Weapon Select",810),count);
8254                                 break;
8255                         case NETPLAYER_STATE_WAITING:                           
8256                                 multi_sync_display_status(XSTR("Waiting",811),count);
8257                                 break;
8258                         case NETPLAYER_STATE_IN_MISSION:                                
8259                                 multi_sync_display_status(XSTR("In Mission",812),count);
8260                                 break;                  
8261                         case NETPLAYER_STATE_MISSION_LOADED:                    
8262                                 multi_sync_display_status(XSTR("Mission Loaded",813),count);
8263                                 break;                  
8264                         case NETPLAYER_STATE_DATA_LOAD:
8265                                 multi_sync_display_status(XSTR("Data loading",814),count);
8266                                 break;
8267                         case NETPLAYER_STATE_SETTINGS_ACK:
8268                                 multi_sync_display_status(XSTR("Ready To Enter Mission",815),count);
8269                                 break;                                  
8270                         case NETPLAYER_STATE_INGAME_SHIPS:
8271                                 multi_sync_display_status(XSTR("Ingame Ships Packet Ack",816),count);
8272                                 break;
8273                         case NETPLAYER_STATE_INGAME_WINGS:
8274                                 multi_sync_display_status(XSTR("Ingame Wings Packet Ack",817),count);
8275                                 break;          
8276                         case NETPLAYER_STATE_INGAME_RPTS:
8277                                 multi_sync_display_status(XSTR("Ingame Respawn Points Ack",818),count);
8278                                 break;
8279                         case NETPLAYER_STATE_SLOTS_ACK:
8280                                 multi_sync_display_status(XSTR("Ingame Weapon Slots Ack",819),count);
8281                                 break;                  
8282                         case NETPLAYER_STATE_POST_DATA_ACK:
8283                                 multi_sync_display_status(XSTR("Post Briefing Data Block Ack",820),count);
8284                                 break;
8285                         case NETPLAYER_STATE_FLAG_ACK :
8286                                 multi_sync_display_status(XSTR("Flags Ack",821),count);
8287                                 break;
8288                         case NETPLAYER_STATE_MT_STATS :
8289                                 multi_sync_display_status(XSTR("Parallax Online Stats Updating",822),count);
8290                                 break;
8291                         case NETPLAYER_STATE_WSS_ACK :
8292                                 multi_sync_display_status(XSTR("Weapon Slots Ack",823),count);
8293                                 break;
8294                         case NETPLAYER_STATE_HOST_SETUP :
8295                                 multi_sync_display_status(XSTR("Host setup",824),count);
8296                                 break;
8297                         case NETPLAYER_STATE_DEBRIEF_ACCEPT:
8298                                 multi_sync_display_status(XSTR("Debrief accept",825),count);
8299                                 break;
8300                         case NETPLAYER_STATE_DEBRIEF_REPLAY:
8301                                 multi_sync_display_status(XSTR("Debrief replay",826),count);
8302                                 break;
8303                         case NETPLAYER_STATE_CPOOL_ACK:
8304                                 multi_sync_display_status(XSTR("Campaign ship/weapon ack",827),count);
8305                                 break;                          
8306                         case NETPLAYER_STATE_MISSION_XFER :                             
8307                                 memset(txt,0,255);
8308                                 // server should display the pct completion of all clients                              
8309                                 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8310                                         if(Net_players[idx].s_info.xfer_handle != -1){                                  
8311                                                 pct_complete = multi_xfer_pct_complete(Net_players[idx].s_info.xfer_handle);
8312
8313                                                 // if we've got a valid xfer handle
8314                                                 if((pct_complete >= 0.0) && (pct_complete <= 1.0)){                                             
8315                                                         sprintf(txt,XSTR("Mission file xfer %d%%",828),(int)(pct_complete * 100.0f));
8316                                                 }
8317                                                 // otherwise
8318                                                 else {
8319                                                         strcpy(txt,XSTR("Mission file xfer",829));
8320                                                 }                                       
8321                                         } else {
8322                                                 strcpy(txt,XSTR("Mission file xfer",829));
8323                                         }
8324                                 }
8325                                 // clients should display only for themselves (which is the only thing they know)
8326                                 else {
8327                                         // if we've got a valid file xfer handle
8328                                         if((&Net_players[idx] == Net_player) && (Net_player->s_info.xfer_handle != -1)){
8329                                                 pct_complete = multi_xfer_pct_complete(Net_player->s_info.xfer_handle);
8330
8331                                                 // if we've got a valid xfer handle
8332                                                 if((pct_complete >= 0.0) && (pct_complete <= 1.0)){                                             
8333                                                         sprintf(txt,XSTR("Mission file xfer %d%%",828),(int)(pct_complete * 100.0f));
8334                                                 }
8335                                                 // otherwise
8336                                                 else {
8337                                                         strcpy(txt,XSTR("Mission file xfer",829));
8338                                                 }
8339                                         }
8340                                         // otherwise
8341                                         else {
8342                                                 strcpy(txt,XSTR("Mission file xfer",829));
8343                                         }
8344                                 }
8345
8346                                 // display the text
8347                                 multi_sync_display_status(txt,count);
8348                                 break;
8349                         default :
8350                                 nprintf(("Network","Unhandled player state : %d !\n",Net_players[idx].state));
8351                                 break;
8352                         }
8353                         count++;
8354                 }
8355         }       
8356
8357         // display the mission start countdown timer (if any)
8358         anim_render_all(GS_STATE_MULTI_MISSION_SYNC,flFrametime);       
8359
8360         // process and show the chatbox thingie 
8361         chatbox_render();
8362
8363         // draw tooltips
8364         Multi_sync_window.draw_tooltip();
8365
8366         // display the voice status indicator
8367         multi_common_voice_display_status();
8368 }
8369
8370 void multi_sync_check_buttons()
8371 {
8372         int idx;
8373         for(idx=0;idx<Multi_sync_button_count;idx++){
8374                 // we only really need to check for one button pressed at a time, so we can break after 
8375                 // finding one.
8376                 if(Multi_sync_buttons[gr_screen.res][idx].button.pressed()){
8377                         multi_sync_button_pressed(idx);
8378                         break;
8379                 }
8380         }
8381 }
8382
8383 void multi_sync_button_pressed(int n)
8384 {
8385         switch(n){      
8386         // exit the game
8387         case MS_CANCEL:
8388                 gamesnd_play_iface(SND_USER_SELECT);            
8389                 multi_quit_game(PROMPT_ALL);            
8390                 break;  
8391         
8392         // scroll the info box up
8393         case MS_SCROLL_INFO_UP:
8394                 multi_common_scroll_text_up();          
8395                 break;
8396         
8397         // scroll the info box down
8398         case MS_SCROLL_INFO_DOWN:
8399                 multi_common_scroll_text_down();
8400                 break;  
8401
8402         // KICK (host only)
8403         case MS_KICK:
8404                 // if we have a currently selected player, kick him
8405                 if(Multi_sync_player_select >= 0){
8406                         multi_kick_player(Multi_sync_player_select);
8407                 }
8408                 break;
8409         
8410         // start the final launch countdown (post-sync only)
8411         case MS_LAUNCH:
8412                 multi_sync_start_countdown();
8413                 break;
8414         
8415         // doesn't do anything
8416         default :
8417                 Int3();         
8418         }
8419 }
8420
8421 void multi_sync_pre_init()
8422 {
8423         int idx;
8424
8425         Netgame.game_state = NETGAME_STATE_MISSION_SYNC;
8426
8427         // if we're in teamplay mode, always force skill level to be medium
8428         if((Netgame.type_flags & NG_TYPE_TEAM) && (Net_player->flags & NETINFO_FLAG_GAME_HOST)){
8429                 Netgame.options.skill_level = NUM_SKILL_LEVELS / 2;
8430                 Game_skill_level = NUM_SKILL_LEVELS / 2;
8431                 multi_options_update_netgame();
8432         }
8433
8434         // notify everyone of when we get here
8435         if(!(Game_mode & GM_STANDALONE_SERVER)){
8436                 Net_player->state = NETPLAYER_STATE_MISSION_SYNC;
8437                 send_netplayer_update_packet();
8438         }
8439         
8440         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){ 
8441                 // NETLOG
8442                 ml_string(NOX("Server performing pre-briefing data sync"));
8443
8444                 if(!(Game_mode & GM_STANDALONE_SERVER)){
8445                         multi_common_set_text(XSTR("Server performing sync\n",830),1);
8446                 }
8447                 
8448                 // maybe initialize tvt and squad war stuff
8449                 if(Netgame.type_flags & NG_TYPE_TEAM){
8450                         multi_team_level_init();
8451                 }       
8452
8453                 // force everyone into this state               
8454                 send_netgame_update_packet();           
8455
8456                 if(!(Game_mode & GM_STANDALONE_SERVER)){
8457                         multi_common_add_text(XSTR("Send update packet\n",831),1);
8458                 }
8459
8460                 // setup some of my own data
8461                 Net_player->flags |= NETINFO_FLAG_MISSION_OK;           
8462
8463                 // do any output stuff
8464                 if(Game_mode & GM_STANDALONE_SERVER){
8465                         std_debug_set_standalone_state_string("Mission Sync");                  
8466                 }               
8467
8468                 // do this here to insure we have the most up to date file checksum info
8469                 multi_get_mission_checksum(Game_current_mission_filename);
8470                 // parse_get_file_signature(Game_current_mission_filename);
8471                 
8472                 if(!(Game_mode & GM_STANDALONE_SERVER)){
8473                         multi_common_add_text(XSTR("Got file signatures\n",832),1);
8474                 }
8475         } else {
8476                 if(!(Game_mode & GM_STANDALONE_SERVER)){
8477                         multi_common_add_text(XSTR("Sending update state packet\n",833),1);
8478                 }
8479         }
8480
8481         // if we're not in team vs. team mode - set all player teams to be 0, and unset all captaincy bits
8482         if(!(Netgame.type_flags & NG_TYPE_TEAM)){
8483                 for(idx=0;idx<MAX_PLAYERS;idx++){
8484                         Net_players[idx].p_info.team = 0;
8485                         Net_players[idx].flags &= ~(NETINFO_FLAG_TEAM_CAPTAIN);
8486                 }
8487         }
8488
8489         // we aren't necessarily xferring the mission file yet  
8490         SDL_assert(Net_player->s_info.xfer_handle == -1);
8491
8492         // always call this for good measure
8493         multi_campaign_flush_data();
8494
8495         Mission_sync_flags = 0;
8496         Multi_mission_loaded = 0;
8497 }
8498
8499 void multi_sync_pre_do()
8500 {               
8501         int idx;
8502         
8503         // If I'm the server, wait for everyone to arrive in this state, then begin transferring data, etc.
8504         // all servers (standalone or no, go through this)
8505         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8506                 // wait for everyone to arrive, then request filesig from all of them
8507                 if(multi_netplayer_state_check(NETPLAYER_STATE_MISSION_SYNC) && !(Mission_sync_flags & MS_FLAG_SENT_FILESIG) && !multi_endgame_ending()){
8508                         send_file_sig_request(Netgame.mission_name);
8509                         Mission_sync_flags |= MS_FLAG_SENT_FILESIG;
8510
8511                         if(!(Game_mode & GM_STANDALONE_SERVER)){
8512                                 multi_common_add_text(XSTR("Sent filesig request\n",834),1);
8513                         }
8514                 }               
8515
8516                 // if we're waiting for players to receive files, then check on their status
8517                 if((Mission_sync_flags & MS_FLAG_SENT_FILESIG) && !multi_netplayer_flag_check(NETINFO_FLAG_MISSION_OK) && !multi_endgame_ending()){
8518                         for(idx=0;idx<MAX_PLAYERS;idx++){
8519                                 // if this player is in the process of xferring a file
8520                                 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].s_info.xfer_handle != -1)){
8521                                         switch(multi_xfer_get_status(Net_players[idx].s_info.xfer_handle)){
8522                                         // if it has successfully completed, set his ok flag
8523                                         case MULTI_XFER_SUCCESS :
8524                                                 // set his ok flag
8525                                                 Net_players[idx].flags |= NETINFO_FLAG_MISSION_OK;
8526
8527                                                 // release the xfer instance handle
8528                                                 multi_xfer_release_handle(Net_players[idx].s_info.xfer_handle);
8529                                                 Net_players[idx].s_info.xfer_handle = -1;
8530                                                 break;
8531                                         // if it has failed or timed-out, kick the player
8532                                         case MULTI_XFER_TIMEDOUT:
8533                                         case MULTI_XFER_FAIL:
8534                                                 // release the xfer handle
8535                                                 multi_xfer_release_handle(Net_players[idx].s_info.xfer_handle);
8536                                                 Net_players[idx].s_info.xfer_handle = -1;
8537                                                 
8538                                                 // kick the loser
8539                                                 multi_kick_player(idx, 0, KICK_REASON_BAD_XFER);
8540                                                 break;
8541                                         }
8542                                 }
8543                         }
8544                 }
8545
8546                 // NOTE : this is now obsolete
8547                 // once everyone is verified, do any data transfer necessary
8548                 if(multi_netplayer_flag_check(NETINFO_FLAG_MISSION_OK) && !(Mission_sync_flags & MS_FLAG_DATA_DONE) && !multi_endgame_ending()){
8549                         // do nothing for now
8550                         Mission_sync_flags |= MS_FLAG_DATA_DONE;                                                
8551                         
8552                         // send campaign pool data
8553                         multi_campaign_send_pool_status();
8554                 }
8555
8556                 // wait for everyone to ack on campaign pool data (even in non-campaign situations)
8557                 if((Mission_sync_flags & MS_FLAG_DATA_DONE) && !(Mission_sync_flags & MS_FLAG_CAMP_DONE) && !multi_endgame_ending()){
8558                         // check to see if everyone has acked the campaign pool data
8559                         if(multi_netplayer_state_check(NETPLAYER_STATE_CPOOL_ACK)){
8560                                 Mission_sync_flags |= MS_FLAG_CAMP_DONE;
8561                         }
8562                 }
8563                                 
8564                 // once everyone is verified, tell them to load the mission
8565                 // also make sure to load the mission myself _AFTER_ telling everyone to do so. This makes the whole process
8566                 // move along faster
8567                 if((Mission_sync_flags & MS_FLAG_CAMP_DONE) && !(Mission_sync_flags & MS_FLAG_SENT_LOAD) && !multi_endgame_ending()){           
8568                         send_netplayer_load_packet(NULL);                       
8569                         Mission_sync_flags |= MS_FLAG_SENT_LOAD;
8570
8571                         if(!(Game_mode & GM_STANDALONE_SERVER)){
8572                                 multi_common_add_text(XSTR("Sent load packet\n",835),1);
8573                         }
8574
8575                         // load the mission myself, as soon as possible
8576                         if(!Multi_mission_loaded){
8577                                 nprintf(("Network","Server loading mission..."));
8578         
8579                                 // update everyone about my status
8580                                 Net_player->state = NETPLAYER_STATE_MISSION_LOADING;
8581                                 send_netplayer_update_packet();
8582
8583                                 game_start_mission();
8584                                 psnet_flush();
8585                                 nprintf(("Network","Done\n"));
8586                                 Multi_mission_loaded = 1;                               
8587
8588                                 // update everyone about my status
8589                                 Net_player->state = NETPLAYER_STATE_MISSION_LOADED;
8590                                 send_netplayer_update_packet();
8591
8592                                 if(!(Game_mode & GM_STANDALONE_SERVER)){
8593                                         multi_common_add_text(XSTR("Loaded mission locally\n",836),1);
8594                                 }
8595                         }
8596                 }
8597                 
8598                 // if everyone has loaded the mission, randomly assign players to ships 
8599                 if(multi_netplayer_state_check(NETPLAYER_STATE_MISSION_LOADED) && !(Mission_sync_flags & MS_FLAG_TS_SLOTS) && !multi_endgame_ending()){
8600                         // call the team select function to assign players to their ships, wings, etc
8601                         multi_ts_assign_players_all();
8602                         send_netplayer_slot_packet();                                                                           
8603
8604                         // mark this flag
8605                         Mission_sync_flags |= MS_FLAG_TS_SLOTS;
8606                 }
8607
8608                 // if everyone has loaded the mission, move to the team select stage
8609                 if(Sync_test && multi_netplayer_state_check(NETPLAYER_STATE_SLOT_ACK) && !(Mission_sync_flags & MS_FLAG_PUSHED_BRIEFING) && !multi_endgame_ending()){
8610                         Netgame.game_state = NETGAME_STATE_BRIEFING;
8611                         send_netgame_update_packet();   // this will push everyone into the next state
8612
8613                         // the standalone moves to his own wait state, whereas in the normal game mode, the server/host moves in to the
8614                         // team select state
8615                         if(Game_mode & GM_STANDALONE_SERVER){
8616                                 gameseq_post_event(GS_EVENT_MULTI_STD_WAIT);
8617                         } else {
8618                                 gameseq_post_event(GS_EVENT_START_GAME);                                        
8619                         }
8620
8621                         Mission_sync_flags |= MS_FLAG_PUSHED_BRIEFING;
8622
8623                         if(!(Game_mode & GM_STANDALONE_SERVER)){
8624                                 multi_common_add_text(XSTR("Moving to team select\n",837),1);
8625                         }
8626                 }               
8627         } else {
8628                 // clients should detect here if they are doing a file xfer and do error processing
8629                 if((Net_player->state == NETPLAYER_STATE_MISSION_XFER) && (Net_player->s_info.xfer_handle != -1) && !multi_endgame_ending()){
8630                         switch(multi_xfer_get_status(Net_player->s_info.xfer_handle)){
8631                         // if it has successfully completed, set his ok flag
8632                         case MULTI_XFER_SUCCESS :       
8633                                 // release my xfer handle
8634                                 multi_xfer_release_handle(Net_player->s_info.xfer_handle);
8635                                 Net_player->s_info.xfer_handle = -1;                            
8636                                 break;
8637                                 
8638                         // if it has failed or timed-out, kick the player
8639                         case MULTI_XFER_TIMEDOUT:
8640                         case MULTI_XFER_FAIL:
8641                                 // release my xfer handle
8642                                 multi_xfer_release_handle(Net_player->s_info.xfer_handle);
8643                                 Net_player->s_info.xfer_handle = -1;
8644
8645                                 // leave the game qith an error code
8646                                 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_XFER_FAIL);
8647                                 break;
8648                         }
8649                 }
8650         }
8651
8652         // blit stuff
8653         if(!(Game_mode & GM_STANDALONE_SERVER)){
8654                 gr_reset_clip();
8655                 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8656                 if(Multi_sync_bitmap != -1){
8657                         gr_set_bitmap(Multi_sync_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8658                         gr_bitmap(0,0);
8659                 }
8660                 Multi_sync_window.draw();
8661
8662                 multi_sync_blit_screen_all();
8663
8664                 gr_flip();
8665         }
8666 }
8667
8668 void multi_sync_pre_close()
8669 {
8670         // at this point, we should shut down any file xfers...
8671         if(Net_player->s_info.xfer_handle != -1){
8672                 nprintf(("Network","WARNING - killing file xfer while leaving mission sync state!!!\n"));
8673
8674                 multi_xfer_abort(Net_player->s_info.xfer_handle);
8675                 Net_player->s_info.xfer_handle = -1;
8676         }
8677 }
8678
8679 void multi_sync_post_init()
8680 {       
8681         multi_reset_timestamps();
8682
8683         Multi_state_timestamp = timestamp(0);
8684
8685         // NETLOG
8686         ml_string(NOX("Performing post-briefing data sync"));
8687         
8688         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8689                 multi_common_add_text(XSTR("Server performing sync\n",830),1);
8690         } else {
8691                 multi_common_add_text(XSTR("Client performing sync\n",838),1);
8692         }
8693
8694         // everyone should re-initialize these 
8695         init_multiplayer_stats();
8696
8697         // reset all sequencing info
8698         multi_oo_reset_sequencing();
8699
8700         // if I am not the master of the game, then send the firing information for my ship
8701         // to the host
8702         if ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) ){
8703                 send_firing_info_packet();
8704         }       
8705
8706         // if I'm not a standalone server, load up the countdown stuff
8707         if(!(Game_mode & GM_STANDALONE_SERVER)){                                
8708                 Multi_sync_countdown_anim = NULL;
8709                 Multi_sync_countdown_instance = NULL;
8710                 Multi_sync_countdown_anim = anim_load(Multi_sync_countdown_fname[gr_screen.res]);                               
8711                 if(Multi_sync_countdown_anim == NULL){
8712                         nprintf(("General","WARNING!, Could not load countdown animation %s!\n",Multi_sync_countdown_fname[gr_screen.res]));
8713                 }
8714         }
8715
8716         // create objects for all permanent observers
8717         multi_obs_level_init();
8718
8719         // clear the game start countdown timer
8720         Multi_sync_countdown_timer = -1.0f;     
8721         Multi_sync_countdown = -1;
8722
8723         // if this is a team vs. team mission, mark all ship teams appropriately
8724         if(Netgame.type_flags & NG_TYPE_TEAM){
8725                 multi_team_mark_all_ships();
8726         }
8727
8728         Mission_sync_flags = 0;
8729         Multi_sync_launch_pressed = 0;
8730 }
8731
8732 #define MULTI_POST_TIMESTAMP                    7000
8733
8734 extern int create_wings();
8735
8736 void multi_sync_post_do()
8737 {       
8738         int idx;
8739         
8740         // only if the host is also the master should he be doing this (non-standalone situation)
8741         if ( Net_player->flags & NETINFO_FLAG_AM_MASTER ) {
8742
8743                 // once everyone gets to this screen, send them the ship classes of all ships.
8744                 if(multi_netplayer_state_check(NETPLAYER_STATE_WAITING) && !(Mission_sync_flags & MS_FLAG_POST_DATA)) {                 
8745                         // only the host should ever do this
8746                         if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8747                                 // at this point we want to delete all necessary ships, change all necessary ship classes, and set all weapons up
8748                                 multi_ts_create_wings();
8749
8750                                 // update player ets settings
8751                                 for(idx=0;idx<MAX_PLAYERS;idx++){
8752                                         if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].player->objnum != -1)){
8753                                                 multi_server_update_player_weapons(&Net_players[idx],&Ships[Objects[Net_players[idx].player->objnum].instance]);
8754                                         }
8755                                 }                       
8756                         }                       
8757
8758                         // note that this is done a little differently for standalones and nonstandalones
8759                         send_post_sync_data_packet();
8760                         
8761                         multi_common_add_text(XSTR("Sending post briefing block information\n",839),1);                 
8762
8763                         Mission_sync_flags |= MS_FLAG_POST_DATA;
8764                 }
8765
8766                 // send weapon slots data
8767                 if(multi_netplayer_state_check(NETPLAYER_STATE_POST_DATA_ACK) && !(Mission_sync_flags & MS_FLAG_WSS_SLOTS)) {                   
8768                         // note that this is done a little differently for standalones and nonstandalones
8769                         if(Netgame.type_flags & NG_TYPE_TEAM){
8770                                 send_wss_slots_data_packet(0,0);
8771                                 send_wss_slots_data_packet(1,1);
8772                         } else {
8773                                 send_wss_slots_data_packet(0,1);
8774                         }
8775                         
8776                         multi_common_add_text(XSTR("Sending weapon slots information\n",840),1);                        
8777
8778                         Mission_sync_flags |= MS_FLAG_WSS_SLOTS;
8779                 }
8780                         
8781                 // once weapon information is received, send player settings info
8782                 if ( multi_netplayer_state_check(NETPLAYER_STATE_WSS_ACK) && !(Mission_sync_flags & MS_FLAG_PSETTINGS)) {                                                                       
8783                         send_player_settings_packet();
8784                         
8785                         // server (specifically, the standalone), should set this here
8786                         Net_player->state = NETPLAYER_STATE_SETTINGS_ACK;
8787                         send_netplayer_update_packet();
8788                         
8789                         multi_common_add_text(XSTR("Sending player settings packets\n",841),1);                         
8790
8791                         Mission_sync_flags |= MS_FLAG_PSETTINGS;                        
8792                 }                                       
8793
8794                 // check to see if the countdown timer has started and act appropriately
8795                 if( Multi_sync_countdown_timer > -1.0f ) {
8796
8797                         // increment by frametime.
8798                         Multi_sync_countdown_timer += flFrametime;
8799
8800                         // if the animation is not playing, start it
8801                         if(!(Game_mode & GM_STANDALONE_SERVER) && (Multi_sync_countdown_instance == NULL) && (Multi_sync_countdown_anim != NULL)){
8802                                 anim_play_struct aps;                           
8803
8804                                 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]);
8805                                 aps.screen_id = GS_STATE_MULTI_MISSION_SYNC;
8806                                 aps.framerate_independent = 1;
8807
8808                                 Multi_sync_countdown_instance = anim_play(&aps);
8809                         }
8810
8811                         // if the next second has expired
8812                         if( Multi_sync_countdown_timer >= 1.0f ) {
8813
8814                                 Multi_sync_countdown--;
8815                                 Multi_sync_countdown_timer = 0.0f;
8816
8817                                 // if the countdown has reached 0, launch the mission
8818                                 if(Multi_sync_countdown == 0){
8819                                         Multi_sync_countdown_timer = -1.0f;
8820
8821                                         Multi_sync_launch_pressed = 0;
8822                                         multi_sync_launch();
8823                                 }
8824                                 // otherwise send a countdown packet
8825                                 else {
8826                                         send_countdown_packet(Multi_sync_countdown);
8827                                 }
8828                         }
8829                 }
8830                         
8831                 // jump into the mission myself
8832                 if((Multi_sync_countdown == 0) && multi_netplayer_state_check(NETPLAYER_STATE_IN_MISSION)){
8833                         if(!((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !Multi_sync_launch_pressed)){                                                      
8834                                 Net_player->state = NETPLAYER_STATE_IN_MISSION;
8835                                 Netgame.game_state = NETGAME_STATE_IN_MISSION;                          
8836                                 gameseq_post_event(GS_EVENT_ENTER_GAME);
8837                         
8838                                 multi_common_add_text(XSTR("Moving into game\n",842),1);                        
8839                         }
8840                 }
8841         } else {
8842                 // maybe start the animation countdown 
8843                 if((Multi_sync_countdown >= 0) && (Multi_sync_countdown_instance == NULL) && (Multi_sync_countdown_anim != NULL)){
8844                         anim_play_struct aps;                           
8845
8846                         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]);
8847                         aps.screen_id = GS_STATE_MULTI_MISSION_SYNC;
8848                         aps.framerate_independent = 1;
8849
8850                         Multi_sync_countdown_instance = anim_play(&aps);
8851                 }
8852         }
8853
8854         // host - specific stuff
8855         if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8856                 // create the launch button so the host can click
8857                 if( Sync_test && multi_netplayer_state_check(NETPLAYER_STATE_SETTINGS_ACK) ){
8858                         multi_sync_create_launch_button();
8859                 }
8860         }
8861
8862         // blit stuff
8863         if(!(Game_mode & GM_STANDALONE_SERVER)){
8864                 gr_reset_clip();        
8865                 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8866                 if(Multi_sync_bitmap != -1){
8867                         gr_set_bitmap(Multi_sync_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8868                         gr_bitmap(0,0);
8869                 }
8870                 Multi_sync_window.draw();
8871
8872                 multi_sync_blit_screen_all();
8873
8874                 gr_flip();
8875         }
8876 }
8877
8878 void multi_sync_post_close()
8879 {
8880         int idx;
8881
8882         // if I'm not a standalone server, unload up the countdown stuff
8883         if(!(Game_mode & GM_STANDALONE_SERVER)){                                
8884                 // release all rendering animation instances (should only be 1)
8885                 anim_release_all_instances(GS_STATE_MULTI_MISSION_SYNC);
8886                 Multi_sync_countdown_instance = NULL;
8887
8888                 // free up the countdown animation
8889                 if(Multi_sync_countdown_anim != NULL){
8890                         anim_free(Multi_sync_countdown_anim);
8891                         Multi_sync_countdown_anim = NULL;
8892                 }
8893         }
8894         
8895         // all players should reset sequencing
8896         for(idx=0;idx<MAX_PLAYERS;idx++){
8897                 if(Net_player->flags & NETINFO_FLAG_CONNECTED){
8898                         Net_players[idx].client_cinfo_seq = 0;
8899                         Net_players[idx].client_server_seq = 0; 
8900                 }
8901         }
8902
8903         // multiplayer dogfight
8904         multi_df_level_pre_enter();                             
8905
8906         // clients should clear obj_pair array and add pair for themselves
8907         /*
8908         if ( MULTIPLAYER_CLIENT ) {
8909                 obj_reset_pairs();
8910                 obj_add_pairs( OBJ_INDEX(Player_obj) ); 
8911         }
8912         */
8913 }
8914
8915 void multi_sync_display_name(const char *name, int index, int np_index)
8916 {
8917         char fit[CALLSIGN_LEN]; 
8918         
8919         // make sure the string actually fits
8920         strcpy(fit,name);
8921
8922         // if we're in team vs. team mode
8923         if(Netgame.type_flags & NG_TYPE_TEAM){
8924                 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]);                 
8925
8926                 // if this is the currently selected player, draw him highlighted
8927                 if(np_index == Multi_sync_player_select){
8928                         gr_set_color_fast(&Color_text_selected);
8929                 } else {
8930                         gr_set_color_fast(&Color_text_normal);
8931                 }
8932
8933                 // blit the string
8934                 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);
8935
8936                 // blit his team icon 
8937                 // team 0               
8938                 if(Net_players[np_index].p_info.team == 0){
8939                         // blit the team captain icon
8940                         if(Net_players[np_index].flags & NETINFO_FLAG_TEAM_CAPTAIN){                            
8941                                 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
8942                                         gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8943                                         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);
8944                                 } 
8945                         }
8946                         // normal team member icon
8947                         else {
8948                                 if(Multi_common_icons[MICON_TEAM0] != -1){
8949                                         gr_set_bitmap(Multi_common_icons[MICON_TEAM0], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8950                                         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);
8951                                 }
8952                         }
8953                 }
8954                 // team 1
8955                 else if(Net_players[np_index].p_info.team == 1){                        
8956                         // blit the team captain icon
8957                         if(Net_players[np_index].flags & NETINFO_FLAG_TEAM_CAPTAIN){
8958                                 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
8959                                         gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8960                                         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);
8961                                 }
8962                         }
8963                         // normal team member icon
8964                         else {
8965                                 if(Multi_common_icons[MICON_TEAM1] != -1){
8966                                         gr_set_bitmap(Multi_common_icons[MICON_TEAM1], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8967                                         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);
8968                                 }
8969                         }
8970                 }               
8971         } else {
8972                 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]);
8973
8974                 // if this is the currently selected player, draw him highlighted
8975                 if(np_index == Multi_sync_player_select){
8976                         gr_set_color_fast(&Color_text_selected);
8977                 } else {
8978                         gr_set_color_fast(&Color_text_normal);
8979                 }
8980
8981                 // blit the string
8982                 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);
8983         }
8984
8985         // maybe blit his CD status icon
8986         if((Net_players[np_index].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
8987                 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8988                 gr_bitmap(Ms_status_coords[gr_screen.res][MS_X_COORD], Ms_status_coords[gr_screen.res][MS_Y_COORD] + (index * 10));
8989         }
8990 }
8991
8992 void multi_sync_display_status(const char *status, int index)
8993 {
8994         char fit[250];
8995
8996         // make sure the string actually fits
8997         strcpy(fit, status);
8998         gr_force_fit_string(fit, 250, Ms_status2_coords[gr_screen.res][MS_W_COORD] - 20);
8999         gr_set_color_fast(&Color_bright);       
9000         gr_string(Ms_status2_coords[gr_screen.res][MS_X_COORD], Ms_status2_coords[gr_screen.res][MS_Y_COORD] + (index * 10), fit);              
9001 }
9002
9003 void multi_sync_force_start_pre()
9004 {
9005         int idx;
9006         int want_state = NETPLAYER_STATE_SLOT_ACK;      // kick any players who are still in this state
9007
9008         // go through the player list and boot anyone who isn't in the right state
9009         for(idx=0;idx<MAX_PLAYERS;idx++){
9010                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_player != &Net_players[idx]) && (Net_players[idx].state == want_state)){            
9011                         multi_kick_player(idx,0);                               
9012                 }
9013         }
9014 }
9015
9016 void multi_sync_force_start_post()
9017 {
9018         int idx,idx2;
9019         int kill_state[3];      
9020         int num_kill_states;
9021         
9022         // determine the state we want all players in so that we can find those who are not in the state        
9023         kill_state[0] = NETPLAYER_STATE_BRIEFING;
9024         kill_state[1] = NETPLAYER_STATE_SHIP_SELECT;
9025         kill_state[2] = NETPLAYER_STATE_WEAPON_SELECT;
9026         num_kill_states = 3;    
9027
9028         // go through the player list and boot anyone who isn't in the right state
9029         for(idx=0;idx<MAX_PLAYERS;idx++){
9030                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_player != &Net_players[idx])){
9031                         // check against all kill state
9032                         for(idx2 = 0;idx2<num_kill_states;idx2++){
9033                                 if(Net_players[idx].state == kill_state[idx2]){
9034                                         multi_kick_player(idx,0);
9035                                         break;
9036                                 }
9037                         }
9038                 }
9039         }
9040 }
9041
9042 void multi_sync_start_countdown()
9043 {
9044         // don't allow repeat button presses
9045         if(Multi_sync_launch_pressed){
9046                 return;
9047         }
9048         
9049         Multi_sync_launch_pressed = 1;
9050
9051         // if I'm the server, begin the countdown
9052         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
9053                 gamesnd_play_iface(SND_COMMIT_PRESSED);
9054                 Multi_sync_countdown_timer = 0.0f;
9055                 Multi_sync_countdown = MULTI_SYNC_COUNTDOWN_TIME;
9056
9057                 // send an initial countdown value
9058                 send_countdown_packet(Multi_sync_countdown);
9059         }
9060         // otherwise send the "start countdown" packet to the standalone
9061         else {
9062                 SDL_assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
9063                 send_countdown_packet(-1);
9064         }
9065 }
9066
9067 void multi_sync_launch()
9068 {       
9069         // don't allow repeat button presses
9070         if(Multi_sync_launch_pressed){
9071                 return;
9072         }
9073
9074         Multi_sync_launch_pressed = 1;
9075
9076         // NETLOG
9077         ml_printf(NOX("Entering mission %s"), Game_current_mission_filename);
9078
9079         // tell everyone to jump into the mission
9080         send_jump_into_mission_packet();
9081         Multi_state_timestamp = timestamp(MULTI_POST_TIMESTAMP);
9082
9083         // set the # of players at the start of the mission
9084         Multi_num_players_at_start = multi_num_players();
9085         nprintf(("Network","# of players at start of mission : %d\n", Multi_num_players_at_start));
9086         
9087         // initialize datarate limiting for all clients
9088         multi_oo_rate_init_all();       
9089                                 
9090         multi_common_add_text(XSTR("Sending mission start packet\n",843),1);                            
9091 }
9092
9093 void multi_sync_create_launch_button()
9094 {
9095         if (!Multi_launch_button_created) {             
9096                 // create the object
9097                 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);
9098
9099                 // set the sound to play when highlighted
9100                 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_highlight_action(common_play_highlight_sound);
9101
9102                 // set the ani for the button
9103                 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_bmaps(Multi_sync_buttons[gr_screen.res][MS_LAUNCH].filename, 3, 0);
9104
9105                 // set the hotspot
9106                 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.link_hotspot(Multi_sync_buttons[gr_screen.res][MS_LAUNCH].hotspot);
9107
9108                 // hotkey
9109                 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_hotkey(KEY_CTRLED+SDLK_RETURN);
9110
9111 #ifndef MAKE_FS1
9112                 // create the text for the button
9113                 Multi_sync_window.add_XSTR(&Multi_sync_text[gr_screen.res][MST_LAUNCH]);
9114 #endif
9115
9116                 // increment the button count so we start checking this one
9117                 Multi_sync_button_count++;
9118
9119                 Multi_launch_button_created = 1;
9120         }
9121 }
9122
9123 void multi_sync_handle_plist()
9124 {
9125         int idx;
9126         int my;
9127         int select_index;
9128         
9129         // if we don't have a currently selected player, select one
9130         if((Multi_sync_player_select < 0) || !MULTI_CONNECTED(Net_players[Multi_sync_player_select])){
9131                 for(idx=0;idx<MAX_PLAYERS;idx++){
9132                         if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
9133                                 Multi_sync_player_select = idx;
9134                                 break;
9135                         }
9136                 }
9137         }
9138         
9139         // check for button list presses
9140         if(Multi_sync_plist_button.pressed()){
9141                 // get the y mouse coords
9142                 Multi_sync_plist_button.get_mouse_pos(NULL,&my);
9143
9144                 // get the index of the item selected
9145                 select_index = my / 10;
9146
9147                 // if the index is greater than the current # connections, do nothing
9148                 if(select_index > (multi_num_connections() - 1)){
9149                         return;
9150                 }
9151
9152                 // translate into an absolute Net_players[] index (get the Nth net player)
9153                 Multi_sync_player_select = -1;
9154                 for(idx=0;idx<MAX_PLAYERS;idx++){
9155                         if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
9156                                 select_index--;
9157                         }
9158
9159                         // if we've found the item we're looking for
9160                         if(select_index < 0){
9161                                 Multi_sync_player_select = idx;
9162                                 break;
9163                         }
9164                 }
9165
9166                 // if for some bizarre reason, this is an invalid player, unselect him and wait for the next interation
9167                 // of the loop
9168                 if((Multi_sync_player_select >= 0) && (!MULTI_CONNECTED(Net_players[Multi_sync_player_select]) || MULTI_STANDALONE(Net_players[Multi_sync_player_select])) ){
9169                         Multi_sync_player_select = -1;
9170                 }
9171         }
9172 }
9173
9174
9175 // -------------------------------------------------------------------------------------------------------------
9176 // 
9177 // MULTIPLAYER DEBRIEF SCREEN 
9178 //
9179
9180 // other relevant data
9181 int Multi_debrief_accept_hit;
9182 int Multi_debrief_replay_hit;
9183
9184 // set if the server has left the game
9185 int Multi_debrief_server_left = 0;
9186
9187 // if we've reported on TvT status all players are in the debrief
9188 int Multi_debrief_reported_tvt = 0;
9189
9190 // whether stats are being accepted
9191 // -1 == no decision yet
9192 // 1 == accepted
9193 // 0 == tossed
9194 int Multi_debrief_stats_accept_code = -1;
9195
9196 int Multi_debrief_server_framecount = 0;
9197
9198 float Multi_debrief_time = 0.0f;
9199 float Multi_debrief_resend_time = 10.0f;
9200
9201 void multi_debrief_init()
9202 {                       
9203         int idx;
9204
9205         Multi_debrief_time = 0.0f;
9206         Multi_debrief_resend_time = 10.0f;
9207         
9208         // do this to notify the standalone or the normal server that we're in the debrief state and ready to receive packets
9209         if (!(Net_player->flags & NETINFO_FLAG_AM_MASTER)) {
9210                 Net_player->state = NETPLAYER_STATE_DEBRIEF;
9211                 send_netplayer_update_packet();
9212         }       
9213
9214         // unflag some stuff
9215         for(idx=0;idx<MAX_PLAYERS;idx++){
9216                 if(MULTI_CONNECTED(Net_players[idx])){
9217                         Net_players[idx].flags &= ~(NETINFO_FLAG_RESPAWNING | NETINFO_FLAG_LIMBO | NETINFO_FLAG_WARPING_OUT);
9218                 }
9219         }
9220         
9221         // if text input mode is active, clear it
9222         multi_msg_text_flush(); 
9223         
9224         // the server has not left yet
9225         Multi_debrief_server_left = 0;
9226
9227         // have not hit accept or replay yet
9228         Multi_debrief_accept_hit = 0;
9229         Multi_debrief_replay_hit = 0;
9230
9231         // stats have not been accepted yet
9232         Multi_debrief_stats_accept_code = -1;
9233
9234         // mark stats as not being store yet
9235         Netgame.flags &= ~(NG_FLAG_STORED_MT_STATS);
9236
9237         // no report on TvT yet
9238         Multi_debrief_reported_tvt = 0;
9239
9240         Multi_debrief_server_framecount = 0;
9241 }
9242
9243 void multi_debrief_do_frame()
9244 {
9245         Multi_debrief_time += flFrametime;
9246
9247         // set the netgame state to be debriefing when appropriate
9248         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)){
9249                 Netgame.game_state = NETGAME_STATE_DEBRIEF;
9250                 send_netgame_update_packet();
9251         }
9252         
9253         // evaluate all server stuff
9254         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
9255                 multi_debrief_server_process();
9256         }                       
9257 }
9258
9259 void multi_debrief_close()
9260 {       
9261         if ( MULTIPLAYER_CLIENT && (Netgame.game_state == NETGAME_STATE_MISSION_SYNC) ){
9262                 gamesnd_play_iface( SND_COMMIT_PRESSED );
9263         }
9264 }
9265
9266 // handle optional mission loop
9267 void multi_maybe_set_mission_loop()
9268 {
9269         int cur = Campaign.current_mission;
9270         if (Campaign.missions[cur].has_mission_loop) {
9271                 SDL_assert(Campaign.loop_mission != CAMPAIGN_LOOP_MISSION_UNINITIALIZED);
9272         }
9273         bool require_repeat_mission = (Campaign.current_mission == Campaign.next_mission);
9274
9275         // check for (1) mission loop available, (2) dont have to repeat last mission
9276         if ( (Campaign.missions[cur].has_mission_loop && (Campaign.loop_mission != -1)) && !require_repeat_mission ) {
9277
9278                 char buffer[512];
9279                 debrief_assemble_optional_mission_popup_text(buffer, Campaign.missions[cur].mission_loop_desc);
9280
9281                 int choice = popup(0 , 2, POPUP_NO, POPUP_YES, buffer);
9282                 if (choice == 1) {
9283                         Campaign.loop_enabled = 1;
9284                         Campaign.next_mission = Campaign.loop_mission;
9285                 }
9286         }
9287 }
9288
9289 // handle all cases for when the accept key is hit in a multiplayer debriefing
9290 void multi_debrief_accept_hit()
9291 {
9292         // if we already accepted, do nothing
9293         // but he may need to hit accept again after the server has left the game, so allow this
9294         if(Multi_debrief_accept_hit){
9295                 return;
9296         }
9297         
9298         // mark this so that we don't hit it again
9299         Multi_debrief_accept_hit = 1;
9300
9301         gamesnd_play_iface(SND_COMMIT_PRESSED);
9302
9303         // if the server has left the game, always just end the game. 
9304         if(Multi_debrief_server_left){
9305                 if(!multi_quit_game(PROMPT_ALL)){
9306                         Multi_debrief_server_left = 1;
9307                         Multi_debrief_accept_hit = 0;
9308                 } else {
9309                         Multi_debrief_server_left = 0;
9310                 }                       
9311         } else {                
9312                 // query the host and see if he wants to accept stats
9313                 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
9314                         // if we're on a tracker game, he gets no choice for storing stats
9315                         if(MULTI_IS_TRACKER_GAME){
9316                                 multi_maybe_set_mission_loop();
9317                         } else {
9318                                 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));
9319                 
9320                                 // evaluate the result
9321                                 switch(res){
9322                                 // undo the accept
9323                                 case -1:
9324                                 case 0:
9325                                         Multi_debrief_accept_hit = 0;
9326                                         return;
9327
9328                                 // set the accept code to be "not accepting"
9329                                 case 2 :
9330                                         multi_debrief_stats_toss();
9331                                         multi_maybe_set_mission_loop();
9332                                         break;
9333                                 
9334                                 // accept the stats and continue
9335                                 case 1 :
9336                                         multi_debrief_stats_accept();
9337                                         multi_maybe_set_mission_loop();
9338                                         break;
9339                                 }
9340                         }
9341                 }
9342
9343                 // set my netplayer state to be "debrief_accept", and be done with it
9344                 Net_player->state = NETPLAYER_STATE_DEBRIEF_ACCEPT;
9345                 send_netplayer_update_packet();
9346         }
9347 }
9348
9349 // handle all cases for when the escape key is hit in a multiplayer debriefing
9350 void multi_debrief_esc_hit()
9351 {
9352         int res;
9353
9354         // if the server has left
9355         if(Multi_debrief_server_left){
9356                 multi_quit_game(PROMPT_ALL);
9357                 return;
9358         }
9359         
9360         // display a popup
9361         if(Net_player->flags & NETINFO_FLAG_GAME_HOST){         
9362                 // if the stats have already been accepted
9363                 if((Multi_debrief_stats_accept_code != -1) || (MULTI_IS_TRACKER_GAME)){
9364                         multi_quit_game(PROMPT_HOST);
9365                 } else {
9366                         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));
9367                 
9368                         // evaluate the result
9369                         switch(res){                    
9370                         // undo the accept
9371                         case -1:
9372                         case 0:                         
9373                                 break;
9374
9375                         // set the accept code to be "not accepting"
9376                         case 2 :
9377                                 multi_debrief_stats_toss();
9378                                 multi_quit_game(PROMPT_NONE);
9379                                 break;
9380                                 
9381                         // accept the stats and continue
9382                         case 1 :
9383                                 multi_debrief_stats_accept();
9384                                 multi_quit_game(PROMPT_NONE);
9385                                 break;                                          
9386                         }               
9387                 }
9388         } else {                
9389                 // if the stats haven't been accepted yet, or this is a tracker game
9390                 if((Multi_debrief_stats_accept_code == -1) && !(MULTI_IS_TRACKER_GAME)){
9391                         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));
9392
9393                         // evaluate the result
9394                         if(res == 1){
9395                                 multi_quit_game(PROMPT_NONE);
9396                         }
9397                 }
9398                 // otherwise go through the normal endgame channels
9399                 else {
9400                         multi_quit_game(PROMPT_ALL);
9401                 }               
9402         }
9403 }
9404
9405 void multi_debrief_replay_hit()
9406 {
9407         // only the host should ever get here
9408         SDL_assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
9409
9410         // if the button was already pressed, do nothing
9411         if(Multi_debrief_accept_hit){
9412                 return;
9413         }
9414         
9415         // same as hittin the except button except no stats are kept
9416         Multi_debrief_accept_hit = 1;
9417
9418         // mark myself as being in the replay state so we know what to do next
9419         Net_player->state = NETPLAYER_STATE_DEBRIEF_REPLAY;
9420         send_netplayer_update_packet();
9421 }
9422
9423 // call this when the server has left and we would otherwise be saying "contact lost with server
9424 void multi_debrief_server_left()
9425 {
9426         // the server left
9427         Multi_debrief_server_left = 1;
9428
9429         // undo any "accept" hit so that clients can hit accept again to leave
9430         Multi_debrief_accept_hit = 0;
9431 }
9432
9433 void multi_debrief_stats_accept()
9434 {
9435         // don't do anything if we've already accepted
9436         if(Multi_debrief_stats_accept_code != -1){
9437                 return;
9438         }
9439         
9440         Multi_debrief_stats_accept_code = 1;
9441
9442         // if we're the host, and we're on a standalone, tell the standalone to begin the stats storing process
9443         if((Net_player->flags & NETINFO_FLAG_GAME_HOST) || (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
9444                 // send a packet to the players telling them to store their stats
9445                 send_store_stats_packet(1);
9446         } 
9447
9448         // add a chat line saying "stats have been accepted"
9449         multi_display_chat_msg(XSTR("<stats have been accepted>",850),0,0);
9450
9451         // NETLOG
9452         ml_string(NOX("Stats stored"));
9453 }
9454
9455 void multi_debrief_stats_toss()
9456 {
9457         // don't do anything if we've already accepted
9458         if(Multi_debrief_stats_accept_code != -1){
9459                 return;
9460         }
9461         
9462         Multi_debrief_stats_accept_code = 0;
9463
9464         // if we're the host, and we're on a standalone, tell everyone to "toss" stats
9465         if((Net_player->flags & NETINFO_FLAG_GAME_HOST) || (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
9466                 // send a packet to the players telling them to store their stats
9467                 send_store_stats_packet(0);
9468         } 
9469
9470         // add a chat line saying "stats have been accepted"
9471         multi_display_chat_msg(XSTR("<stats have been tossed>",851),0,0);
9472
9473         // NETLOG
9474         ml_string(NOX("Stats tossed"));
9475 }
9476
9477 int multi_debrief_stats_accept_code()
9478 {
9479         return Multi_debrief_stats_accept_code;
9480 }
9481
9482 void multi_debrief_server_process()
9483 {       
9484         int idx;
9485         int player_status,other_status;
9486
9487         Multi_debrief_server_framecount++;
9488
9489         // if we're > 10 seconds into the debrief and not everyone is here, try warping everyone out again
9490         if((Multi_debrief_time >= Multi_debrief_resend_time) && !multi_netplayer_state_check3(NETPLAYER_STATE_DEBRIEF, NETPLAYER_STATE_DEBRIEF_ACCEPT, NETPLAYER_STATE_DEBRIEF_REPLAY, 1)){
9491                 // find all players who are not in the debrief state and hit them with the endgame packet
9492                 for(idx=0; idx<MAX_PLAYERS; idx++){
9493                         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)) ){
9494                                 send_endgame_packet(&Net_players[idx]);
9495                         }
9496                 }
9497
9498                 // next check time
9499                 Multi_debrief_resend_time += 7.0f;
9500         }
9501
9502         // evaluate the status of all players in the game (0 == not ready, 1 == ready to continue, 2 == ready to replay)
9503         other_status = 1;
9504
9505         // check all players
9506         for(idx=0;idx<MAX_PLAYERS;idx++){
9507                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_HOST(Net_players[idx])){                                  
9508                         if(Net_players[idx].state != NETPLAYER_STATE_DEBRIEF_ACCEPT){
9509                                 other_status = 0;
9510                                 break;
9511                         }
9512                 }
9513         }
9514
9515         // if we haven't already reported TvT results
9516         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)){
9517                 multi_team_report();
9518                 Multi_debrief_reported_tvt = 1;
9519         }       
9520
9521         // if all other players are good to go, check the host
9522         if(other_status){
9523                 // if he is ready to continue
9524                 if(Netgame.host->state == NETPLAYER_STATE_DEBRIEF_ACCEPT){                                      
9525                         player_status = 1;                      
9526                 } 
9527                 // if he wants to replay the mission
9528                 else if(Netgame.host->state == NETPLAYER_STATE_DEBRIEF_REPLAY){
9529                         player_status = 2;                      
9530                 } 
9531                 // if he is not ready
9532                 else {
9533                         player_status = 0;
9534                 }
9535         }
9536         // if all players are _not_ good to go
9537         else {
9538                 player_status = 0;
9539         }
9540                 
9541         // if we're in the debriefing state in a campaign mode, process accordingly
9542         if(Netgame.campaign_mode == MP_CAMPAIGN){
9543                 multi_campaign_do_debrief(player_status);
9544         }
9545         // otherwise process as normal (looking for all players to be ready to go to the next mission
9546         else {
9547                 if(player_status == 1){
9548                         multi_flush_mission_stuff();
9549
9550                         // set the netgame state to be forming and continue
9551                         Netgame.game_state = NETGAME_STATE_FORMING;
9552                         send_netgame_update_packet();
9553
9554                         // move to the proper state
9555                         if(Game_mode & GM_STANDALONE_SERVER){
9556                                 gameseq_post_event(GS_EVENT_STANDALONE_MAIN);
9557                         } else {
9558                                 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
9559                         }
9560
9561                         multi_reset_timestamps();
9562                 } else if(player_status == 2){
9563                         multi_flush_mission_stuff();
9564
9565                         // tell everyone to move into the pre-briefing sync state
9566                         Netgame.game_state = NETGAME_STATE_MISSION_SYNC;
9567                         send_netgame_update_packet();
9568
9569                         // move back to the mission sync screen for the same mission again
9570                         Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
9571                         gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
9572
9573                         multi_reset_timestamps();
9574                 }
9575         }       
9576 }
9577
9578
9579 // -------------------------------------------------------------------------------------------------------------
9580 // 
9581 // MULTIPLAYER PASSWORD POPUP
9582 //
9583
9584 //XSTR:OFF
9585 // bitmaps defs
9586 static const char *Multi_pwd_bitmap_fname[GR_NUM_RESOLUTIONS] = {
9587         "Password",                     // GR_640
9588         "2_Password"            // GR_1024
9589 };
9590
9591 static const char *Multi_pwd_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
9592         "Password-M",           // GR_640
9593         "2_Password-M"          // GR_1024
9594 };
9595
9596 //XSTR:ON
9597
9598 // constants for coordinate lookup
9599 #define MPWD_X_COORD 0
9600 #define MPWD_Y_COORD 1
9601 #define MPWD_W_COORD 2
9602 #define MPWD_H_COORD 3
9603
9604 // button defs
9605 #define MULTI_PWD_NUM_BUTTONS                   2
9606 #define MPWD_CANCEL                     0
9607 #define MPWD_COMMIT                     1
9608
9609 // password area defs
9610 int Mpwd_coords[GR_NUM_RESOLUTIONS][4] = {
9611         { // GR_640
9612 #ifdef MAKE_FS1
9613                 147, 114, 343, 13
9614 #else
9615                 134, 112, 367, 15
9616 #endif
9617         },
9618         { // GR_1024
9619                 215, 190, 587, 24
9620         }
9621 };
9622
9623 UI_WINDOW Multi_pwd_window;                                                                                             // the window object for the join screen
9624 UI_INPUTBOX     Multi_pwd_passwd;                                                                                               // for Netgame.passwd
9625 int Multi_pwd_bitmap;                                                                                                           // the background bitmap
9626 int Multi_passwd_background = -1;
9627 int Multi_passwd_done = -1;
9628 int Multi_passwd_running = 0;
9629
9630 // password buttons
9631 ui_button_info Multi_pwd_buttons[GR_NUM_RESOLUTIONS][MULTI_PWD_NUM_BUTTONS] = {
9632         { // GR_640
9633 #ifdef MAKE_FS1
9634                 ui_button_info("PWB_00",        402,    134,    -1,     -1,     0),
9635                 ui_button_info("PWB_01",        450,    134,    -1,     -1,     1),
9636 #else
9637                 ui_button_info("PWB_00",        411,    151,    405,    141,    0),
9638                 ui_button_info("PWB_01",        460,    151,    465,    141,    1),
9639 #endif
9640         }, 
9641         { // GR_1024
9642                 ui_button_info("2_PWB_00",      659,    242,    649,    225,    0),
9643                 ui_button_info("2_PWB_01",      737,    242,    736,    225,    1),
9644         }, 
9645 };
9646
9647 // text
9648 #ifndef MAKE_FS1
9649 #define MULTI_PWD_NUM_TEXT                              3
9650
9651 UI_XSTR Multi_pwd_text[GR_NUM_RESOLUTIONS][MULTI_PWD_NUM_TEXT] = {
9652         { // GR_640
9653                 { "Cancel",                     387,    400,    141,    UI_XSTR_COLOR_GREEN, -1,        &Multi_pwd_buttons[0][MPWD_CANCEL].button},
9654                 { "Commit",                     1062,   455,    141,    UI_XSTR_COLOR_GREEN, -1,        &Multi_pwd_buttons[0][MPWD_COMMIT].button},
9655                 { "Enter Password",     1332,   149,    92,     UI_XSTR_COLOR_GREEN, -1,        NULL},
9656         },
9657         { // GR_1024
9658                 { "Cancel",                     387,    649,    225,    UI_XSTR_COLOR_GREEN, -1,        &Multi_pwd_buttons[1][MPWD_CANCEL].button},
9659                 { "Commit",                     1062,   736,    225,    UI_XSTR_COLOR_GREEN, -1,        &Multi_pwd_buttons[1][MPWD_COMMIT].button},
9660                 { "Enter Password",     1332,   239,    148,    UI_XSTR_COLOR_GREEN, -1,        NULL},
9661         }
9662 };
9663 #endif
9664
9665 // initialize all graphics, etc
9666 void multi_passwd_init()
9667 {
9668         int idx;
9669
9670         // store the background as it currently is
9671         Multi_passwd_background = gr_save_screen();     
9672         
9673         // create the interface window
9674         Multi_pwd_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
9675         Multi_pwd_window.set_mask_bmap(Multi_pwd_bitmap_mask_fname[gr_screen.res]);
9676
9677         // load the background bitmap
9678         Multi_pwd_bitmap = bm_load(Multi_pwd_bitmap_fname[gr_screen.res]);
9679         if(Multi_pwd_bitmap < 0){
9680                 // we failed to load the bitmap - this is very bad
9681                 Int3();
9682         }
9683                         
9684         // create the interface buttons
9685         for(idx=0; idx<MULTI_PWD_NUM_BUTTONS; idx++){
9686                 // create the object
9687                 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);
9688
9689                 // set the sound to play when highlighted
9690                 Multi_pwd_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
9691
9692                 // set the ani for the button
9693                 Multi_pwd_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pwd_buttons[gr_screen.res][idx].filename);
9694
9695                 // set the hotspot
9696                 Multi_pwd_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pwd_buttons[gr_screen.res][idx].hotspot);
9697         }       
9698
9699 #ifndef MAKE_FS1
9700         // add all xstrs
9701         for(idx=0; idx<MULTI_PWD_NUM_TEXT; idx++){
9702                 Multi_pwd_window.add_XSTR(&Multi_pwd_text[gr_screen.res][idx]);
9703         }
9704 #endif
9705         
9706         // create the password input box
9707         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);
9708         Multi_pwd_passwd.set_focus();
9709         
9710         // link the enter key to ACCEPT
9711         Multi_pwd_buttons[gr_screen.res][MPWD_COMMIT].button.set_hotkey(SDLK_RETURN);
9712
9713         Multi_passwd_done = -1;
9714         Multi_passwd_running = 1;
9715 }
9716
9717 // close down all graphics, etc
9718 void multi_passwd_close()
9719 {
9720         // unload any bitmaps
9721         bm_release(Multi_pwd_bitmap);           
9722                 
9723         // destroy the UI_WINDOW
9724         Multi_pwd_window.destroy();
9725
9726         // free up the saved background screen
9727         if(Multi_passwd_background >= 0){
9728                 gr_free_screen(Multi_passwd_background);        
9729                 Multi_passwd_background = -1;
9730         }
9731
9732         Multi_passwd_running = 0;
9733 }
9734
9735 // process any button pressed
9736 void multi_passwd_process_buttons()
9737 {
9738         // if the accept button was pressed
9739         if(Multi_pwd_buttons[gr_screen.res][MPWD_COMMIT].button.pressed()){
9740                 gamesnd_play_iface(SND_USER_SELECT);
9741                 Multi_passwd_done = 1;
9742         }
9743
9744         // if the cancel button was pressed
9745         if(Multi_pwd_buttons[gr_screen.res][MPWD_CANCEL].button.pressed()){
9746                 gamesnd_play_iface(SND_USER_SELECT);
9747                 Multi_passwd_done = 0;
9748         }
9749 }
9750
9751 // run the passwd popup
9752 void multi_passwd_do(char *passwd)
9753 {
9754         int k;
9755
9756         while(Multi_passwd_done == -1){ 
9757                 // set frametime and run background stuff
9758                 game_set_frametime(-1);
9759                 game_do_state_common(gameseq_get_state());
9760
9761                 k = Multi_pwd_window.process();
9762
9763                 // process any keypresses
9764                 switch(k){
9765                 case SDLK_ESCAPE :
9766                         // set this to indicate the user has cancelled for one reason or another
9767                         Multi_passwd_done = 0;
9768                         break;          
9769                 }       
9770
9771                 // if the input box text has changed
9772                 if(Multi_pwd_passwd.changed()){
9773                         strcpy(passwd,"");
9774                         Multi_pwd_passwd.get_text(passwd);
9775                 }
9776
9777                 // process any button pressed
9778                 multi_passwd_process_buttons();
9779         
9780                 // draw the background, etc
9781                 gr_reset_clip();
9782                 gr_clear();
9783                 if(Multi_passwd_background >= 0){
9784                         gr_restore_screen(Multi_passwd_background);             
9785                 }
9786                 gr_set_bitmap(Multi_pwd_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
9787                 gr_bitmap(0,0);
9788                 Multi_pwd_window.draw();
9789                         
9790                 // flip the buffer
9791                 gr_flip();
9792         }
9793 }
9794
9795 // bring up the password string popup, fill in passwd (return 1 if accept was pressed, 0 if cancel was pressed)
9796 int multi_passwd_popup(char *passwd)
9797 {
9798         // if the popup is already running for some reason, don't do anything
9799         if(Multi_passwd_running){
9800                 return 0;
9801         }
9802
9803         // initialize all graphics
9804         multi_passwd_init();
9805
9806         // run the popup
9807         multi_passwd_do(passwd);
9808
9809         // shut everything down
9810         multi_passwd_close();
9811
9812         return Multi_passwd_done;
9813 }