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