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