use a better multi_sw_ok_to_commit() check
[taylor/freespace2.git] / src / network / multiui.cpp
1 /*
2  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell 
5  * or otherwise commercially exploit the source or things you created based on
6  * the source.
7  */
8
9 /*
10  * $Logfile: /Freespace2/code/Network/MultiUI.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  *
15  * C file for all the UI controls of the mulitiplayer screens
16  *
17  * $Log$
18  * Revision 1.12  2005/10/02 09:27:49  taylor
19  * fix interface problems with MultiJoinWait screen in FS1
20  *
21  * Revision 1.11  2005/03/29 02:18:47  taylor
22  * Various 64-bit platform fixes
23  * Fix compiler errors with MAKE_FS1 and fix gr_set_bitmap() too
24  * Make sure that turrets can fire at asteroids for FS1 (needed for a couple missions)
25  * Streaming audio support (big thanks to Pierre Willenbrock!!)
26  * Removed dependance on strings.tbl for FS1 since we don't actually need it now
27  *
28  * Revision 1.10  2004/09/20 01:31:44  theoddone33
29  * GCC 3.4 fixes.
30  *
31  * Revision 1.9  2004/07/04 11:39:06  taylor
32  * fix missing debrief text, crash on exit, path separator's, warning fixes, no GR_SOFT
33  *
34  * Revision 1.8  2003/05/25 02:30:43  taylor
35  * Freespace 1 support
36  *
37  * Revision 1.7  2002/06/09 04:41:24  relnev
38  * added copyright header
39  *
40  * Revision 1.6  2002/06/02 06:02:59  relnev
41  * tcp.cfg namefix
42  *
43  * Revision 1.5  2002/06/02 00:31:35  relnev
44  * implemented osregistry
45  *
46  * Revision 1.4  2002/06/01 07:12:33  relnev
47  * a few NDEBUG updates.
48  *
49  * removed a few warnings.
50  *
51  * Revision 1.3  2002/05/26 20:49:54  theoddone33
52  * More progress
53  *
54  * Revision 1.2  2002/05/07 03:16:47  theoddone33
55  * The Great Newline Fix
56  *
57  * Revision 1.1.1.1  2002/05/03 03:28:10  root
58  * Initial import.
59  *
60  * 
61  * 94    6/16/00 3:16p Jefff
62  * sim of the year dvd version changes, a few german soty localization
63  * fixes
64  * 
65  * 93    10/14/99 2:51p Jefff
66  * localization fixes
67  * 
68  * 92    10/13/99 3:50p Jefff
69  * fixed unnumbered XSTRs
70  * 
71  * 91    9/15/99 1:45a Dave
72  * Don't init joystick on standalone. Fixed campaign mode on standalone.
73  * Fixed no-score-report problem in TvT
74  * 
75  * 90    9/14/99 12:51a Jefff
76  * small text nudge
77  * 
78  * 89    9/13/99 4:52p Dave
79  * RESPAWN FIX
80  * 
81  * 88    9/13/99 11:30a Dave
82  * Added checkboxes and functionality for disabling PXO banners as well as
83  * disabling d3d zbuffer biasing.
84  * 
85  * 87    9/12/99 10:06p Jefff
86  * changed instances of "Squad War" to "SquadWar"
87  * 
88  * 86    9/03/99 1:32a Dave
89  * CD checking by act. Added support to play 2 cutscenes in a row
90  * seamlessly. Fixed super low level cfile bug related to files in the
91  * root directory of a CD. Added cheat code to set campaign mission # in
92  * main hall.
93  * 
94  * 85    9/01/99 10:49p Dave
95  * Added nice SquadWar checkbox to the client join wait screen.
96  * 
97  * 84    8/30/99 2:49p Jefff
98  * 
99  * 83    8/26/99 8:49p Jefff
100  * Updated medals screen and about everything that ever touches medals in
101  * one way or another.  Sheesh.
102  * 
103  * 82    8/25/99 4:38p Dave
104  * Updated PXO stuff. Make squad war report stuff much more nicely.
105  * 
106  * 81    8/20/99 2:09p Dave
107  * PXO banner cycling.
108  * 
109  * 80    8/20/99 10:06a Jefff
110  * removed closed/rstricted buttons from multi start screen
111  * 
112  * 79    8/18/99 11:30a Jefff
113  * 
114  * 78    8/18/99 10:38a Jefff
115  * 
116  * 77    8/16/99 4:06p Dave
117  * Big honking checkin.
118  * 
119  * 76    8/16/99 1:08p Jefff
120  * added sounds to a few controls, made input boxes lose focus on ENTER
121  * 
122  * 75    8/16/99 9:52a Jefff
123  * fixed bitmap loading on buttons in multi-sync screen
124  * 
125  * 74    8/11/99 5:54p Dave
126  * Fixed collision problem. Fixed standalone ghost problem.
127  * 
128  * 73    8/10/99 4:35p Jefff
129  * fixed hi-res coords
130  * 
131  * 72    8/06/99 12:29a Dave
132  * Multiple bug fixes.
133  * 
134  * 71    8/05/99 3:13p Jasenw
135  * tweaked some text placement coords.
136  * 
137  * 70    8/04/99 1:38p Jefff
138  * moved some text in multi join wait
139  * 
140  * 69    8/03/99 12:45p Dave
141  * Update checksums.
142  * 
143  * 68    7/25/99 5:17p Jefff
144  * campaign descriptions show up on multicreate screen
145  * 
146  * 67    7/20/99 1:49p Dave
147  * Peter Drake build. Fixed some release build warnings.
148  * 
149  * 66    7/19/99 2:13p Dave
150  * Added some new strings for Heiko.
151  * 
152  * 65    7/15/99 9:20a Andsager
153  * FS2_DEMO initial checkin
154  * 
155  * 64    7/08/99 10:53a Dave
156  * New multiplayer interpolation scheme. Not 100% done yet, but still
157  * better than the old way.
158  * 
159  * 63    6/30/99 10:49a Jasenw
160  * Fixed coords for new launch countdown ani
161  * 
162  * 62    6/29/99 7:39p Dave
163  * Lots of small bug fixes.
164  * 
165  * 61    6/25/99 11:59a Dave
166  * Multi options screen.
167  * 
168  * 60    6/09/99 2:17p Dave
169  * Fixed up pleasewait bitmap rendering.
170  * 
171  * 59    6/05/99 3:42p Dave
172  * New multi sync screen.
173  * 
174  * 58    6/01/99 6:07p Dave
175  * New loading/pause/please wait bar.
176  * 
177  * 57    5/21/99 6:45p Dave
178  * Sped up ui loading a bit. Sped up localization disk access stuff. Multi
179  * start game screen, multi password, and multi pxo-help screen.
180  * 
181  * 56    5/06/99 11:10a Dave
182  * Fixed coord on multi create screen.
183  * 
184  * 55    5/04/99 6:38p Dave
185  * Finished multi join-wait screen.
186  * 
187  * 54    5/04/99 5:20p Dave
188  * Fixed up multiplayer join screen and host options screen. Should both
189  * be at 100% now.
190  * 
191  * 53    5/03/99 11:04p Dave
192  * Most of the way done with the multi join screen.
193  * 
194  * 52    5/03/99 8:32p Dave
195  * New version of multi host options screen.
196  * 
197  * 51    4/29/99 2:15p Neilk
198  * slider2 code got modified; changed parameters for create
199  * 
200  * 50    4/25/99 3:02p Dave
201  * Build defines for the E3 build.
202  * 
203  * 49    4/21/99 6:15p Dave
204  * Did some serious housecleaning in the beam code. Made it ready to go
205  * for anti-fighter "pulse" weapons. Fixed collision pair creation. Added
206  * a handy macro for recalculating collision pairs for a given object.
207  * 
208  * 48    4/16/99 5:27p Neilk
209  * added slider support and hir res for multi_create
210  * 0
211  * 47    4/14/99 6:37p Dave
212  * Fixed scroll button bug on host create screen.
213  * 
214  * 46    4/14/99 5:28p Dave
215  * Minor bug fixes.
216  * 
217  * 45    4/12/99 10:07p Dave
218  * Made network startup more forgiving. Added checkmarks to dogfight
219  * screen for players who hit commit.
220  * 
221  * 44    4/09/99 2:21p Dave
222  * Multiplayer beta stuff. CD checking.
223  * 
224  * 43    4/08/99 1:28p Dave
225  * Small bug fixes for refresh button on the multi create screen.
226  * 
227  * 42    4/08/99 11:55a Neilk
228  * Converted Multi_Create to new artwork (just lowres)
229  * 
230  * 41    4/08/99 2:10a Dave
231  * Numerous bug fixes for the beta. Added builtin mission info for the
232  * beta.
233  * 
234  * 40    3/20/99 3:48p Andsager
235  * Do mission_loop stuff for PXO
236  * 
237  * 39    3/10/99 6:50p Dave
238  * Changed the way we buffer packets for all clients. Optimized turret
239  * fired packets. Did some weapon firing optimizations.
240  * 
241  * 38    3/09/99 6:24p Dave
242  * More work on object update revamping. Identified several sources of
243  * unnecessary bandwidth.
244  * 
245  * 37    3/08/99 7:03p Dave
246  * First run of new object update system. Looks very promising.
247  * 
248  * 36    2/25/99 4:19p Dave
249  * Added multiplayer_beta defines. Added cd_check define. Fixed a few
250  * release build warnings. Added more data to the squad war request and
251  * response packets.
252  * 
253  * 35    2/24/99 3:26p Anoop
254  * Make sure the host is the only guy who bashes skill level for TvT.
255  * 
256  * 34    2/24/99 2:25p Dave
257  * Fixed up chatbox bugs. Made squad war reporting better. Fixed a respawn
258  * bug for dogfight more.
259  * 
260  * 33    2/23/99 2:29p Dave
261  * First run of oldschool dogfight mode. 
262  * 
263  * 32    2/17/99 2:11p Dave
264  * First full run of squad war. All freespace and tracker side stuff
265  * works.
266  * 
267  * 31    2/12/99 6:16p Dave
268  * Pre-mission Squad War code is 95% done.
269  * 
270  * 30    2/11/99 3:08p Dave
271  * PXO refresh button. Very preliminary squad war support.
272  * 
273  * 29    2/08/99 5:07p Dave
274  * FS2 chat server support. FS2 specific validated missions.
275  * 
276  * 28    2/04/99 6:29p Dave
277  * First full working rev of FS2 PXO support.  Fixed Glide lighting
278  * problems.
279  * 
280  * 27    1/30/99 5:08p Dave
281  * More new hi-res stuff.Support for nice D3D textures.
282  * 
283  * 26    1/29/99 2:08a Dave
284  * Fixed beam weapon collisions with players. Reduced size of scoring
285  * struct for multiplayer. Disabled PXO.
286  * 
287  * 25    1/15/99 2:36p Neilk
288  * fixed multi_jw coordinates
289  * 
290  * 24    1/13/99 7:19p Neilk
291  * Converted Mission Brief, Barracks, Synch to high res support
292  * 
293  * 23    1/12/99 7:17p Neilk
294  * 
295  * 22    1/12/99 5:45p Dave
296  * Moved weapon pipeline in multiplayer to almost exclusively client side.
297  * Very good results. Bandwidth goes down, playability goes up for crappy
298  * connections. Fixed object update problem for ship subsystems.
299  * 
300  * 21    1/12/99 4:07a Dave
301  * Put in barracks code support for selecting squad logos. Properly
302  * distribute squad logos in a multiplayer game.
303  * 
304  * 20    1/11/99 7:19p Neilk
305  * Converted multi_join interface to support multiple resolutions
306  * 
307  * 19    12/18/98 1:13a Dave
308  * Rough 1024x768 support for Direct3D. Proper detection and usage through
309  * the launcher.
310  * 
311  * 18    12/17/98 4:50p Andsager
312  * Added debrief_assemble_optional_mission_popup_text() for single and
313  * multiplayer
314  * 
315  * 17    12/14/98 12:13p Dave
316  * Spiffed up xfer system a bit. Put in support for squad logo file xfer.
317  * Need to test now.
318  * 
319  * 16    12/10/98 10:19a Andsager
320  * Fix mission loop assert
321  * 
322  * 15    12/10/98 9:59a Andsager
323  * Fix some bugs with mission loops
324  * 
325  * 14    12/09/98 1:56p Andsager
326  * Initial checkin of mission loop
327  * 
328  * 13    12/03/98 5:22p Dave
329  * Ported over Freespace 1 multiplayer ships.tbl and weapons.tbl
330  * checksumming.
331  * 
332  * 12    11/30/98 1:07p Dave
333  * 16 bit conversion, first run.
334  * 
335  * 11    11/20/98 11:16a Dave
336  * Fixed up IPX support a bit. Making sure that switching modes and
337  * loading/saving pilot files maintains proper state.
338  * 
339  * 10    11/19/98 4:57p Dave
340  * Ignore PXO option if IPX is selected.
341  * 
342  * 9     11/19/98 4:19p Dave
343  * Put IPX sockets back in psnet. Consolidated all multiplayer config
344  * files into one.
345  * 
346  * 8     11/19/98 8:04a Dave
347  * Full support for D3-style reliable sockets. Revamped packet lag/loss
348  * system, made it receiver side and at the lowest possible level.
349  * 
350  * 7     11/17/98 11:12a Dave
351  * Removed player identification by address. Now assign explicit id #'s.
352  * 
353  * 6     10/19/98 11:15a Dave
354  * Changed requirements for stats storing in PXO mode.
355  * 
356  * 5     10/16/98 9:40a Andsager
357  * Remove ".h" files from model.h
358  * 
359  * 4     10/13/98 9:29a Dave
360  * Started neatening up freespace.h. Many variables renamed and
361  * reorganized. Added AlphaColors.[h,cpp]
362  * 
363  * 3     10/07/98 6:27p Dave
364  * Globalized mission and campaign file extensions. Removed Silent Threat
365  * special code. Moved \cache \players and \multidata into the \data
366  * directory.
367  * 
368  * 2     10/07/98 10:53a Dave
369  * Initial checkin.
370  * 
371  * 1     10/07/98 10:50a Dave
372  * 
373  * 333   10/02/98 3:22p Allender
374  * fix up the -connect option and fix the -port option
375  * 
376  * 332   9/17/98 9:26p Dave
377  * Externalized new string.
378  * 
379  * 331   9/17/98 3:08p Dave
380  * PXO to non-pxo game warning popup. Player icon stuff in create and join
381  * game screens. Upped server count refresh time in PXO to 35 secs (from
382  * 20).
383  * 
384  * 330   9/17/98 9:43a Allender
385  * removed an SDL_assert that Dave called bogus.
386  * 
387  * 329   9/16/98 6:54p Dave
388  * Upped  max sexpression nodes to 1800 (from 1600). Changed FRED to sort
389  * the ship list box. Added code so that tracker stats are not stored with
390  * only 1 player.
391  * 
392  * 328   9/15/98 7:24p Dave
393  * Minor UI changes. Localized bunch of new text.
394  * 
395  * 327   9/15/98 4:03p Dave
396  * Changed readyroom and multi screens to display "st" icon for all
397  * missions with mission disk content (not necessarily just those that
398  * come with Silent Threat).
399  * 
400  * 326   9/15/98 11:44a Dave
401  * Renamed builtin ships and wepaons appropriately in FRED. Put in scoring
402  * scale factors. Fixed standalone filtering of MD missions to non-MD
403  * hosts.
404  * 
405  * 325   9/13/98 9:36p Dave
406  * Support for new info icons for multiplayer missions (from-volition,
407  * valid, mission disk, etc).
408  * 
409  * 324   9/11/98 4:14p Dave
410  * Fixed file checksumming of < file_size. Put in more verbose kicking and
411  * PXO stats store reporting.
412  * 
413  * 323   9/10/98 1:17p Dave
414  * Put in code to flag missions and campaigns as being MD or not in Fred
415  * and Freespace. Put in multiplayer support for filtering out MD
416  * missions. Put in multiplayer popups for warning of non-valid missions.
417  * 
418  * 322   9/04/98 3:51p Dave
419  * Put in validated mission updating and application during stats
420  * updating.
421  * 
422  * 321   8/31/98 2:06p Dave
423  * Make cfile sort the ordering or vp files. Added support/checks for
424  * recognizing "mission disk" players.
425  * 
426  * 320   8/21/98 1:15p Dave
427  * Put in log system hooks in useful places.
428  * 
429  * 319   8/20/98 5:31p Dave
430  * Put in handy multiplayer logfile system. Now need to put in useful
431  * applications of it all over the code.
432  * 
433  * 318   8/12/98 4:53p Dave
434  * Put in 32 bit checksumming for PXO missions. No validation on the
435  * actual tracker yet, though.
436  * 
437  * 317   8/07/98 10:40a Allender
438  * new command line flags for starting netgames.  Only starting currently
439  * works, and PXO isn't implemented yet
440  * 
441  * 316   7/24/98 9:27a Dave
442  * Tidied up endgame sequencing by removing several old flags and
443  * standardizing _all_ endgame stuff with a single function call.
444  * 
445  * 315   7/14/98 10:04a Allender
446  * fixed the countdown code to not be reliant on timer_get_fixed_seconds
447  * 
448  * 314   7/10/98 5:04p Dave
449  * Fix connection speed bug on standalone server.
450  * 
451  * 313   7/09/98 6:01p Dave
452  * Firsts full version of PXO updater. Put in stub for displaying
453  * connection status.
454  * 
455  * 312   7/07/98 2:49p Dave
456  * UI bug fixes.
457  * 
458  * 311   6/30/98 2:17p Dave
459  * Revised object update system. Removed updates for all weapons. Put
460  * button info back into control info packet.
461  * 
462  * 310   6/13/98 9:32p Mike
463  * Kill last character in file which caused "Find in Files" to report the
464  * file as "not a text file." 
465  *
466  * $NoKeywords: $
467  */
468
469 #ifndef PLAT_UNIX
470 #include <winsock2.h>   // for inet_addr()
471 #else
472 #include <sys/types.h>
473 #include <sys/socket.h>
474 #include <netinet/in.h>
475 #include <arpa/inet.h>
476 #include <netdb.h>
477 #endif
478 #include "multi.h"
479 #include "multiui.h"
480 #include "multiutil.h"
481 #include "multimsgs.h"
482 #include "multi.h"
483 #include "ui.h"
484 #include "2d.h"
485 #include "key.h"
486 #include "timer.h"
487 #include "gamesequence.h"
488 #include "freespace.h"
489 #include "contexthelp.h"
490 #include "psnet.h"
491 #include "player.h"
492 #include "cfile.h"
493 #include "ship.h"
494 #include "missionshipchoice.h"
495 #include "multi_xfer.h"
496 #include "cmdline.h"
497 #include "stand_server.h"
498 #include "linklist.h"
499 #include "multiteamselect.h"
500 #include "missioncampaign.h"
501 #include "bmpman.h"
502 #include "font.h"
503 #include "mouse.h"
504 #include "gamesnd.h"
505 #include "chatbox.h"
506 #include "popup.h"
507 #include "missiondebrief.h"
508 #include "multi_ingame.h"
509 #include "multi_kick.h"
510 #include "multi_data.h"
511 #include "multi_campaign.h"
512 #include "multi_team.h"
513 #include "multi_pinfo.h"
514 #include "multi_observer.h"
515 #include "multi_voice.h"
516 #include "multi_endgame.h"
517 #include "managepilot.h"
518 #include "stats.h"
519 #include "object.h"
520 #include "objcollide.h"
521 #include "palman.h"
522 #include "multi_pmsg.h"
523 #include "multi_obj.h"
524 #include "multi_log.h"
525 #include "alphacolors.h"
526 #include "animplay.h"
527 #include "multi_dogfight.h"
528 #include "missionpause.h"
529 #include "multi_fstracker.h"
530 #include "multi_sw.h"
531
532 // -------------------------------------------------------------------------------------------------------------
533 // 
534 // MULTIPLAYER COMMON interface controls
535 //
536
537 // the common text info box stuff. This is lifted almost directly from Alans briefing code (minus the spiffy colored, scrolling
538 // text crap :)
539 int Multi_common_text_coords[GR_NUM_RESOLUTIONS][4] = {
540         { // GR_640
541 #ifdef MAKE_FS1
542                 47, 405, 374, 58
543 #else
544                 29, 396, 393, 76
545 #endif
546         },
547         { // GR_1024
548                 47, 634, 630, 122
549         }
550 };
551
552 int Multi_common_text_max_display[GR_NUM_RESOLUTIONS] = {
553 #ifdef MAKE_FS1
554         6,
555 #else
556         8,              // GR_640
557 #endif
558         12,     // GR_1024
559 };
560
561 #define MULTI_COMMON_TEXT_META_CHAR                             '$'
562 #define MULTI_COMMON_TEXT_MAX_LINE_LENGTH               100
563 #define MULTI_COMMON_TEXT_MAX_LINES                             20
564 #define MULTI_COMMON_MAX_TEXT                                           (MULTI_COMMON_TEXT_MAX_LINES * MULTI_COMMON_TEXT_MAX_LINE_LENGTH)
565
566 char Multi_common_all_text[MULTI_COMMON_MAX_TEXT];
567 char Multi_common_text[MULTI_COMMON_TEXT_MAX_LINES][MULTI_COMMON_TEXT_MAX_LINE_LENGTH];
568
569 int Multi_common_top_text_line = -1;            // where to start displaying from
570 int Multi_common_num_text_lines = 0;            // how many lines we have
571
572 void multi_common_scroll_text_up();
573 void multi_common_scroll_text_down();
574 void multi_common_move_to_bottom();
575 void multi_common_render_text();
576 void multi_common_split_text();
577
578 #define MAX_IP_STRING           255                             // maximum length for ip string
579
580 void multi_common_scroll_text_up()
581 {
582         Multi_common_top_text_line--;
583         if ( Multi_common_top_text_line < 0 ) {
584                 Multi_common_top_text_line = 0;
585                 if ( !mouse_down(MOUSE_LEFT_BUTTON) )
586                         gamesnd_play_iface(SND_GENERAL_FAIL);
587
588         } else {
589                 gamesnd_play_iface(SND_SCROLL);
590         }
591 }
592
593 void multi_common_scroll_text_down()
594 {
595         Multi_common_top_text_line++;
596         if ( (Multi_common_num_text_lines - Multi_common_top_text_line) < Multi_common_text_max_display[gr_screen.res] ) {
597                 Multi_common_top_text_line--;
598                 if ( !mouse_down(MOUSE_LEFT_BUTTON) ){
599                         gamesnd_play_iface(SND_GENERAL_FAIL);
600                 }
601         } else {
602                 gamesnd_play_iface(SND_SCROLL);
603         }
604 }
605
606 void multi_common_move_to_bottom()
607 {
608         // if there's nowhere to scroll down, do nothing
609         if(Multi_common_num_text_lines <= Multi_common_text_max_display[gr_screen.res]){
610                 return;
611         }
612                 
613         Multi_common_top_text_line = Multi_common_num_text_lines - Multi_common_text_max_display[gr_screen.res];
614 }
615
616 void multi_common_set_text(const char *str, int auto_scroll)
617 {
618         // make sure it fits
619         // store the entire string as well
620         if(strlen(str) > MULTI_COMMON_MAX_TEXT){
621                 return ;
622         } else {
623                 SDL_strlcpy(Multi_common_all_text, str, SDL_arraysize(Multi_common_all_text));
624         }
625         
626         // split the whole thing up
627         multi_common_split_text();
628
629         // scroll to the bottom if we're supposed to
630         if(auto_scroll){
631                 multi_common_move_to_bottom();
632         }
633 }
634
635 void multi_common_add_text(const char *str, int auto_scroll)
636 {
637         // make sure it fits
638         // store the entire string as well
639         if((strlen(str) + strlen(Multi_common_all_text)) > MULTI_COMMON_MAX_TEXT){
640                 return ;
641         } else {
642                 SDL_strlcat(Multi_common_all_text, str, SDL_arraysize(Multi_common_all_text));
643         }
644         
645         // split the whole thing up
646         multi_common_split_text();
647
648         // scroll to the bottom if we're supposed to
649         if(auto_scroll){
650                 multi_common_move_to_bottom();
651         }
652 }
653
654 void multi_common_split_text()
655 {
656         int     n_lines, i;
657         int     n_chars[MAX_BRIEF_LINES];
658         char    *p_str[MAX_BRIEF_LINES];
659
660         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);
661         SDL_assert(n_lines != -1);
662
663         for ( i = 0; i < n_lines; i++ ) {
664                 SDL_assert(n_chars[i] < MULTI_COMMON_TEXT_MAX_LINE_LENGTH);
665                 int len = SDL_min(n_chars[i] + 1, MULTI_COMMON_TEXT_MAX_LINE_LENGTH);
666                 SDL_strlcpy(Multi_common_text[i], p_str[i], len);
667                 Multi_common_text[i][n_chars[i]] = 0;
668                 drop_leading_white_space(Multi_common_text[i]);         
669         }
670
671         Multi_common_top_text_line = 0;
672         Multi_common_num_text_lines = n_lines;  
673 }
674
675 void multi_common_render_text()
676 {
677         int i, fh, line_count;
678
679         fh = gr_get_font_height();
680         
681         line_count = 0;
682         gr_set_color_fast(&Color_text_normal);
683         for ( i = Multi_common_top_text_line; i < Multi_common_num_text_lines; i++ ) {
684                 if ( line_count >= Multi_common_text_max_display[gr_screen.res] ){
685                         break;  
686                 }
687                 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]);              
688                 line_count++;
689         }
690
691         if ( (Multi_common_num_text_lines - Multi_common_top_text_line) > Multi_common_text_max_display[gr_screen.res] ) {
692                 gr_set_color_fast(&Color_bright_red);
693                 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));
694         }
695 }
696
697 // common notification messaging stuff
698 #define MULTI_COMMON_NOTIFY_TIME                3500
699 int Multi_common_join_y[GR_NUM_RESOLUTIONS] = {
700         375,            // GR_640
701         605             // GR_1024
702 };
703 int Multi_common_create_y[GR_NUM_RESOLUTIONS] = {
704         380,            // GR_640
705         610             // GR_1024
706 };
707
708 int Multi_common_jw_y[GR_NUM_RESOLUTIONS] = {
709         380,            // GR_640
710         610             // GR_1024
711 };
712
713 int Multi_common_msg_y[GR_NUM_RESOLUTIONS] = {
714         380,            // GR_640
715         610             // GR_1024
716 };
717
718 char Multi_common_notify_text[200];
719 int Multi_common_notify_stamp;
720
721 void multi_common_notify_init()
722 {
723         SDL_strlcpy(Multi_common_notify_text, "", SDL_arraysize(Multi_common_notify_text));
724         Multi_common_notify_stamp = -1;
725 }
726
727 // add a notification string, drawing appropriately depending on the state/screen we're in
728 void multi_common_add_notify(const char *str)
729 {
730         if(str){
731                 SDL_strlcpy(Multi_common_notify_text, str, SDL_arraysize(Multi_common_notify_text));
732                 Multi_common_notify_stamp = timestamp(MULTI_COMMON_NOTIFY_TIME);
733         }
734 }
735
736 // process/display notification messages
737 void multi_common_notify_do()
738 {
739         if(Multi_common_notify_stamp != -1){
740                 if(timestamp_elapsed(Multi_common_notify_stamp)){
741                         Multi_common_notify_stamp = -1;
742                 } else {
743                         int w,h,y;
744                         gr_get_string_size(&w,&h,Multi_common_notify_text);
745                         gr_set_color_fast(&Color_white);
746                         
747                         // determine where it should be placed based upon which screen we're on
748                         y = -1;
749                         switch(gameseq_get_state()){
750                         case GS_STATE_MULTI_JOIN_GAME :
751                                 y = Multi_common_join_y[gr_screen.res];
752                                 break;
753                         case GS_STATE_MULTI_HOST_SETUP :
754                                 y = Multi_common_create_y[gr_screen.res];
755                                 break;
756                         case GS_STATE_MULTI_CLIENT_SETUP :
757                                 y = Multi_common_jw_y[gr_screen.res];
758                                 break;
759                         case GS_STATE_MULTI_START_GAME :
760                                 y = Multi_common_msg_y[gr_screen.res];
761                                 break;                  
762                         }
763                         if(y != -1){
764                                 gr_string((gr_screen.max_w - w)/2, y, Multi_common_notify_text);
765                         }
766                 }
767         }
768 }
769
770 // common icon stuff
771 int Multi_common_icons[MULTI_NUM_COMMON_ICONS];
772 //XSTR:OFF
773 const char *Multi_common_icon_names[MULTI_NUM_COMMON_ICONS] = {
774         "DotRed",                               // voice denied
775         "DotGreen",                             // voice recording
776         "OvalGreen",                    // team 0
777         "OvalGreen01",                  // team 0 select
778         "OvalRed",                              // team 1
779         "OvalRed01",                    // team 1 select
780         "mp_coop",                              // coop mission
781         "mp_teams",                             // TvT mission
782         "mp_furball",                   // furball mission
783         "icon-volition",                // volition mission     
784         "icon-valid",                   // mission is valid
785 #ifndef MAKE_FS1
786         "cd"                                            // cd icon
787 #else
788         "cd",                                   // cd icon
789         "icon-silent"                   // SilentThreat
790 #endif
791 };
792 //XSTR:ON
793 // width and height of the icons
794 int Multi_common_icon_dims[MULTI_NUM_COMMON_ICONS][2] = {
795         {11, 11},                               // voice denied
796         {11, 11},                               // voice recording
797         {11, 11},                               // team 0
798         {11, 11},                               // team 0 select
799         {11, 11},                               // team 1
800         {11, 11},                               // team 1 select
801         {18, 11},                               // mp coop
802         {18, 11},                               // mp TvT
803         {18, 11},                               // mp furball
804         {9, 9},                                 // volition mission     
805         {8, 8},                                 // mission is valid
806 #ifndef MAKE_FS1
807         {8, 8}                                  // cd icon
808 #else
809         {8, 8},                                 // cd icon
810         {16, 7}                                 // silent threat
811 #endif
812 };
813
814 void multi_load_common_icons()
815 {
816         int idx;
817
818         // load all icons
819         for(idx=0; idx<MULTI_NUM_COMMON_ICONS; idx++){
820                 Multi_common_icons[idx] = -1;
821                 Multi_common_icons[idx] = bm_load(Multi_common_icon_names[idx]);
822         }
823 }
824
825 void multi_unload_common_icons()
826 {
827         int idx;
828
829         // unload all icons
830         for(idx=0; idx<MULTI_NUM_COMMON_ICONS; idx++){
831                 if(Multi_common_icons[idx] != -1){
832                         bm_unload(Multi_common_icons[idx]);
833                         Multi_common_icons[idx] = -1;
834                 }
835         }
836 }
837
838 // display any relevant voice status icons
839 void multi_common_voice_display_status()
840 {       
841         switch(multi_voice_status()){
842         // i have been denied the voice token
843         case MULTI_VOICE_STATUS_DENIED:
844                 if(Multi_common_icons[MICON_VOICE_DENIED] != -1){
845                         gr_set_bitmap(Multi_common_icons[MICON_VOICE_DENIED], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
846                         gr_bitmap(0,0);
847                 }
848                 break;
849
850         // i am currently recording
851         case MULTI_VOICE_STATUS_RECORDING:
852                 if(Multi_common_icons[MICON_VOICE_RECORDING] != -1){
853                         gr_set_bitmap(Multi_common_icons[MICON_VOICE_RECORDING], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
854                         gr_bitmap(0,0);
855                 }
856                 break;
857
858         // i am currently playing back sound
859         case MULTI_VOICE_STATUS_PLAYING:
860                 break;
861
862         // the system is currently idle
863         case MULTI_VOICE_STATUS_IDLE:
864                 break;
865         }
866 }
867
868 //XSTR:OFF
869 // palette initialization stuff
870 #define MULTI_COMMON_PALETTE_FNAME                      "InterfacePalette"
871 //XSTR:ON
872
873 int Multi_common_interface_palette = -1;
874
875 void multi_common_load_palette();
876 void multi_common_set_palette();
877 void multi_common_unload_palette();
878
879 // load in the palette if it doesn't already exist
880 void multi_common_load_palette()
881 {
882         if(Multi_common_interface_palette != -1){
883                 return;
884         }
885
886         Multi_common_interface_palette = bm_load(MULTI_COMMON_PALETTE_FNAME);
887         if(Multi_common_interface_palette == -1){
888                 nprintf(("Network","Error loading multiplayer common palette!\n"));
889         }
890 }
891
892 // set the common palette to be the active one
893 void multi_common_set_palette()
894 {
895         // if the palette is not loaded yet, do so now
896         if(Multi_common_interface_palette == -1){
897                 multi_common_load_palette();
898         }
899 }
900
901 // unload the bitmap palette
902 void multi_common_unload_palette()
903 {
904         if(Multi_common_interface_palette != -1){
905                 bm_unload(Multi_common_interface_palette);
906                 Multi_common_interface_palette = -1;
907         }
908 }
909
910 void multi_common_verify_cd()
911 {
912         Multi_has_cd = 1;
913 }
914
915
916 // -------------------------------------------------------------------------------------------------------------
917 // 
918 // MULTIPLAYER JOIN SCREEN 
919 //
920
921 #define MULTI_JOIN_NUM_BUTTONS  11
922
923 //XSTR:OFF
924 // bitmaps defs
925 #define MULTI_JOIN_PALETTE                              "InterfacePalette"
926
927 static const char *Multi_join_bitmap_fname[GR_NUM_RESOLUTIONS] = {
928         "MultiJoin",            // GR_640
929         "2_MultiJoin"                   // GR_1024
930 };
931
932 static const char *Multi_join_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
933         "MultiJoin-M",          // GR_640
934         "2_MultiJoin-M"         // GR_1024
935 };
936 //XSTR:ON
937
938 // slider
939 #ifndef MAKE_FS1
940 const char *Mj_slider_name[GR_NUM_RESOLUTIONS] = {
941         "slider",
942         "2_slider"
943 };
944 int Mj_slider_coords[GR_NUM_RESOLUTIONS][4] = {
945         { // GR_640
946                 2, 91, 15, 202
947         },
948         { // GR_1024
949                 8, 147, 30, 322
950         }
951 };
952 #endif
953
954 // button defs
955 #define MJ_SCROLL_UP                            0
956 #define MJ_SCROLL_DOWN                  1
957 #define MJ_REFRESH                              2
958 #define MJ_SCROLL_INFO_UP               3
959 #define MJ_SCROLL_INFO_DOWN     4
960 #define MJ_JOIN_OBSERVER                5
961 #define MJ_START_GAME                   6
962 #define MJ_CANCEL                                       7
963 #define MJ_HELP                                 8
964 #define MJ_OPTIONS                              9
965 #define MJ_ACCEPT                                       10
966
967 // uses MULTI_JOIN_REFRESH_TIME as its timestamp
968 int Multi_join_glr_stamp;
969
970 #define MULTI_JOIN_PING_TIME     15000        // how often we ping all the known servers
971 int Multi_join_ping_stamp;
972 UI_WINDOW Multi_join_window;                                                                                    // the window object for the join screen
973 UI_BUTTON Multi_join_select_button;                                                                     // for selecting list items
974 #ifndef MAKE_FS1
975 UI_SLIDER2 Multi_join_slider;                                                                                   // handy dandy slider
976 #endif
977 int Multi_join_bitmap;                                                                                                  // the background bitmap
978
979 ui_button_info Multi_join_buttons[GR_NUM_RESOLUTIONS][MULTI_JOIN_NUM_BUTTONS] = {
980         { // GR_640
981 #ifdef MAKE_FS1
982                 ui_button_info("MJ_00",         0,              85,             -1,     -1,     0),
983                 ui_button_info("MJ_01",         0,              125,    -1,     -1,     1),
984                 ui_button_info("MJ_03",         20,             324,    -1,     -1,     3),
985                 ui_button_info("MJ_04",         0,              399,    -1,     -1,     4),
986                 ui_button_info("MJ_05",         0,              436,    -1,     -1,     5),
987                 ui_button_info("MJ_15",         450,    323,    -1,     -1,     15),
988                 ui_button_info("MJ_10",         519,    323,    -1,     -1,     10),
989                 ui_button_info("MJ_06",         574,    323,    -1,     -1,     6),
990                 ui_button_info("MJ_08",         470,    427,    -1,     -1,     8),
991                 ui_button_info("MJ_09",         448,    454,    -1,     -1,     9),
992                 ui_button_info("MJ_07",         563,    411,    -1,     -1,     7),
993 #else
994                 ui_button_info( "MJ_00",        1,              57,     -1,     -1,     0 ),                                            // scroll up
995                 ui_button_info( "MJ_02",        1,              297,    -1,     -1,     2 ),                                            // scroll down
996                 ui_button_info( "MJ_03",        10,     338,    65,     364,    3 ),                                            // refresh
997                 ui_button_info( "MJ_04",        1,              405,    -1,     -1,     4 ),                                            // scroll info up
998                 ui_button_info( "MJ_05",        1,              446,    -1,     -1,     5 ),                                            // scroll info down
999                 ui_button_info( "MJ_06",        489,    339,    -1,     -1,     6 ),                                            // join as observer
1000                 ui_button_info( "MJ_07",        538,    339,    -1,     -1,     7 ),                                            // create game
1001                 ui_button_info( "MJ_08",        583,    339,    588,    376,    8 ),                                            // cancel
1002                 ui_button_info( "MJ_09",        534,    426,    -1,     -1,     9 ),                                            // help
1003                 ui_button_info( "MJ_10",        534,    454,    -1,     -1,     10 ),                                           // options
1004                 ui_button_info( "MJ_11",        571,    426,    589,    416,    11 ),                                           // join
1005 #endif
1006         },
1007         { // GR_1024
1008                 ui_button_info( "2_MJ_00",      2,              92,     -1,     -1,     0 ),                                            // scroll up
1009                 ui_button_info( "2_MJ_02",      2,              475,    -1,     -1,     2 ),                                            // scroll down
1010                 ui_button_info( "2_MJ_03",      16,     541,    104,    582,    3 ),                                            // refresh
1011                 ui_button_info( "2_MJ_04",      2,              648,    -1,     -1,     4 ),                                            // scroll info up
1012                 ui_button_info( "2_MJ_05",      2,              713,    -1,     -1,     5 ),                                            // scroll info down
1013                 ui_button_info( "2_MJ_06",      783,    542,    -1,     -1,     6 ),                                            // join as observer
1014                 ui_button_info( "2_MJ_07",      861,    542,    -1,     -1,     7 ),                                            // create game
1015                 ui_button_info( "2_MJ_08",      933,    542,    588,    376,    8 ),                                            // cancel
1016                 ui_button_info( "2_MJ_09",      854,    681,    -1,     -1,     9 ),                                            // help
1017                 ui_button_info( "2_MJ_10",      854,    727,    -1,     -1,     10 ),                                           // options
1018                 ui_button_info( "2_MJ_11",      914,    681,    937,    668,    11 ),                                           // join
1019         }
1020 };
1021
1022 #ifndef MAKE_FS1
1023 #define MULTI_JOIN_NUM_TEXT                     13
1024
1025 UI_XSTR Multi_join_text[GR_NUM_RESOLUTIONS][MULTI_JOIN_NUM_TEXT] = {
1026         { // GR_640             
1027                 {"Refresh",                                                     1299, 65,       364,    UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_REFRESH].button},
1028                 {"Join as",                                                     1300,   476,    376,    UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_JOIN_OBSERVER].button},
1029                 {"Observer",                                            1301,   467,    385,    UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_JOIN_OBSERVER].button},
1030                 {"Create",                                                      1408,   535,    376,    UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_START_GAME].button}, 
1031                 {"Game",                                                                1302,   541,    385,    UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_START_GAME].button},
1032                 {"Cancel",                                                      387,    588,    376,    UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[0][MJ_CANCEL].button},      
1033                 {"Help",                                                                928,    479,    436,    UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_HELP].button},
1034                 {"Options",                                                     1036,   479,    460,    UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_OPTIONS].button},
1035                 {"Join",                                                                1303,   589,    416,    UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[0][MJ_ACCEPT].button},
1036                 {"Status",                                                      1304,   37,     37,     UI_XSTR_COLOR_GREEN,    -1, NULL},
1037                 {"Server",                                                      1305,   116,    37,     UI_XSTR_COLOR_GREEN, -1, NULL},
1038                 {"Players",                                                     1306,   471,    37,     UI_XSTR_COLOR_GREEN,    -1, NULL},
1039                 {"Ping",                                                                1307,   555,    37,     UI_XSTR_COLOR_GREEN, -1, NULL}
1040         },
1041         { // GR_1024            
1042                 {"Refresh",                                                     1299, 104,      582,    UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_REFRESH].button},
1043                 {"Join as",                                                     1300,   783,    602,    UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_JOIN_OBSERVER].button},
1044                 {"Observer",                                            1301,   774,    611,    UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_JOIN_OBSERVER].button},              
1045                 {"Create",                                                      1408,   868,    602,    UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_START_GAME].button},
1046                 {"Game",                                                                1302,   872,    611,    UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_START_GAME].button},
1047                 {"Cancel",                                                      387,    941,    602,    UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[1][MJ_CANCEL].button},
1048                 {"Help",                                                                928,    782,    699,    UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_HELP].button},
1049                 {"Options",                                                     1036,   782,    736,    UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_OPTIONS].button},
1050                 {"Join",                                                                1303,   937,    668,    UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[1][MJ_ACCEPT].button},
1051                 {"Status",                                                      1304,   60,     60,     UI_XSTR_COLOR_GREEN,    -1, NULL},
1052                 {"Server",                                                      1305,   186,    60,     UI_XSTR_COLOR_GREEN, -1, NULL},
1053                 {"Players",                                                     1306,   753,    60,     UI_XSTR_COLOR_GREEN,    -1, NULL},
1054                 {"Ping",                                                                1307,   888,    60,     UI_XSTR_COLOR_GREEN, -1, NULL}
1055         }
1056 };
1057 #endif
1058
1059 // constants for coordinate look ups
1060 #define MJ_X_COORD 0
1061 #define MJ_Y_COORD 1
1062 #define MJ_W_COORD 2
1063 #define MJ_H_COORD 3
1064
1065 #define MULTI_JOIN_SENT_WAIT            10000                                   // wait this long since a join was sent to allow another
1066 int Multi_join_sent_stamp;
1067
1068 // game information text areas
1069 int Mj_max_game_items[GR_NUM_RESOLUTIONS] = {
1070         28,                     // GR_640
1071         46                              // GR_1024
1072 };
1073
1074 int Mj_list_y[GR_NUM_RESOLUTIONS] = {
1075         53,                     // GR_640
1076         77                              // GR_1024
1077 };
1078
1079 int Mj_status_coords[GR_NUM_RESOLUTIONS][4] = {
1080         { // GR_640
1081                 34, 53, 55, 287
1082         },
1083         { // GR_1024
1084                 53, 77, 57, 459
1085         }
1086 };
1087
1088 int Mj_game_icon_coords[GR_NUM_RESOLUTIONS][3] = {
1089         { // GR_640
1090                 98, 53, 28
1091         },
1092         { // GR_1024
1093                 124, 77, 28
1094         }
1095 };      
1096
1097 int Mj_speed_coords[GR_NUM_RESOLUTIONS][4] = {
1098         { // GR_640
1099                 122, 53, 56, 261
1100         },
1101         { // GR_1024
1102                 152, 77, 56, 261
1103         }
1104 };      
1105
1106 int Mj_game_name_coords[GR_NUM_RESOLUTIONS][4] = {
1107         { // GR_640
1108                 186, 53, 280, 261
1109         },
1110         { // GR_1024
1111                 206, 77, 311, 261
1112         }
1113 };      
1114
1115 int Mj_players_coords[GR_NUM_RESOLUTIONS][4] = {
1116         { // GR_640
1117                 473, 53, 50, 261
1118         },
1119         { // GR_1024
1120                 748, 77, 67, 459
1121         }
1122 };      
1123
1124 int Mj_ping_coords[GR_NUM_RESOLUTIONS][4] = {
1125         { // GR_640
1126                 551, 53, 47, 261
1127         },
1128         { // GR_1024
1129                 880, 77, 48, 459
1130         }
1131 };      
1132
1133 // game speed labels
1134 #define MJ_NUM_SPEED_LABELS             5
1135 const char *Multi_join_speed_labels[MJ_NUM_SPEED_LABELS] = {
1136         "< 56k",
1137         "56k",
1138         "isdn",
1139         "cable",
1140         "t1/adsl+"
1141 };
1142 color *Multi_join_speed_colors[MJ_NUM_SPEED_LABELS] = {
1143         &Color_bright_red,
1144         &Color_bright_red,
1145         &Color_bright_green,
1146         &Color_bright_green,
1147         &Color_bright_green
1148 };
1149
1150 int Mj_cd_coords[GR_NUM_RESOLUTIONS] = {
1151         20, 30
1152 };
1153
1154 //XSTR:OFF
1155 // extents of the entire boundable game info region
1156 // NOTE : these numbers are completely empirical
1157 #define MJ_PING_GREEN                           160
1158 #define MJ_PING_YELLOW                          300
1159
1160 int Mj_list_area_coords[GR_NUM_RESOLUTIONS][4] = {
1161         { // GR_640
1162                 23, 53, 565, 279 
1163         },
1164         { // GR_1024
1165                 53, 76, 887, 461 
1166         }
1167 };      
1168
1169 // PXO channel filter
1170 #define MJ_PXO_FILTER_Y                         0
1171
1172 // special chars to indicate various status modes for servers
1173 #define MJ_CHAR_STANDALONE                      "*"
1174 #define MJ_CHAR_CAMPAIGN                        "c"
1175 //XSTR:ON
1176
1177 // various interface indices
1178 int Multi_join_list_start;                                                      // where to start displaying from
1179 active_game *Multi_join_list_start_item;                // a pointer to the corresponding active_game
1180 int Multi_join_list_selected;                                           // which item we have selected
1181 active_game *Multi_join_selected_item;                  // a pointer to the corresponding active_game
1182
1183 // use this macro to modify the list start
1184 #define MJ_LIST_START_INC()                     do { Multi_join_list_start++; } while(0);
1185 #define MJ_LIST_START_DEC()                     do { Multi_join_list_start--; } while(0);
1186 #define MJ_LIST_START_SET(vl)                   do { Multi_join_list_start = vl; } while(0);
1187
1188 // if we should be sending a join request at the end of the frame
1189 int Multi_join_should_send = -1;
1190
1191 // master tracker details
1192 int Multi_join_frame_count;                                             // keep a count of frames displayed
1193 int Multi_join_mt_tried_verify;                                 // already tried verifying the pilot with the tracker
1194
1195 // data stuff for auto joining a game
1196 #define MULTI_AUTOJOIN_JOIN_STAMP               2000
1197 #define MULTI_AUTOJOIN_QUERY_STAMP              2000
1198
1199 int Multi_did_autojoin;
1200 net_addr Multi_autojoin_addr;
1201 int Multi_autojoin_join_stamp;
1202 int Multi_autojoin_query_stamp;
1203
1204 // our join request
1205 join_request Multi_join_request;
1206
1207 // LOCAL function definitions
1208 void multi_join_check_buttons();
1209 void multi_join_button_pressed(int n);
1210 void multi_join_display_games();
1211 void multi_join_blit_game_status(active_game *game, int y);
1212 void multi_join_load_tcp_addrs();
1213 void multi_join_do_netstuff();
1214 void multi_join_ping_all();
1215 void multi_join_process_select();
1216 void multi_join_list_scroll_up();
1217 void multi_join_list_scroll_down();
1218 void multi_join_list_page_up();
1219 void multi_join_list_page_down();
1220 active_game *multi_join_get_game(int n);
1221 void multi_join_cull_timeouts();
1222 void multi_join_handle_item_cull(active_game *item, int item_index);
1223 void multi_join_send_join_request(int as_observer);
1224 void multi_join_create_game();
1225 void multi_join_blit_top_stuff();
1226 int multi_join_maybe_warn();
1227 int multi_join_warn_pxo();
1228 void multi_join_blit_protocol();
1229
1230 DCF(mj_make, "")
1231 {
1232         active_game ag, *newitem;;
1233         int idx;
1234
1235         dc_get_arg(ARG_INT);
1236         for(idx=0; idx<Dc_arg_int; idx++){
1237                 // stuff some fake info
1238                 memset(&ag, 0, sizeof(active_game));
1239                 SDL_snprintf(ag.name, SDL_arraysize(ag.name), "Game %d", idx);
1240                 ag.version = MULTI_FS_SERVER_VERSION;
1241                 ag.comp_version = MULTI_FS_SERVER_VERSION;
1242                 ag.server_addr.addr[0] = (char)idx;
1243                 ag.flags = (AG_FLAG_COOP | AG_FLAG_FORMING | AG_FLAG_STANDALONE);               
1244
1245                 // add the game
1246                 newitem = multi_update_active_games(&ag);
1247
1248                 // timestamp it so we get random timeouts
1249                 if(newitem != NULL){
1250                         // newitem->heard_from_timer = timestamp((int)frand_range(500.0f, 10000.0f));
1251                 }
1252         }       
1253 }
1254
1255 void multi_join_notify_new_game()
1256 {       
1257 #ifndef MAKE_FS1
1258         // reset the # of items 
1259         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);
1260         Multi_join_slider.force_currentItem(Multi_join_list_start);
1261 #endif
1262 }
1263
1264 int multi_join_autojoin_do()
1265 {
1266         // if we have an active game on the list, then return a positive value so that we
1267         // can join the game
1268         if ( Active_game_head && (Active_game_count > 0) ) {
1269                 Multi_join_selected_item = Active_game_head;
1270                 return 1;
1271         }
1272
1273         // send out a server_query again
1274         if ( timestamp_elapsed(Multi_autojoin_query_stamp) ) {
1275                 send_server_query(&Multi_autojoin_addr);
1276                 Multi_autojoin_query_stamp = timestamp(MULTI_AUTOJOIN_QUERY_STAMP);
1277         }
1278
1279         return -1;
1280 }
1281
1282 void multi_join_game_init()
1283 {
1284         int idx;
1285
1286         // do the multiplayer init stuff - multi_level_init() now does all net_player zeroing.
1287         // setup various multiplayer things
1288         SDL_assert( Game_mode & GM_MULTIPLAYER );
1289         SDL_assert( Net_player != NULL );
1290
1291         memset( &Netgame, 0, sizeof(Netgame) );
1292
1293         multi_level_init();             
1294         Net_player->flags |= NETINFO_FLAG_DO_NETWORKING;        
1295         Net_player->player = Player;
1296         memcpy(&Net_player->p_info.addr,&Psnet_my_addr,sizeof(net_addr));
1297
1298         // check for the existence of a CD
1299         multi_common_verify_cd();
1300
1301         // load my local netplayer options
1302         multi_options_local_load(&Net_player->p_info.options, Net_player);      
1303
1304         game_flush();
1305
1306         // set the palette
1307 #ifdef MAKE_FS1
1308         common_set_interface_palette(MULTI_JOIN_PALETTE);
1309 #endif
1310
1311         // destroy any chatbox contents which previously existed (from another game)
1312         chatbox_clear();
1313
1314         // create the interface window
1315         Multi_join_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
1316         Multi_join_window.set_mask_bmap(Multi_join_bitmap_mask_fname[gr_screen.res]);
1317
1318         // load the background bitmap
1319         Multi_join_bitmap = bm_load(Multi_join_bitmap_fname[gr_screen.res]);
1320         if(Multi_join_bitmap < 0){
1321                 // we failed to load the bitmap - this is very bad
1322                 Int3();
1323         }
1324
1325         // intialize the endgame system
1326         multi_endgame_init();   
1327
1328         // initialize the common notification messaging
1329         multi_common_notify_init();
1330
1331         // initialize the common text area
1332         multi_common_set_text("");      
1333
1334         // load and use the common interface palette
1335         multi_common_load_palette();
1336         multi_common_set_palette();
1337
1338         // load the help overlay
1339         help_overlay_load(MULTI_JOIN_OVERLAY);
1340         help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1341         
1342         // do TCP and VMT specific initialization
1343         if ( !Multi_options_g.pxo ) {
1344                 // if this is a TCP (non tracker) game, we'll load up our default address list right now                
1345                 multi_join_load_tcp_addrs();            
1346         }
1347
1348         // initialize any and all timestamps    
1349         Multi_join_glr_stamp = -1;
1350         Multi_join_ping_stamp = -1;
1351         Multi_join_sent_stamp = -1;
1352
1353         // reset frame count
1354         Multi_join_frame_count = 0;
1355
1356         // haven't tried to verify on the tracker yet.
1357         Multi_join_mt_tried_verify = 0;
1358
1359         // clear our all game lists to save hassles
1360         multi_join_clear_game_list();
1361
1362         // create the interface buttons
1363         for(idx=0; idx<MULTI_JOIN_NUM_BUTTONS; idx++){
1364                 // create the object
1365                 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);
1366
1367                 // set the sound to play when highlighted
1368                 Multi_join_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
1369
1370                 // set the ani for the button
1371                 Multi_join_buttons[gr_screen.res][idx].button.set_bmaps(Multi_join_buttons[gr_screen.res][idx].filename);
1372
1373                 // set the hotspot
1374                 Multi_join_buttons[gr_screen.res][idx].button.link_hotspot(Multi_join_buttons[gr_screen.res][idx].hotspot);
1375         }               
1376
1377 #ifndef MAKE_FS1
1378         // create all xstrs
1379         for(idx=0; idx<MULTI_JOIN_NUM_TEXT; idx++){
1380                 Multi_join_window.add_XSTR(&Multi_join_text[gr_screen.res][idx]);
1381         }
1382 #endif
1383
1384         Multi_join_should_send = -1;
1385
1386         // close any previously open chatbox
1387         chatbox_close();
1388
1389         // create the list item select button
1390         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);
1391         Multi_join_select_button.hide();
1392
1393 #ifndef MAKE_FS1
1394         // slider
1395         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);
1396 #endif
1397
1398         // if starting a network game, then go to the create game screen
1399         if ( Cmdline_start_netgame ) {
1400                 multi_join_create_game();               
1401         } else if ( Cmdline_connect_addr != NULL ) {
1402                 char *p;
1403                 short port_num;
1404                 int ip_addr;
1405
1406                 // joining a game.  Send a join request to the given IP address, and wait for the return.
1407                 memset( &Multi_autojoin_addr, 0, sizeof(net_addr) );
1408                 Multi_autojoin_addr.type = NET_TCP;
1409
1410                 // create the address, looking out for port number at the end
1411                 port_num = DEFAULT_GAME_PORT;
1412                 p = strrchr(Cmdline_connect_addr, ':');
1413                 if ( p ) {
1414                         *p = 0;
1415                         p++;
1416                         port_num = (short)atoi(p);
1417                 }
1418                 ip_addr = inet_addr(Cmdline_connect_addr);
1419                 memcpy(Multi_autojoin_addr.addr, &ip_addr, IP_ADDRESS_LENGTH);
1420                 Multi_autojoin_addr.port = port_num;
1421
1422                 send_server_query(&Multi_autojoin_addr);
1423                 Multi_autojoin_query_stamp = timestamp(MULTI_AUTOJOIN_QUERY_STAMP);
1424                 Multi_did_autojoin = 0;
1425         }
1426 }
1427
1428 void multi_join_clear_game_list()
1429 {
1430         // misc data    
1431         Multi_join_list_selected = -1;  
1432         Multi_join_selected_item = NULL;        
1433         MJ_LIST_START_SET(-1);
1434         Multi_join_list_start_item = NULL;      
1435
1436         // free up the active game list
1437         multi_free_active_games();
1438
1439         // initialize the active game list
1440         Active_game_head = NULL;
1441         Active_game_count = 0;
1442 }
1443
1444 void multi_join_game_do_frame()
1445 {
1446         // check the status of our reliable socket.  If not valid, popup error and return to main menu
1447         // I put this code here to avoid nasty gameseq issues with states.  Also, we will have nice
1448         // background for the popup
1449         if ( !psnet_rel_check() ) {
1450                 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));
1451                 gameseq_post_event(GS_EVENT_MAIN_MENU);
1452                 return;
1453         }       
1454
1455         // return here since we will be moving to the next stage anyway -- I don't want to see the backgrounds of
1456         // all the screens for < 1 second for every screen we automatically move to.
1457         if ( Cmdline_start_netgame ) {
1458                 return;
1459         }
1460
1461         // when joining a network game, wait for the server query to come back, and then join the game
1462         if ( Cmdline_connect_addr != NULL ) {
1463                 int rval;
1464
1465                 if ( !Multi_did_autojoin ) {
1466                         rval = popup_till_condition(multi_join_autojoin_do, XSTR("&Cancel", 779), XSTR("Joining netgame", 1500) );
1467                         if ( rval == 0 ) {
1468                                 // cancel was hit.  Send the user back to the main hall
1469                                 gameseq_post_event(GS_EVENT_MAIN_MENU);
1470                                 Cmdline_connect_addr = NULL;            // reset this value.
1471                         }
1472
1473                         // when we get here, we have the data -- join the game.
1474                         multi_join_send_join_request(0);
1475                         Multi_autojoin_join_stamp = timestamp(MULTI_AUTOJOIN_JOIN_STAMP);
1476                         Multi_did_autojoin = 1;
1477                 }
1478
1479                 if ( timestamp_elapsed(Multi_autojoin_join_stamp) ) {
1480                         multi_join_send_join_request(0);
1481                         Multi_autojoin_join_stamp = timestamp(MULTI_AUTOJOIN_JOIN_STAMP);
1482                 }
1483                 return;
1484
1485         }       
1486
1487         // reset the should send var
1488         Multi_join_should_send = -1;
1489
1490         int k = Multi_join_window.process();
1491
1492         // process any keypresses
1493         switch(k){
1494         case SDLK_ESCAPE :
1495                 if(help_overlay_active(MULTI_JOIN_OVERLAY)){
1496                         help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1497                 } else {
1498                         if (Multi_options_g.pxo == 1) {
1499                                 gameseq_post_event(GS_EVENT_PXO);
1500                         } else {
1501                                 gameseq_post_event(GS_EVENT_MAIN_MENU);
1502                         }
1503
1504                         gamesnd_play_iface(SND_USER_SELECT);
1505                 }
1506                 break;
1507
1508         // page up the game list
1509         case SDLK_PAGEUP:
1510                 multi_join_list_page_up();      
1511 #ifndef MAKE_FS1
1512                 Multi_join_slider.force_currentItem(Multi_join_list_start);
1513 #endif
1514                 break;
1515
1516         case SDLK_t:
1517                 multi_pinfo_popup(Net_player);
1518                 break;
1519
1520         // page down the game list
1521         case SDLK_PAGEDOWN:
1522                 multi_join_list_page_down();
1523 #ifndef MAKE_FS1
1524                 Multi_join_slider.force_currentItem(Multi_join_list_start);
1525 #endif
1526                 break;
1527
1528         // send out a ping-all
1529         case SDLK_p :
1530                 multi_join_ping_all();          
1531                 Multi_join_ping_stamp = timestamp(MULTI_JOIN_PING_TIME);
1532                 break;  
1533
1534         // shortcut to start a game     
1535         case SDLK_s :
1536                 multi_join_create_game();               
1537                 break;
1538
1539         // scroll the game list up
1540         case SDLK_UP:
1541                 multi_join_list_scroll_up();
1542 #ifndef MAKE_FS1
1543                 Multi_join_slider.force_currentItem(Multi_join_list_start);
1544 #endif
1545                 break;
1546
1547         // scroll the game list down
1548         case SDLK_DOWN:
1549                 multi_join_list_scroll_down();
1550 #ifndef MAKE_FS1
1551                 Multi_join_slider.force_currentItem(Multi_join_list_start);
1552 #endif
1553                 break;
1554         }       
1555
1556         if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
1557                 help_overlay_set_state(MULTI_JOIN_OVERLAY, 0);
1558         }
1559
1560         // do any network related stuff
1561         multi_join_do_netstuff(); 
1562
1563         // process any button clicks
1564         multi_join_check_buttons();
1565
1566         // process any list selection stuff
1567         multi_join_process_select();
1568
1569         // draw the background, etc
1570         gr_reset_clip();
1571         GR_MAYBE_CLEAR_RES(Multi_join_bitmap);
1572         if(Multi_join_bitmap != -1){            
1573                 gr_set_bitmap(Multi_join_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1574                 gr_bitmap(0,0);
1575         }
1576         Multi_join_window.draw();
1577
1578         // display the active games
1579         multi_join_display_games();
1580
1581         // display any text in the info area
1582         multi_common_render_text();
1583
1584         // display any pending notification messages
1585         multi_common_notify_do();
1586
1587         // blit the CD icon and any PXO filter stuff
1588         multi_join_blit_top_stuff();
1589
1590         // draw the help overlay
1591         help_overlay_maybe_blit(MULTI_JOIN_OVERLAY);
1592         
1593         // flip the buffer
1594         gr_flip();
1595
1596         // if we are supposed to be sending a join request
1597         if(Multi_join_should_send != -1){               
1598                 multi_join_send_join_request(Multi_join_should_send);
1599         }
1600         Multi_join_should_send = -1;
1601
1602         // increment the frame count
1603         Multi_join_frame_count++;
1604 }
1605
1606 void multi_join_game_close()
1607 {
1608         // unload any bitmaps
1609         if(!bm_unload(Multi_join_bitmap)){
1610                 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_join_bitmap_fname[gr_screen.res]));
1611         }
1612
1613         // unload the help overlay
1614         help_overlay_unload(MULTI_JOIN_OVERLAY);        
1615
1616         // free up the active game list
1617         multi_free_active_games();
1618         
1619         // destroy the UI_WINDOW
1620         Multi_join_window.destroy();
1621
1622 #ifdef MAKE_FS1
1623         common_free_interface_palette();
1624 #endif
1625 }
1626
1627 void multi_join_check_buttons()
1628 {
1629         int idx;
1630         for(idx=0;idx<MULTI_JOIN_NUM_BUTTONS;idx++){
1631                 // we only really need to check for one button pressed at a time, so we can break after 
1632                 // finding one.
1633                 if(Multi_join_buttons[gr_screen.res][idx].button.pressed()){
1634                         multi_join_button_pressed(idx);
1635                         break;
1636                 }
1637         }
1638 }
1639
1640 void multi_join_button_pressed(int n)
1641 {
1642         switch(n){
1643         case MJ_CANCEL :
1644                 // if we're player PXO, go back there
1645                 if (Multi_options_g.pxo == 1) {
1646                         gameseq_post_event(GS_EVENT_PXO);
1647                 } else {
1648                         gameseq_post_event(GS_EVENT_MAIN_MENU);
1649                 }
1650                 gamesnd_play_iface(SND_USER_SELECT);
1651                 break;
1652         case MJ_ACCEPT :
1653                 if(Active_game_count <= 0){
1654                         multi_common_add_notify(XSTR("No games found!",757));
1655                         gamesnd_play_iface(SND_GENERAL_FAIL);
1656                 } else if(Multi_join_list_selected == -1){
1657                         multi_common_add_notify(XSTR("No game selected!",758));
1658                         gamesnd_play_iface(SND_GENERAL_FAIL);
1659                 } else if((Multi_join_sent_stamp != -1) && !timestamp_elapsed(Multi_join_sent_stamp)){
1660                         multi_common_add_notify(XSTR("Still waiting on previous join request!",759));
1661                         gamesnd_play_iface(SND_GENERAL_FAIL);
1662                 } else {                        
1663                         // otherwise, if he's already played PXO games, warn him        
1664                         /*
1665                         if(Player->flags & PLAYER_FLAGS_HAS_PLAYED_PXO){
1666                                 if(!multi_join_warn_pxo()){
1667                                         break;
1668                                 }                       
1669                         }
1670                         */
1671
1672                         // send the join request here
1673                         SDL_assert(Multi_join_selected_item != NULL);
1674
1675                         // send a join request packet
1676                         Multi_join_should_send = 0;                     
1677                         
1678                         gamesnd_play_iface(SND_COMMIT_PRESSED);
1679                 }
1680                 break;
1681
1682         // help overlay
1683         case MJ_HELP:
1684                 if(!help_overlay_active(MULTI_JOIN_OVERLAY)){
1685                         help_overlay_set_state(MULTI_JOIN_OVERLAY,1);
1686                 } else {
1687                         help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1688                 }
1689                 break;
1690
1691         // scroll the game list up
1692         case MJ_SCROLL_UP:
1693                 multi_join_list_scroll_up();
1694 #ifndef MAKE_FS1
1695                 Multi_join_slider.force_currentItem(Multi_join_list_start);
1696 #endif
1697                 break;
1698
1699         // scroll the game list down
1700         case MJ_SCROLL_DOWN:
1701                 multi_join_list_scroll_down();
1702 #ifndef MAKE_FS1
1703                 Multi_join_slider.force_currentItem(Multi_join_list_start);
1704 #endif
1705                 break;
1706
1707         // scroll the info text box up
1708         case MJ_SCROLL_INFO_UP:
1709                 multi_common_scroll_text_up();
1710                 break;
1711
1712         // scroll the info text box down
1713         case MJ_SCROLL_INFO_DOWN:
1714                 multi_common_scroll_text_down();
1715                 break;
1716
1717         // go to the options screen
1718         case MJ_OPTIONS:
1719                 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
1720                 break;
1721
1722         // go to the start game screen  
1723         case MJ_START_GAME:
1724                 multi_join_create_game();               
1725                 break;
1726
1727         // refresh the game/server list
1728         case MJ_REFRESH:                
1729                 gamesnd_play_iface(SND_USER_SELECT);
1730                 broadcast_game_query();         
1731                 break;
1732
1733         // join a game as an observer
1734         case MJ_JOIN_OBSERVER:
1735                 if(Active_game_count <= 0){
1736                         multi_common_add_notify(XSTR("No games found!",757));
1737                         gamesnd_play_iface(SND_GENERAL_FAIL);
1738                 } else if(Multi_join_list_selected == -1){
1739                         multi_common_add_notify(XSTR("No game selected!",758));
1740                         gamesnd_play_iface(SND_GENERAL_FAIL);
1741                 } else if((Multi_join_sent_stamp != -1) && !timestamp_elapsed(Multi_join_sent_stamp)){
1742                         multi_common_add_notify(XSTR("Still waiting on previous join request!",759));
1743                         gamesnd_play_iface(SND_GENERAL_FAIL);
1744                 } else {                        
1745                         // send the join request here
1746                         SDL_assert(Multi_join_selected_item != NULL);
1747
1748                         Multi_join_should_send = 1;             
1749
1750                         gamesnd_play_iface(SND_COMMIT_PRESSED);
1751                 }
1752                 break;
1753
1754         default :
1755                 multi_common_add_notify(XSTR("Not implemented yet!",760));
1756                 gamesnd_play_iface(SND_GENERAL_FAIL);
1757                 break;
1758         }
1759 }
1760
1761 // display all relevant info for active games
1762 void multi_join_display_games()
1763 {
1764         active_game *moveup = Multi_join_list_start_item;       
1765         char str[200];          
1766         int w,h;
1767         int con_type;
1768         int y_start = Mj_list_y[gr_screen.res];
1769         int count = 0;
1770         
1771         if(moveup != NULL){
1772                 do {                    
1773                         // blit the game status (including text and type icon)
1774                         multi_join_blit_game_status(moveup,y_start);                    
1775                         
1776                         // get the connection type
1777                         con_type = (moveup->flags & AG_FLAG_CONNECTION_SPEED_MASK) >> AG_FLAG_CONNECTION_BIT;
1778                         if((con_type > 4) || (con_type < 0)){
1779                                 con_type = 0;
1780                         }
1781
1782                         // display the connection speed
1783                         str[0] = '\0';
1784                         SDL_strlcpy(str, Multi_join_speed_labels[con_type], SDL_arraysize(str));
1785                         gr_set_color_fast(Multi_join_speed_colors[con_type]);
1786                         gr_string(Mj_speed_coords[gr_screen.res][MJ_X_COORD], y_start, str);
1787
1788                         // we'll want to have different colors for highlighted items, etc.
1789                         if(moveup == Multi_join_selected_item){
1790                                 gr_set_color_fast(&Color_text_selected);
1791                         } else {
1792                                 gr_set_color_fast(&Color_text_normal);
1793                         }
1794
1795                         // display the game name, adding appropriate status chars
1796                         str[0] = '\0';
1797                         if(moveup->flags & AG_FLAG_STANDALONE){
1798                                 SDL_strlcat(str, MJ_CHAR_STANDALONE, SDL_arraysize(str));
1799                         }
1800                         if(moveup->flags & AG_FLAG_CAMPAIGN){
1801                                 SDL_strlcat(str, MJ_CHAR_CAMPAIGN, SDL_arraysize(str));
1802                         }
1803
1804                         // tack on the actual server name                       
1805                         SDL_strlcat(str, " ", SDL_arraysize(str));
1806                         SDL_strlcat(str, moveup->name, SDL_arraysize(str));
1807                         if(strlen(moveup->mission_name) > 0){
1808                                 SDL_strlcat(str, " / ", SDL_arraysize(str));
1809                                 SDL_strlcat(str, moveup->mission_name, SDL_arraysize(str));
1810                         } 
1811
1812                         // make sure the string fits in the display area and draw it
1813                         gr_force_fit_string(str,200,Mj_game_name_coords[gr_screen.res][MJ_W_COORD]);                    
1814                         gr_string(Mj_game_name_coords[gr_screen.res][MJ_X_COORD],y_start,str);
1815
1816                         // display the ping time
1817                         if(moveup->ping.ping_avg > 0){
1818                                 if(moveup->ping.ping_avg > 1000){
1819                                         gr_set_color_fast(&Color_bright_red);
1820                                         SDL_strlcpy(str, XSTR("> 1 sec",761), SDL_arraysize(str));
1821                                 } else {
1822                                         // set the appropriate ping time color indicator
1823                                         if(moveup->ping.ping_avg > MJ_PING_YELLOW){
1824                                                 gr_set_color_fast(&Color_bright_red);
1825                                         } else if(moveup->ping.ping_avg > MJ_PING_YELLOW){
1826                                                 gr_set_color_fast(&Color_bright_yellow);
1827                                         } else {
1828                                                 gr_set_color_fast(&Color_bright_green);
1829                                         }
1830
1831                                         SDL_snprintf(str, SDL_arraysize(str), "%d%s", moveup->ping.ping_avg, XSTR(" ms",762));
1832                                 }
1833
1834                                 gr_string(Mj_ping_coords[gr_screen.res][MJ_X_COORD],y_start,str);
1835                         }
1836
1837                         // display the number of players (be sure to center it)
1838                         if(moveup == Multi_join_selected_item){
1839                                 gr_set_color_fast(&Color_text_selected);
1840                         } else {
1841                                 gr_set_color_fast(&Color_text_normal);
1842                         }
1843                         SDL_snprintf(str, SDL_arraysize(str), "%d", moveup->num_players);
1844                         gr_get_string_size(&w,&h,str);
1845                         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);                     
1846
1847                         count++;
1848                         y_start += 10;
1849                         moveup = moveup->next;
1850                 } while((moveup != Active_game_head) && (count < Mj_max_game_items[gr_screen.res]));
1851         }
1852         // if there are no items on the list, display this info
1853         else {
1854                 gr_set_color_fast(&Color_bright);
1855                 gr_string(Mj_game_name_coords[gr_screen.res][MJ_X_COORD] - 30,y_start,XSTR("<No game servers found>",763));
1856         }
1857 }
1858
1859 void multi_join_blit_game_status(active_game *game, int y)
1860 {
1861         int draw,str_w;
1862         char status_text[25];
1863
1864         // blit the proper icon
1865         draw = 0;       
1866         switch( game->flags & AG_FLAG_TYPE_MASK ){
1867         // coop game
1868         case AG_FLAG_COOP:
1869                 if(Multi_common_icons[MICON_COOP] != -1){
1870                         gr_set_bitmap(Multi_common_icons[MICON_COOP], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1871                         draw = 1;
1872                 }
1873                 break;  
1874         
1875         // team vs. team game
1876         case AG_FLAG_TEAMS:
1877                 if(Multi_common_icons[MICON_TVT] != -1){
1878                         gr_set_bitmap(Multi_common_icons[MICON_TVT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1879                         draw = 1;
1880                 } 
1881                 break;  
1882
1883         // dogfight game
1884 #ifndef MAKE_FS1
1885         case AG_FLAG_DOGFIGHT:
1886                 if(Multi_common_icons[MICON_DOGFIGHT] != -1){
1887                         gr_set_bitmap(Multi_common_icons[MICON_DOGFIGHT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1888                         draw = 1;
1889                 } 
1890                 break;  
1891 #endif
1892         }
1893         // if we're supposed to draw a bitmap
1894         if(draw){
1895                 gr_bitmap(Mj_game_icon_coords[gr_screen.res][MJ_X_COORD],y-1);
1896         }
1897
1898         // blit the proper status text
1899         memset(status_text,0,25);
1900
1901         switch( game->flags & AG_FLAG_STATE_MASK ){
1902         case AG_FLAG_FORMING:
1903                 gr_set_color_fast(&Color_bright_green);
1904                 SDL_strlcpy(status_text, XSTR("Forming", 764), SDL_arraysize(status_text));
1905                 break;
1906         case AG_FLAG_BRIEFING:
1907                 gr_set_color_fast(&Color_bright_red);
1908                 SDL_strlcpy(status_text, XSTR("Briefing", 765), SDL_arraysize(status_text));
1909                 break;
1910         case AG_FLAG_DEBRIEF:
1911                 gr_set_color_fast(&Color_bright_red);
1912                 SDL_strlcpy(status_text, XSTR("Debrief", 766), SDL_arraysize(status_text));
1913                 break;
1914         case AG_FLAG_PAUSE:
1915                 gr_set_color_fast(&Color_bright_red);
1916                 SDL_strlcpy(status_text, XSTR("Paused", 767), SDL_arraysize(status_text));
1917                 break;
1918         case AG_FLAG_IN_MISSION:
1919                 gr_set_color_fast(&Color_bright_red);
1920                 SDL_strlcpy(status_text, XSTR("Playing", 768), SDL_arraysize(status_text));
1921                 break;
1922         default:
1923                 gr_set_color_fast(&Color_bright);
1924                 SDL_strlcpy(status_text, XSTR("Unknown", 769), SDL_arraysize(status_text));
1925                 break;
1926         }               
1927         gr_get_string_size(&str_w,NULL,status_text);
1928         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);
1929 }
1930
1931 // load in a list of active games from our tcp.cfg file
1932 void multi_join_load_tcp_addrs()
1933 {
1934         char line[MAX_IP_STRING];
1935         net_addr addr;  
1936         server_item *item;
1937         CFILE *file = NULL;
1938
1939         // attempt to open the ip list file
1940         file = cfopen(IP_CONFIG_FNAME,"rt",CFILE_NORMAL,CF_TYPE_DATA);  
1941         if(file == NULL){
1942                 nprintf(("Network","Error loading tcp.cfg file!\n"));
1943                 return;
1944         }
1945
1946         // free up any existing server list
1947         multi_free_server_list();
1948
1949         // read in all the strings in the file
1950         while(!cfeof(file)){
1951                 line[0] = '\0';
1952                 cfgets(line,MAX_IP_STRING,file);
1953
1954                 // strip off any newline character
1955                 if(line[strlen(line) - 1] == '\n'){
1956                         line[strlen(line) - 1] = '\0';
1957                 }
1958                 
1959                 // empty lines don't get processed
1960                 if( (line[0] == '\0') || (line[0] == '\n') ){
1961                         continue;
1962                 }
1963
1964                 if ( !psnet_is_valid_ip_string(line) ) {
1965                         nprintf(("Network","Invalid ip string (%s)\n",line));
1966                 } else {                         
1967                         // copy the server ip address
1968                         memset(&addr,0,sizeof(net_addr));
1969                         addr.type = NET_TCP;
1970                         psnet_string_to_addr(&addr, line, SDL_arraysize(line));
1971                         if ( addr.port == 0 ){
1972                                 addr.port = DEFAULT_GAME_PORT;
1973                         }
1974
1975                         // create a new server item on the list
1976                         item = multi_new_server_item();
1977                         if(item != NULL){
1978                                 memcpy(&item->server_addr,&addr,sizeof(net_addr));
1979                         }                       
1980                 }
1981         }
1982
1983         cfclose(file);  
1984 }
1985
1986 // do stuff like pinging servers, sending out requests, etc
1987 void multi_join_do_netstuff()
1988 {
1989         // handle game query stuff
1990         if(Multi_join_glr_stamp == -1){
1991                 broadcast_game_query();
1992                 
1993                 if(Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST){
1994                         Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME_LOCAL);
1995                 } else {
1996                         Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME);
1997                 }
1998         } 
1999         // otherwise send out game query and restamp
2000         else if(timestamp_elapsed(Multi_join_glr_stamp)){                       
2001                 broadcast_game_query();
2002
2003                 if(Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST){
2004                         Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME_LOCAL);
2005                 } else {
2006                         Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME);
2007                 }               
2008         }
2009
2010         // check to see if we've been accepted.  If so, put up message saying so
2011         if ( Net_player->flags & (NETINFO_FLAG_ACCEPT_INGAME|NETINFO_FLAG_ACCEPT_CLIENT|NETINFO_FLAG_ACCEPT_HOST|NETINFO_FLAG_ACCEPT_OBSERVER) ) {
2012                 multi_common_add_notify(XSTR("Accepted.  Waiting for player data.",770));
2013         }
2014
2015         // check to see if any join packets we have sent have timed out
2016         if((Multi_join_sent_stamp != -1) && (timestamp_elapsed(Multi_join_sent_stamp))){
2017                 Multi_join_sent_stamp = -1;
2018                 multi_common_add_notify(XSTR("Join request timed out!",771));
2019         }
2020
2021         // check to see if we should be pinging everyone
2022         if((Multi_join_ping_stamp == -1) || (timestamp_elapsed(Multi_join_ping_stamp))){
2023                 multi_join_ping_all();
2024                 Multi_join_ping_stamp = timestamp(MULTI_JOIN_PING_TIME);
2025         } 
2026
2027         // cull timeouts
2028         multi_join_cull_timeouts();
2029 }
2030
2031 // evaluate a returned pong.
2032 void multi_join_eval_pong(net_addr *addr, fix pong_time)
2033 {       
2034 //      int found;
2035         active_game *moveup = Active_game_head;
2036
2037 //      found = 0;
2038         if(moveup != NULL){
2039                 do {                            
2040                         if(psnet_same(&moveup->server_addr,addr)){
2041                         //      found = 1;
2042                                 multi_ping_eval_pong(&moveup->ping);
2043                                 
2044                                 break;
2045                         } else {
2046                                 moveup = moveup->next;
2047                         }
2048                 } while(moveup != Active_game_head);
2049         }       
2050
2051         // update the game's ping
2052         /*
2053         if(found && (moveup->ping_end > moveup->ping_start)){           
2054                 moveup->ping_time = f2fl(moveup->ping_end - moveup->ping_start);
2055                 moveup->ping_start = -1;
2056                 moveup->ping_end = -1;
2057         }
2058         */
2059 }
2060
2061 // ping all the server on the list
2062 void multi_join_ping_all()
2063 {
2064         active_game *moveup = Active_game_head; 
2065         
2066         if(moveup != NULL){
2067                 do {
2068                         /*
2069                         moveup->ping_start = timer_get_fixed_seconds();
2070                         moveup->ping_end = -1;
2071                         send_ping(&moveup->server_addr);
2072                         */
2073                         multi_ping_send(&moveup->server_addr,&moveup->ping);
2074         
2075                         moveup = moveup->next;
2076                 } while(moveup != Active_game_head);
2077         }
2078 }
2079
2080 void multi_join_process_select()
2081 {
2082         // if we don't have anything selected and there are items on the list - select the first one
2083         if((Multi_join_list_selected == -1) && (Active_game_count > 0)){
2084                 Multi_join_list_selected = 0;
2085                 Multi_join_selected_item = multi_join_get_game(0);                              
2086                 MJ_LIST_START_SET(0);
2087                 Multi_join_list_start_item = Multi_join_selected_item;
2088
2089                 // send a mission description request to this guy               
2090                 send_netgame_descript_packet(&Multi_join_selected_item->server_addr,0);
2091                 multi_common_set_text("");
2092
2093                 // I sure hope this doesn't happen
2094                 SDL_assert(Multi_join_selected_item != NULL);           
2095                 return;
2096         } 
2097         // otherwise see if he's clicked on an item
2098         else if(Multi_join_select_button.pressed() && (Active_game_count > 0)){          
2099                 int y,item;             
2100                 Multi_join_select_button.get_mouse_pos(NULL,&y);
2101                 item = y / 10;
2102                 if(item + Multi_join_list_start < Active_game_count){           
2103                         gamesnd_play_iface(SND_IFACE_MOUSE_CLICK);
2104
2105                         Multi_join_list_selected = item + Multi_join_list_start;
2106                         Multi_join_selected_item = multi_join_get_game(Multi_join_list_selected);
2107                         
2108                         // I sure hope this doesn't happen
2109                         SDL_assert(Multi_join_selected_item != NULL);
2110
2111                         // send a mission description request to this guy
2112                         send_netgame_descript_packet(&Multi_join_selected_item->server_addr,0);
2113                         multi_common_set_text("");                      
2114                 }               
2115         }
2116
2117         // if he's double clicked, then select it and accept            
2118         if(Multi_join_select_button.double_clicked()){                  
2119                 int y,item;             
2120                 Multi_join_select_button.get_mouse_pos(NULL,&y);
2121                 item = y / 10;
2122                 if(item == Multi_join_list_selected){           
2123                         multi_join_button_pressed(MJ_ACCEPT);
2124                 }
2125         }
2126 }
2127
2128 // return game n (0 based index)
2129 active_game *multi_join_get_game(int n)
2130 {
2131         active_game *moveup = Active_game_head;
2132
2133         if(moveup != NULL){
2134                 if(n == 0){
2135                         return moveup;
2136                 } else {
2137                         int count = 1;
2138                         moveup = moveup->next;
2139                         while((moveup != Active_game_head) && (count != n)){
2140                                 moveup = moveup->next;
2141                                 count++;
2142                         }
2143                         if(moveup == Active_game_head){
2144                                 nprintf(("Network","Warning, couldn't find game item %d!\n",n));
2145                                 return NULL;
2146                         } else {
2147                                 return moveup;
2148                         }
2149                 }
2150         } 
2151         return NULL;
2152 }
2153
2154 // scroll through the game list
2155 void multi_join_list_scroll_up()
2156 {
2157         // if we're not at the beginning of the list, scroll up 
2158         if(Multi_join_list_start_item != Active_game_head){
2159                 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2160                 
2161                 MJ_LIST_START_DEC();            
2162
2163                 gamesnd_play_iface(SND_SCROLL);
2164         } else {
2165                 gamesnd_play_iface(SND_GENERAL_FAIL);
2166         }
2167 }
2168
2169 // scroll through the game list
2170 void multi_join_list_scroll_down()
2171 {
2172         if((Active_game_count - Multi_join_list_start) > Mj_max_game_items[gr_screen.res]){
2173                 Multi_join_list_start_item = Multi_join_list_start_item->next;
2174
2175                 MJ_LIST_START_INC();            
2176
2177                 gamesnd_play_iface(SND_SCROLL);
2178         } else {
2179                 gamesnd_play_iface(SND_GENERAL_FAIL);
2180         }
2181 }
2182
2183 void multi_join_list_page_up()
2184 {
2185         // in this case, just set us to the beginning of the list
2186         if((Multi_join_list_start - Mj_max_game_items[gr_screen.res]) < 0){
2187                 Multi_join_list_start_item = Active_game_head;          
2188
2189                 MJ_LIST_START_SET(0);
2190
2191                 gamesnd_play_iface(SND_SCROLL);         
2192         } else {
2193                 // otherwise page the whole thing up
2194                 int idx;
2195                 for(idx=0; idx<Mj_max_game_items[gr_screen.res]; idx++){
2196                         Multi_join_list_start_item = Multi_join_list_start_item->prev;
2197
2198                         MJ_LIST_START_DEC();                    
2199                 }
2200                 gamesnd_play_iface(SND_SCROLL);
2201         }
2202 }
2203
2204 void multi_join_list_page_down()
2205 {       
2206         int count = 0;
2207
2208         // page the whole thing down            
2209         while((count < Mj_max_game_items[gr_screen.res]) && ((Active_game_count - Multi_join_list_start) > Mj_max_game_items[gr_screen.res])){
2210                 Multi_join_list_start_item = Multi_join_list_start_item->next;
2211                 MJ_LIST_START_INC();                    
2212
2213                 // next 
2214                 count++;
2215         }       
2216         gamesnd_play_iface(SND_SCROLL);  
2217 }
2218
2219 void multi_join_cull_timeouts()
2220 {
2221         active_game *backup;
2222         int count;
2223         active_game *moveup = Active_game_head;
2224
2225         // traverse through the entire list if any items exist  
2226         count = 0;
2227         if(moveup != NULL){
2228                 do {
2229                         if((moveup->heard_from_timer != -1) && (timestamp_elapsed(moveup->heard_from_timer))){
2230                                 Active_game_count--;
2231
2232                                 // if this is the head of the list
2233                                 if(moveup == Active_game_head){                                 
2234                                         // if this is the _only_ item on the list
2235                                         if(moveup->next == Active_game_head){
2236                                                 // handle any gui details related to deleting this item
2237                                                 multi_join_handle_item_cull(Active_game_head, count);
2238                                                 
2239                                                 free(Active_game_head);
2240                                                 Active_game_head = NULL;                                                
2241                                                 return;
2242                                         } 
2243                                         // if there are other items on the list
2244                                         else {
2245                                                 // handle any gui details related to deleting this item
2246                                                 multi_join_handle_item_cull(moveup, count);
2247                                                 
2248                                                 Active_game_head = moveup->next;
2249                                                 Active_game_head->prev = moveup->prev;
2250                                                 Active_game_head->prev->next = Active_game_head;
2251                                                 free(moveup);
2252                                                 moveup = Active_game_head;                                                                                      
2253                                         }
2254                                 }
2255                                 // if its somewhere else on the list
2256                                 else {
2257                                         // handle any gui details related to deleting this item
2258                                         multi_join_handle_item_cull(moveup, count);
2259                                         
2260                                         // if its the last item on the list                                     
2261                                         moveup->next->prev = moveup->prev;
2262                                         moveup->prev->next = moveup->next;                                      
2263                                         
2264                                         // if it was the last element on the list, return
2265                                         if(moveup->next == Active_game_head){
2266                                                 free(moveup);
2267                                                 return;
2268                                         } else {
2269                                                 backup = moveup->next;
2270                                                 free(moveup);
2271                                                 moveup = backup;                                                
2272                                         }
2273                                 }
2274                         } else {
2275                                 moveup = moveup->next;                          
2276                                 count++;
2277                         }
2278                 } while(moveup != Active_game_head);
2279         }
2280 }
2281
2282 // deep magic begins here. 
2283 void multi_join_handle_item_cull(active_game *item, int item_index)
2284 {       
2285         // if this is the only item on the list, unset everything
2286         if(item->next == item){
2287                 Multi_join_list_selected = -1;
2288                 Multi_join_selected_item = NULL;
2289         
2290 #ifndef MAKE_FS1
2291                 Multi_join_slider.set_numberItems(0);
2292 #endif
2293                 MJ_LIST_START_SET(-1);
2294                 Multi_join_list_start_item = NULL;
2295
2296                 // return
2297                 return;
2298         }       
2299         
2300         // see if we should be adjusting our currently selected item
2301         if(item_index <= Multi_join_list_selected){
2302                 // the selected item is the head of the list
2303                 if(Multi_join_selected_item == Active_game_head){
2304                         // move the pointer up since this item is about to be destroyed
2305                         Multi_join_selected_item = Multi_join_selected_item->next;
2306                 } else {                        
2307                         // if this is the item being deleted, select the previous one
2308                         if(item == Multi_join_selected_item){
2309                                 // previous item
2310                                 Multi_join_selected_item = Multi_join_selected_item->prev;
2311
2312                                 // decrement the selected index by 1
2313                                 Multi_join_list_selected--;             
2314                         }
2315                         // now we know its a previous item, so our pointer stays the same but our index goes down by one, since there will be
2316                         // 1 less item on the list
2317                         else {
2318                                 // decrement the selected index by 1
2319                                 Multi_join_list_selected--;             
2320                         }
2321                 }
2322         }
2323         
2324         // see if we should be adjusting out current start position
2325         if(item_index <= Multi_join_list_start){
2326                 // the start position is the head of the list
2327                 if(Multi_join_list_start_item == Active_game_head){
2328                         // move the pointer up since this item is about to be destroyed
2329                         Multi_join_list_start_item = Multi_join_list_start_item->next;
2330                 } else {
2331                         // if this is the item being deleted, select the previous one
2332                         if(item == Multi_join_list_start_item){
2333                                 Multi_join_list_start_item = Multi_join_list_start_item->prev;                  
2334                                 
2335                                 // decrement the starting index by 1
2336                                 MJ_LIST_START_DEC();                                                            
2337                         } else {
2338                                 // but decrement the starting index by 1
2339                                 MJ_LIST_START_DEC();                                                            
2340                         }
2341                 }
2342         }
2343
2344         // maybe go back up a bit so that we always have a full page of items   
2345         if(Active_game_count > Mj_max_game_items[gr_screen.res]){
2346                 while((Active_game_count - Multi_join_list_start) < Mj_max_game_items[gr_screen.res]){
2347                         Multi_join_list_start_item = Multi_join_list_start_item->prev;
2348                         MJ_LIST_START_DEC();
2349                 }
2350         }       
2351
2352         // set slider location
2353 #ifndef MAKE_FS1
2354         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);
2355         Multi_join_slider.force_currentItem(Multi_join_list_start);     
2356 #endif
2357 }
2358
2359 void multi_join_send_join_request(int as_observer)
2360 {       
2361         // don't do anything if we have no items selected
2362         if(Multi_join_selected_item == NULL){
2363                 return;
2364         }
2365
2366         // 5/26/98 -- for team v team games, don't allow ingame joining :-(
2367         if ( (Multi_join_selected_item->flags & AG_FLAG_TEAMS) && (Multi_join_selected_item->flags & (AG_FLAG_PAUSE|AG_FLAG_IN_MISSION)) ) {
2368                 popup(0, 1, POPUP_OK, XSTR("Joining ingame is currently not allowed for team vs. team games",772));
2369                 return;
2370         }
2371
2372         memset(&Multi_join_request,0,sizeof(join_request));
2373
2374         // if the netgame is in password mode, put up a request for the password
2375         if(Multi_join_selected_item->flags & AG_FLAG_PASSWD){
2376                 if(!multi_passwd_popup(Multi_join_request.passwd, SDL_arraysize(Multi_join_request.passwd))){
2377                         return;
2378                 }
2379
2380                 mprintf(("Password : %s\n",Multi_join_request.passwd));
2381         }       
2382                 
2383         // fill out the join request struct     
2384         SDL_strlcpy(Multi_join_request.callsign, Player->callsign, SDL_arraysize(Multi_join_request.callsign));
2385         if(strlen(Player->image_filename) > 0){
2386                 SDL_strlcpy(Multi_join_request.image_filename, Player->image_filename, SDL_arraysize(Multi_join_request.image_filename));
2387         }       
2388 #ifndef MAKE_FS1
2389         if(strlen(Player->squad_filename) > 0){
2390                 SDL_strlcpy(Multi_join_request.squad_filename, Player->squad_filename, SDL_arraysize(Multi_join_request.squad_filename));
2391         }
2392 #endif
2393
2394         // tracker id (if any)
2395         Multi_join_request.tracker_id = Multi_tracker_id;
2396
2397         // player's rank (at least, what he wants you to _believe_)
2398         Multi_join_request.player_rank = (ubyte)Player->stats.rank;
2399         
2400         // misc join flags
2401         Multi_join_request.flags = 0;
2402         if(as_observer){
2403                 Multi_join_request.flags |= JOIN_FLAG_AS_OBSERVER;
2404         }       
2405
2406         // if the player has hacked data
2407         if(game_hacked_data()){
2408                 Multi_join_request.flags |= JOIN_FLAG_HAXOR;
2409         }
2410         
2411         // pxo squad info
2412 #ifndef MAKE_FS1
2413         SDL_strlcpy(Multi_join_request.pxo_squad_name, Multi_tracker_squad_name, LOGIN_LEN);
2414 #endif
2415
2416         // version of this server
2417         Multi_join_request.version = MULTI_FS_SERVER_VERSION;
2418
2419         // server compatible version
2420         Multi_join_request.comp_version = MULTI_FS_SERVER_COMPATIBLE_VERSION;
2421
2422         // his local player options
2423         memcpy(&Multi_join_request.player_options,&Player->m_local_options,sizeof(multi_local_options));
2424                         
2425         // set the server address for the netgame
2426         memcpy(&Netgame.server_addr,&Multi_join_selected_item->server_addr,sizeof(net_addr));
2427
2428         // send a join request to the guy
2429         send_join_packet(&Multi_join_selected_item->server_addr,&Multi_join_request);
2430
2431    // now we wait
2432         Multi_join_sent_stamp = timestamp(MULTI_JOIN_SENT_WAIT);
2433
2434         psnet_flush();
2435         multi_common_add_notify(XSTR("Sending join request...",773));
2436 }
2437
2438 void multi_join_create_game()
2439 {
2440         // maybe warn the player about possible crappy server conditions
2441         if(!multi_join_maybe_warn()){
2442                 return;
2443         }
2444
2445         // make sure to flag ourself as being the master
2446         Net_player->flags |= (NETINFO_FLAG_AM_MASTER | NETINFO_FLAG_GAME_HOST);
2447         Net_player->state = NETPLAYER_STATE_HOST_SETUP; 
2448
2449         // if we're in PXO mode, mark it down in our player struct
2450         if(MULTI_IS_TRACKER_GAME){
2451                 Player->flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
2452                 Player->save_flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
2453         } 
2454         // otherwise, if he's already played PXO games, warn him
2455         else {
2456                 /*
2457                 if(Player->flags & PLAYER_FLAGS_HAS_PLAYED_PXO){
2458                         if(!multi_join_warn_pxo()){
2459                                 return;
2460                         }                       
2461                 }
2462                 */
2463         }
2464
2465         gameseq_post_event(GS_EVENT_MULTI_START_GAME);
2466         gamesnd_play_iface(SND_USER_SELECT);                                                            
2467 }
2468
2469 void multi_join_reset_join_stamp()
2470 {
2471         // unset the timestamp here so the user can immediately send another join request
2472         Multi_join_sent_stamp = -1;
2473         multi_common_add_notify("");
2474 }
2475
2476 void multi_join_blit_top_stuff()
2477 {       
2478         // blit the cd icon if he has one
2479         if(Multi_has_cd && (Multi_common_icons[MICON_CD] != -1)){
2480                 // get bitmap width
2481                 int cd_w;
2482                 bm_get_info(Multi_common_icons[MICON_CD], &cd_w, NULL, NULL, NULL, NULL);
2483
2484                 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
2485                 gr_bitmap((gr_screen.max_w / 2) - (cd_w / 2), Mj_cd_coords[gr_screen.res]);
2486         }       
2487 }
2488
2489 #define CW_CODE_CANCEL                          0                               // cancel the action
2490 #define CW_CODE_OK                                      1                               // continue anyway
2491 #define CW_CODE_INFO                                    2                               // gimme some more information
2492
2493 #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)
2494 #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)
2495
2496 #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)
2497 #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)
2498
2499 int multi_join_warn_update_low(int code)
2500 {
2501         switch(code){
2502         case CW_CODE_OK:
2503                 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),LOW_WARN_TEXT);
2504
2505         case CW_CODE_INFO:
2506                 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),LOW_INFO_TEXT);
2507         }
2508
2509         return CW_CODE_CANCEL;
2510 }
2511
2512 int multi_join_warn_update_medium(int code)
2513 {
2514         switch(code){
2515         case CW_CODE_OK:
2516                 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),MED_WARN_TEXT);
2517
2518         case CW_CODE_INFO:
2519                 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),MED_INFO_TEXT);
2520         }
2521
2522         return CW_CODE_CANCEL;
2523 }
2524
2525 int multi_join_maybe_warn()
2526 {
2527         int code;
2528
2529         // if the player is set for low updates
2530         if(Player->m_local_options.obj_update_level == OBJ_UPDATE_LOW){
2531                 code = CW_CODE_OK;
2532                 do {
2533                         code = multi_join_warn_update_low(code);
2534                 } while((code != CW_CODE_CANCEL) && (code != CW_CODE_OK));
2535                 
2536                 return code;
2537         }
2538         
2539         // if the player is set for medium updates
2540         else if(Player->m_local_options.obj_update_level == OBJ_UPDATE_MEDIUM){
2541                 code = CW_CODE_OK;
2542                 do {
2543                         code = multi_join_warn_update_medium(code);
2544                 } while((code != CW_CODE_CANCEL) && (code != CW_CODE_OK));
2545                 
2546                 return code;
2547         } 
2548         
2549         return 1;
2550 }
2551
2552 int multi_join_warn_pxo()
2553 {
2554         // 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;
2555         return 1;
2556 }
2557
2558 void multi_join_blit_protocol()
2559 {
2560         gr_set_color_fast(&Color_bright);
2561
2562         switch(Socket_type){
2563                 case NET_TCP:
2564                         // straight TCP
2565                         gr_string(5, 2, "TCP");
2566                         break;
2567
2568                 default:
2569                         Int3();
2570         }
2571 }
2572
2573
2574 // -------------------------------------------------------------------------------------------------
2575 //
2576 // MULTIPLAYER START GAME screen
2577 //
2578
2579 //XSTR:OFF
2580 // bitmap defs
2581 #define MULTI_SG_PALETTE                        "InterfacePalette"
2582
2583 static const char *Multi_sg_bitmap_fname[GR_NUM_RESOLUTIONS] = {
2584         "MultiStartGame",                       // GR_640
2585         "2_MultiStartGame"                      // GR_1024
2586 };
2587
2588 static const char *Multi_sg_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
2589         "MultiStartGame-M",                     // GR_640
2590         "2_MultiStartGame-M"                    // GR_1024
2591 };
2592
2593 //XSTR:ON
2594
2595 int Multi_sg_rank_max_display[GR_NUM_RESOLUTIONS] = {
2596         2,              // GR_640
2597         4               // GR_1024
2598 };
2599
2600 // constants for coordinate look ups
2601 #define MSG_X_COORD 0
2602 #define MSG_Y_COORD 1
2603 #define MSG_W_COORD 2
2604 #define MSG_H_COORD 3
2605
2606 // area definitions
2607
2608 // input password field
2609 int Msg_passwd_coords[GR_NUM_RESOLUTIONS][4] = {
2610         { // GR_640
2611 #ifdef MAKE_FS1
2612                 247, 179, 285, 9
2613 #else
2614                 36, 236, 408, 20
2615 #endif
2616         },
2617         { // GR_1024
2618                 58, 377, 652, 32
2619         }
2620 };
2621
2622 // input game title field
2623 int Msg_title_coords[GR_NUM_RESOLUTIONS][4] = {
2624         { // GR_640
2625 #ifdef MAKE_FS1
2626                 178, 66, 354, 9
2627 #else
2628                 29, 49, 415, 23
2629 #endif
2630         },
2631         { // GR_1024
2632                 46, 78, 664, 36
2633         }
2634 };
2635
2636 // rank selected field
2637 int Msg_rank_sel_coords[GR_NUM_RESOLUTIONS][4] = {
2638         { // GR_640
2639 #ifdef MAKE_FS1
2640                 241, 253, 129, 10
2641 #else
2642                 242, 254, 126, 12
2643 #endif
2644         },
2645         { // GR_1024
2646                 242, 254, 126, 12
2647         }
2648 };
2649
2650 // rank list field
2651 int Msg_rank_list_coords[GR_NUM_RESOLUTIONS][4] = {
2652         { // GR_640
2653 #ifdef MAKE_FS1
2654                 241, 285, 129, 51
2655 #else
2656                 37, 297, 131, 16
2657 #endif
2658         },
2659         { // GR_1024
2660                 60, 469, 652, 32
2661         }
2662 };
2663
2664
2665 // button defs
2666 #ifdef MAKE_FS1
2667 #define MULTI_SG_NUM_BUTTONS    12
2668 #define MSG_OPEN_GAME                   0
2669 #define MSG_CLOSED_GAME                 1
2670 #define MSG_PASSWD_GAME                 2
2671 #define MSG_RESTRICTED_GAME             3
2672 #define MSG_RANK_SET_GAME               4
2673 #define MSG_RANK_SCROLL_UP              5
2674 #define MSG_RANK_SCROLL_DOWN    6
2675 #define MSG_RANK_ABOVE                  7
2676 #define MSG_RANK_BELOW                  8
2677 #define MSG_HELP                                9
2678 #define MSG_OPTIONS                             10
2679 #define MSG_ACCEPT                              11
2680 #else
2681 #define MULTI_SG_NUM_BUTTONS    10
2682 #define MSG_OPEN_GAME                   0
2683 //#define MSG_CLOSED_GAME                       1
2684 //#define MSG_RESTRICTED_GAME           2
2685 #define MSG_PASSWD_GAME                 1
2686 #define MSG_RANK_SET_GAME               2
2687 #define MSG_RANK_SCROLL_UP              3
2688 #define MSG_RANK_SCROLL_DOWN    4
2689 #define MSG_RANK_ABOVE                  5
2690 #define MSG_RANK_BELOW                  6
2691 #define MSG_HELP                                        7
2692 #define MSG_OPTIONS                             8
2693 #define MSG_ACCEPT                              9
2694 #endif
2695
2696 UI_WINDOW Multi_sg_window;                                                                                              // the window object for the join screen
2697 UI_BUTTON Multi_sg_rank_button;                                                                         // for selecting the rank marker
2698 UI_INPUTBOX     Multi_sg_game_name;                                                                             // for Netgame.name
2699 UI_INPUTBOX Multi_sg_game_passwd;                                                                       // for Netgame.passwd
2700 int Multi_sg_bitmap;                                                                                                            // the background bitmap
2701
2702 ui_button_info Multi_sg_buttons[GR_NUM_RESOLUTIONS][MULTI_SG_NUM_BUTTONS] = {
2703         { // GR_640
2704 #ifdef MAKE_FS1
2705                 ui_button_info("MSG_00",        75,             111,    -1,     -1,     0),
2706                 ui_button_info("MSG_01",        75,             139,    -1,     -1,     1),
2707                 ui_button_info("MSG_02",        75,             164,    -1,     -1,     2),
2708                 ui_button_info("MSG_03",        75,             199,    -1,     -1,     3),
2709                 ui_button_info("MSG_04",        75,             243,    -1,     -1,     4),
2710                 ui_button_info("MSG_05",        376,    231,    -1,     -1,     5),
2711                 ui_button_info("MSG_06",        376,    258,    -1,     -1,     6),
2712                 ui_button_info("MSG_07",        376,    291,    -1,     -1,     7),
2713                 ui_button_info("MSG_08",        376,    320,    -1,     -1,     8),
2714                 ui_button_info("MSG_09",        469,    427,    -1,     -1,     9),
2715                 ui_button_info("MSG_10",        447,    452,    -1,     -1,     10),
2716                 ui_button_info("MSG_11",        561,    411,    -1,     -1,     11),
2717 #else
2718                 ui_button_info("MSG_00",        1,              184,    34,     191,    2),             // open
2719 //              ui_button_info("MSG_01",        1,              159,    34,     166,    1),             // closed
2720 //              ui_button_info("MSG_02",        1,              184,    34,     191,    2),             // restricted
2721                 ui_button_info("MSG_03",        1,              209,    34,     218,    3),             // password
2722                 ui_button_info("MSG_04",        1,              257,    34,     266,    4),             // rank set
2723                 ui_button_info("MSG_05",        1,              282,    -1,     -1,     5),             // rank scroll up
2724                 ui_button_info("MSG_06",        1,              307,    -1,     -1,     6),             // rank scroll down
2725                 ui_button_info("MSG_07",        177,    282,    210,    290,    7),             // rank above
2726                 ui_button_info("MSG_08",        177,    307,    210,    315,    8),             // rank below
2727                 ui_button_info("MSG_09",        536,    429,    500,    440,    9),             // help
2728                 ui_button_info("MSG_10",        536,    454,    479,    464,    10),            // options
2729                 ui_button_info("MSG_11",        576,    432,    571,    415,    11),            // accept
2730 #endif
2731         },
2732         { // GR_1024
2733                 ui_button_info("2_MSG_00",      2,              295,    51,     307,    2),             // open
2734 //              ui_button_info("2_MSG_01",      2,              254,    51,     267,    1),             // closed
2735 //              ui_button_info("2_MSG_02",      2,              295,    51,     307,    2),             // restricted
2736                 ui_button_info("2_MSG_03",      2,              335,    51,     350,    3),             // password
2737                 ui_button_info("2_MSG_04",      2,              412,    51,     426,    4),             // rank set
2738                 ui_button_info("2_MSG_05",      2,              452,    -1,     -1,     5),             // rank scroll up
2739                 ui_button_info("2_MSG_06",      2,              492,    -1,     -1,     6),             // rank scroll down
2740                 ui_button_info("2_MSG_07",      284,    452,    335,    465,    7),             // rank above
2741                 ui_button_info("2_MSG_08",      284,    492,    335,    505,    8),             // rank below
2742                 ui_button_info("2_MSG_09",      858,    687,    817,    706,    9),             // help
2743                 ui_button_info("2_MSG_10",      858,    728,    797,    743,    10),            // options
2744                 ui_button_info("2_MSG_11",      921,    692,    921,    664,    11),            // accept
2745 #ifdef MAKE_FS1         // filler for extra FS1 buttons
2746                 ui_button_info("none",  -1,     -1,     -1,     -1,     -1),
2747                 ui_button_info("none",  -1, -1, -1, -1, -1),
2748 #endif
2749         },
2750 };
2751
2752 #ifndef MAKE_FS1
2753 #define MULTI_SG_NUM_TEXT                       11
2754
2755 UI_XSTR Multi_sg_text[GR_NUM_RESOLUTIONS][MULTI_SG_NUM_TEXT] = {
2756         { // GR_640
2757                 {"Open",                                        1322,           34,     191,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_sg_buttons[0][MSG_OPEN_GAME].button},
2758 //              {"Closed",                              1323,           34,     166,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_sg_buttons[0][MSG_CLOSED_GAME].button},
2759 //              {"Restricted",                  1324,           34,     191,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_sg_buttons[0][MSG_RESTRICTED_GAME].button},
2760                 {"Password Protected",  1325,   34,     218,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_sg_buttons[0][MSG_PASSWD_GAME].button},
2761                 {"Allow Rank",                  1326,           34,     266,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_sg_buttons[0][MSG_RANK_SET_GAME].button},
2762                 {"Above",                               1327,           210,    290,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_sg_buttons[0][MSG_RANK_ABOVE].button},
2763                 {"Below",                               1328,           210,    315,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_sg_buttons[0][MSG_RANK_BELOW].button},
2764                 {"Help",                                        928,            500,    440,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_sg_buttons[0][MSG_HELP].button},
2765                 {"Options",                             1036,           479,    464,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_sg_buttons[0][MSG_OPTIONS].button},
2766                 {"Accept",                              1035,           571,    415,    UI_XSTR_COLOR_PINK,     -1,     &Multi_sg_buttons[0][MSG_ACCEPT].button},
2767                 {"Start Game",                  1329,           26,     10,     UI_XSTR_COLOR_GREEN,    -1,     NULL},
2768                 {"Title",                               1330,           26,     31,     UI_XSTR_COLOR_GREEN,    -1,     NULL},
2769                 {"Game Type",                   1331,           12,     165,    UI_XSTR_COLOR_GREEN,    -1,     NULL},
2770         },
2771         { // GR_1024
2772                 {"Open",                                        1322,           51,     307,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_sg_buttons[1][MSG_OPEN_GAME].button},
2773 //              {"Closed",                              1323,           51,     267,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_sg_buttons[1][MSG_CLOSED_GAME].button},
2774 //              {"Restricted",                  1324,           51,     307,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_sg_buttons[1][MSG_RESTRICTED_GAME].button},
2775                 {"Password Protected",  1325,   51,     350,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_sg_buttons[1][MSG_PASSWD_GAME].button},
2776                 {"Allow Rank",                  1326,           51,     426,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_sg_buttons[1][MSG_RANK_SET_GAME].button},
2777                 {"Above",                               1327,           335,    465,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_sg_buttons[1][MSG_RANK_ABOVE].button},
2778                 {"Below",                               1328,           335,    505,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_sg_buttons[1][MSG_RANK_BELOW].button},
2779                 {"Help",                                        928,            817,    706,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_sg_buttons[1][MSG_HELP].button},
2780                 {"Options",                             1036,           797,    743,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_sg_buttons[1][MSG_OPTIONS].button},
2781                 {"Accept",                              1035,           921,    664,    UI_XSTR_COLOR_PINK,     -1,     &Multi_sg_buttons[1][MSG_ACCEPT].button},
2782                 {"Start Game",                  1329,           42,     22,     UI_XSTR_COLOR_GREEN,    -1,     NULL},
2783                 {"Title",                               1330,           42,     50,     UI_XSTR_COLOR_GREEN,    -1,     NULL},
2784                 {"Game Type",                   1331,           20,     264,    UI_XSTR_COLOR_GREEN,    -1,     NULL},
2785         }
2786 };
2787 #endif
2788
2789 // starting index for displaying ranks
2790 int Multi_sg_rank_start;
2791 int Multi_sg_rank_select;
2792
2793 // netgame pointer to indirect through
2794 netgame_info *Multi_sg_netgame;
2795
2796 // hold temporary values in this structure when on a standalone server
2797 netgame_info Multi_sg_netgame_temp;
2798
2799 // forward declarations
2800 void multi_sg_check_buttons();
2801 void multi_sg_button_pressed(int n);
2802 void multi_sg_init_gamenet();
2803 void multi_sg_draw_radio_buttons();
2804 void multi_sg_rank_scroll_up();
2805 void multi_sg_rank_scroll_down();
2806 void multi_sg_rank_display_stuff();
2807 void multi_sg_rank_process_select();
2808 void multi_sg_rank_build_name(char *in, char *out, const int max_outlen);
2809 void multi_sg_check_passwd();
2810 void multi_sg_check_name();
2811 void multi_sg_release_passwd();
2812 int multi_sg_rank_select_valid(int rank);
2813 void multi_sg_select_rank_default();
2814
2815 // function which takes a rank name and returns the index.  Useful for commandline options
2816 // for above and below rank.  We return the index of the rank in the Ranks[] array.  If
2817 // the rank isn't found, we return -1
2818 int multi_start_game_rank_from_name( char *rank ) {
2819         int i;
2820
2821 #ifdef MAKE_FS1
2822         for ( i = 0; i <= MAX_FREESPACE1_RANK; i++ ) {
2823 #else
2824         for ( i = 0; i <= MAX_FREESPACE2_RANK; i++ ) {
2825 #endif
2826                 if ( !SDL_strcasecmp(Ranks[i].name, rank) ) {
2827                         return i;
2828                 }
2829         }
2830
2831         return -1;
2832 }
2833
2834 void multi_start_game_init()
2835 {
2836         int idx;
2837
2838         // initialize the gamenet
2839         multi_sg_init_gamenet();
2840         
2841         // create the interface window
2842         Multi_sg_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
2843         Multi_sg_window.set_mask_bmap(Multi_sg_bitmap_mask_fname[gr_screen.res]);
2844
2845         // load the background bitmap
2846         Multi_sg_bitmap = bm_load(Multi_sg_bitmap_fname[gr_screen.res]);
2847         if(Multi_sg_bitmap < 0){
2848                 // we failed to load the bitmap - this is very bad
2849                 Int3();
2850         }
2851         
2852         // initialize the common notification messaging
2853         multi_common_notify_init();
2854
2855         // initialize the common text area
2856         multi_common_set_text("");
2857
2858         // use the common interface palette
2859         multi_common_set_palette();
2860         
2861         // create the interface buttons
2862         for(idx=0; idx<MULTI_SG_NUM_BUTTONS; idx++){
2863                 // create the object
2864                 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);
2865
2866                 // set the sound to play when highlighted
2867                 Multi_sg_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
2868
2869                 // set the ani for the button
2870                 Multi_sg_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sg_buttons[gr_screen.res][idx].filename);
2871
2872                 // set the hotspot
2873                 Multi_sg_buttons[gr_screen.res][idx].button.link_hotspot(Multi_sg_buttons[gr_screen.res][idx].hotspot);
2874         }       
2875
2876 #ifndef MAKE_FS1
2877         // add all xstrs
2878         for(idx=0; idx<MULTI_SG_NUM_TEXT; idx++){
2879                 Multi_sg_window.add_XSTR(&Multi_sg_text[gr_screen.res][idx]);
2880         }
2881 #endif
2882
2883         // load the help overlay
2884         help_overlay_load(MULTI_START_OVERLAY);
2885         help_overlay_set_state(MULTI_START_OVERLAY,0);
2886
2887         // intiialize the rank selection items  
2888         multi_sg_select_rank_default(); 
2889         Multi_sg_rank_start = Multi_sg_rank_select;
2890
2891         // create the rank select button
2892         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);       
2893         Multi_sg_rank_button.hide();            
2894
2895         // create the netgame name input box
2896         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);
2897
2898         // create the netgame password input box, and disable it by default
2899         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);
2900         Multi_sg_game_passwd.hide();
2901         Multi_sg_game_passwd.disable();
2902
2903         // set the netgame text to this gadget and make it have focus
2904         Multi_sg_game_name.set_text(Multi_sg_netgame->name);
2905         Multi_sg_game_name.set_focus(); 
2906
2907         // if starting a netgame, set the name of the game and any other options that are appropriate
2908         if ( Cmdline_start_netgame ) {
2909                 if ( Cmdline_game_name != NULL ) {
2910                         SDL_strlcpy( Multi_sg_netgame->name, Cmdline_game_name, SDL_arraysize(Multi_sg_netgame->name) );
2911                         Multi_sg_game_name.set_text(Multi_sg_netgame->name);
2912                 }
2913
2914                 // deal with the different game types -- only one should even be active, so we will just go down
2915                 // the line.  Last one wins.
2916                 if ( Cmdline_closed_game ) {
2917                         Multi_sg_netgame->mode = NG_MODE_CLOSED;
2918                 } else if ( Cmdline_restricted_game ) {
2919                         Multi_sg_netgame->mode = NG_MODE_RESTRICTED;
2920                 } else if ( Cmdline_game_password != NULL ) {
2921                         Multi_sg_netgame->mode = NG_MODE_PASSWORD;
2922                         SDL_strlcpy(Multi_sg_netgame->passwd, Cmdline_game_password, SDL_arraysize(Multi_sg_netgame->passwd));
2923                         Multi_sg_game_passwd.set_text(Multi_sg_netgame->passwd);
2924                 }
2925
2926                 // deal with rank above and rank below
2927                 if ( (Cmdline_rank_above != NULL) || (Cmdline_rank_below != NULL) ) {
2928                         int rank;
2929                         char *rank_str;
2930
2931                         if ( Cmdline_rank_above != NULL ) {
2932                                 rank_str = Cmdline_rank_above;
2933                         } else {
2934                                 rank_str = Cmdline_rank_below;
2935                         }
2936
2937                         // try and get the rank index from the name -- if found, then set the rank base
2938                         // and the game type.  apparently we only support either above or below, not both
2939                         // together, so I make a random choice
2940                         rank = multi_start_game_rank_from_name( rank_str );
2941                         if ( rank != -1 ) {
2942                                 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
2943
2944                                 // now an arbitrary decision
2945                                 if ( Cmdline_rank_above != NULL ) {
2946                                         Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
2947                                 } else {
2948                                         Multi_sg_netgame->mode = NG_MODE_RANK_BELOW;
2949                                 }
2950                         }
2951                 }
2952
2953                 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
2954         }
2955
2956         if ( multi_fs_tracker_inited() ) {
2957                 multi_fs_tracker_login_freespace();
2958         }
2959 }
2960
2961 void multi_start_game_do()
2962 {
2963         // return here since we will be moving to the next stage anyway -- I don't want to see the backgrounds of
2964         // all the screens for < 1 second for every screen we automatically move to.
2965         if ( Cmdline_start_netgame ) {
2966                 return;
2967         }
2968
2969         int k = Multi_sg_window.process();
2970
2971         // process any keypresses
2972         switch(k){
2973         case SDLK_ESCAPE :
2974                 if(help_overlay_active(MULTI_START_OVERLAY)){
2975                         help_overlay_set_state(MULTI_START_OVERLAY,0);
2976                 } else {
2977                         gamesnd_play_iface(SND_USER_SELECT);
2978                         multi_quit_game(PROMPT_NONE);
2979                 }
2980                 break;
2981         
2982         // same as ACCEPT
2983         case SDLK_LCTRL + SDLK_RETURN :
2984         case SDLK_RCTRL + SDLK_RETURN :
2985                 gamesnd_play_iface(SND_COMMIT_PRESSED);
2986                 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
2987                 break;
2988         }       
2989
2990         if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
2991                 help_overlay_set_state(MULTI_START_OVERLAY, 0);
2992         }
2993
2994         // check to see if the user has selected a different rank
2995         multi_sg_rank_process_select();
2996
2997         // check any button presses
2998         multi_sg_check_buttons();
2999
3000         // check to see if any of the input boxes have changed, and update the appropriate Netgame fields if necessary
3001         multi_sg_check_passwd();
3002         multi_sg_check_name();
3003
3004         // draw the background, etc
3005         gr_reset_clip();
3006         GR_MAYBE_CLEAR_RES(Multi_sg_bitmap);
3007         if(Multi_sg_bitmap != -1){
3008                 gr_set_bitmap(Multi_sg_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
3009                 gr_bitmap(0,0);
3010         }
3011         Multi_sg_window.draw();
3012         
3013         // display rank stuff
3014         multi_sg_rank_display_stuff();
3015
3016         // display any pending notification messages
3017         multi_common_notify_do();
3018
3019         // draw all radio button
3020         multi_sg_draw_radio_buttons();
3021
3022         // draw the help overlay
3023         help_overlay_maybe_blit(MULTI_START_OVERLAY);
3024         
3025         // flip the buffer
3026         gr_flip();
3027 }
3028
3029 void multi_start_game_close()
3030 {
3031         // if i'm the host on a standalone server, send him my start game options (passwd, mode, etc)
3032         if((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
3033                 multi_options_update_start_game(Multi_sg_netgame);
3034         }
3035         
3036         // unload any bitmaps
3037         if(!bm_unload(Multi_sg_bitmap)){
3038                 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_sg_bitmap_fname[gr_screen.res]));
3039         }
3040
3041         // unload the help overlay
3042         help_overlay_unload(MULTI_START_OVERLAY);
3043         
3044         // destroy the UI_WINDOW
3045         Multi_sg_window.destroy();      
3046 }
3047
3048 void multi_sg_check_buttons()
3049 {
3050         int idx;
3051         for(idx=0;idx<MULTI_SG_NUM_BUTTONS;idx++){
3052                 // we only really need to check for one button pressed at a time, so we can break after 
3053                 // finding one.
3054                 if(Multi_sg_buttons[gr_screen.res][idx].button.pressed()){
3055                         multi_sg_button_pressed(idx);
3056                         break;
3057                 }
3058         }
3059 }
3060
3061 void multi_sg_button_pressed(int n)
3062 {
3063         switch(n){              
3064         // go to the options screen
3065         case MSG_OPTIONS:
3066                 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
3067                 break;  
3068
3069         // help overlay 
3070         case MSG_HELP:
3071                 if(!help_overlay_active(MULTI_START_OVERLAY)){
3072                         help_overlay_set_state(MULTI_START_OVERLAY,1);
3073                 } else {
3074                         help_overlay_set_state(MULTI_START_OVERLAY,0);
3075                 }
3076                 break;
3077
3078         // the open button was pressed
3079         case MSG_OPEN_GAME:             
3080                 // if the closed option is selected
3081                 if(Multi_sg_netgame->mode != NG_MODE_OPEN){
3082                         Multi_sg_netgame->mode = NG_MODE_OPEN;
3083
3084                         gamesnd_play_iface(SND_USER_SELECT);
3085                         
3086                         // release the password control if necessary
3087                         multi_sg_release_passwd();
3088                 }
3089                 // if its already selected
3090                 else {
3091                         gamesnd_play_iface(SND_GENERAL_FAIL);
3092                 }
3093                 break;
3094
3095 #ifdef MAKE_FS1
3096         // the open button was pressed
3097         case MSG_CLOSED_GAME:           
3098                 // if the closed option is selected
3099                 if(Multi_sg_netgame->mode != NG_MODE_CLOSED){
3100                         Multi_sg_netgame->mode = NG_MODE_CLOSED;
3101
3102                         gamesnd_play_iface(SND_USER_SELECT);
3103                         
3104                         // release the password control if necessary
3105                         multi_sg_release_passwd();
3106                 }
3107                 // if its already selected
3108                 else {
3109                         gamesnd_play_iface(SND_GENERAL_FAIL);
3110                 }
3111                 break;
3112 #endif
3113
3114         // toggle password protection
3115         case MSG_PASSWD_GAME:           
3116                 // if we selected it
3117                 if(Multi_sg_netgame->mode != NG_MODE_PASSWORD){
3118                         gamesnd_play_iface(SND_USER_SELECT);            
3119
3120                         Multi_sg_game_passwd.enable();                  
3121                         Multi_sg_game_passwd.unhide();
3122                         Multi_sg_game_passwd.set_focus();
3123
3124                         Multi_sg_netgame->mode = NG_MODE_PASSWORD;                      
3125
3126                         // copy in the current network password
3127                         Multi_sg_game_passwd.set_text(Multi_sg_netgame->passwd);