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