FS2_Open
Open source remastering of the Freespace 2 engine
multiui.cpp
Go to the documentation of this file.
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 the
6  * source.
7  *
8 */
9 
10 
11 
12 
13 #ifdef _WIN32
14 #include <winsock.h> // for inet_addr()
15 #else
16 #include <arpa/inet.h>
17 #include <netinet/in.h>
18 #endif
19 
20 #include "network/multiui.h"
21 #include "freespace2/freespace.h"
22 #include "network/multi.h"
23 #include "network/multiutil.h"
24 #include "network/multimsgs.h"
25 #include "io/key.h"
26 #include "io/timer.h"
28 #include "gamehelp/contexthelp.h"
29 #include "playerman/player.h"
30 #include "network/multi_xfer.h"
31 #include "cmdline/cmdline.h"
32 #include "network/stand_gui.h"
35 #include "graphics/font.h"
36 #include "io/mouse.h"
37 #include "gamesnd/gamesnd.h"
38 #include "missionui/chatbox.h"
39 #include "popup/popup.h"
41 #include "network/multi_ingame.h"
42 #include "network/multi_kick.h"
43 #include "network/multi_data.h"
44 #include "network/multi_campaign.h"
45 #include "network/multi_team.h"
46 #include "network/multi_pinfo.h"
47 #include "network/multi_observer.h"
48 #include "network/multi_voice.h"
49 #include "network/multi_endgame.h"
50 #include "playerman/managepilot.h"
51 #include "pilotfile/pilotfile.h"
52 #include "stats/stats.h"
53 #include "network/multi_pmsg.h"
54 #include "network/multi_obj.h"
55 #include "network/multi_log.h"
56 #include "globalincs/alphacolors.h"
57 #include "anim/animplay.h"
58 #include "network/multi_dogfight.h"
59 #include "missionui/missionpause.h"
60 #include "ship/ship.h"
61 #include "osapi/osregistry.h"
63 #include "parse/parselo.h"
64 #include "cfile/cfile.h"
65 #include "fs2netd/fs2netd_client.h"
66 #include "menuui/mainhallmenu.h"
67 #include "debugconsole/console.h"
68 
69 #include <algorithm>
70 
71 
72 // -------------------------------------------------------------------------------------------------------------
73 //
74 // MULTIPLAYER COMMON interface controls
75 //
76 
77 // the common text info box stuff. This is lifted almost directly from Alans briefing code (minus the spiffy colored, scrolling
78 // text crap :)
80  { // GR_640
81  29, 396, 393, 76
82  },
83  { // GR_1024
84  47, 634, 630, 122
85  }
86 };
87 
89  8, // GR_640
90  12, // GR_1024
91 };
92 
93 #define MULTI_COMMON_TEXT_META_CHAR '$'
94 #define MULTI_COMMON_TEXT_MAX_LINE_LENGTH 200
95 #define MULTI_COMMON_TEXT_MAX_LINES 20
96 #define MULTI_COMMON_MAX_TEXT (MULTI_COMMON_TEXT_MAX_LINES * MULTI_COMMON_TEXT_MAX_LINE_LENGTH)
97 
100 
101 int Multi_common_top_text_line = -1; // where to start displaying from
102 int Multi_common_num_text_lines = 0; // how many lines we have
103 
109 
110 #define MAX_IP_STRING 255 // maximum length for ip string
111 
113 {
115  if ( Multi_common_top_text_line < 0 ) {
119 
120  } else {
122  }
123 }
124 
126 {
130  if ( !mouse_down(MOUSE_LEFT_BUTTON) ){
132  }
133  } else {
135  }
136 }
137 
139 {
140  // if there's nowhere to scroll down, do nothing
142  return;
143  }
144 
146 }
147 
148 void multi_common_set_text(const char *str,int auto_scroll)
149 {
150  // make sure it fits
151  // store the entire string as well
152  if(strlen(str) >= MULTI_COMMON_MAX_TEXT){
153  return ;
154  } else {
156  }
157 
158  // split the whole thing up
160 
161  // scroll to the bottom if we're supposed to
162  if(auto_scroll){
164  }
165 }
166 
167 void multi_common_add_text(const char *str,int auto_scroll)
168 {
169  // make sure it fits
170  // store the entire string as well
171  if((strlen(str) + strlen(Multi_common_all_text)) >= MULTI_COMMON_MAX_TEXT){
172  return ;
173  } else {
175  }
176 
177  // split the whole thing up
179 
180  // scroll to the bottom if we're supposed to
181  if(auto_scroll){
183  }
184 }
185 
187 {
188  int n_lines, i;
189  int n_chars[MAX_BRIEF_LINES];
190  const char *p_str[MAX_BRIEF_LINES];
191 
193  Assert(n_lines != -1);
194 
195  for ( i = 0; i < n_lines; i++ ) {
196  //The E -- This check is unnecessary, and will break when fonts that aren't bank gothic are used
197  //split_str already ensured that everything will fit in the text window for us already.
198  //Assert(n_chars[i] < MULTI_COMMON_TEXT_MAX_LINE_LENGTH);
199  strncpy(Multi_common_text[i], p_str[i], n_chars[i]);
200  Multi_common_text[i][n_chars[i]] = 0;
202  }
203 
205  Multi_common_num_text_lines = n_lines;
206 }
207 
209 {
210  int i, fh, line_count;
211 
212  fh = gr_get_font_height();
213 
214  line_count = 0;
217  if ( line_count >= Multi_common_text_max_display[gr_screen.res] ){
218  break;
219  }
221  line_count++;
222  }
223 
224  if ( (Multi_common_num_text_lines - Multi_common_top_text_line) > Multi_common_text_max_display[gr_screen.res] ) {
227  }
228 }
229 
230 // common notification messaging stuff
231 #define MULTI_COMMON_NOTIFY_TIME 3500
233  375, // GR_640
234  605 // GR_1024
235 };
237  380, // GR_640
238  610 // GR_1024
239 };
240 
242  380, // GR_640
243  610 // GR_1024
244 };
245 
247  380, // GR_640
248  610 // GR_1024
249 };
250 
253 
255 {
258 }
259 
260 // add a notification string, drawing appropriately depending on the state/screen we're in
261 void multi_common_add_notify(const char *str)
262 {
263  if(str){
266  }
267 }
268 
269 // process/display notification messages
271 {
272  if(Multi_common_notify_stamp != -1){
275  } else {
276  int w,h,y;
279 
280  // determine where it should be placed based upon which screen we're on
281  y = -1;
282  switch(gameseq_get_state()){
285  break;
288  break;
291  break;
294  break;
295  }
296  if(y != -1){
298  }
299  }
300  }
301 }
302 
303 // common icon stuff
305 //XSTR:OFF
307  "DotRed", // voice denied
308  "DotGreen", // voice recording
309  "OvalGreen", // team 0
310  "OvalGreen01", // team 0 select
311  "OvalRed", // team 1
312  "OvalRed01", // team 1 select
313  "mp_coop", // coop mission
314  "mp_teams", // TvT mission
315  "mp_furball", // furball mission
316  "icon-volition", // volition mission
317  "icon-valid", // mission is valid
318  "cd" // cd icon
319 };
320 //XSTR:ON
321 // width and height of the icons
323  {11, 11}, // voice denied
324  {11, 11}, // voice recording
325  {11, 11}, // team 0
326  {11, 11}, // team 0 select
327  {11, 11}, // team 1
328  {11, 11}, // team 1 select
329  {18, 11}, // mp coop
330  {18, 11}, // mp TvT
331  {18, 11}, // mp furball
332  {9, 9}, // volition mission
333  {8, 8}, // mission is valid
334  {8, 8} // cd icon
335 };
336 
338 {
339  int idx;
340 
341  if (Is_standalone)
342  return;
343 
344  // load all icons
345  for(idx=0; idx<MULTI_NUM_COMMON_ICONS; idx++){
346  Multi_common_icons[idx] = -1;
348  }
349 }
350 
352 {
353  int idx;
354 
355  if (Is_standalone)
356  return;
357 
358  // unload all icons
359  for(idx=0; idx<MULTI_NUM_COMMON_ICONS; idx++){
360  if(Multi_common_icons[idx] != -1){
361  // don't bm_release() here, used in multiple places - taylor
363  Multi_common_icons[idx] = -1;
364  }
365  }
366 }
367 
368 // display any relevant voice status icons
370 {
371  switch(multi_voice_status()){
372  // i have been denied the voice token
377  }
378  break;
379 
380  // i am currently recording
385  }
386  break;
387 
388  // i am currently playing back sound
390  break;
391 
392  // the system is currently idle
394  break;
395  }
396 }
397 
398 //XSTR:OFF
399 // palette initialization stuff
400 #define MULTI_COMMON_PALETTE_FNAME "InterfacePalette"
401 //XSTR:ON
402 
404 
408 
409 // load in the palette if it doesn't already exist
411 {
413  return;
414  }
415 
418  nprintf(("Network","Error loading multiplayer common palette!\n"));
419  }
420 }
421 
422 // set the common palette to be the active one
424 {
425  // if the palette is not loaded yet, do so now
428  }
429 
431 #ifndef HARDWARE_ONLY
433 #endif
434  }
435 }
436 
437 // unload the bitmap palette
439 {
443  }
444 }
445 
447 {
448 #ifdef GAME_CD_CHECK
449  // otherwise, call the freespace function to determine if we have a cd
450  Multi_has_cd = 0;
452  Multi_has_cd = 1;
453  }
454 #else
455  Multi_has_cd = 1;
456 #endif
457 }
458 
459 
460 // -------------------------------------------------------------------------------------------------------------
461 //
462 // MULTIPLAYER JOIN SCREEN
463 //
464 
465 #define MULTI_JOIN_NUM_BUTTONS 11
466 
467 //XSTR:OFF
468 // bitmaps defs
469 #define MULTI_JOIN_PALETTE "InterfacePalette"
470 
471 static char *Multi_join_bitmap_fname[GR_NUM_RESOLUTIONS] = {
472  "MultiJoin", // GR_640
473  "2_MultiJoin" // GR_1024
474 };
475 
476 static char *Multi_join_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
477  "MultiJoin-M", // GR_640
478  "2_MultiJoin-M" // GR_1024
479 };
480 //XSTR:ON
481 
482 // slider
484  "slider",
485  "2_slider"
486 };
488  { // GR_640
489  2, 91, 15, 202
490  },
491  { // GR_1024
492  8, 147, 30, 322
493  }
494 };
495 
496 // button defs
497 #define MJ_SCROLL_UP 0
498 #define MJ_SCROLL_DOWN 1
499 #define MJ_REFRESH 2
500 #define MJ_SCROLL_INFO_UP 3
501 #define MJ_SCROLL_INFO_DOWN 4
502 #define MJ_JOIN_OBSERVER 5
503 #define MJ_START_GAME 6
504 #define MJ_CANCEL 7
505 #define MJ_HELP 8
506 #define MJ_OPTIONS 9
507 #define MJ_ACCEPT 10
508 
509 // uses MULTI_JOIN_REFRESH_TIME as its timestamp
511 
512 #define MULTI_JOIN_PING_TIME 15000 // how often we ping all the known servers
514 UI_WINDOW Multi_join_window; // the window object for the join screen
515 UI_BUTTON Multi_join_select_button; // for selecting list items
516 UI_SLIDER2 Multi_join_slider; // handy dandy slider
517 int Multi_join_bitmap; // the background bitmap
518 
520  { // GR_640
521  ui_button_info( "MJ_00", 1, 57, -1, -1, 0 ), // scroll up
522  ui_button_info( "MJ_02", 1, 297, -1, -1, 2 ), // scroll down
523  ui_button_info( "MJ_03", 10, 338, 65, 364, 3 ), // refresh
524  ui_button_info( "MJ_04", 1, 405, -1, -1, 4 ), // scroll info up
525  ui_button_info( "MJ_05", 1, 446, -1, -1, 5 ), // scroll info down
526  ui_button_info( "MJ_06", 489, 339, -1, -1, 6 ), // join as observer
527  ui_button_info( "MJ_07", 538, 339, -1, -1, 7 ), // create game
528  ui_button_info( "MJ_08", 583, 339, 588, 376, 8 ), // cancel
529  ui_button_info( "MJ_09", 534, 426, -1, -1, 9 ), // help
530  ui_button_info( "MJ_10", 534, 454, -1, -1, 10 ), // options
531  ui_button_info( "MJ_11", 571, 426, 589, 416, 11 ), // join
532  },
533  { // GR_1024
534  ui_button_info( "2_MJ_00", 2, 92, -1, -1, 0 ), // scroll up
535  ui_button_info( "2_MJ_02", 2, 475, -1, -1, 2 ), // scroll down
536  ui_button_info( "2_MJ_03", 16, 541, 104, 582, 3 ), // refresh
537  ui_button_info( "2_MJ_04", 2, 648, -1, -1, 4 ), // scroll info up
538  ui_button_info( "2_MJ_05", 2, 713, -1, -1, 5 ), // scroll info down
539  ui_button_info( "2_MJ_06", 783, 542, -1, -1, 6 ), // join as observer
540  ui_button_info( "2_MJ_07", 861, 542, -1, -1, 7 ), // create game
541  ui_button_info( "2_MJ_08", 933, 542, 588, 376, 8 ), // cancel
542  ui_button_info( "2_MJ_09", 854, 681, -1, -1, 9 ), // help
543  ui_button_info( "2_MJ_10", 854, 727, -1, -1, 10 ), // options
544  ui_button_info( "2_MJ_11", 914, 681, 937, 668, 11 ), // join
545  }
546 };
547 
548 #define MULTI_JOIN_NUM_TEXT 13
549 
551  { // GR_640
552  {"Refresh", 1299, 65, 364, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_REFRESH].button},
553  {"Join as", 1300, 476, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_JOIN_OBSERVER].button},
554  {"Observer", 1301, 467, 385, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_JOIN_OBSERVER].button},
555  {"Create", 1408, 535, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_START_GAME].button},
556  {"Game", 1302, 541, 385, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_START_GAME].button},
557  {"Cancel", 387, 588, 376, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[0][MJ_CANCEL].button},
558  {"Help", 928, 496, 436, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_HELP].button},
559  {"Options", 1036, 479, 460, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_OPTIONS].button},
560  {"Join", 1303, 589, 416, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[0][MJ_ACCEPT].button},
561  {"Status", 1304, 37, 37, UI_XSTR_COLOR_GREEN, -1, NULL},
562  {"Server", 1305, 116, 37, UI_XSTR_COLOR_GREEN, -1, NULL},
563  {"Players", 1306, 471, 37, UI_XSTR_COLOR_GREEN, -1, NULL},
564  {"Ping", 1307, 555, 37, UI_XSTR_COLOR_GREEN, -1, NULL}
565  },
566  { // GR_1024
567  {"Refresh", 1299, 104, 582, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_REFRESH].button},
568  {"Join as", 1300, 783, 602, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_JOIN_OBSERVER].button},
569  {"Observer", 1301, 774, 611, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_JOIN_OBSERVER].button},
570  {"Create", 1408, 868, 602, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_START_GAME].button},
571  {"Game", 1302, 872, 611, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_START_GAME].button},
572  {"Cancel", 387, 941, 602, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[1][MJ_CANCEL].button},
573  {"Help", 928, 806, 699, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_HELP].button},
574  {"Options", 1036, 782, 736, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_OPTIONS].button},
575  {"Join", 1303, 937, 668, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[1][MJ_ACCEPT].button},
576  {"Status", 1304, 60, 60, UI_XSTR_COLOR_GREEN, -1, NULL},
577  {"Server", 1305, 186, 60, UI_XSTR_COLOR_GREEN, -1, NULL},
578  {"Players", 1306, 753, 60, UI_XSTR_COLOR_GREEN, -1, NULL},
579  {"Ping", 1307, 888, 60, UI_XSTR_COLOR_GREEN, -1, NULL}
580  }
581 };
582 
583 // constants for coordinate look ups
584 #define MJ_X_COORD 0
585 #define MJ_Y_COORD 1
586 #define MJ_W_COORD 2
587 #define MJ_H_COORD 3
588 
589 #define MULTI_JOIN_SENT_WAIT 10000 // wait this long since a join was sent to allow another
591 
592 // game information text areas
594  28, // GR_640
595  46 // GR_1024
596 };
597 
599  53, // GR_640
600  77 // GR_1024
601 };
602 
604  { // GR_640
605  34, 53, 55, 287
606  },
607  { // GR_1024
608  53, 77, 57, 459
609  }
610 };
611 
613  { // GR_640
614  98, 53, 28
615  },
616  { // GR_1024
617  124, 77, 28
618  }
619 };
620 
622  { // GR_640
623  122, 53, 56, 261
624  },
625  { // GR_1024
626  152, 77, 56, 261
627  }
628 };
629 
631  { // GR_640
632  186, 53, 280, 261
633  },
634  { // GR_1024
635  206, 77, 311, 261
636  }
637 };
638 
640  { // GR_640
641  473, 53, 50, 261
642  },
643  { // GR_1024
644  748, 77, 67, 459
645  }
646 };
647 
649  { // GR_640
650  551, 53, 47, 261
651  },
652  { // GR_1024
653  880, 77, 48, 459
654  }
655 };
656 
657 // game speed labels
658 #define MJ_NUM_SPEED_LABELS 5
660  "< 56k",
661  "56k",
662  "isdn",
663  "cable",
664  "t1/adsl+"
665 };
671  &Color_bright_green
672 };
673 
675  20, 30
676 };
677 
678 //XSTR:OFF
679 #define IP_CONFIG_FNAME "tcp.cfg" // name of the file which contains known TCP addresses
680 
681 // extents of the entire boundable game info region
682 // NOTE : these numbers are completely empirical
683 #define MJ_PING_GREEN 160
684 #define MJ_PING_YELLOW 300
685 #define MJ_PING_RED 700
686 #define MJ_PING_ONE_SECOND 1000
687 
689  { // GR_640
690  23, 53, 565, 279
691  },
692  { // GR_1024
693  53, 76, 887, 461
694  }
695 };
696 
697 // PXO channel filter
698 #define MJ_PXO_FILTER_Y 0
699 
700 // special chars to indicate various status modes for servers
701 #define MJ_CHAR_STANDALONE "*"
702 #define MJ_CHAR_CAMPAIGN "c"
703 //XSTR:ON
704 
705 // various interface indices
706 int Multi_join_list_start; // where to start displaying from
707 active_game *Multi_join_list_start_item; // a pointer to the corresponding active_game
708 int Multi_join_list_selected; // which item we have selected
709 active_game *Multi_join_selected_item; // a pointer to the corresponding active_game
710 
711 // use this macro to modify the list start
712 #define MJ_LIST_START_INC() do { Multi_join_list_start++; } while(0);
713 #define MJ_LIST_START_DEC() do { Multi_join_list_start--; } while(0);
714 #define MJ_LIST_START_SET(vl) do { Multi_join_list_start = vl; } while(0);
715 
716 // if we should be sending a join request at the end of the frame
718 
719 // master tracker details
720 int Multi_join_frame_count; // keep a count of frames displayed
721 int Multi_join_mt_tried_verify; // already tried verifying the pilot with the tracker
722 
723 // data stuff for auto joining a game
724 #define MULTI_AUTOJOIN_JOIN_STAMP 2000
725 #define MULTI_AUTOJOIN_QUERY_STAMP 2000
726 
731 
732 // our join request
734 
736 
737 // LOCAL function definitions
739 void multi_join_button_pressed(int n);
741 void multi_join_blit_game_status(active_game *game, int y);
744 void multi_join_ping_all();
752 void multi_join_handle_item_cull(active_game *item, int item_index);
753 void multi_join_send_join_request(int as_observer);
757 int multi_join_warn_pxo();
759 
760 DCF(mj_make, "Makes a multijoin game? (Multiplayer)")
761 {
762  active_game ag, *newitem;
763  int idx;
764  int idx_max;
765 
766  if (dc_optional_string_either("help", "--help")) {
767  dc_printf("Usage: mj_make <num_games>\n");
768  return;
769  }
770 
771  dc_stuff_int(&idx_max);
772 
773  for(idx = 0; idx < idx_max; idx++){
774  // stuff some fake info
775  memset(&ag, 0, sizeof(active_game));
776  sprintf(ag.name, "Game %d", idx);
779  ag.server_addr.addr[0] = (char)idx;
781 
782  // add the game
783  newitem = multi_update_active_games(&ag);
784 
785  // timestamp it so we get random timeouts
786  if(newitem != NULL){
787  // newitem->heard_from_timer = timestamp((int)frand_range(500.0f, 10000.0f));
788  }
789  }
790 }
791 
793 {
794  // reset the # of items
796  Multi_join_slider.force_currentItem(Multi_join_list_start);
797 }
798 
800 {
801  // if we have an active game on the list, then return a positive value so that we
802  // can join the game
803  if ( Active_game_head && (Active_game_count > 0) ) {
804  Multi_join_selected_item = Active_game_head;
805  return 1;
806  }
807 
808  // send out a server_query again
810  send_server_query(&Multi_autojoin_addr);
812  }
813 
814  return -1;
815 }
816 
818 {
819  int idx;
820 
821  // do the multiplayer init stuff - multi_level_init() now does all net_player zeroing.
822  // setup various multiplayer things
824  Assert( Net_player != NULL );
825 
826  switch (Multi_options_g.protocol) {
827  case NET_TCP:
830  break;
831 
832  default :
833  Int3();
834  } // end switch
835 
836  HEADER_LENGTH = 1;
837 
838  memset( &Netgame, 0, sizeof(Netgame) );
839 
840  multi_level_init();
843  memcpy(&Net_player->p_info.addr,&Psnet_my_addr,sizeof(net_addr));
844 
845  // check for the existence of a CD
847 
848  // load my local netplayer options
850 
851  game_flush();
852 
853  // destroy any chatbox contents which previously existed (from another game)
854  chatbox_clear();
855 
856  // create the interface window
857  Multi_join_window.create(0,0,gr_screen.max_w_unscaled,gr_screen.max_h_unscaled,0);
858  Multi_join_window.set_mask_bmap(Multi_join_bitmap_mask_fname[gr_screen.res]);
859 
860  // load the background bitmap
861  Multi_join_bitmap = bm_load(Multi_join_bitmap_fname[gr_screen.res]);
862  if(Multi_join_bitmap < 0){
863  // we failed to load the bitmap - this is very bad
864  Int3();
865  }
866 
867  // intialize the endgame system
869 
870  // initialize the common notification messaging
872 
873  // initialize the common text area
875 
876  // load and use the common interface palette
879 
880  // load the help overlay
883 
884  // try to login to the tracker
885  if (MULTI_IS_TRACKER_GAME) {
886  if ( !fs2netd_login() ) {
887  // failed! go back to the main hall
889  return;
890  }
891  }
892 
893  // do TCP and VMT specific initialization
895  // if this is a TCP (non tracker) game, we'll load up our default address list right now
897  }
898 
899  // initialize any and all timestamps
903 
904  // reset frame count
906 
907  // haven't tried to verify on the tracker yet.
909 
910  // clear our all game lists to save hassles
912 
913  // create the interface buttons
914  for(idx=0; idx<MULTI_JOIN_NUM_BUTTONS; idx++){
915  // create the object
916  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);
917 
918  // set the sound to play when highlighted
920 
921  // set the ani for the button
922  Multi_join_buttons[gr_screen.res][idx].button.set_bmaps(Multi_join_buttons[gr_screen.res][idx].filename);
923 
924  // set the hotspot
925  Multi_join_buttons[gr_screen.res][idx].button.link_hotspot(Multi_join_buttons[gr_screen.res][idx].hotspot);
926  }
927 
928  // create all xstrs
929  for(idx=0; idx<MULTI_JOIN_NUM_TEXT; idx++){
930  Multi_join_window.add_XSTR(&Multi_join_text[gr_screen.res][idx]);
931  }
932 
934 
935  // close any previously open chatbox
936  chatbox_close();
937 
938  // create the list item select button
940  Multi_join_select_button.hide();
941 
942  // slider
943  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);
944 
945  // make sure that we turn music/sounds back on (will be disabled after playing a mission)
947 
948  // if starting a network game, then go to the create game screen
949  if ( Cmdline_start_netgame ) {
951  } else if ( Cmdline_connect_addr != NULL ) {
952  char *p;
953  short port_num;
954  int ip_addr;
955 
956  // joining a game. Send a join request to the given IP address, and wait for the return.
957  memset( &Multi_autojoin_addr, 0, sizeof(net_addr) );
958  Multi_autojoin_addr.type = NET_TCP;
959 
960  // create the address, looking out for port number at the end
961  port_num = DEFAULT_GAME_PORT;
962  p = strrchr(Cmdline_connect_addr, ':');
963  if ( p ) {
964  *p = '\0';
965  p++;
966  port_num = (short)atoi(p);
967  }
968  ip_addr = inet_addr(Cmdline_connect_addr);
969  memcpy(Multi_autojoin_addr.addr, &ip_addr, 4);
970  Multi_autojoin_addr.port = port_num;
971 
972  send_server_query(&Multi_autojoin_addr);
974  Multi_did_autojoin = 0;
975  }
976 }
977 
979 {
980  // misc data
982  Multi_join_selected_item = NULL;
983  MJ_LIST_START_SET(-1);
984  Multi_join_list_start_item = NULL;
985 
986  // free up the active game list
988 
989  // initialize the active game list
990  Active_game_head = NULL;
991  Active_game_count = 0;
992 }
993 
995 {
996  // check the status of our reliable socket. If not valid, popup error and return to main menu
997  // I put this code here to avoid nasty gameseq issues with states. Also, we will have nice
998  // background for the popup
999  if ( !psnet_rel_check() ) {
1000  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));
1002  return;
1003  }
1004 
1005  // return here since we will be moving to the next stage anyway -- I don't want to see the backgrounds of
1006  // all the screens for < 1 second for every screen we automatically move to.
1007  if ( Cmdline_start_netgame ) {
1008  return;
1009  }
1010 
1011  // when joining a network game, wait for the server query to come back, and then join the game
1012  if ( Cmdline_connect_addr != NULL ) {
1013  int rval;
1014 
1015  if ( !Multi_did_autojoin ) {
1016  rval = popup_till_condition(multi_join_autojoin_do, XSTR("&Cancel", 779), XSTR("Joining netgame", 1500) );
1017  if ( rval == 0 ) {
1018  // cancel was hit. Send the user back to the main hall
1020  Cmdline_connect_addr = NULL; // reset this value.
1021  }
1022 
1023  // when we get here, we have the data -- join the game.
1026  Multi_did_autojoin = 1;
1027  }
1028 
1032  }
1033  return;
1034 
1035  }
1036 
1037  // reset the should send var
1039 
1040  int k = Multi_join_window.process();
1041 
1042  // process any keypresses
1043  switch(k){
1044  case KEY_ESC :
1047  } else {
1048  if (MULTI_IS_TRACKER_GAME) {
1050  } else {
1052  }
1054  }
1055  break;
1056 
1057  // page up the game list
1058  case KEY_PAGEUP:
1060  Multi_join_slider.force_currentItem(Multi_join_list_start);
1061  break;
1062 
1063  case KEY_T:
1065  break;
1066 
1067  // page down the game list
1068  case KEY_PAGEDOWN:
1070  Multi_join_slider.force_currentItem(Multi_join_list_start);
1071  break;
1072 
1073  // send out a ping-all
1074  case KEY_P :
1077  break;
1078 
1079  // shortcut to start a game
1080  case KEY_S :
1082  break;
1083 
1084  // scroll the game list up
1085  case KEY_UP:
1087  Multi_join_slider.force_currentItem(Multi_join_list_start);
1088  break;
1089 
1090  // scroll the game list down
1091  case KEY_DOWN:
1093  Multi_join_slider.force_currentItem(Multi_join_list_start);
1094  break;
1095  }
1096 
1097  if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
1099  }
1100 
1101  // do any network related stuff
1103 
1104  // process any button clicks
1106 
1107  // process any list selection stuff
1109 
1110  // draw the background, etc
1111  gr_reset_clip();
1113  if(Multi_join_bitmap != -1){
1116  }
1117  Multi_join_window.draw();
1118 
1119  // display the active games
1121 
1122  // display any text in the info area
1124 
1125  // display any pending notification messages
1127 
1128  // blit the CD icon and any PXO filter stuff
1130 
1131  // draw the help overlay
1133 
1134  // flip the buffer
1135  gr_flip();
1136 
1137  // if we are supposed to be sending a join request
1138  if(Multi_join_should_send != -1){
1140  }
1142 
1143  // increment the frame count
1145 }
1146 
1148 {
1149  // unload any bitmaps
1151  nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_join_bitmap_fname[gr_screen.res]));
1152  }
1153 
1154  // free up the active game list
1156 
1157  // destroy the UI_WINDOW
1158  Multi_join_window.destroy();
1159 }
1160 
1162 {
1163  int idx;
1164  for(idx=0;idx<MULTI_JOIN_NUM_BUTTONS;idx++){
1165  // we only really need to check for one button pressed at a time, so we can break after
1166  // finding one.
1167  if(Multi_join_buttons[gr_screen.res][idx].button.pressed()){
1169  break;
1170  }
1171  }
1172 }
1173 
1175 {
1176 
1177  switch(n){
1178  case MJ_CANCEL :
1179  // if we're player PXO, go back there
1180  if (MULTI_IS_TRACKER_GAME) {
1182  } else {
1184  }
1186  break;
1187  case MJ_ACCEPT :
1188  if(Active_game_count <= 0){
1189  multi_common_add_notify(XSTR("No games found!",757));
1191  } else if(Multi_join_list_selected == -1){
1192  multi_common_add_notify(XSTR("No game selected!",758));
1195  multi_common_add_notify(XSTR("Still waiting on previous join request!",759));
1197  } else {
1198  // otherwise, if he's already played PXO games, warn him
1199 
1201  if(!multi_join_warn_pxo()){
1202  break;
1203  }
1204  }
1205 
1206 
1207  // send the join request here
1208  Assert(Multi_join_selected_item != NULL);
1209 
1210  // send a join request packet
1212 
1214  }
1215  break;
1216 
1217  // help overlay
1218  case MJ_HELP:
1221  } else {
1223  }
1224  break;
1225 
1226  // scroll the game list up
1227  case MJ_SCROLL_UP:
1229  Multi_join_slider.force_currentItem(Multi_join_list_start);
1230  break;
1231 
1232  // scroll the game list down
1233  case MJ_SCROLL_DOWN:
1235  Multi_join_slider.force_currentItem(Multi_join_list_start);
1236  break;
1237 
1238  // scroll the info text box up
1239  case MJ_SCROLL_INFO_UP:
1241  break;
1242 
1243  // scroll the info text box down
1244  case MJ_SCROLL_INFO_DOWN:
1246  break;
1247 
1248  // go to the options screen
1249  case MJ_OPTIONS:
1251  break;
1252 
1253  // go to the start game screen
1254  case MJ_START_GAME:
1256  break;
1257 
1258  // refresh the game/server list
1259  case MJ_REFRESH:
1262  break;
1263 
1264  // join a game as an observer
1265  case MJ_JOIN_OBSERVER:
1266  if(Active_game_count <= 0){
1267  multi_common_add_notify(XSTR("No games found!",757));
1269  } else if(Multi_join_list_selected == -1){
1270  multi_common_add_notify(XSTR("No game selected!",758));
1273  multi_common_add_notify(XSTR("Still waiting on previous join request!",759));
1275  } else {
1276  // send the join request here
1277  Assert(Multi_join_selected_item != NULL);
1278 
1280 
1282  }
1283  break;
1284 
1285  default :
1286  multi_common_add_notify(XSTR("Not implemented yet!",760));
1288  break;
1289  }
1290 }
1291 
1292 // display all relevant info for active games
1294 {
1296  char str[200];
1297  int w,h;
1298  int con_type;
1299  int y_start = Mj_list_y[gr_screen.res];
1300  int line_height = gr_get_font_height() + 1;
1301  int count = 0;
1302 
1303  if(moveup != NULL){
1304  do {
1305  // blit the game status (including text and type icon)
1306  multi_join_blit_game_status(moveup,y_start);
1307 
1308  // get the connection type
1310  if((con_type > 4) || (con_type < 0)){
1311  con_type = 0;
1312  }
1313 
1314  // display the connection speed
1315  str[0] = '\0';
1316  strcpy_s(str, Multi_join_speed_labels[con_type]);
1317  gr_set_color_fast(Multi_join_speed_colors[con_type]);
1319 
1320  // we'll want to have different colors for highlighted items, etc.
1321  if(moveup == Multi_join_selected_item){
1323  } else {
1325  }
1326 
1327  // display the game name, adding appropriate status chars
1328  str[0] = '\0';
1329  if(moveup->flags & AG_FLAG_STANDALONE){
1331  }
1332  if(moveup->flags & AG_FLAG_CAMPAIGN){
1334  }
1335 
1336  // tack on the actual server name
1337  strcat_s(str," ");
1338  strcat_s(str,moveup->name);
1339  if(moveup->mission_name[0] != '\0'){
1340  strcat_s(str, " / ");
1341  strcat_s(str,moveup->mission_name);
1342  }
1343 
1344  // make sure the string fits in the display area and draw it
1346  gr_string(Mj_game_name_coords[gr_screen.res][MJ_X_COORD],y_start,str,GR_RESIZE_MENU);
1347 
1348  // display the ping time
1349  if(moveup->ping.ping_avg > 0){
1350  if(moveup->ping.ping_avg > MJ_PING_ONE_SECOND){
1352  strcpy_s(str,XSTR("> 1 sec",761));
1353  } else {
1354  // set the appropriate ping time color indicator
1355  if(moveup->ping.ping_avg > MJ_PING_RED){
1357  } else if(moveup->ping.ping_avg > MJ_PING_YELLOW){
1359  } else {
1361  }
1362 
1363  sprintf(str,"%d",moveup->ping.ping_avg);
1364  strcat_s(str,XSTR(" ms",762)); // [[ Milliseconds ]]
1365  }
1366 
1367  gr_string(Mj_ping_coords[gr_screen.res][MJ_X_COORD],y_start,str,GR_RESIZE_MENU);
1368  }
1369 
1370  // display the number of players (be sure to center it)
1371  if(moveup == Multi_join_selected_item){
1373  } else {
1375  }
1376  sprintf(str,"%d",moveup->num_players);
1377  gr_get_string_size(&w,&h,str);
1378  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,GR_RESIZE_MENU);
1379 
1380  count++;
1381  y_start += line_height;
1382  moveup = moveup->next;
1383  } while((moveup != Active_game_head) && (count < Mj_max_game_items[gr_screen.res]));
1384  }
1385  // if there are no items on the list, display this info
1386  else {
1388  gr_string(Mj_game_name_coords[gr_screen.res][MJ_X_COORD] - 30,y_start,XSTR("<No game servers found>",763),GR_RESIZE_MENU);
1389  }
1390 }
1391 
1393 {
1394  int draw,str_w;
1395  char status_text[25];
1396 
1397  // blit the proper icon
1398  draw = 0;
1399  switch( game->flags & AG_FLAG_TYPE_MASK ){
1400  // coop game
1401  case AG_FLAG_COOP:
1402  if(Multi_common_icons[MICON_COOP] != -1){
1404  draw = 1;
1405  }
1406  break;
1407 
1408  // team vs. team game
1409  case AG_FLAG_TEAMS:
1410  if(Multi_common_icons[MICON_TVT] != -1){
1412  draw = 1;
1413  }
1414  break;
1415 
1416  // dogfight game
1417  case AG_FLAG_DOGFIGHT:
1420  draw = 1;
1421  }
1422  break;
1423  }
1424  // if we're supposed to draw a bitmap
1425  if(draw){
1427  }
1428 
1429  // blit the proper status text
1430  memset(status_text,0,25);
1431 
1432  switch( game->flags & AG_FLAG_STATE_MASK ){
1433  case AG_FLAG_FORMING:
1435  strcpy_s(status_text,XSTR("Forming",764));
1436  break;
1437  case AG_FLAG_BRIEFING:
1439  strcpy_s(status_text,XSTR("Briefing",765));
1440  break;
1441  case AG_FLAG_DEBRIEF:
1443  strcpy_s(status_text,XSTR("Debrief",766));
1444  break;
1445  case AG_FLAG_PAUSE:
1447  strcpy_s(status_text,XSTR("Paused",767));
1448  break;
1449  case AG_FLAG_IN_MISSION:
1451  strcpy_s(status_text,XSTR("Playing",768));
1452  break;
1453  default:
1455  strcpy_s(status_text,XSTR("Unknown",769));
1456  break;
1457  }
1458  gr_get_string_size(&str_w,NULL,status_text);
1460 }
1461 
1463 {
1464  char line[MAX_IP_STRING];
1465  net_addr addr;
1466  server_item *item;
1467  CFILE *file = NULL;
1468 
1469  // attempt to open the ip list file
1471  if(file == NULL){
1472  nprintf(("Network","Error loading tcp.cfg file!\n"));
1473  return;
1474  }
1475 
1476  // free up any existing server list
1478 
1479  // read in all the strings in the file
1480  while(!cfeof(file)){
1481  line[0] = '\0';
1482  cfgets(line,MAX_IP_STRING,file);
1483 
1484  // strip off any newline character
1485  if(line[strlen(line) - 1] == '\n'){
1486  line[strlen(line) - 1] = '\0';
1487  }
1488 
1489  // empty lines don't get processed
1490  if( (line[0] == '\0') || (line[0] == '\n') ){
1491  continue;
1492  }
1493 
1494  if ( !psnet_is_valid_ip_string(line) ) {
1495  nprintf(("Network","Invalid ip string (%s)\n",line));
1496  } else {
1497  // copy the server ip address
1498  memset(&addr,0,sizeof(net_addr));
1499  addr.type = NET_TCP;
1500  psnet_string_to_addr(&addr,line);
1501  if ( addr.port == 0 ){
1502  addr.port = DEFAULT_GAME_PORT;
1503  }
1504 
1505  // create a new server item on the list
1506  item = multi_new_server_item();
1507  if(item != NULL){
1508  memcpy(&item->server_addr,&addr,sizeof(net_addr));
1509  }
1510  }
1511  }
1512 
1513  cfclose(file);
1514 }
1515 
1516 // do stuff like pinging servers, sending out requests, etc
1518 {
1519  // handle game query stuff
1520  if (Multi_join_glr_stamp == -1) {
1522 
1525  } else {
1527  }
1528  }
1529  // otherwise send out game query and restamp
1530  else if ( timestamp_elapsed(Multi_join_glr_stamp) ) {
1532 
1535  } else {
1537  }
1538  }
1539 
1540  // check to see if we've been accepted. If so, put up message saying so
1542  multi_common_add_notify(XSTR("Accepted. Waiting for player data.",770));
1543  }
1544 
1545  // check to see if any join packets we have sent have timed out
1547  Multi_join_sent_stamp = -1;
1548  multi_common_add_notify(XSTR("Join request timed out!",771));
1549  }
1550 
1551  // check to see if we should be pinging everyone
1555  }
1556 
1557  // cull timeouts
1559 }
1560 
1561 // evaluate a returned pong.
1563 {
1564  active_game *moveup = Active_game_head;
1565 
1566  if(moveup != NULL){
1567  do {
1568  if(psnet_same(&moveup->server_addr,addr)){
1569  multi_ping_eval_pong(&moveup->ping);
1570 
1571  break;
1572  } else {
1573  moveup = moveup->next;
1574  }
1575  } while(moveup != Active_game_head);
1576  }
1577 }
1578 
1579 // ping all the server on the list
1581 {
1582  active_game *moveup = Active_game_head;
1583 
1584  if(moveup != NULL){
1585  do {
1586  /*
1587  moveup->ping_start = timer_get_fixed_seconds();
1588  moveup->ping_end = -1;
1589  send_ping(&moveup->server_addr);
1590  */
1591 
1592  send_server_query(&moveup->server_addr);
1593  multi_ping_send(&moveup->server_addr,&moveup->ping);
1594 
1595  moveup = moveup->next;
1596  } while(moveup != Active_game_head);
1597  }
1598 }
1599 
1601 {
1602  int line_height = gr_get_font_height() + 1;
1603 
1604  // if we don't have anything selected and there are items on the list - select the first one
1605  if((Multi_join_list_selected == -1) && (Active_game_count > 0)){
1607  Multi_join_selected_item = multi_join_get_game(0);
1608  MJ_LIST_START_SET(0);
1609  Multi_join_list_start_item = Multi_join_selected_item;
1610 
1611  // send a mission description request to this guy
1612  send_netgame_descript_packet(&Multi_join_selected_item->server_addr,0);
1614 
1615  // I sure hope this doesn't happen
1616  Assert(Multi_join_selected_item != NULL);
1617  return;
1618  }
1619  // otherwise see if he's clicked on an item
1620  else if(Multi_join_select_button.pressed() && (Active_game_count > 0)){
1621  int y,item;
1622  Multi_join_select_button.get_mouse_pos(NULL,&y);
1623  item = y / line_height;
1626 
1628  Multi_join_selected_item = multi_join_get_game(Multi_join_list_selected);
1629 
1630  // I sure hope this doesn't happen
1631  Assert(Multi_join_selected_item != NULL);
1632 
1633  // send a mission description request to this guy
1634  send_netgame_descript_packet(&Multi_join_selected_item->server_addr,0);
1635  multi_common_set_text("");
1636  }
1637  }
1638 
1639  // if he's double clicked, then select it and accept
1640  if(Multi_join_select_button.double_clicked()){
1641  int y,item;
1642  Multi_join_select_button.get_mouse_pos(NULL,&y);
1643  item = y / line_height;
1644  if(item == Multi_join_list_selected){
1646  }
1647  }
1648 }
1649 
1650 // return game n (0 based index)
1652 {
1653  active_game *moveup = Active_game_head;
1654 
1655  if(moveup != NULL){
1656  if(n == 0){
1657  return moveup;
1658  } else {
1659  int count = 1;
1660  moveup = moveup->next;
1661  while((moveup != Active_game_head) && (count != n)){
1662  moveup = moveup->next;
1663  count++;
1664  }
1665  if(moveup == Active_game_head){
1666  nprintf(("Network","Warning, couldn't find game item %d!\n",n));
1667  return NULL;
1668  } else {
1669  return moveup;
1670  }
1671  }
1672  }
1673  return NULL;
1674 }
1675 
1676 // scroll through the game list
1678 {
1679  // if we're not at the beginning of the list, scroll up
1680  if(Multi_join_list_start_item != Active_game_head){
1681  Multi_join_list_start_item = Multi_join_list_start_item->prev;
1682 
1683  MJ_LIST_START_DEC();
1684 
1686  } else {
1688  }
1689 }
1690 
1691 // scroll through the game list
1693 {
1695  Multi_join_list_start_item = Multi_join_list_start_item->next;
1696 
1697  MJ_LIST_START_INC();
1698 
1700  } else {
1702  }
1703 }
1704 
1706 {
1707  // in this case, just set us to the beginning of the list
1709  Multi_join_list_start_item = Active_game_head;
1710 
1711  MJ_LIST_START_SET(0);
1712 
1714  } else {
1715  // otherwise page the whole thing up
1716  int idx;
1717  for(idx=0; idx<Mj_max_game_items[gr_screen.res]; idx++){
1718  Multi_join_list_start_item = Multi_join_list_start_item->prev;
1719 
1720  MJ_LIST_START_DEC();
1721  }
1723  }
1724 }
1725 
1727 {
1728  int count = 0;
1729 
1730  // page the whole thing down
1732  Multi_join_list_start_item = Multi_join_list_start_item->next;
1733  MJ_LIST_START_INC();
1734 
1735  // next
1736  count++;
1737  }
1739 }
1740 
1742 {
1743  active_game *backup;
1744  int count;
1745  active_game *moveup = Active_game_head;
1746 
1747  // traverse through the entire list if any items exist
1748  count = 0;
1749  if(moveup != NULL){
1750  do {
1751  if((moveup->heard_from_timer != -1) && (timestamp_elapsed(moveup->heard_from_timer))){
1753 
1754  // if this is the head of the list
1755  if(moveup == Active_game_head){
1756  // if this is the _only_ item on the list
1757  if(moveup->next == Active_game_head){
1758  // handle any gui details related to deleting this item
1760 
1762  Active_game_head = NULL;
1763  return;
1764  }
1765  // if there are other items on the list
1766  else {
1767  // handle any gui details related to deleting this item
1768  multi_join_handle_item_cull(moveup, count);
1769 
1770  Active_game_head = moveup->next;
1771  Active_game_head->prev = moveup->prev;
1773  vm_free(moveup);
1774  moveup = Active_game_head;
1775  }
1776  }
1777  // if its somewhere else on the list
1778  else {
1779  // handle any gui details related to deleting this item
1780  multi_join_handle_item_cull(moveup, count);
1781 
1782  // if its the last item on the list
1783  moveup->next->prev = moveup->prev;
1784  moveup->prev->next = moveup->next;
1785 
1786  // if it was the last element on the list, return
1787  if(moveup->next == Active_game_head){
1788  vm_free(moveup);
1789  return;
1790  } else {
1791  backup = moveup->next;
1792  vm_free(moveup);
1793  moveup = backup;
1794  }
1795  }
1796  } else {
1797  moveup = moveup->next;
1798  count++;
1799  }
1800  } while(moveup != Active_game_head);
1801  }
1802 }
1803 
1804 // deep magic begins here.
1805 void multi_join_handle_item_cull(active_game *item, int item_index)
1806 {
1807  // if this is the only item on the list, unset everything
1808  if(item->next == item){
1810  Multi_join_selected_item = NULL;
1811 
1812  Multi_join_slider.set_numberItems(0);
1813  MJ_LIST_START_SET(-1);
1814  Multi_join_list_start_item = NULL;
1815 
1816  // return
1817  return;
1818  }
1819 
1820  // see if we should be adjusting our currently selected item
1821  if(item_index <= Multi_join_list_selected){
1822  // the selected item is the head of the list
1823  if(Multi_join_selected_item == Active_game_head){
1824  // move the pointer up since this item is about to be destroyed
1825  Multi_join_selected_item = Multi_join_selected_item->next;
1826  } else {
1827  // if this is the item being deleted, select the previous one
1828  if(item == Multi_join_selected_item){
1829  // previous item
1830  Multi_join_selected_item = Multi_join_selected_item->prev;
1831 
1832  // decrement the selected index by 1
1834  }
1835  // now we know its a previous item, so our pointer stays the same but our index goes down by one, since there will be
1836  // 1 less item on the list
1837  else {
1838  // decrement the selected index by 1
1840  }
1841  }
1842  }
1843 
1844  // see if we should be adjusting out current start position
1845  if(item_index <= Multi_join_list_start){
1846  // the start position is the head of the list
1847  if(Multi_join_list_start_item == Active_game_head){
1848  // move the pointer up since this item is about to be destroyed
1849  Multi_join_list_start_item = Multi_join_list_start_item->next;
1850  } else {
1851  // if this is the item being deleted, select the previous one
1852  if(item == Multi_join_list_start_item){
1853  Multi_join_list_start_item = Multi_join_list_start_item->prev;
1854 
1855  // decrement the starting index by 1
1856  MJ_LIST_START_DEC();
1857  } else {
1858  // but decrement the starting index by 1
1859  MJ_LIST_START_DEC();
1860  }
1861  }
1862  }
1863 
1864  // maybe go back up a bit so that we always have a full page of items
1867  Multi_join_list_start_item = Multi_join_list_start_item->prev;
1869  }
1870  }
1871 
1872  // set slider location
1874  Multi_join_slider.force_currentItem(Multi_join_list_start);
1875 }
1876 
1877 void multi_join_send_join_request(int as_observer)
1878 {
1879  // don't do anything if we have no items selected
1880  if(Multi_join_selected_item == NULL){
1881  return;
1882  }
1883 
1884  // 5/26/98 -- for team v team games, don't allow ingame joining :-(
1885  if ( (Multi_join_selected_item->flags & AG_FLAG_TEAMS) && (Multi_join_selected_item->flags & (AG_FLAG_PAUSE|AG_FLAG_IN_MISSION)) ) {
1886  popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("Joining ingame is currently not allowed for team vs. team games",772));
1887  return;
1888  }
1889 
1890  memset(&Multi_join_request,0,sizeof(join_request));
1891 
1892  // if the netgame is in password mode, put up a request for the password
1893  if(Multi_join_selected_item->flags & AG_FLAG_PASSWD){
1894  if(!multi_passwd_popup(Multi_join_request.passwd)){
1895  return;
1896  }
1897 
1898  nprintf(("Network", "Password : %s\n", Multi_join_request.passwd));
1899  }
1900 
1901  // fill out the join request struct
1902  strcpy_s(Multi_join_request.callsign,Player->callsign);
1903  if(Player->image_filename[0] != '\0'){
1904  strcpy_s(Multi_join_request.image_filename, Player->image_filename);
1905  }
1906  if(Player->m_squad_filename[0] != '\0'){
1907  strcpy_s(Multi_join_request.squad_filename, Player->m_squad_filename);
1908  }
1909 
1910  // tracker id (if any)
1911  Multi_join_request.tracker_id = Multi_tracker_id;
1912 
1913  // player's rank (at least, what he wants you to _believe_)
1914  Multi_join_request.player_rank = (ubyte)Player->stats.rank;
1915 
1916  // misc join flags
1917  Multi_join_request.flags = 0;
1918  if(as_observer){
1919  Multi_join_request.flags |= JOIN_FLAG_AS_OBSERVER;
1920  }
1921 
1922  // if the player has hacked data
1923  if(game_hacked_data()){
1924  Multi_join_request.flags |= JOIN_FLAG_HAXOR;
1925  }
1926 
1927  // pxo squad info
1928  strcpy_s(Multi_join_request.pxo_squad_name, Multi_tracker_squad_name);
1929 
1930  // version of this server
1931  Multi_join_request.version = MULTI_FS_SERVER_VERSION;
1932 
1933  // server compatible version
1934  Multi_join_request.comp_version = MULTI_FS_SERVER_COMPATIBLE_VERSION;
1935 
1936  // his local player options
1937  memcpy(&Multi_join_request.player_options,&Player->m_local_options,sizeof(multi_local_options));
1938 
1939  // set the server address for the netgame
1940  memcpy(&Netgame.server_addr,&Multi_join_selected_item->server_addr,sizeof(net_addr));
1941 
1942  // send a join request to the guy
1943  send_join_packet(&Multi_join_selected_item->server_addr,&Multi_join_request);
1944 
1945  // now we wait
1947 
1948  psnet_flush();
1949  multi_common_add_notify(XSTR("Sending join request...",773));
1950 }
1951 
1953 {
1954  // maybe warn the player about possible crappy server conditions
1955  if(!multi_join_maybe_warn()){
1956  return;
1957  }
1958 
1959  // make sure to flag ourself as being the master
1962 
1963  // if we're in PXO mode, mark it down in our player struct
1967  }
1968  // otherwise, if he's already played PXO games, warn him
1969  else {
1970 
1972  if(!multi_join_warn_pxo()){
1973  return;
1974  }
1975  }
1976 
1977  }
1978 
1981 }
1982 
1984 {
1985  // unset the timestamp here so the user can immediately send another join request
1986  Multi_join_sent_stamp = -1;
1988 }
1989 
1991 {
1992  // blit the cd icon if he has one
1993  if(Multi_has_cd && (Multi_common_icons[MICON_CD] != -1)){
1994  // get bitmap width
1995  int cd_w;
1996  bm_get_info(Multi_common_icons[MICON_CD], &cd_w, NULL, NULL, NULL, NULL);
1997 
1998  gr_set_bitmap(Multi_common_icons[MICON_CD]);
2000  }
2001 }
2002 
2003 #define CW_CODE_CANCEL 0 // cancel the action
2004 #define CW_CODE_OK 1 // continue anyway
2005 #define CW_CODE_INFO 2 // gimme some more information
2006 
2007 #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)
2008 #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)
2009 
2010 #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)
2011 #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)
2012 
2014 {
2015  switch(code){
2016  case CW_CODE_OK:
2017  return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),LOW_WARN_TEXT);
2018 
2019  case CW_CODE_INFO:
2020  return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),LOW_INFO_TEXT);
2021  }
2022 
2023  return CW_CODE_CANCEL;
2024 }
2025 
2027 {
2028  switch(code){
2029  case CW_CODE_OK:
2030  return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),MED_WARN_TEXT);
2031 
2032  case CW_CODE_INFO:
2033  return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),MED_INFO_TEXT);
2034  }
2035 
2036  return CW_CODE_CANCEL;
2037 }
2038 
2040 {
2041  int code;
2042 
2043  // if the player is set for low updates
2045  code = CW_CODE_OK;
2046  do {
2047  code = multi_join_warn_update_low(code);
2048  } while((code != CW_CODE_CANCEL) && (code != CW_CODE_OK));
2049 
2050  return code;
2051  }
2052 
2053  // if the player is set for medium updates
2055  code = CW_CODE_OK;
2056  do {
2057  code = multi_join_warn_update_medium(code);
2058  } while((code != CW_CODE_CANCEL) && (code != CW_CODE_OK));
2059 
2060  return code;
2061  }
2062 
2063  return 1;
2064 }
2065 
2067 {
2068  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;
2069  //return 1;
2070 }
2071 
2073 {
2075  gr_string(5, 2, "TCP", GR_RESIZE_MENU);
2076 }
2077 
2078 
2079 // -------------------------------------------------------------------------------------------------
2080 //
2081 // MULTIPLAYER START GAME screen
2082 //
2083 
2084 //XSTR:OFF
2085 // bitmap defs
2086 #define MULTI_SG_PALETTE "InterfacePalette"
2087 
2088 static char *Multi_sg_bitmap_fname[GR_NUM_RESOLUTIONS] = {
2089  "MultiStartGame", // GR_640
2090  "2_MultiStartGame" // GR_1024
2091 };
2092 
2093 static char *Multi_sg_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
2094  "MultiStartGame-M", // GR_640
2095  "2_MultiStartGame-M" // GR_1024
2096 };
2097 
2098 //XSTR:ON
2099 
2101  2, // GR_640
2102  4 // GR_1024
2103 };
2104 
2105 // constants for coordinate look ups
2106 #define MSG_X_COORD 0
2107 #define MSG_Y_COORD 1
2108 #define MSG_W_COORD 2
2109 #define MSG_H_COORD 3
2110 
2111 // area definitions
2112 
2113 // input password field
2115  { // GR_640
2116  36, 236, 408, 20
2117  },
2118  { // GR_1024
2119  58, 377, 652, 32
2120  }
2121 };
2122 
2123 // input game title field
2125  { // GR_640
2126  29, 49, 415, 23
2127  },
2128  { // GR_1024
2129  46, 78, 664, 36
2130  }
2131 };
2132 
2133 // rank selected field
2135  { // GR_640
2136  242, 254, 126, 12
2137  },
2138  { // GR_1024
2139  242, 254, 126, 12
2140  }
2141 };
2142 
2143 // rank list field
2145  { // GR_640
2146  37, 297, 131, 16
2147  },
2148  { // GR_1024
2149  60, 469, 652, 32
2150  }
2151 };
2152 
2153 
2154 // button defs
2155 #define MULTI_SG_NUM_BUTTONS 10
2156 //#define MULTI_SG_NUM_BUTTONS 12
2157 
2158 #define MSG_OPEN_GAME 0
2159 //#define MSG_CLOSED_GAME 1
2160 //#define MSG_RESTRICTED_GAME 2
2161 #define MSG_PASSWD_GAME 1
2162 #define MSG_RANK_SET_GAME 2
2163 #define MSG_RANK_SCROLL_UP 3
2164 #define MSG_RANK_SCROLL_DOWN 4
2165 #define MSG_RANK_ABOVE 5
2166 #define MSG_RANK_BELOW 6
2167 #define MSG_HELP 7
2168 #define MSG_OPTIONS 8
2169 #define MSG_ACCEPT 9
2170 
2171 UI_WINDOW Multi_sg_window; // the window object for the join screen
2172 UI_BUTTON Multi_sg_rank_button; // for selecting the rank marker
2173 UI_INPUTBOX Multi_sg_game_name; // for Netgame.name
2174 UI_INPUTBOX Multi_sg_game_passwd; // for Netgame.passwd
2175 int Multi_sg_bitmap; // the background bitmap
2176 
2178  { // GR_640
2179  ui_button_info("MSG_00", 1, 184, 34, 191, 2), // open
2180 //******ui_button_info("MSG_01", 1, 159, 34, 166, 1), // closed
2181 //******ui_button_info("MSG_02", 1, 184, 34, 191, 2), // restricted
2182  ui_button_info("MSG_03", 1, 209, 34, 218, 3), // password
2183  ui_button_info("MSG_04", 1, 257, 34, 266, 4), // rank set
2184  ui_button_info("MSG_05", 1, 282, -1, -1, 5), // rank scroll up
2185  ui_button_info("MSG_06", 1, 307, -1, -1, 6), // rank scroll down
2186  ui_button_info("MSG_07", 177, 282, 210, 290, 7), // rank above
2187  ui_button_info("MSG_08", 177, 307, 210, 315, 8), // rank below
2188  ui_button_info("MSG_09", 536, 429, 500, 440, 9), // help
2189  ui_button_info("MSG_10", 536, 454, 479, 464, 10), // options
2190  ui_button_info("MSG_11", 576, 432, 571, 415, 11), // accept
2191  },
2192  { // GR_1024
2193  ui_button_info("2_MSG_00", 2, 295, 51, 307, 2), // open
2194 //******ui_button_info("2_MSG_01", 2, 254, 51, 267, 1), // closed
2195 //******ui_button_info("2_MSG_02", 2, 295, 51, 307, 2), // restricted
2196  ui_button_info("2_MSG_03", 2, 335, 51, 350, 3), // password
2197  ui_button_info("2_MSG_04", 2, 412, 51, 426, 4), // rank set
2198  ui_button_info("2_MSG_05", 2, 452, -1, -1, 5), // rank scroll up
2199  ui_button_info("2_MSG_06", 2, 492, -1, -1, 6), // rank scroll down
2200  ui_button_info("2_MSG_07", 284, 452, 335, 465, 7), // rank above
2201  ui_button_info("2_MSG_08", 284, 492, 335, 505, 8), // rank below
2202  ui_button_info("2_MSG_09", 858, 687, 817, 706, 9), // help
2203  ui_button_info("2_MSG_10", 858, 728, 797, 743, 10), // options
2204  ui_button_info("2_MSG_11", 921, 692, 921, 664, 11), // accept
2205  },
2206 };
2207 
2208 //#define MULTI_SG_NUM_TEXT 13
2209 #define MULTI_SG_NUM_TEXT 11
2211  { // GR_640
2212  {"Open", 1322, 34, 191, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_OPEN_GAME].button},
2213 //******{"Closed", 1323, 34, 166, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_CLOSED_GAME].button},
2214 //******{"Restricted", 1324, 34, 191, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RESTRICTED_GAME].button},
2215  {"Password Protected", 1325, 34, 218, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_PASSWD_GAME].button},
2216  {"Allow Rank", 1326, 34, 266, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_SET_GAME].button},
2217  {"Above", 1327, 210, 290, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_ABOVE].button},
2218  {"Below", 1328, 210, 315, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_BELOW].button},
2219  {"Help", 928, 500, 440, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_HELP].button},
2220  {"Options", 1036, 479, 464, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_OPTIONS].button},
2221  {"Accept", 1035, 571, 415, UI_XSTR_COLOR_PINK, -1, &Multi_sg_buttons[0][MSG_ACCEPT].button},
2222  {"Start Game", 1329, 26, 10, UI_XSTR_COLOR_GREEN, -1, NULL},
2223  {"Title", 1330, 26, 31, UI_XSTR_COLOR_GREEN, -1, NULL},
2224  {"Game Type", 1331, 12, 165, UI_XSTR_COLOR_GREEN, -1, NULL},
2225  },
2226  { // GR_1024
2227  {"Open", 1322, 51, 307, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_OPEN_GAME].button},
2228 //******{"Closed", 1323, 51, 267, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_CLOSED_GAME].button},
2229 //******{"Restricted", 1324, 51, 307, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RESTRICTED_GAME].button},
2230  {"Password Protected", 1325, 51, 350, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_PASSWD_GAME].button},
2231  {"Allow Rank", 1326, 51, 426, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RANK_SET_GAME].button},
2232  {"Above", 1327, 335, 465, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RANK_ABOVE].button},
2233  {"Below", 1328, 335, 505, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RANK_BELOW].button},
2234  {"Help", 928, 817, 706, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_HELP].button},
2235  {"Options", 1036, 797, 743, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_OPTIONS].button},
2236  {"Accept", 1035, 921, 664, UI_XSTR_COLOR_PINK, -1, &Multi_sg_buttons[1][MSG_ACCEPT].button},
2237  {"Start Game", 1329, 42, 22, UI_XSTR_COLOR_GREEN, -1, NULL},
2238  {"Title", 1330, 42, 50, UI_XSTR_COLOR_GREEN, -1, NULL},
2239  {"Game Type", 1331, 20, 264, UI_XSTR_COLOR_GREEN, -1, NULL},
2240  }
2241 };
2242 
2243 // starting index for displaying ranks
2246 
2247 // netgame pointer to indirect through
2249 
2250 // hold temporary values in this structure when on a standalone server
2252 
2254 
2255 // forward declarations
2256 void multi_sg_check_buttons();
2257 void multi_sg_button_pressed(int n);
2258 void multi_sg_init_gamenet();
2264 void multi_sg_rank_build_name(char *in,char *out);
2265 void multi_sg_check_passwd();
2266 void multi_sg_check_name();
2268 int multi_sg_rank_select_valid(int rank);
2270 
2271 // function which takes a rank name and returns the index. Useful for commandline options
2272 // for above and below rank. We return the index of the rank in the Ranks[] array. If
2273 // the rank isn't found, we return -1
2275  int i;
2276 
2277  for ( i = 0; i <= MAX_FREESPACE2_RANK; i++ ) {
2278  if ( !stricmp(Ranks[i].name, rank) ) {
2279  return i;
2280  }
2281  }
2282 
2283  return -1;
2284 }
2285 
2287 {
2288  int idx;
2289 
2290  // initialize the gamenet
2292 
2293  // create the interface window
2294  Multi_sg_window.create(0,0,gr_screen.max_w_unscaled,gr_screen.max_h_unscaled,0);
2295  Multi_sg_window.set_mask_bmap(Multi_sg_bitmap_mask_fname[gr_screen.res]);
2296 
2297  // load the background bitmap
2298  Multi_sg_bitmap = bm_load(Multi_sg_bitmap_fname[gr_screen.res]);
2299  if(Multi_sg_bitmap < 0){
2300  // we failed to load the bitmap - this is very bad
2301  Int3();
2302  }
2303 
2304  // initialize the common notification messaging
2306 
2307  // initialize the common text area
2309 
2310  // use the common interface palette
2312 
2313  // create the interface buttons
2314  for(idx=0; idx<MULTI_SG_NUM_BUTTONS; idx++){
2315  // create the object
2316  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);
2317 
2318  // set the sound to play when highlighted
2320 
2321  // set the ani for the button
2322  Multi_sg_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sg_buttons[gr_screen.res][idx].filename);
2323 
2324  // set the hotspot
2325  Multi_sg_buttons[gr_screen.res][idx].button.link_hotspot(Multi_sg_buttons[gr_screen.res][idx].hotspot);
2326  }
2327 
2328  // add all xstrs
2329  for(idx=0; idx<MULTI_SG_NUM_TEXT; idx++){
2330  Multi_sg_window.add_XSTR(&Multi_sg_text[gr_screen.res][idx]);
2331  }
2332 
2333  // load the help overlay
2334  Multi_sg_overlay_id = help_overlay_get_index(MULTI_START_OVERLAY);
2335  help_overlay_set_state(Multi_sg_overlay_id,gr_screen.res,0);
2336 
2337  // intiialize the rank selection items
2339  Multi_sg_rank_start = Multi_sg_rank_select;
2340 
2341  // create the rank select button
2342  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);
2343  Multi_sg_rank_button.hide();
2344 
2345  // create the netgame name input box
2346  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);
2347 
2348  // create the netgame password input box, and disable it by default
2349  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);
2350  Multi_sg_game_passwd.hide();
2351  Multi_sg_game_passwd.disable();
2352 
2353  // set the netgame text to this gadget and make it have focus
2354  Multi_sg_game_name.set_text(Multi_sg_netgame->name);
2355  Multi_sg_game_name.set_focus();
2356 
2357  // if starting a netgame, set the name of the game and any other options that are appropriate
2358  if ( Cmdline_start_netgame ) {
2359  if ( Cmdline_game_name != NULL ) {
2360  strcpy_s( Multi_sg_netgame->name, Cmdline_game_name );
2361  Multi_sg_game_name.set_text(Multi_sg_netgame->name);
2362  }
2363 
2364  // deal with the different game types -- only one should even be active, so we will just go down
2365  // the line. Last one wins.
2366  if ( Cmdline_closed_game ) {
2367  Multi_sg_netgame->mode = NG_MODE_CLOSED;
2368  } else if ( Cmdline_restricted_game ) {
2369  Multi_sg_netgame->mode = NG_MODE_RESTRICTED;
2370  } else if ( Cmdline_game_password != NULL ) {
2371  Multi_sg_netgame->mode = NG_MODE_PASSWORD;
2372  strcpy_s(Multi_sg_netgame->passwd, Cmdline_game_password);
2373  Multi_sg_game_passwd.set_text(Multi_sg_netgame->passwd);
2374  }
2375 
2376  // deal with rank above and rank below
2377  if ( (Cmdline_rank_above != NULL) || (Cmdline_rank_below != NULL) ) {
2378  int rank;
2379  char *rank_str;
2380 
2381  if ( Cmdline_rank_above != NULL ) {
2382  rank_str = Cmdline_rank_above;
2383  } else {
2384  rank_str = Cmdline_rank_below;
2385  }
2386 
2387  // try and get the rank index from the name -- if found, then set the rank base
2388  // and the game type. apparently we only support either above or below, not both
2389  // together, so I make a random choice
2390  rank = multi_start_game_rank_from_name( rank_str );
2391  if ( rank != -1 ) {
2392  Multi_sg_netgame->rank_base = Multi_sg_rank_select;
2393 
2394  // now an arbitrary decision
2395  if ( Cmdline_rank_above != NULL ) {
2396  Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
2397  } else {
2398  Multi_sg_netgame->mode = NG_MODE_RANK_BELOW;
2399  }
2400  }
2401  }
2402 
2404  }
2405 }
2406 
2408 {
2409  // return here since we will be moving to the next stage anyway -- I don't want to see the backgrounds of
2410  // all the screens for < 1 second for every screen we automatically move to.
2411  if ( Cmdline_start_netgame ) {
2412  Cmdline_start_netgame = 0; // DTP no quit fix; by using -startgame Quit was sort of out of function
2413  return;
2414  }
2415 
2416  int k = Multi_sg_window.process();
2417 
2418  // process any keypresses
2419  switch(k){
2420  case KEY_ESC :
2421  if(help_overlay_active(Multi_sg_overlay_id)){
2422  help_overlay_set_state(Multi_sg_overlay_id,gr_screen.res,0);
2423  } else {
2426  }
2427  break;
2428 
2429  // same as ACCEPT
2430  case KEY_LCTRL + KEY_ENTER :
2431  case KEY_RCTRL + KEY_ENTER :
2434  break;
2435  }
2436 
2437  if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
2438  help_overlay_set_state(Multi_sg_overlay_id, gr_screen.res, 0);
2439  }
2440 
2441  // check to see if the user has selected a different rank
2443 
2444  // check any button presses
2446 
2447  // check to see if any of the input boxes have changed, and update the appropriate Netgame fields if necessary
2450 
2451  // draw the background, etc
2452  gr_reset_clip();
2453  GR_MAYBE_CLEAR_RES(Multi_sg_bitmap);
2454  if(Multi_sg_bitmap != -1){
2455  gr_set_bitmap(Multi_sg_bitmap);
2457  }
2458  Multi_sg_window.draw();
2459 
2460  // display rank stuff
2462 
2463  // display any pending notification messages
2465 
2466  // draw all radio button
2468 
2469  // draw the help overlay
2470  help_overlay_maybe_blit(Multi_sg_overlay_id, gr_screen.res);
2471 
2472  // flip the buffer
2473  gr_flip();
2474 }
2475 
2477 {
2478  // if i'm the host on a standalone server, send him my start game options (passwd, mode, etc)
2480  multi_options_update_start_game(Multi_sg_netgame);
2481  }
2482 
2483  // unload any bitmaps
2484  if(!bm_unload(Multi_sg_bitmap)){
2485  nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_sg_bitmap_fname[gr_screen.res]));
2486  }
2487 
2488  // destroy the UI_WINDOW
2489  Multi_sg_window.destroy();
2490 }
2491 
2493 {
2494  int idx;
2495  for(idx=0;idx<MULTI_SG_NUM_BUTTONS;idx++){
2496  // we only really need to check for one button pressed at a time, so we can break after
2497  // finding one.
2498  if(Multi_sg_buttons[gr_screen.res][idx].button.pressed()){
2500  break;
2501  }
2502  }
2503 }
2504 
2506 {
2507  switch(n){
2508  // go to the options screen
2509  case MSG_OPTIONS:
2511  break;
2512 
2513  // help overlay
2514  case MSG_HELP:
2515  if(!help_overlay_active(Multi_sg_overlay_id)){
2516  help_overlay_set_state(Multi_sg_overlay_id,gr_screen.res,1);
2517  } else {
2518  help_overlay_set_state(Multi_sg_overlay_id,gr_screen.res,0);
2519  }
2520  break;
2521 
2522  // the open button was pressed
2523  case MSG_OPEN_GAME:
2524  // if the closed option is selected
2525  if(Multi_sg_netgame->mode != NG_MODE_OPEN){
2526  Multi_sg_netgame->mode = NG_MODE_OPEN;
2527 
2529 
2530  // release the password control if necessary
2532  }
2533  // if its already selected
2534  else {
2536  }
2537  break;
2538 
2539 /*
2540  // the open button was pressed
2541  case MSG_CLOSED_GAME:
2542  // if the closed option is selected
2543  if(Multi_sg_netgame->mode != NG_MODE_CLOSED){
2544  Multi_sg_netgame->mode = NG_MODE_CLOSED;
2545 
2546  gamesnd_play_iface(SND_USER_SELECT);
2547 
2548  // release the password control if necessary
2549  multi_sg_release_passwd();
2550  }
2551  // if its already selected
2552  else {
2553  gamesnd_play_iface(SND_GENERAL_FAIL);
2554  }
2555  break;
2556 */
2557  // toggle password protection
2558  case MSG_PASSWD_GAME:
2559  // if we selected it
2560  if(Multi_sg_netgame->mode != NG_MODE_PASSWORD){
2562 
2563  Multi_sg_game_passwd.enable();
2564  Multi_sg_game_passwd.unhide();
2565  Multi_sg_game_passwd.set_focus();
2566 
2567  Multi_sg_netgame->mode = NG_MODE_PASSWORD;
2568 
2569  // copy in the current network password
2570  Multi_sg_game_passwd.set_text(Multi_sg_netgame->passwd);
2571  } else {
2573  }
2574  break;
2575 
2576 /*
2577  // toggle "restricted" on or off
2578  case MSG_RESTRICTED_GAME:
2579  if(Multi_sg_netgame->mode != NG_MODE_RESTRICTED){
2580  gamesnd_play_iface(SND_USER_SELECT);
2581  Multi_sg_netgame->mode = NG_MODE_RESTRICTED;
2582 
2583  // release the password control if necessary
2584  multi_sg_release_passwd();
2585  } else {
2586  gamesnd_play_iface(SND_GENERAL_FAIL);
2587  }
2588  break;
2589 */
2590  // turn off all rank requirements
2591  case MSG_RANK_SET_GAME:
2592  // if either is set, then turn then both off
2593  if((Multi_sg_netgame->mode != NG_MODE_RANK_BELOW) && (Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE)){
2595 
2596  // set it to the default case if we're turning it off
2598  Multi_sg_rank_start = Multi_sg_rank_select;
2599 
2600  Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
2601 
2602  // release the password control if necessary
2604  } else {
2606  }
2607  break;
2608 
2609  // rank above was pressed
2610  case MSG_RANK_ABOVE :
2611  if((Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE) || (Multi_sg_netgame->mode == NG_MODE_RANK_BELOW)){
2612  Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
2613 
2614  // select the first item
2616  Multi_sg_rank_start = Multi_sg_rank_select;
2617 
2618  // play a sound
2620  } else {
2622  }
2623  break;
2624 
2625  // rank below was pressed
2626  case MSG_RANK_BELOW :
2627  if((Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE) || (Multi_sg_netgame->mode == NG_MODE_RANK_BELOW)){
2628  Multi_sg_netgame->mode = NG_MODE_RANK_BELOW;
2629 
2630  // select the first item
2632  Multi_sg_rank_start = Multi_sg_rank_select;
2633 
2634  // play a sound
2636  } else {
2638  }
2639  break;
2640 
2641  // scroll the rank list up
2642  case MSG_RANK_SCROLL_UP:
2644  break;
2645 
2646  // scroll the rank list down
2647  case MSG_RANK_SCROLL_DOWN:
2649  break;
2650 
2651  // move to the create game screen
2652  case MSG_ACCEPT:
2655  break;
2656 
2657  default :
2659  multi_common_add_notify(XSTR("Not implemented yet!",760));
2660  break;
2661  }
2662 }
2663 
2664 // NOTE : this is where all Netgame initialization should take place on the host
2666 {
2667  char buf[128],out_name[128];
2668  net_addr save;
2669  net_player *server_save;
2670 
2671  // back this data up in case we are already connected to a standalone
2672  memcpy(&save,&Netgame.server_addr,sizeof(net_addr));
2673  server_save = Netgame.server;
2674 
2675  // remove campaign flags
2677 
2678  // clear out the Netgame structure and start filling in the values
2679  memset( &Netgame, 0, sizeof(Netgame) );
2680  memset( &Multi_sg_netgame_temp, 0, sizeof(netgame_info) );
2681 
2682  // if we're on the standalone, we're not the server, so we don't care about setting the netgame state
2685  Multi_sg_netgame = &Netgame;
2686 
2687  // NETLOG
2688  ml_string(NOX("Starting netgame as Host/Server"));
2689  } else {
2690  Multi_sg_netgame = &Multi_sg_netgame_temp;
2691 
2692  // NETLOG
2693  in_addr temp_addr;
2694  memcpy(&temp_addr.s_addr, &Netgame.server_addr, 4);
2695  char *server_addr = inet_ntoa(temp_addr);
2696  ml_printf(NOX("Starting netgame as Host on Standalone server : %s"), (server_addr == NULL) ? NOX("Unknown") : server_addr);
2697  }
2698 
2700 
2701  Multi_sg_netgame->security = (rand() % 32766) + 1; // get some random security number
2702  Multi_sg_netgame->mode = NG_MODE_OPEN;
2703  Multi_sg_netgame->rank_base = RANK_ENSIGN;
2704  if(Multi_sg_netgame->security < 16){
2705  Multi_sg_netgame->security += 16;
2706  }
2707 
2708  // set the version_info field
2709  Multi_sg_netgame->version_info = NG_VERSION_ID;
2710 
2713  }
2714 
2715  // set the default netgame flags
2716  Multi_sg_netgame->flags = 0;
2717 
2718  // intialize endgame stuff
2720 
2721  // load in my netgame options
2723 
2724  // load my local netplayer options
2726 
2727  // setup the default game name, taking care of string length and player callsigns
2728  memset(out_name,0,128);
2729  memset(buf,0,128);
2731  sprintf(buf, XSTR("%s game",782), out_name); // [[ %s will be a pilot's name ]]
2732  if ( strlen(buf) > MAX_GAMENAME_LEN ){
2733  strcpy_s(buf, XSTR("Temporary name",783));
2734  }
2735  strcpy_s(Multi_sg_netgame->name, buf);
2736 
2737  // set the default qos and duration
2739 
2740  // make sure to set the server correctly (me or the standalone)
2742  memcpy(&Netgame.server_addr, &Psnet_my_addr,sizeof(net_addr));
2745 
2746  // setup debug flags
2747  Netgame.debug_flags = 0;
2748  } else {
2749  memcpy(&Netgame.server_addr,&save,sizeof(net_addr));
2750  Netgame.server = server_save;
2751  }
2752 
2753  // if I have a cd or not
2754  if(Multi_has_cd){
2756  }
2757 
2758  // if I have hacked data
2759  if(game_hacked_data()){
2761  }
2762 
2763  // assign my player struct and other data
2766 
2767  // if we're supposed to flush our cache directory, do so now
2770 
2771  // NETLOG
2772  ml_string(NOX("Flushing multi-data cache"));
2773  }
2774 
2775  game_flush();
2776 }
2777 
2779 {
2780  // draw the appropriate radio button
2781  switch(Multi_sg_netgame->mode){
2782  case NG_MODE_OPEN:
2783  Multi_sg_buttons[gr_screen.res][MSG_OPEN_GAME].button.draw_forced(2);
2784  break;
2785 /*
2786  case NG_MODE_CLOSED:
2787  Multi_sg_buttons[gr_screen.res][MSG_CLOSED_GAME].button.draw_forced(2);
2788  break;
2789 */
2790  case NG_MODE_PASSWORD:
2791  Multi_sg_buttons[gr_screen.res][MSG_PASSWD_GAME].button.draw_forced(2);
2792  break;
2793 /*
2794  case NG_MODE_RESTRICTED:
2795  Multi_sg_buttons[gr_screen.res][MSG_RESTRICTED_GAME].button.draw_forced(2);
2796  break;
2797 */
2798  case NG_MODE_RANK_ABOVE:
2799  Multi_sg_buttons[gr_screen.res][MSG_RANK_SET_GAME].button.draw_forced(2);
2800  Multi_sg_buttons[gr_screen.res][MSG_RANK_ABOVE].button.draw_forced(2);
2801  break;
2802  case NG_MODE_RANK_BELOW:
2803  Multi_sg_buttons[gr_screen.res][MSG_RANK_SET_GAME].button.draw_forced(2);
2804  Multi_sg_buttons[gr_screen.res][MSG_RANK_BELOW].button.draw_forced(2);
2805  break;
2806  }
2807 }
2808 
2810 {
2811  // if he doesn't have either of the rank flags set, then ignore this
2812  if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
2813  return;
2814  }
2815 
2816  if(Multi_sg_rank_start > 0){
2817  Multi_sg_rank_start--;
2819  } else {
2821  }
2822 }
2823 
2825 {
2826  // if he doesn't have either of the rank flags set, then ignore this
2827  if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
2828  return;
2829  }
2830 
2831  if((NUM_RANKS - Multi_sg_rank_start) > Multi_sg_rank_max_display[gr_screen.res]){
2832  Multi_sg_rank_start++;
2834  } else {
2836  }
2837 }
2838 
2840 {
2841  int y,line_height,idx,count;
2842  char rank_name[40];
2843 
2844  // if he doesn't have either of the rank flags set, then ignore this
2845  if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
2846  return;
2847  }
2848 
2849  // display the list of ranks
2850  y = Msg_rank_list_coords[gr_screen.res][MSG_Y_COORD];
2851  line_height = gr_get_font_height() + 1;
2852  idx = Multi_sg_rank_start;
2853  count = 0;
2854  while((count < NUM_RANKS) && (count < Multi_sg_rank_max_display[gr_screen.res]) && (idx < NUM_RANKS)){
2855  // if its the selected item, then color it differently
2856  if(idx == Multi_sg_rank_select){
2858  } else {
2860  }
2861 
2862  // print the text
2863  multi_sg_rank_build_name(Ranks[idx].name,rank_name);
2864  gr_string(Msg_rank_list_coords[gr_screen.res][MSG_X_COORD],y,rank_name,GR_RESIZE_MENU);
2865 
2866  // increment stuff
2867  y+=line_height;
2868  idx++;
2869  count++;
2870  }
2871 
2872  // display the selected rank
2873 
2875  multi_sg_rank_build_name(Ranks[Multi_sg_netgame->rank_base].name,rank_name);
2876  gr_string(Msg_rank_sel_coords[gr_screen.res][MSG_X_COORD],Msg_rank_sel_coords[gr_screen.res][MSG_Y_COORD],rank_name,GR_RESIZE_MENU);
2877 
2878 }
2879 
2881 {
2882  char string[255];
2883 
2884  // if he doesn't have either of the rank flags set, then ignore this
2885  if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
2886  return;
2887  }
2888 
2889  // see if he's clicked on an item on the rank list
2890  if(Multi_sg_rank_button.pressed()){
2891  int y,item;
2892  Multi_sg_rank_button.get_mouse_pos(NULL,&y);
2893  item = y / (gr_get_font_height() + 1);
2894 
2895  if(item + Multi_sg_rank_start < NUM_RANKS){
2896  // evaluate whether this rank is valid for the guy to pick
2897  if(multi_sg_rank_select_valid(item + Multi_sg_rank_start)){
2899 
2900  Multi_sg_rank_select = item + Multi_sg_rank_start;
2901 
2902  // set the Netgame rank
2903  Multi_sg_netgame->rank_base = Multi_sg_rank_select;
2904  } else {
2906 
2907  memset(string,0,255);
2908  sprintf(string,XSTR("Illegal value for a host of your rank (%s)\n",784),Ranks[Net_player->m_player->stats.rank].name);
2909  multi_common_add_notify(string);
2910  }
2911  }
2912  }
2913 }
2914 
2915 void multi_sg_rank_build_name(char *in,char *out)
2916 {
2917  char use[100];
2918  char *first;
2919 
2920  strcpy_s(use,in);
2921  first = strtok(use," ");
2922 
2923  // just copy the string
2924  if(first == NULL){
2925  strcpy(out,in);
2926  }
2927 
2928  // if the first part of the string is lieutenant, then abbreivate it and tack on the rest of the string
2929  if (stricmp(first,XSTR("lieutenant",785)) == 0) {
2930  first = strtok(NULL, NOX("\n"));
2931 
2932  // if he's not just a plain lieutenant
2933  if(first != NULL){
2934  strcpy(out,XSTR("Lt. ",786)); // [[ lieutenant ]]
2935  strcat(out,first);
2936  }
2937  // if he _is_ just a plain lieutenant
2938  else {
2939  strcpy(out,in);
2940  }
2941  } else {
2942  strcpy(out,in);
2943  }
2944 }
2945 
2947 {
2948  // check to see if the password input box has been pressed
2949  if(Multi_sg_game_passwd.changed()){
2950  Multi_sg_game_passwd.get_text(Multi_sg_netgame->passwd);
2951  }
2952 }
2953 
2955 {
2956  // check to see if the game name input box has been pressed
2957  if(Multi_sg_game_name.changed()){
2958  Multi_sg_game_name.get_text(Multi_sg_netgame->name);
2959  }
2960 }
2961 
2963 {
2964  // hide and disable the password input box
2965  Multi_sg_game_passwd.hide();
2966  Multi_sg_game_passwd.disable();
2967 
2968  // set the focus back to the name input box
2969  Multi_sg_game_name.set_focus();
2970 }
2971 
2973 {
2974  // rank above mode
2975  if(Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE){
2976  if(Net_player->m_player->stats.rank >= rank){
2977  return 1;
2978  }
2979  }
2980  // rank below mode
2981  else {
2982  if(Net_player->m_player->stats.rank <= rank){
2983  return 1;
2984  }
2985  }
2986 
2987  return 0;
2988 }
2989 
2991 {
2992  // pick our rank for now
2993  Multi_sg_rank_select = Net_player->m_player->stats.rank;
2994 
2995  // set the Netgame rank
2996  Multi_sg_netgame->rank_base = Multi_sg_rank_select;
2997 }
2998 
2999 // -------------------------------------------------------------------------------------------------
3000 //
3001 // MULTIPLAYER CREATE GAME screen
3002 //
3003 
3004 //XSTR:OFF
3005 // bitmaps defs
3007  "MultiCreate", // GR_640
3008  "2_MultiCreate" // GR_1024
3009 };
3010 
3012  "MultiCreate-M", // GR_640
3013  "2_MultiCreate-M" // GR_1024
3014 };
3015 
3017  "PleaseWait", // GR_640
3018  "2_PleaseWait" // GR_1024
3019 };
3020 //XSTR:ON
3021 
3022 #define MULTI_CREATE_NUM_BUTTONS 23
3023 
3024 // button defs
3025 #define MC_SHOW_ALL 0
3026 #define MC_SHOW_COOP 1
3027 #define MC_SHOW_TEAM 2
3028 #define MC_SHOW_DOGFIGHT 3
3029 #define MC_PXO_REFRESH 4
3030 #define MC_PILOT_INFO 5
3031 #define MC_SCROLL_LIST_UP 6
3032 #define MC_SCROLL_LIST_DOWN 7
3033 #define MC_SCROLL_PLAYERS_UP 8
3034 #define MC_SCROLL_PLAYERS_DOWN 9
3035 #define MC_MISSION_FILTER 10
3036 #define MC_CAMPAIGN_FILTER 11
3037 #define MC_CANCEL 12
3038 #define MC_TEAM0 13
3039 #define MC_TEAM1 14
3040 #define MC_KICK 15
3041 #define MC_CLOSE 16
3042 #define MC_SCROLL_INFO_UP 17
3043 #define MC_SCROLL_INFO_DOWN 18
3044 #define MC_HOST_OPTIONS 19
3045 #define MC_HELP 20
3046 #define MC_OPTIONS 21
3047 #define MC_ACCEPT 22
3048 
3049 
3050 UI_WINDOW Multi_create_window; // the window object for the create screen
3052 UI_BUTTON Multi_create_list_select_button; // for selecting missions/campaigns
3053 int Multi_create_bitmap = -1; // the background bitmap
3054 UI_SLIDER2 Multi_create_slider; // for create list
3055 
3056 // constants for coordinate look ups
3057 #define MC_X_COORD 0
3058 #define MC_Y_COORD 1
3059 #define MC_W_COORD 2
3060 #define MC_H_COORD 3
3061 
3063  { // GR_640
3064  ui_button_info("MC_00", 32, 129, 36, 158, 0), // show all missions
3065  ui_button_info("MC_01", 76, 129, 71, 158, 1), // show coop missions
3066  ui_button_info("MC_02", 121, 129, 119, 158, 2), // show team missions
3067  ui_button_info("MC_03", 164, 129, 166, 158, 3), // show dogfight missions
3068  ui_button_info("MC_04", 399, 129, 229, 130, 4), // pxo mission refresh
3069  ui_button_info("MC_05", 567, 123, 467, 132, 5), // pilot info
3070  ui_button_info("MC_06", 1, 161, -1, -1, 6), // scroll mission info up
3071  ui_button_info("MC_08", 1, 304, -1, -1, 8), // scroll mission info down
3072  ui_button_info("MC_09", 613, 160, -1, -1, 9), // scroll players up
3073  ui_button_info("MC_10", 613, 202, -1, -1, 10), // scroll players down
3074  ui_button_info("MC_11", 22, 346, 27, 376, 11), // mission filter
3075  ui_button_info("MC_12", 104, 346, 110, 376, 12), // campaign filter
3076  ui_button_info("MC_13", 392, 341, 328, 364, 13), // cancel
3077  ui_button_info("MC_14", 472, 352, 482, 381, 14), // team 0
3078  ui_button_info("MC_15", 506, 352, 514, 381, 15), // team 1
3079  ui_button_info("MC_16", 539, 346, 539, 381, 16), // kick
3080  ui_button_info("MC_17", 589, 346, 582, 381, 17), // close
3081  ui_button_info("MC_18", 1, 406, -1, -1, 18), // scroll list up
3082  ui_button_info("MC_19", 1, 447, -1, -1, 19), // scroll list down
3083  ui_button_info("MC_20", 499, 434, 436, 423, 20), // host options
3084  ui_button_info("MC_21", 534, 426, -1, -1, 21), // help
3085  ui_button_info("MC_22", 534, 452, -1, -1, 22), // options
3086  ui_button_info("MC_23", 571, 426, 572, 413, 23), // commit
3087  },
3088  { // GR_1024
3089  ui_button_info("2_MC_00", 51, 207, 61, 253, 0), // show all missions
3090  ui_button_info("2_MC_01", 122, 207, 124, 253, 1), // show coop missions
3091  ui_button_info("2_MC_02", 193, 207, 194, 253, 2), // show team missions
3092  ui_button_info("2_MC_03", 263, 207, 261, 253, 3), // show dogfight missions
3093  ui_button_info("2_MC_04", 639, 207, 479, 218, 4), // pxo mission refresh
3094  ui_button_info("2_MC_05", 907, 197, 748, 216, 5), // pilot info
3095  ui_button_info("2_MC_06", 1, 258, -1, -1, 6), // scroll mission info up
3096  ui_button_info("2_MC_08", 1, 487, -1, -1, 8), // scroll mission info down
3097  ui_button_info("2_MC_09", 981, 256, -1, -1, 9), // scroll players up
3098  ui_button_info("2_MC_10", 981, 323, -1, -1, 10), // scroll players down
3099  ui_button_info("2_MC_11", 35, 554, 46, 601, 11), // mission filter
3100  ui_button_info("2_MC_12", 166, 554, 174, 601, 12), // campaign filter
3101  ui_button_info("2_MC_13", 628, 545, 559, 582, 13), // cancel
3102  ui_button_info("2_MC_14", 756, 564, 772, 610, 14), // team 0
3103  ui_button_info("2_MC_15", 810, 564, 826, 610, 15), // team 1
3104  ui_button_info("2_MC_16", 862, 554, 872, 610, 16), // kick
3105  ui_button_info("2_MC_17", 943, 554, 949, 610, 17), // close
3106  ui_button_info("2_MC_18", 1, 649, -1, -1, 18), // scroll list up
3107  ui_button_info("2_MC_19", 1, 716, -1, -1, 19), // scroll list down
3108  ui_button_info("2_MC_20", 798, 695, 726, 667, 20), // host options
3109  ui_button_info("2_MC_21", 854, 681, -1, -1, 21), // help
3110  ui_button_info("2_MC_22", 854, 724, -1, -1, 22), // options
3111  ui_button_info("2_MC_23", 914, 681, 932, 667, 23), // commit
3112  },
3113 };
3114 
3115 #define MULTI_CREATE_NUM_TEXT 15
3117  { // GR_640
3118  {"All", 1256, 36, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_ALL].button},
3119  {"Coop", 1257, 71, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_COOP].button},
3120  {"Team", 1258, 119, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_TEAM].button},
3121  {"Dogfight", 1259, 166, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_DOGFIGHT].button},
3122  {"Refresh Missions", 1260, 229, 130, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_PXO_REFRESH].button},
3123  {"Pilot Info", 1261, 467, 132, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_PILOT_INFO].button},
3124  {"Missions", 1262, 27, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_MISSION_FILTER].button},
3125  {"Campaigns", 1263, 110, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_CAMPAIGN_FILTER].button},
3126  {"Cancel", 387, 328, 364, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_CANCEL].button},
3127  {"1", 1264, 482, 381, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_TEAM0].button},
3128  {"2", 1265, 514, 381, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_TEAM1].button},
3129  {"Kick", 1266, 539, 381, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_KICK].button},
3130  {"Close", 1508, 582, 381, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_CLOSE].button},
3131  {"Host Options", 1267, 436, 423, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_HOST_OPTIONS].button},
3132  {"Commit", 1062, 572, 413, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_ACCEPT].button}
3133  },
3134  { // GR_1024
3135  {"All", 1256, 61, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_ALL].button},
3136  {"Coop", 1257, 124, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_COOP].button},
3137  {"Team", 1258, 194, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_TEAM].button},
3138  {"Dogfight", 1259, 261, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_DOGFIGHT].button},
3139  {"Refresh Missions", 1260, 501, 218, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_PXO_REFRESH].button},
3140  {"Pilot Info", 1261, 814, 216, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_PILOT_INFO].button},
3141  {"Missions", 1262, 46, 601, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_MISSION_FILTER].button},
3142  {"Campaigns", 1263, 174, 601, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_CAMPAIGN_FILTER].button},
3143  {"Cancel", 387, 559, 582, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_CANCEL].button},
3144  {"1", 1264, 772, 610, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_TEAM0].button},
3145  {"2", 1265, 826, 610, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_TEAM1].button},
3146  {"Kick", 1266, 872, 610, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_KICK].button},
3147  {"Close", 1508, 949, 610, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_CLOSE].button},
3148  {"Host Options", 1267, 755, 683, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_HOST_OPTIONS].button},
3149  {"Commit", 1062, 932, 667, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_ACCEPT].button}
3150  },
3151 };
3152 
3153 // squad war checkbox
3156  "MC_SW_00",
3157  "MC_SW_00",
3158 };
3160  { // GR_640
3161  6, 75
3162  },
3163  { // GR_1024
3164  18, 135
3165  }
3166 };
3168  { // GR_640
3169  6, 95
3170  },
3171  { // GR_640
3172  18, 155
3173  },
3174 };
3175 
3176 // game information text areas
3178  { // GR_640
3179  105, 173, 311, 152
3180  },
3181  { // GR_1024
3182  62, 275, 600, 262
3183  }
3184 };
3185 
3187  { // GR_640
3188  463, 164, 144, 180
3189  },
3190  { // GR_1024
3191  741, 262, 144, 180
3192  }
3193 };
3194 
3196  { // GR_640
3197  47, 405, 363, 59
3198  },
3199  { // GR_1024
3200  75, 648, 363, 59
3201  }
3202 };
3203 
3204 // mission icon stuff
3206  { // GR_640
3207  38, -2 // y is an offset
3208  },
3209  { // GR_1024
3210  61, -2 // y is an offset
3211  }
3212 };
3213 
3215  { // GR_640
3216  61, -1 // y is an offset
3217  },
3218  { // GR_1024
3219  98, 1 // y is an offset
3220  }
3221 };
3222 
3224  { // GR_640
3225  72, 0 // y is an offset
3226  },
3227  { // GR_1024
3228  115, 0 // y is an offset
3229  }
3230 };
3231 
3233  { // GR_640
3234  91, 0 // y is an offset
3235  },
3236  { // GR_1024
3237  146, 0 // y is an offset
3238  }
3239 };
3240 
3241 // mission/campaign list column areas
3243  194, // GR_640
3244  310 // GR_1024
3245 };
3246 
3248  38, // GR_640
3249  61 // GR_1024
3250 };
3251 
3253  77, // GR_640
3254  123 // GR_1024
3255 };
3256 
3258  105, // GR_640
3259  168 // GR_1024
3260 };
3261 
3263  314, // GR_640
3264  502 // GR_1024
3265 };
3266 
3268  337, // GR_640
3269  539 // GR_1024
3270 };
3271 
3273  {13, 116}, // GR_640
3274  {21, 186} // GR_1024
3275 };
3276 
3278  {467, 150}, // GR_640
3279  {747, 240} // GR_1024
3280 };
3281 
3283  {484, 342}, // GR_640
3284  {774, 547} // GR_1024
3285 };
3286 
3288  {
3289  3, 197, 13, 105 // GR_640
3290  },
3291  {
3292  5, 316, 20, 168 // GR_1024
3293  }
3294 };
3295 
3297  "slider",
3298  "2_slider"
3299 };
3300 
3301 // player list control thingie defs
3302 #define MULTI_CREATE_PLIST_MAX_DISPLAY 20
3303 int Multi_create_plist_select_flag; // flag indicating if we have a play selected
3304 short Multi_create_plist_select_id; // the net address of the currently selected player (for lookup)
3305 
3306 // master tracker details
3307 int Multi_create_frame_count; // framecount
3308 int Multi_create_mt_tried_login; // attempted to login this server on the MT
3309 
3310 // mission filter settings
3311 int Multi_create_filter; // what mode we're in
3312 
3313 // game/campaign list control defs
3315  15, // GR_640
3316  26 // GR_1024
3317 };
3318 
3319 
3321 int Multi_create_list_mode; // 0 == mission mode, 1 == campaign mode
3322 int Multi_create_list_start; // where to start displaying from
3323 int Multi_create_list_select; // which item is currently highlighted
3325 
3328 
3330 
3331 // LOCAL function definitions
3333 void multi_create_button_pressed(int n);
3344 void multi_create_list_do();
3346 void multi_create_list_blit_icons(int list_index, int y_start);
3355 
3356 // since we can selectively filter out mission/campaign types we always need to map a selected index (which is relative
3357 // to the displayed list), to an absolute index (which is relative to the total file list - some of which may filtered out)
3358 void multi_create_select_to_filename(int select_index,char *filename);
3359 int multi_create_select_to_index(int select_index);
3360 
3362 
3363 bool Multi_create_sort_mode = false; // default to mission name sorting, "true" is mission filename sorting
3364 
3365 
3366 // sorting function to sort mission lists.. Basic sorting on mission name
3368 {
3369  if (Multi_create_filter != MISSION_TYPE_MULTI) {
3370  if ( (m1.flags & Multi_create_filter) && !(m2.flags & Multi_create_filter) ) {
3371  return false;
3372  } else if ( (m2.flags & Multi_create_filter) && !(m1.flags & Multi_create_filter) ) {
3373  return true;
3374  }
3375  }
3376 
3377  int test = 0;
3378 
3379  if (Multi_create_sort_mode) {
3380  test = stricmp(m1.filename, m2.filename);
3381  } else {
3382  test = stricmp(m1.name, m2.name);
3383  }
3384 
3385  if (test < 0) {
3386  return true;
3387  } else if (test > 0) {
3388  return false;
3389  } else {
3390  return true;
3391  }
3392 }
3393 
3395 {
3396  bool sort_missions = false;
3397  bool sort_campaigns = false;
3398  char selected_name[255];
3399  int new_index = -1;
3400 
3401  if (Multi_create_list_count < 0) {
3402  return;
3403  }
3404 
3405  switch (mode) {
3407  sort_missions = true;
3408  break;
3409 
3411  sort_campaigns = true;
3412  break;
3413 
3414  default:
3415  sort_missions = true;
3416  sort_campaigns = true;
3417  break;
3418  }
3419 
3420  // save our current selected item so that we can restore that one after sorting
3421  multi_create_select_to_filename(Multi_create_list_select, selected_name);
3422 
3423  if (sort_missions) {
3424  std::sort( Multi_create_mission_list.begin(), Multi_create_mission_list.end(), multi_create_sort_func);
3425 
3426  for (size_t idx = 0; idx < Multi_create_mission_list.size(); idx++) {
3427  if ( !strcmp(selected_name, Multi_create_mission_list[idx].filename) ) {
3428  new_index = (int)idx;
3429  break;
3430  }
3431  }
3432  }
3433 
3434  if (sort_campaigns) {
3435  std::sort( Multi_create_campaign_list.begin(), Multi_create_campaign_list.end(), multi_create_sort_func);
3436 
3437  for (size_t idx = 0; idx < Multi_create_campaign_list.size(); idx++) {
3438  if ( !strcmp(selected_name, Multi_create_campaign_list[idx].filename) ) {
3439  new_index = (int)idx;
3440  break;
3441  }
3442  }
3443  }
3444 
3445  multi_create_list_select_item(new_index);
3446 }
3447 
3449 {
3450  int idx,should_sort,switched_modes;
3451 
3452  // set the current mode
3453  should_sort = 0;
3454  switched_modes = 0;
3455  if ( (Multi_create_list_mode != mode) && (mode != -1) ) {
3456  Multi_create_list_mode = mode;
3457  switched_modes = 1;
3458  } else if (mode == -1) {
3459  switched_modes = 1;
3460  }
3461 
3462  // get the mission count based upon the filter selected
3463  if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
3464  switch (Multi_create_filter) {
3465  case MISSION_TYPE_MULTI:
3466  Multi_create_list_count = (int)Multi_create_mission_list.size();
3467 
3468  // if we switched modes and we have more than 0 items, sort them
3469  if (switched_modes && (Multi_create_list_count > 0)){
3470  should_sort = 1;
3471  }
3472  break;
3473 
3474  default :
3475  Multi_create_list_count = 0;
3476 
3477  // find all missions which match
3478  for (idx = 0; idx < (int)Multi_create_mission_list.size(); idx++) {
3479  if(Multi_create_mission_list[idx].flags & Multi_create_filter){
3480  Multi_create_list_count++;
3481  }
3482  }
3483 
3484  // if we switched modes and we have more than 0 items, sort them
3485  if (switched_modes && (Multi_create_list_count > 0)){
3486  should_sort = 1;
3487  }
3488 
3489  break;
3490  }
3491  } else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
3492  switch (Multi_create_filter) {
3493  case MISSION_TYPE_MULTI:
3494  Multi_create_list_count = (int)Multi_create_campaign_list.size();
3495 
3496  // if we switched modes and we have more than 0 items, sort them
3497  if (switched_modes && (Multi_create_list_count > 0)){
3498  should_sort = 1;
3499  }
3500  break;
3501 
3502  default :
3503  Multi_create_list_count = 0;
3504 
3505  // find all missions which match
3506  for (idx = 0; idx < (int)Multi_create_campaign_list.size(); idx++) {
3507  if(Multi_create_campaign_list[idx].flags & Multi_create_filter){
3508  Multi_create_list_count++;
3509  }
3510  }
3511 
3512  // if we switched modes and we have more than 0 items, sort them
3513  if(switched_modes && (Multi_create_list_count > 0)){
3514  should_sort = 1;
3515  }
3516  break;
3517  }
3518  }
3519 
3520  // reset the list start and selected indices
3521  Multi_create_list_start = 0;
3522  Multi_create_list_select = -1;
3523  multi_create_list_select_item(Multi_create_list_start);
3524 
3525  // sort the list of missions if necessary
3526  if (should_sort) {
3527  multi_create_list_sort(mode);
3528  }
3529 
3530  // reset the slider
3531  Multi_create_slider.set_numberItems(Multi_create_list_count > Multi_create_list_max_display[gr_screen.res] ? Multi_create_list_count-Multi_create_list_max_display[gr_screen.res] : 0);
3532 }
3533 
3535 {
3536  int idx;
3537  ui_button_info *b;
3538 
3539  // now make sure to initialze various netgame stuff based upon whether we're on a standalone or not
3542  } else {
3544  }
3545 
3546  // initialize the player list data
3547  Multi_create_plist_select_flag = 0;
3548  Multi_create_plist_select_id = -1;
3549 
3550  // create the interface window
3551  Multi_create_window.create(0,0,gr_screen.max_w_unscaled,gr_screen.max_h_unscaled,0);
3552  Multi_create_window.set_mask_bmap(Multi_create_bitmap_mask_fname[gr_screen.res]);
3553 
3554  // load the background bitmap
3555  Multi_create_bitmap = bm_load(Multi_create_bitmap_fname[gr_screen.res]);
3556  if(Multi_create_bitmap < 0){
3557  // we failed to load the bitmap - this is very bad
3558  Int3();
3559  }
3560 
3561  // close any previous existing instances of the chatbox and create a new one
3562  chatbox_close();
3563  chatbox_create();
3564 
3565  // load the help overlay
3566  Multi_create_overlay_id = help_overlay_get_index(MULTI_CREATE_OVERLAY);
3567  help_overlay_set_state(Multi_create_overlay_id, gr_screen.res, 0);
3568 
3569  // initialize the common notification messaging
3571 
3572  // use the common interface palette
3574 
3575  // create the interface buttons
3576  for(idx=0; idx<MULTI_CREATE_NUM_BUTTONS; idx++){
3577  b = &Multi_create_buttons[gr_screen.res][idx];
3578 
3579  // create the object
3580  b->button.create(&Multi_create_window, "", b->x, b->y, 1, 1, ((idx == MC_SCROLL_LIST_UP) || (idx == MC_SCROLL_LIST_DOWN)), 1);
3581 
3582  // set the sound to play when highlighted
3584 
3585  // set the ani for the button
3586  b->button.set_bmaps(b->filename);
3587 
3588  // set the hotspot
3589  b->button.link_hotspot(b->hotspot);
3590 
3591  // some special case stuff for the pxo refresh button
3592  if(idx == MC_PXO_REFRESH){
3593  // if not a PXO game, or if I'm not a server disable and hide the button
3595  b->button.hide();
3596  b->button.disable();
3597  }
3598  }
3599  }
3600 
3601  // create xstrs
3602  for(idx=0; idx<MULTI_CREATE_NUM_TEXT; idx++){
3603  Multi_create_window.add_XSTR(&Multi_create_text[gr_screen.res][idx]);
3604  }
3605 
3606  // if this is a PXO game, enable the squadwar checkbox
3607  Multi_create_sw_checkbox.create(&Multi_create_window, "", Multi_create_sw_checkbox_coords[gr_screen.res][0], Multi_create_sw_checkbox_coords[gr_screen.res][1], 0);
3608  Multi_create_sw_checkbox.set_bmaps(Multi_create_sw_checkbox_fname[gr_screen.res], 6, 0);
3609  if(!MULTI_IS_TRACKER_GAME){
3610  Multi_create_sw_checkbox.hide();
3611  Multi_create_sw_checkbox.disable();
3612  }
3613 
3614  // initialize the mission type filtering mode
3615  Multi_create_filter = MISSION_TYPE_MULTI;
3616 
3617  // initialize the list mode, and load in a list
3618  Multi_create_mission_list.clear();
3619  Multi_create_campaign_list.clear();
3620 
3621  Multi_create_list_mode = MULTI_CREATE_SHOW_MISSIONS;
3622  Multi_create_list_start = -1;
3623  Multi_create_list_select = -1;
3624  Multi_create_list_count = 0;
3625 
3626  Multi_create_slider.create(&Multi_create_window, Mc_slider_coords[gr_screen.res][MC_X_COORD], Mc_slider_coords[gr_screen.res][MC_Y_COORD], Mc_slider_coords[gr_screen.res][MC_W_COORD],Mc_slider_coords[gr_screen.res][MC_H_COORD], -1, Mc_slider_bitmap[gr_screen.res], &multi_create_list_scroll_up, &multi_create_list_scroll_down, NULL);
3627 
3628  // create the player list select button
3629  Multi_create_player_select_button.create(&Multi_create_window, "", Mc_players_coords[gr_screen.res][MC_X_COORD], Mc_players_coords[gr_screen.res][MC_Y_COORD], Mc_players_coords[gr_screen.res][MC_W_COORD], Mc_players_coords[gr_screen.res][MC_H_COORD], 0, 1);
3630  Multi_create_player_select_button.hide();
3631 
3632  // create the mission/campaign list select button
3633  Multi_create_list_select_button.create(&Multi_create_window, "", Mc_list_coords[gr_screen.res][MC_X_COORD], Mc_list_coords[gr_screen.res][MC_Y_COORD], Mc_list_coords[gr_screen.res][MC_W_COORD], Mc_list_coords[gr_screen.res][MC_H_COORD], 0, 1);
3634  Multi_create_list_select_button.hide();
3635 
3636  // set hotkeys for a couple of things.
3637  Multi_create_buttons[gr_screen.res][MC_ACCEPT].button.set_hotkey(KEY_CTRLED+KEY_ENTER);
3638 
3639  // init some master tracker stuff
3640  Multi_create_frame_count = 0;
3641  Multi_create_mt_tried_login = 0;
3642 
3643  // remove campaign flags
3645 
3646  // send any pilots as appropriate
3648 
3649  Multi_create_files_loaded = 0;
3650 }
3651 
3653 {
3654  //DTP CHECK ALMISSION FLAG HERE AND SKIP THE BITMAP LOADING PROGRESS
3655  //SINCE WE ALREADY HAVE A MISSION SELECTED IF THIS MISSION IS A VALID MULTIPLAYER MISSION
3656  //IF NOT A VALID MULTIPLAYER MISSION CONTINUE LOADING, MAYBE CALL POPUP.
3658  multi_create_list_do(); //uhm here because off, hehe, my mind is failing right now
3659 
3660  // DTP Var section for the is mission multi player Check.
3661 
3662  char mission_name[NAME_LENGTH+1];
3663  int flags;
3664  char *filename;
3665  filename = cf_add_ext( Cmdline_almission, FS_MISSION_FILE_EXT ); //DTP ADD EXTENSION needed next line
3666  flags = mission_parse_is_multi(filename, mission_name); //DTP flags will set if mission is multi
3667 
3668  if (flags) { //only continue if mission is multiplayer mission
3669  netgame_info *ng;
3670  ng = &Netgame;
3671 
3672  char almissionname[256]; // needed, for the strncpy below
3673  strncpy(almissionname, Cmdline_almission,MAX_FILENAME_LEN); //DTP; copying name from cmd_almission line
3674 
3675  Netgame.options.respawn = 99; //override anything //for debugging, i often forget this.
3677 
3678  Netgame.campaign_mode = MP_SINGLE_MISSION; //multiplayer single mission. meaning Single mission, not single player
3679 
3680  strcpy_s(Game_current_mission_filename,almissionname); // copying almissionname to Game_current_mission_filename
3681  strcpy_s(Netgame.mission_name,almissionname);// copying almission name to netgame.mission_name
3682 
3683  Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING; //DTP must be set before a call to gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC) is done as it is below.
3685 
3686  Cmdline_almission = NULL; // we don't want to autoload anymore do we, we will be able to quit. halleluja. Startgame has already been disabled, so no need to turn of "Cmdline_start_netgame"
3687  return; // we don't need to check or set regarding ships/weapons anything as we are already progressing into mission so return
3688  }
3689  else {
3690  popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR(" Not a multi player-mission",9999)); //DTP startgame popup pilot error
3692  Cmdline_almission = NULL; //DTP make sure this gets nullified.
3693 
3694  }
3695  }
3696 
3697  int player_index;
3698  const char *loading_str = XSTR("Loading", 1336);
3699  int str_w, str_h;
3700 
3701  // set this if we want to show the pilot info popup
3702  Multi_create_should_show_popup = 0;
3703 
3704  // first thing is to load the files
3705  if ( !Multi_create_files_loaded ) {
3706  // if I am a client, send a list request to the server for the missions
3707  if ( MULTIPLAYER_CLIENT ) {
3709  } else {
3710  int loading_bitmap;
3711 
3712  loading_bitmap = bm_load(Multi_create_loading_fname[gr_screen.res]);
3713 
3714  // draw the background, etc
3715  gr_reset_clip();
3716  GR_MAYBE_CLEAR_RES(Multi_create_bitmap);
3717  if(Multi_create_bitmap != -1){
3718  gr_set_bitmap(Multi_create_bitmap);
3719  gr_bitmap(0, 0, GR_RESIZE_MENU);
3720  }
3721  chatbox_render();
3722  if ( loading_bitmap > -1 ){
3723  gr_set_bitmap(loading_bitmap);
3724  }
3726 
3727  // draw "Loading" on it
3729  gr_set_font(FONT2);
3730  gr_get_string_size(&str_w, &str_h, loading_str);
3731  gr_string((gr_screen.max_w_unscaled - str_w) / 2, (gr_screen.max_h_unscaled - str_h) / 2, loading_str, GR_RESIZE_MENU);
3732  gr_set_font(FONT1);
3733 
3734  gr_flip();
3735 
3738 
3739  // if this is a tracker game, validate missions
3740  if (MULTI_IS_TRACKER_GAME) {
3743  }
3744 
3745  // update the file list
3747  // the above function doesn't sort initially, so we need this to take care of it
3749  // the sort function probably changed our selection, but here we always need it to zero
3751  }
3752 
3753  // don't bother setting netgame state if ont the server
3757 
3758  // tell FS2NetD about our game as well
3759  if (MULTI_IS_TRACKER_GAME) {
3761  }
3762  }
3763 
3764  // if we're on the standalone we have to tell him that we're now in the host setup screen
3767 
3768  Multi_create_files_loaded = 1;
3769  }
3770 
3771  int k = chatbox_process();
3772  k = Multi_create_window.process(k,0);
3773 
3774  switch(k){
3775  // same as the cancel button
3776  case KEY_ESC: {
3777  if ( help_overlay_active(Multi_create_overlay_id) ) {
3778  help_overlay_set_state(Multi_create_overlay_id, gr_screen.res, 0);
3779  } else {
3782  }
3783 
3784  break;
3785  }
3786 
3787  case KEY_CTRLED | KEY_SHIFTED | KEY_S:
3788  Multi_create_sort_mode = !Multi_create_sort_mode;
3789  multi_create_list_sort(Multi_create_list_mode);
3790  break;
3791  }
3792 
3793  if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
3794  help_overlay_set_state(Multi_create_overlay_id, gr_screen.res, 0);
3795  }
3796 
3797  // process any button clicks
3799 
3800  // do any network related stuff
3802 
3803  // draw the background, etc
3804  gr_reset_clip();
3805  GR_MAYBE_CLEAR_RES(Multi_create_bitmap);
3806  if(Multi_create_bitmap != -1){
3807  gr_set_bitmap(Multi_create_bitmap);
3809  }
3810 
3811  // if we're not in team vs. team mode, don't draw the team buttons
3812  if(!(Netgame.type_flags & NG_TYPE_TEAM)){
3813  Multi_create_buttons[gr_screen.res][MC_TEAM0].button.hide();
3814  Multi_create_buttons[gr_screen.res][MC_TEAM1].button.hide();
3815  Multi_create_buttons[gr_screen.res][MC_TEAM0].button.disable();
3816  Multi_create_buttons[gr_screen.res][MC_TEAM1].button.disable();
3817  } else {
3818  Multi_create_buttons[gr_screen.res][MC_TEAM0].button.enable();
3819  Multi_create_buttons[gr_screen.res][MC_TEAM1].button.enable();
3820  Multi_create_buttons[gr_screen.res][MC_TEAM0].button.unhide();
3821  Multi_create_buttons[gr_screen.res][MC_TEAM1].button.unhide();
3822  }
3823 
3824  // draw the window itself
3825  Multi_create_window.draw();
3826 
3828 
3829  // draw Create Game text
3830  gr_string(Mc_create_game_text[gr_screen.res][MC_X_COORD], Mc_create_game_text[gr_screen.res][MC_Y_COORD], XSTR("Create Game", 1268), GR_RESIZE_MENU);
3831 
3832  // draw players text
3833  gr_string(Mc_players_text[gr_screen.res][MC_X_COORD], Mc_players_text[gr_screen.res][MC_Y_COORD], XSTR("Players", 1269), GR_RESIZE_MENU);
3834 
3835  // draw players text
3836  gr_string(Mc_team_text[gr_screen.res][MC_X_COORD], Mc_team_text[gr_screen.res][MC_Y_COORD], XSTR("Team", 1258), GR_RESIZE_MENU);
3837 
3838  // process and display the player list
3839  // NOTE : this must be done before the buttons are checked to insure that a player hasn't dropped
3843  } else {
3845  }
3846 
3847  // process and display the game/campaign list
3849 
3850  // draw the correct mission filter button
3852 
3853  // display any text in the info area
3855 
3856  // display any pending notification messages
3858 
3859  // force the correct mission/campaign button to light up
3860  if( Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ){
3861  Multi_create_buttons[gr_screen.res][MC_MISSION_FILTER].button.draw_forced(2);
3862  } else {
3863  Multi_create_buttons[gr_screen.res][MC_CAMPAIGN_FILTER].button.draw_forced(2);
3864  }
3865 
3866  // force draw the closed button if it is toggled on
3868  Multi_create_buttons[gr_screen.res][MC_CLOSE].button.draw_forced(2);
3869  }
3870 
3871  // process and show the chatbox thingie
3872  chatbox_render();
3873 
3874  // draw tooltips
3875  Multi_create_window.draw_tooltip();
3876 
3877  // display the voice status indicator
3879 
3880  // blit the help overlay if necessary
3881  help_overlay_maybe_blit(Multi_create_overlay_id, gr_screen.res);
3882 
3883  // test code
3887  } else {
3889  }
3890  gr_string(Multi_create_sw_checkbox_text[gr_screen.res][0], Multi_create_sw_checkbox_text[gr_screen.res][1], "SquadWar", GR_RESIZE_MENU);
3891  }
3892 
3893  // flip the buffer
3894  gr_flip();
3895 
3896  // if we're supposed to show the pilot info popup, do it now
3897  if(Multi_create_should_show_popup){
3898  // get the player index and address of the player item the mouse is currently over
3899  if(Multi_create_plist_select_flag){
3900  player_index = find_player_id(Multi_create_plist_select_id);
3901  if(player_index != -1){
3902  multi_pinfo_popup(&Net_players[player_index]);
3903  }
3904  }
3905  }
3906 
3907  // increment the frame count
3908  Multi_create_frame_count++;
3909 }
3910 
3912 {
3913  // unload any bitmaps
3914  if(!bm_unload(Multi_create_bitmap)){
3915  nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_create_bitmap_fname[gr_screen.res]));
3916  }
3917 
3918  // destroy the chatbox
3919  // chatbox_close();
3920 
3921  // destroy the UI_WINDOW
3922  Multi_create_window.destroy();
3923 }
3924 
3926 {
3927  int idx;
3928  for(idx=0;idx<MULTI_CREATE_NUM_BUTTONS;idx++){
3929  // we only really need to check for one button pressed at a time, so we can break after
3930  // finding one.
3931  if(Multi_create_buttons[gr_screen.res][idx].button.pressed()){
3933  break;
3934  }
3935  }
3936 
3937  // if the squad war checkbox was clicked
3938  if(Multi_create_sw_checkbox.changed()){
3940  }
3941 }
3942 
3944 {
3945  int idx;
3946 
3947  switch(n){
3948  case MC_CANCEL :
3951  break;
3952  case MC_ACCEPT :
3953  // if valid commit conditions have not been met
3955  break;
3956  }
3957 
3958  // commit
3960  break;
3961 
3962  // help button
3963  case MC_HELP :
3964  if(!help_overlay_active(Multi_create_overlay_id)){
3965  help_overlay_set_state(Multi_create_overlay_id,gr_screen.res,1);
3966  } else {
3967  help_overlay_set_state(Multi_create_overlay_id,gr_screen.res,0);
3968  }
3969  break;
3970 
3971  // scroll the info text box up
3972  case MC_SCROLL_INFO_UP:
3974  break;
3975 
3976  // scroll the info text box down
3977  case MC_SCROLL_INFO_DOWN:
3979  break;
3980 
3981  // scroll the player list up
3982  case MC_SCROLL_PLAYERS_UP:
3984  break;
3985 
3986  // scroll the player list down
3989  break;
3990 
3991  // scroll the game/campaign list up
3992  case MC_SCROLL_LIST_UP:
3994  Multi_create_slider.forceUp(); // move slider up
3995  break;
3996 
3997  // scroll the game/campaign list down
3998  case MC_SCROLL_LIST_DOWN:
4000  Multi_create_slider.forceDown(); // move slider down
4001  break;
4002 
4003  // go to the options screen
4004  case MC_OPTIONS:
4007  break;
4008 
4009  // show all missions
4010  case MC_SHOW_ALL:
4011  if(Multi_create_filter != MISSION_TYPE_MULTI){
4013  Multi_create_filter = MISSION_TYPE_MULTI;
4014  multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4015  } else {
4017  }
4018  break;
4019 
4020  // show cooperative missions
4021  case MC_SHOW_COOP:
4022  if(Multi_create_filter != MISSION_TYPE_MULTI_COOP){
4024  Multi_create_filter = MISSION_TYPE_MULTI_COOP;
4025  multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4026  } else {
4028  }
4029  break;
4030 
4031  // show team vs. team missions
4032  case MC_SHOW_TEAM:
4033  if(Multi_create_filter != MISSION_TYPE_MULTI_TEAMS){
4035  Multi_create_filter = MISSION_TYPE_MULTI_TEAMS;
4036  multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4037  } else {
4039  }
4040  break;
4041 
4042  // show dogfight missions
4043  case MC_SHOW_DOGFIGHT:
4044  if (Multi_create_filter != MISSION_TYPE_MULTI_DOGFIGHT){
4046  Multi_create_filter = MISSION_TYPE_MULTI_DOGFIGHT;
4047  multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4048  } else {
4050  }
4051  break;
4052 
4053  // toggle temporary netgame closed on/off
4054  case MC_CLOSE:
4057  } else {
4060  }
4062  break;
4063 
4064  // kick the currently selected player (if possible)
4065  case MC_KICK:
4066  // lookup the player at the specified index
4067  if(Multi_create_plist_select_flag){
4068  idx = find_player_id(Multi_create_plist_select_id);
4069  // kick him - but don't ban him
4070  if(idx != -1){
4071  multi_kick_player(idx,0);
4072  }
4073  }
4074  break;
4075 
4076  // switch to individual mission mode and load in a list
4077  case MC_MISSION_FILTER:
4078  if(Multi_create_list_mode != MULTI_CREATE_SHOW_MISSIONS){
4080 
4082 
4083  // update the file list
4085  } else {
4087  }
4088  break;
4089 
4090  // switch to campaign mode and load in a list
4091  case MC_CAMPAIGN_FILTER:
4092  // switch off squad war
4093  Multi_create_sw_checkbox.set_state(0);
4095 
4096  if(Multi_create_list_mode != MULTI_CREATE_SHOW_CAMPAIGNS){
4098 
4100 
4101  // update the file list
4103  } else {
4105  }
4106  break;
4107 
4108  // attempt to set the selected player's team
4109  case MC_TEAM0:
4113  }
4114  break;
4115 
4116  // attempt to set the selected player's team
4117  case MC_TEAM1:
4121  }
4122  break;
4123 
4124  // popup the pilot info dialog for the currently selected pilot (will occur at the end of the frame)
4125  case MC_PILOT_INFO:
4126  Multi_create_should_show_popup = 1;
4127  break;
4128 
4129  // go to the host options screen
4130  case MC_HOST_OPTIONS:
4133  break;
4134 
4135  // refresh PXO file list
4136  case MC_PXO_REFRESH:
4137  if(!MULTI_IS_TRACKER_GAME){
4138  break;
4139  }
4141  break;
4142 
4143  default :
4145  multi_common_add_notify(XSTR("Not implemented yet!",760));
4146  break;
4147  }
4148 }
4149 
4150 // do stuff like pinging servers, sending out requests, etc
4151 // # Kazan # -- This apparently DOES NOT Apply to Standalones
4153 {
4154 
4155 }
4156 
4157 // if not on a standalone
4159 {
4160  // set me up as the host and master
4162 }
4163 
4164 // if on a standalone
4166 {
4168 }
4169 
4170 // scroll up through the player list
4172 {
4174 }
4175 
4176 // scroll down through the player list
4178 {
4180 }
4181 
4183 {
4184  int test_count,idx,player_index;
4185 
4186  // first determine if there are 0 players in the game. This should never happen since the host is _always_ in the game
4187  test_count = 0;
4188  for(idx=0;idx<MAX_PLAYERS;idx++){
4189  // count anyone except the standalone server (if applicable)
4191  test_count++;
4192  }
4193  }
4194  if(test_count <= 0){
4195  return;
4196  }
4197 
4198  // if we had a selected item but that player has left, select myself instead
4199  if(Multi_create_plist_select_flag){
4200  player_index = find_player_id(Multi_create_plist_select_id);
4201  if(player_index == -1){
4202  Multi_create_plist_select_id = Net_player->player_id;
4203  }
4204  } else {
4205  Multi_create_plist_select_flag = 1;
4206  Multi_create_plist_select_id = Net_player->player_id;
4207  }
4208 
4209  // if the player has clicked somewhere in the player list area
4210  if(Multi_create_player_select_button.pressed()){
4211  short player_id;
4212 
4213  // get the player index and address of the player item the mouse is currently over
4214  player_id = multi_create_get_mouse_id();
4215  player_index = find_player_id(player_id);
4216  if(player_index != -1){
4217  Multi_create_plist_select_flag = 1;
4218  Multi_create_plist_select_id = player_id;
4219  }
4220  }
4221 }
4222 
4224 {
4225  int idx;
4226  char str[CALLSIGN_LEN+5];
4227  int y_start = Mc_players_coords[gr_screen.res][MC_Y_COORD];
4228  int line_height = gr_get_font_height() + 1;
4229  int total_offset;
4230 
4231  // display all the players
4232  for(idx=0;idx<MAX_PLAYERS;idx++){
4233  // count anyone except the standalone server (if applicable)
4235  // total x offset
4236  total_offset = 0;
4237 
4238  // highlight him if he's the host
4240  if(Multi_create_plist_select_id == Net_players[idx].player_id){
4242  } else {
4244  }
4245  } else {
4246  if(Multi_create_plist_select_id == Net_players[idx].player_id){
4248  } else {
4250  }
4251  }
4252 
4253  // optionally draw his CD status
4256  gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1,GR_RESIZE_MENU);
4257 
4258  total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4259  }
4260 
4261  // make sure the string will fit, then display it
4262  strcpy_s(str,Net_players[idx].m_player->callsign);
4264  strcat_s(str,XSTR("(O)",787)); // [[ Observer ]]
4265  }
4266  gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4267  gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str,GR_RESIZE_MENU);
4268 
4269  y_start += line_height;
4270  }
4271  }
4272 }
4273 
4275 {
4276  int idx;
4277  char str[CALLSIGN_LEN+1];
4278  int y_start = Mc_players_coords[gr_screen.res][MC_Y_COORD];
4279  int line_height = gr_get_font_height() + 1;
4280  int total_offset;
4281 
4282  // display all the red players first
4283  for(idx=0;idx<MAX_PLAYERS;idx++){
4284  // count anyone except the standalone server (if applicable)
4285  if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
4286  // reset total offset
4287  total_offset = 0;
4288 
4289  // highlight him if he's the host
4291  if(Multi_create_plist_select_id == Net_players[idx].player_id){
4293 
4294  // be sure to blit the correct team button
4295  Multi_create_buttons[gr_screen.res][MC_TEAM0].button.draw_forced(2);
4296  } else {
4298  }
4299  } else {
4300  if(Multi_create_plist_select_id == Net_players[idx].player_id){
4302 
4303  // be sure to blit the correct team button
4304  Multi_create_buttons[gr_screen.res][MC_TEAM0].button.draw_forced(2);
4305  } else {
4307  }
4308  }
4309 
4310  // optionally draw his CD status
4313  gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1,GR_RESIZE_MENU);
4314 
4315  total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4316  }
4317 
4318  // blit the red team indicator
4322  gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2, GR_RESIZE_MENU);
4323 
4324  total_offset += Multi_common_icon_dims[MICON_TEAM0_SELECT][0] + 1;
4325  }
4326  } else {
4327  if(Multi_common_icons[MICON_TEAM0] != -1){
4329  gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2, GR_RESIZE_MENU);
4330 
4331  total_offset += Multi_common_icon_dims[MICON_TEAM0][0] + 1;
4332  }
4333  }
4334 
4335  // make sure the string will fit
4336  strcpy_s(str,Net_players[idx].m_player->callsign);
4338  strcat_s(str,XSTR("(O)",787));
4339  }
4340  gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4341 
4342  // display him in the correct half of the list depending on his team
4343  gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str,GR_RESIZE_MENU);
4344  y_start += line_height;
4345  }
4346  }
4347 
4348  // display all the green players next
4349  for(idx=0;idx<MAX_PLAYERS;idx++){
4350  // count anyone except the standalone server (if applicable)
4351  if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
4352  // reset total offset
4353  total_offset = 0;
4354 
4355  // highlight him if he's the host
4357  if(Multi_create_plist_select_id == Net_players[idx].player_id){
4359 
4360  // be sure to blit the correct team button
4361  Multi_create_buttons[gr_screen.res][MC_TEAM1].button.draw_forced(2);
4362  } else {
4364  }
4365  } else {
4366  if(Multi_create_plist_select_id == Net_players[idx].player_id){
4368 
4369  // be sure to blit the correct team button
4370  Multi_create_buttons[gr_screen.res][MC_TEAM1].button.draw_forced(2);
4371  } else {
4373  }
4374  }
4375 
4376  // optionally draw his CD status
4379  gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1,GR_RESIZE_MENU);
4380 
4381  total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4382  }
4383 
4384  // blit the red team indicator
4388  gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2, GR_RESIZE_MENU);
4389 
4390  total_offset += Multi_common_icon_dims[MICON_TEAM1_SELECT][0] + 1;
4391  }
4392  } else {
4393  if(Multi_common_icons[MICON_TEAM1] != -1){
4395  gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2, GR_RESIZE_MENU);
4396 
4397  total_offset += Multi_common_icon_dims[MICON_TEAM1][0] + 1;
4398  }
4399  }
4400 
4401  // make sure the string will fit
4402  strcpy_s(str,Net_players[idx].m_player->callsign);
4404  strcat_s(str,XSTR("(O)",787));
4405  }
4406  gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4407 
4408  // display him in the correct half of the list depending on his team
4409  gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str,GR_RESIZE_MENU);
4410  y_start += line_height;
4411  }
4412  }
4413 }
4414 
4416 {
4417  if(Multi_create_list_start > 0){
4418  Multi_create_list_start--;
4419 
4421  } else {
4423  }
4424 }
4425 
4427 {
4428  if((Multi_create_list_count - Multi_create_list_start) > Multi_create_list_max_display[gr_screen.res]){
4429  Multi_create_list_start++;
4430 
4432  } else {
4434  }
4435 }
4436 
4437 // gets a list of multiplayer misisons
4439 {
4440  char *fname, mission_name[NAME_LENGTH+1];
4441  char wild_card[10];
4442  int file_count, idx;
4443  char **file_list = NULL;
4444 
4445  Multi_create_mission_list.clear();
4446 
4447  memset( wild_card, 0, sizeof(wild_card) );
4448  snprintf(wild_card, sizeof(wild_card) - 1, "*%s", FS_MISSION_FILE_EXT);
4449 
4450  file_list = (char**) vm_malloc( sizeof(char*) * 1024 );
4451 
4452  if (file_list == NULL) {
4453  return;
4454  }
4455 
4456  file_count = cf_get_file_list(1024, file_list, CF_TYPE_MISSIONS, wild_card);
4457 
4458  // maybe create a standalone dialog
4460  std_create_gen_dialog("Loading missions");
4461  std_gen_set_text("Mission:", 1);
4462  }
4463 
4464  for (idx = 0; idx < file_count; idx++) {
4465  int flags,max_players;
4466  char *filename;
4467  uint m_respawn;
4468 
4469  fname = file_list[idx];
4470 
4471  // tack on any necessary file extension
4472  filename = cf_add_ext( fname, FS_MISSION_FILE_EXT );
4473 
4474  if (Game_mode & GM_STANDALONE_SERVER) {
4475  std_gen_set_text(filename, 2);
4476  }
4477 
4478  flags = mission_parse_is_multi(filename, mission_name);
4479 
4480  // if the mission is a multiplayer mission, then add it to the mission list
4481  if ( flags ) {
4482  max_players = mission_parse_get_multi_mission_info( filename );
4483  m_respawn = The_mission.num_respawns;
4484 
4485  multi_create_info mcip;
4486 
4487  strcpy_s(mcip.filename, filename );
4488  strcpy_s(mcip.name, mission_name );
4489  mcip.flags = flags;
4490  mcip.respawn = m_respawn;
4491  mcip.max_players = (ubyte)max_players;
4492 
4493  // get any additional information for possibly builtin missions
4495  if(fb != NULL){
4496  }
4497 
4498  Multi_create_mission_list.push_back( mcip );
4499  }
4500  }
4501 
4502  if (file_list) {
4503  for (idx = 0; idx < file_count; idx++) {
4504  if (file_list[idx]) {
4505  vm_free(file_list[idx]);
4506  file_list[idx] = NULL;
4507  }
4508  }
4509 
4510  vm_free(file_list);
4511  file_list = NULL;
4512  }
4513 
4514  Multi_create_slider.set_numberItems(int(Multi_create_mission_list.size()) > Multi_create_list_max_display[gr_screen.res] ? int(Multi_create_mission_list.size())-Multi_create_list_max_display[gr_screen.res] : 0);
4515 
4516  // maybe create a standalone dialog
4517  if (Game_mode & GM_STANDALONE_SERVER) {
4519  }
4520 }
4521 
4523 {
4524  char *fname;
4525  int idx, file_count;
4526  int campaign_type,max_players;
4527  char title[255];
4528  char wild_card[10];
4529  char **file_list = NULL;
4530 
4531  Multi_create_campaign_list.clear();
4532 
4533  memset( wild_card, 0, sizeof(wild_card) );
4534  snprintf(wild_card, sizeof(wild_card) - 1, "*%s", FS_CAMPAIGN_FILE_EXT);
4535 
4536  file_list = (char**) vm_malloc( sizeof(char*) * 1024 );
4537 
4538  if (file_list == NULL) {
4539  return;
4540  }
4541 
4542  file_count = cf_get_file_list(1024, file_list, CF_TYPE_MISSIONS, wild_card);
4543 
4544  // maybe create a standalone dialog
4546  std_create_gen_dialog("Loading campaigns");
4547  std_gen_set_text("Campaign:", 1);
4548  }
4549 
4550  for (idx = 0; idx < file_count; idx++) {
4551  int flags;
4552  char *filename, name[NAME_LENGTH];
4553 
4554  fname = file_list[idx];
4555 
4556  // tack on any necessary file extension
4557  filename = cf_add_ext( fname, FS_CAMPAIGN_FILE_EXT );
4558 
4559  if (Game_mode & GM_STANDALONE_SERVER) {
4560  std_gen_set_text(filename, 2);
4561  }
4562 
4563  // if the campaign is a multiplayer campaign, then add the data to the campaign list items
4564  flags = mission_campaign_parse_is_multi( filename, name );
4565  if( flags != CAMPAIGN_TYPE_SINGLE && mission_campaign_get_info(filename,title,&campaign_type,&max_players)) {
4566  multi_create_info mcip;
4567 
4568  strcpy_s(mcip.filename, filename );
4569  strcpy_s(mcip.name, name );
4570 
4571  // setup various flags
4572  if ( flags == CAMPAIGN_TYPE_MULTI_COOP ){
4574  } else if ( flags == CAMPAIGN_TYPE_MULTI_TEAMS ) {
4576  } else {
4577  Int3(); // bogus campaign multi type -- find allender
4578  }
4579 
4580  // 0 respawns for campaign files (should be contained within the mission files themselves)
4581  mcip.respawn = 0;
4582 
4583  // 0 max players for campaign files
4584  mcip.max_players = (unsigned char)max_players;
4585 
4586  // get any additional information for possibly builtin missions
4588  if(fb != NULL){
4589  }
4590 
4591  Multi_create_campaign_list.push_back( mcip );
4592  }
4593  }
4594 
4595  if (file_list) {
4596  for (idx = 0; idx < file_count; idx++) {
4597  if (file_list[idx]) {
4598  vm_free(file_list[idx]);
4599  file_list[idx] = NULL;
4600  }
4601  }
4602 
4603  vm_free(file_list);
4604  file_list = NULL;
4605  }
4606 
4607  // maybe create a standalone dialog
4608  if (Game_mode & GM_STANDALONE_SERVER) {
4610  }
4611 }
4612 
4614 {
4615  int idx;
4616  int start_index,stop_index;
4617  int line_height = gr_get_font_height() + 1;
4618  char selected_name[255];
4619 
4620  // bail early if there aren't any selectable items
4621  if(Multi_create_list_count == 0){
4622  return;
4623  }
4624 
4625  // first check to see if the user has clicked on an item
4626  if(Multi_create_list_select_button.pressed()){
4627  int y,item;
4628  Multi_create_list_select_button.get_mouse_pos(NULL,&y);
4629  item = (y / line_height);
4630 
4631  // make sure we are selectedin valid indices
4632  if((item < Multi_create_list_max_display[gr_screen.res]) && (item >= 0)){
4633  item += Multi_create_list_start;
4634 
4635  if(item < Multi_create_list_count){
4638  }
4639  }
4640  }
4641 
4642  // bail early if we don't have a start position
4643  if(Multi_create_list_start == -1){
4644  return;
4645  }
4646 
4647  // display the list of individual campaigns/missions
4648  int count = 0;
4649  int y_start = Mc_list_coords[gr_screen.res][MC_Y_COORD];
4650 
4651  start_index = multi_create_select_to_index(Multi_create_list_start);
4652  stop_index = (Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS) ? Multi_create_mission_list.size() : Multi_create_campaign_list.size();
4653 
4654  for (idx = start_index; idx < stop_index; idx++) {
4655  multi_create_info *mcip;
4656 
4657  if (Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS) {
4658  mcip = &Multi_create_mission_list[idx];
4659  } else {
4660  mcip = &Multi_create_campaign_list[idx];
4661  }
4662 
4663  // see if we should drop out
4664  if(count == Multi_create_list_max_display[gr_screen.res]){
4665  break;
4666  }
4667 
4668  // see if we should filter out this mission
4669  if ( !(mcip->flags & Multi_create_filter) ) {
4670  continue;
4671  }
4672 
4673  // highlight the selected item
4674  multi_create_select_to_filename(Multi_create_list_select, selected_name);
4675 
4676  if ( !strcmp(selected_name, mcip->filename) ) {
4678  } else {
4680  }
4681 
4682  // draw the type icon
4683  multi_create_list_blit_icons(idx, y_start);
4684 
4685  // force fit the mission name string
4686  strcpy_s(selected_name, mcip->name);
4687  gr_force_fit_string(selected_name, 255, Mc_column1_w[gr_screen.res]);
4688  gr_string(Mc_mission_name_x[gr_screen.res], y_start, selected_name, GR_RESIZE_MENU);
4689 
4690  // draw the max players if in mission mode
4691  sprintf(selected_name, "%d", (int)mcip->max_players);
4692  gr_string(Mc_mission_count_x[gr_screen.res], y_start, selected_name, GR_RESIZE_MENU);
4693 
4694  // force fit the mission filename string
4695  strcpy_s(selected_name, mcip->filename);
4696  gr_force_fit_string(selected_name, 255, Mc_column3_w[gr_screen.res]);
4697  gr_string(Mc_mission_fname_x[gr_screen.res], y_start, selected_name, GR_RESIZE_MENU);
4698 
4699  y_start += line_height;
4700  count++;
4701  }
4702 }
4703 
4704 // takes care of stuff like changing indices around and setting up the netgame structure
4706 {
4707  int abs_index,campaign_type,max_players;
4708  char title[NAME_LENGTH+1];
4709  netgame_info ng_temp;
4710  netgame_info *ng;
4711  multi_create_info *mcip = NULL;
4712 
4713  char *campaign_desc;
4714 
4715  // if not on the standalone server
4717  ng = &Netgame;
4718  }
4719  // on the standalone
4720  else {
4721  memset(&ng_temp,0,sizeof(netgame_info));
4722  ng = &ng_temp;
4723  }
4724 
4725  if ( n != Multi_create_list_select ) {
4726  // check to see if this is a valid index, and bail if it is not
4727  abs_index = multi_create_select_to_index(n);
4728  if(abs_index == -1){
4729  return;
4730  }
4731 
4732  Multi_create_list_select = n;
4733 
4734  // set the mission name
4735  if (Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS) {
4737  } else {
4739  }
4740 
4741  // make sure the netgame type is properly set
4742  int old_type = Netgame.type_flags;
4743  abs_index = multi_create_select_to_index(n);
4744 
4745  if (abs_index != -1) {
4746  if (Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS) {
4747  mcip = &Multi_create_mission_list[abs_index];
4748  } else {
4749  mcip = &Multi_create_campaign_list[abs_index];
4750  }
4751 
4752  if (mcip->flags & MISSION_TYPE_MULTI_TEAMS) {
4753  // if we're in squad war mode, leave it as squad war
4754  if(old_type & NG_TYPE_SW){
4755  ng->type_flags = NG_TYPE_SW;
4756  } else {
4757  ng->type_flags = NG_TYPE_TVT;
4758  }
4759  } else if (mcip->flags & MISSION_TYPE_MULTI_COOP) {
4760  ng->type_flags = NG_TYPE_COOP;
4761  } else if (mcip->flags & MISSION_TYPE_MULTI_DOGFIGHT) {
4763  }
4764  }
4765 
4766  // if we're no longer in a TvT game, just uncheck the squadwar checkbox
4767  if(!(ng->type_flags & NG_TYPE_TEAM)){
4768  Multi_create_sw_checkbox.set_state(0);
4769  }
4770 
4771  // if we switched from something else to team vs. team mode, do some special processing
4772  if((ng->type_flags & NG_TYPE_TEAM) && (ng->type_flags != old_type) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
4773  multi_team_reset();
4774  }
4775 
4776  switch(Multi_create_list_mode){
4778  // don't forget to update the info box window thingie
4780  ship_level_init(); // mwa -- 10/15/97. Call this function to reset number of ships in mission
4782 
4783  Assert(ng->max_players > 0);
4785 
4786  // set the information area text
4788  }
4789  // if we're on the standalone, send a request for the description
4790  else {
4793  }
4794 
4795  // set the respawns as appropriate
4796  if (mcip) {
4797  if(Netgame.options.respawn <= mcip->respawn){
4799  nprintf(("Network", "Using netgame options for respawn count (%d %d)\n", Netgame.options.respawn, mcip->respawn));
4800  } else {
4801  ng->respawn = mcip->respawn;
4802  nprintf(("Network", "Using mission settings for respawn count (%d %d)\n", Netgame.options.respawn, mcip->respawn));
4803  }
4804  }
4805  break;
4807  // if not on the standalone server
4809  // get the campaign info
4810  memset(title,0,NAME_LENGTH+1);
4811  if(!mission_campaign_get_info(ng->campaign_name,title,&campaign_type,&max_players, &campaign_desc)) {
4812  memset(ng->campaign_name,0,NAME_LENGTH+1);
4813  ng->max_players = 0;
4814  }
4815  // if we successfully got the # of players
4816  else {
4817  memset(ng->title,0,NAME_LENGTH+1);
4818  strcpy_s(ng->title,title);
4819  ng->max_players = max_players;
4820  }
4821 
4822  nprintf(("Network","MC MAX PLAYERS : %d\n",ng->max_players));
4823 
4824  // set the information area text
4825  // multi_common_set_text(ng->title);
4826  if (campaign_desc != NULL)
4827  {
4828  multi_common_set_text(campaign_desc);
4829  }
4830  else
4831  {
4833  }
4834  }
4835  // if on the standalone server, send a request for the description
4836  else {
4837  // no descriptions currently kept for campaigns
4838  }
4839 
4840  // netgame respawns are always 0 for campaigns (until the first mission is loaded)
4841  ng->respawn = 0;
4842  break;
4843  }
4844 
4846  // update players
4848 
4849  // update all machines about stuff like respawns, etc.
4851 
4852  // send update to FS2NetD as well
4853  if (MULTI_IS_TRACKER_GAME) {
4855  }
4856  } else {
4857  multi_options_update_mission(ng, Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ? 1 : 0);
4858  }
4859  }
4860 }
4861 
4862 void multi_create_list_blit_icons(int list_index, int y_start)
4863 {
4864  multi_create_info *mcip;
4865  fs_builtin_mission *fb;
4866  int max_index;
4867 
4868  // get a pointer to the list item
4869  max_index = (Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS) ? Multi_create_mission_list.size() - 1 : Multi_create_campaign_list.size() - 1;
4870 
4871  if ( (list_index < 0) || (list_index > max_index) ) {
4872  return;
4873  }
4874 
4875  if (Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS) {
4876  mcip = &Multi_create_mission_list[list_index];
4877  } else {
4878  mcip = &Multi_create_campaign_list[list_index];
4879  }
4880 
4881  // blit the multiplayer type icons
4882  if(mcip->flags & MISSION_TYPE_MULTI_COOP){
4883  if(Multi_common_icons[MICON_COOP] >= 0){
4885  gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD],GR_RESIZE_MENU);
4886  }
4887  } else if(mcip->flags & MISSION_TYPE_MULTI_TEAMS){
4888  if(Multi_common_icons[MICON_TVT] >= 0){
4890  gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD],GR_RESIZE_MENU);
4891  }
4892  } else if(mcip->flags & MISSION_TYPE_MULTI_DOGFIGHT){
4895  gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD],GR_RESIZE_MENU);
4896  }
4897  }
4898 
4899  // if its a valid mission, blit the valid mission icon
4901  if(Multi_common_icons[MICON_VALID] >= 0){
4903  gr_bitmap(Mc_icon_valid_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_valid_coords[gr_screen.res][MC_Y_COORD],GR_RESIZE_MENU);
4904  }
4905  }
4906 
4907  // now see if its a builtin mission
4908  fb = game_find_builtin_mission(mcip->filename);
4909  // if the mission is from volition, blit the volition icon
4910  if((fb != NULL) && (fb->flags & FSB_FROM_VOLITION)){
4913  gr_bitmap(Mc_icon_volition_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_volition_coords[gr_screen.res][MC_Y_COORD],GR_RESIZE_MENU);
4914  }
4915  }
4916 }
4917 
4919 {
4920  char selected_name[255];
4921  int start_campaign = 0;
4922  int popup_choice = 0;
4923 
4924  // make sure all players have finished joining
4926  popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("Please wait until all clients have finished joining",788));
4928  return;
4929  } else {
4931  }
4932 
4933  // do single mission stuff
4934  switch(Multi_create_list_mode){