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