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